diff --git a/docs/superpowers/specs/2026-05-17-agent-office-grid-redesign-design.md b/docs/superpowers/specs/2026-05-17-agent-office-grid-redesign-design.md new file mode 100644 index 0000000..b1e8866 --- /dev/null +++ b/docs/superpowers/specs/2026-05-17-agent-office-grid-redesign-design.md @@ -0,0 +1,345 @@ +# 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 +