Files
web-page/docs/superpowers/specs/2026-05-17-agent-office-grid-redesign-design.md
gahusb ce09f804b6 docs(agent-office): 3x3 grid redesign design spec
Replace pixel-office canvas with 3x3 agent image grid.
- 5 active agents (stock/music/insta/realestate/lotto) + 4 placeholders
- Drop blog from AGENT_META, insta replaces it
- New assets dir: src/pages/agent-office/assets/agents/
- Remove canvas/ dir + useOfficeCanvas + office-map.json
- Keep useAgentManager (WebSocket) + 4-tab SidePanel

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 20:26:57 +09:00

346 lines
13 KiB
Markdown

# Agent Office 그리드 재설계 — Design Spec
**Date:** 2026-05-17
**Author:** CEO (with Claude)
**Target:** `web-ui` `/agent-office` 페이지
---
## 1. 배경 & 목적
현재 `/agent-office` 페이지는 픽셀 사무실 Canvas 위에서 5명의 에이전트 캐릭터가 무의미하게 걸어다니는 형태다. 시각적 즐거움은 있으나 정보 밀도가 낮고, 각 에이전트가 무슨 일을 하는지 한눈에 파악하기 어렵다.
이를 **3x3 그리드** 기반의 정보 중심 UI로 재설계한다. 왼편에 9개의 에이전트 이미지 카드를 배치하고, 카드 클릭 시 오른편 패널에서 해당 에이전트의 명령·태스크·토큰·로그를 확인한다.
---
## 2. 범위 (Scope)
### In scope
- `src/pages/agent-office/AgentOffice.jsx` 전면 재작성 (Canvas → Grid)
- 그리드 카드 컴포넌트 신규 작성
- `SidePanel.jsx` 헤더 부분 수정 (emoji → 이미지)
- `SidePanel.jsx``AGENT_META`에서 `blog` 제거, `insta` 추가
- TopBar 단순화 (theme/zoom 컨트롤 제거)
- Canvas 관련 파일/디렉토리 전체 삭제
- 이미지 에셋 디렉토리 신설
### Out of scope
- 백엔드 변경 (현재 백엔드의 `insta` 에이전트는 이미 등록 완료, 추가 작업 불필요)
- 새 에이전트 추가 (4개 placeholder는 "준비 중" 표시만)
- 4탭 컨텐츠 (Commands/Tasks/Tokens/Logs) 로직 수정
---
## 3. 에이전트 구성
### 실제 작동 5명
| ID | 표시명 | 색상 | 역할 요약 |
|----|--------|------|-----------|
| `stock` | 주식 트레이더 | `#4488cc` | 주식 매매·뉴스 분석·포트폴리오 |
| `music` | 음악 프로듀서 | `#44aa88` | AI 음악 생성 |
| `insta` | 인스타 큐레이터 | `#d97706` | 매일 09:30 뉴스 수집 → 키워드 추출 → AI 카드 10장 생성 → 텔레그램 푸시 |
| `realestate` | 청약 애널리스트 | `#c026d3` | 부동산 청약 매칭·자치구 5티어 분석 |
| `lotto` | 로또 큐레이터 | `#ef4444` | 로또 번호 추천·브리핑 |
> `blog`는 `insta`로 대체됨. 기존 `SidePanel.jsx`의 `AGENT_META`에서 `blog` 항목 삭제 + `insta` 추가.
### Placeholder 4개
- ID 없음 (그리드 슬롯 인덱스 6/7/8/9로만 식별)
- 모두 동일하게 `agent_undetermined.png` + "준비 중" 라벨
- 클릭 시 정적 안내 패널 노출
---
## 4. 디렉토리 & 파일 구조
### 신설 디렉토리
```
src/pages/agent-office/assets/agents/
├── agent_stock.png (사용자 제공)
├── agent_music.png (사용자 제공)
├── agent_insta.png (사용자 제공)
├── agent_realestate.png (사용자 제공)
├── agent_lotto.png (사용자 제공)
└── agent_undetermined.png (사용자 제공, 4 placeholder 공유)
```
### 파일명 규칙
`agent_{id}.png` 형식. `{id}`는 백엔드의 agent_id와 일치 (소문자, underscore).
### 권장 이미지 사양
- 정사각형 (예: 512x512)
- PNG (투명 배경 허용)
- 카드 표시 시 `object-fit: cover`로 정사각 크롭
### 삭제 대상
```
src/pages/agent-office/
├── canvas/ ← 전체 삭제
│ ├── themes.js
│ ├── FurnitureRenderer.js
│ ├── ProceduralSprite.js
│ ├── AgentSprite.js
│ ├── SpriteLoader.js
│ ├── OverlayRenderer.js
│ ├── Pathfinder.js
│ ├── OfficeRenderer.js
│ └── TileMap.js
├── hooks/
│ └── useOfficeCanvas.js ← 삭제
└── assets/
└── office-map.json ← 삭제
```
### 유지 대상
```
src/pages/agent-office/
├── AgentOffice.jsx ← 재작성
├── AgentOffice.css ← 재작성
├── hooks/
│ └── useAgentManager.js ← 그대로 (WebSocket 로직)
└── components/
├── TopBar.jsx ← 단순화 (theme/zoom 제거)
├── SidePanel.jsx ← 헤더 수정 + AGENT_META 갱신
├── CommandTab.jsx ← 그대로
├── TaskTab.jsx ← 그대로
├── TokenTab.jsx ← 그대로
└── LogTab.jsx ← 그대로
```
### 신규 컴포넌트
```
src/pages/agent-office/components/
├── AgentGrid.jsx ← 3x3 그리드 래퍼
├── AgentCard.jsx ← 카드 1개 (image + state dot + badge + name)
├── PlaceholderCard.jsx ← "준비 중" 카드
└── EmptyDetailPanel.jsx ← 초기 안내 / placeholder 클릭 시 안내
```
---
## 5. 레이아웃
### 전체 화면 구조
```
┌─────────────────────────────────────────────────────────────┐
│ TopBar (connected status only) │
├──────────────────────────────────┬──────────────────────────┤
│ │ │
│ AgentGrid (3x3) │ Right Panel │
│ ┌──────┬──────┬──────┐ │ │
│ │stock │music │insta │ │ ┌─ active 선택 시 ─┐ │
│ ├──────┼──────┼──────┤ │ │ SidePanel │ │
│ │realE │lotto │ ?? │ │ │ - 헤더(이미지+이름)│ │
│ ├──────┼──────┼──────┤ │ │ - 4 tabs │ │
│ │ ?? │ ?? │ ?? │ │ └──────────────────┘ │
│ └──────┴──────┴──────┘ │ │
│ │ ┌─ placeholder 선택 ─┐ │
│ │ │ "준비 중인 에이전트"│ │
│ │ └────────────────────┘ │
│ │ │
│ │ ┌─ 초기(미선택) ──────┐ │
│ │ │ "에이전트를 선택…" │ │
│ │ └────────────────────┘ │
└──────────────────────────────────┴──────────────────────────┘
```
### 그리드 슬롯 순서 (좌→우, 위→아래)
| Index | Slot |
|-------|------|
| 1 (행1·열1) | `stock` |
| 2 (행1·열2) | `music` |
| 3 (행1·열3) | `insta` |
| 4 (행2·열1) | `realestate` |
| 5 (행2·열2) | `lotto` |
| 6 (행2·열3) | placeholder |
| 7 (행3·열1) | placeholder |
| 8 (행3·열2) | placeholder |
| 9 (행3·열3) | placeholder |
### AgentCard 시각 구조
```
┌─────────────────────┐
│ ● state [③] │ ← 상태 dot(좌상, image 약간 위) + 알림 뱃지(우상)
│ ┌───────────────┐ │
│ │ │ │
│ │ agent_xxx │ │ ← 정사각 이미지 (object-fit: cover)
│ │ .png │ │
│ │ │ │
│ └───────────────┘ │
│ 주식 트레이더 │ ← display_name
└─────────────────────┘
```
#### 상태 dot
| state | color | 동작 |
|-------|-------|------|
| `idle` | `#6b7280` (회색) | 정적 |
| `working` | `#22c55e` (초록) | pulse 애니메이션 |
| `error` | `#ef4444` (빨강) | 정적 |
| `waiting_approval` | `#f59e0b` (주황) | pulse |
| `break` | `#94a3b8` (밝은 회색) | 정적 |
상태 dot은 카드의 좌상단, 이미지보다 약간 위쪽에 위치 (이미지 영역 바깥 또는 모서리 살짝 걸침).
#### 알림 뱃지
- `notifications[agentId] > 0`일 때만 우상단에 표시
- 빨강 배경에 흰 숫자 (count > 9면 "9+")
- 카드 클릭 시 자동으로 0으로 리셋 (`clearNotifications` 호출 — 기존 로직 재사용)
---
## 6. 데이터 플로우
```
useAgentManager (그대로 유지)
├── WebSocket /api/agent-office/ws
├── agents: { [id]: { state, detail, task_id } }
├── notifications: { [id]: count }
├── pendingTasks: [...]
├── connected: bool
└── refreshTrigger: number
AgentOffice.jsx
├── agents → AgentGrid에 전달 → 각 AgentCard가 state로 dot 색상 결정
├── notifications → 각 AgentCard가 badge 표시
├── selectedAgent (local state): string | null | "placeholder"
└── 카드 클릭 시 setSelectedAgent + clearNotifications
Right Panel 분기
├── selectedAgent === null → EmptyDetailPanel (초기 안내)
├── selectedAgent === "placeholder"→ EmptyDetailPanel ("준비 중" 메시지)
└── selectedAgent ∈ active 5명 → SidePanel (4탭, 기존 로직)
```
---
## 7. SidePanel 수정 사항
### AGENT_META 갱신
```js
// src/pages/agent-office/components/SidePanel.jsx
import stockImg from '../assets/agents/agent_stock.png';
import musicImg from '../assets/agents/agent_music.png';
import instaImg from '../assets/agents/agent_insta.png';
import realestateImg from '../assets/agents/agent_realestate.png';
import lottoImg from '../assets/agents/agent_lotto.png';
const AGENT_META = {
stock: { displayName: '주식 트레이더', image: stockImg, color: '#4488cc' },
music: { displayName: '음악 프로듀서', image: musicImg, color: '#44aa88' },
insta: { displayName: '인스타 큐레이터', image: instaImg, color: '#d97706' },
realestate: { displayName: '청약 애널리스트', image: realestateImg, color: '#c026d3' },
lotto: { displayName: '로또 큐레이터', image: lottoImg, color: '#ef4444' }
};
// blog 항목 삭제
```
### 헤더 시각 변경
```jsx
// 변경 전: emoji icon
<div className="ao-sidepanel-icon" style={{ background: meta.color }}>
{meta.emoji}
</div>
// 변경 후: 이미지
<div className="ao-sidepanel-icon" style={{ borderColor: meta.color }}>
<img src={meta.image} alt={meta.displayName} />
</div>
```
4탭(Commands/Tasks/Tokens/Logs) 본체 로직은 손대지 않음.
---
## 8. CSS 토큰 (제안)
```css
:root {
--ao-bg: #0f172a;
--ao-card-bg: #1e293b;
--ao-card-border: #334155;
--ao-card-border-active: #60a5fa;
--ao-text: #e2e8f0;
--ao-text-muted: #94a3b8;
--ao-grid-gap: 16px;
--ao-card-radius: 12px;
}
.ao-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--ao-grid-gap);
}
.ao-card {
aspect-ratio: 1 / 1.15; /* 이미지 정사각 + 이름줄 */
background: var(--ao-card-bg);
border: 1px solid var(--ao-card-border);
border-radius: var(--ao-card-radius);
cursor: pointer;
transition: transform 120ms ease, border-color 120ms ease;
}
.ao-card:hover { transform: translateY(-2px); }
.ao-card.active { border-color: var(--ao-card-border-active); }
.ao-card.placeholder { opacity: 0.55; cursor: pointer; }
```
반응형: 모바일에서는 `grid-template-columns: repeat(2, 1fr)` 또는 `repeat(1, 1fr)`로 축소.
---
## 9. 에러 처리 / Edge Cases
| 상황 | 동작 |
|------|------|
| 이미지 로드 실패 | `<img onError>`로 단색 배경 + 첫 글자 fallback |
| WebSocket 끊김 | TopBar에 disconnected 표시. 카드는 마지막 상태 유지 (회색 처리 안 함 — 기존 동작 유지) |
| `agents[id]` 미존재 | dot 회색(`idle`), 정상 표시 |
| placeholder 클릭 | 우측 패널만 변경, WebSocket 호출/clearNotifications 호출 없음 |
---
## 10. 테스트 계획
- [ ] 6개 이미지 파일이 디렉토리에 존재할 때 그리드 정상 렌더링
- [ ] 이미지 누락 시 fallback 표시
- [ ] WebSocket으로 `agent_state` 수신 시 dot 색상 변경
- [ ] `notification` 수신 시 뱃지 표시, 카드 클릭 시 0으로 리셋
- [ ] active 5명 클릭 → SidePanel 4탭 표시 (기존 동작 유지)
- [ ] placeholder 4슬롯 클릭 → "준비 중" 패널
- [ ] TopBar의 connected/disconnected 표시 정상
- [ ] Canvas 잔재(파일 import 누락 등) 없음 — `npm run build` 통과
- [ ] 모바일 뷰(<768px) 그리드 축소 정상
---
## 11. 이행 절차 (사용자 작업 포함)
1. **사용자**: `src/pages/agent-office/assets/agents/` 디렉토리에 6개 PNG 파일 배치
2. **Claude (구현 단계)**: writing-plans 스킬로 단계별 작업 계획 작성
3. 구현·삭제·테스트 후 commit
4. NAS 배포는 별도 (`npm run release:nas`)
---
## 12. 향후 확장
- 9번째 active 에이전트 채용 시: 이미지 추가 + `AGENT_META` 갱신 + 슬롯 인덱스 매핑 변경
- 그리드 자동 정렬(상태별/우선순위별 sort) — 현재는 정적 배치
- 카드 hover 시 미니 프리뷰 (최근 활동 1줄 요약) — 추후 검토