전체 화면 캔버스 중심 UX, BFS 배회, 3테마, 사이드 패널 4탭 구조 설계. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
498 lines
18 KiB
Markdown
498 lines
18 KiB
Markdown
# Agent Office v2 — Pixel Office UX 대규모 업데이트 설계
|
||
|
||
> 참고 프로젝트: `pixel-agents` (VS Code 확장, React 19 + Canvas 2D)
|
||
> 대상: `web-ui/src/pages/agent-office/` (프론트엔드) + `web-backend/agent-office/` (백엔드)
|
||
|
||
---
|
||
|
||
## 1. 목표
|
||
|
||
기존 대시보드 칼럼 중심 UI를 **전체 화면 픽셀 오피스** 중심으로 전환하여, "가상 오피스를 사용한다"는 몰입감을 제공한다.
|
||
|
||
### 핵심 변경
|
||
|
||
- 캔버스가 메인 화면을 차지하고, 에이전트 클릭 시 사이드 패널로 상세 정보 표시
|
||
- BFS 경로 탐색 + 풀 배회 시스템으로 에이전트에 생동감 부여
|
||
- 3가지 오피스 테마 프리셋 (Modern / Retro / Minimal)
|
||
- 캐릭터 프로시저럴 고도화 + 스프라이트 로더 설계 (점진적 전환)
|
||
|
||
### 변경하지 않는 것
|
||
|
||
- 백엔드 FSM 5상태 (`idle`, `working`, `waiting`, `reporting`, `break`)
|
||
- WebSocket 프로토콜 메시지 타입 (init, agent_state, task_complete, agent_move, notification, command_result)
|
||
- REST API 엔드포인트
|
||
- 텔레그램 봇 연동
|
||
|
||
---
|
||
|
||
## 2. 화면 구성
|
||
|
||
### 2.1 데스크톱 레이아웃
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┬──────────────┐
|
||
│ [Agent Office] ● Connected [Theme ▾] [Zoom] │ │
|
||
├──────────────────────────────────────────────────┤ Side Panel │
|
||
│ │ 320px │
|
||
│ │ │
|
||
│ Pixel Office Canvas │ [Agent hdr] │
|
||
│ (flex: 1, 전체 높이) │ [Tabs····] │
|
||
│ │ [Content ] │
|
||
│ - 에이전트 클릭 → 패널 열림 │ [·········] │
|
||
│ - 빈 공간 클릭 → 패널 닫힘 │ │
|
||
│ │ │
|
||
└──────────────────────────────────────────────────┴──────────────┘
|
||
```
|
||
|
||
- **상단 바**: 타이틀, WebSocket 연결 상태(●), 테마 드롭다운, 줌 컨트롤 (1x~4x)
|
||
- **캔버스**: `flex: 1`로 남은 공간 전체 차지, `imageSmoothingEnabled = false`
|
||
- **사이드 패널**: 320px 고정폭, 에이전트 클릭 시 슬라이드 인, X 버튼 또는 빈 공간 클릭으로 닫힘
|
||
- **패널 닫힘 시**: 캔버스가 전체 너비로 확장
|
||
|
||
### 2.2 모바일 레이아웃 (< 768px)
|
||
|
||
```
|
||
┌──────────────────────────┐
|
||
│ [≡] Agent Office ● Conn │
|
||
├──────────────────────────┤
|
||
│ │
|
||
│ Pixel Office Canvas │
|
||
│ (전체 화면) │
|
||
│ 핀치 줌 + 패닝 │
|
||
│ │
|
||
│ │
|
||
├──────────────────────────┤ ← 바텀 시트 (드래그)
|
||
│ [Agent Header] │
|
||
│ [Tabs: Cmd|Task|Tok|Log]│
|
||
│ [Content area] │
|
||
└──────────────────────────┘
|
||
```
|
||
|
||
- 캔버스: 전체 화면, 터치 핀치 줌/패닝
|
||
- 사이드 패널 → 바텀 시트 (에이전트 탭 시 올라옴, 아래로 드래그 시 닫힘)
|
||
- 상단 바: 햄버거 메뉴로 테마/줌 접기
|
||
|
||
---
|
||
|
||
## 3. 사이드 패널 구조
|
||
|
||
### 3.1 헤더
|
||
|
||
```
|
||
┌─────────────────────────────┐
|
||
│ [🎵 32x32] 음악 프로듀서 │
|
||
│ ● working - ... │
|
||
└─────────────────────────────┘
|
||
```
|
||
|
||
- 에이전트 아이콘 (emoji 기반, 32x32 색상 배경)
|
||
- display_name + 현재 상태 + state_detail
|
||
|
||
### 3.2 탭 구성
|
||
|
||
| 탭 | 내용 |
|
||
|----|------|
|
||
| **Commands** (기본) | Quick Action 버튼 (에이전트별 고유), Custom Command 입력, Approval UI (waiting 상태 시) |
|
||
| **Tasks** | 최근 작업 이력 (상태 배지, 타임스탬프, 결과 펼치기) |
|
||
| **Tokens** | 일간/주간 토큰 사용량 차트, 캐시 히트율 |
|
||
| **Logs** | 에이전트 로그 스트림 (level별 색상, 자동 스크롤) |
|
||
|
||
### 3.3 에이전트별 Quick Actions
|
||
|
||
| 에이전트 | 버튼 |
|
||
|---------|------|
|
||
| Stock | Fetch News, Add Alert, Test Telegram |
|
||
| Music | Compose, Check Credits |
|
||
| Blog | Research, Add Keyword, List Keywords |
|
||
| Realestate | Fetch Matches, Dashboard |
|
||
| Lotto | Curate Now, Status |
|
||
|
||
---
|
||
|
||
## 4. 캔버스 엔진
|
||
|
||
### 4.1 타일맵
|
||
|
||
- **그리드**: 32 × 20 타일 (기존 20×14에서 확장)
|
||
- **타일 크기**: 32px × 32px (기본), 줌에 따라 스케일
|
||
- **타일 타입**: VOID(0), FLOOR(1), WALL(2), FURNITURE(3)
|
||
- **렌더링 순서**: 바닥 → 벽 → 가구 → 에이전트 (Y좌표 Z-sorting) → 오버레이
|
||
|
||
### 4.2 오피스 레이아웃 (고정)
|
||
|
||
```
|
||
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW (W=Wall)
|
||
W..............................W
|
||
W...[Stock]...[Music]..........W
|
||
W...desk+mon..desk+inst........W
|
||
W..............................W
|
||
W...[Blog]....[RE]....[Lotto]..W
|
||
W...desk+mon..desk+mon.desk+monW
|
||
W..............................W
|
||
W..............................W
|
||
W..........[Meeting]...........W
|
||
W..........table 4x2...........W
|
||
W..............................W
|
||
W..............................W
|
||
W....[Coffee]...[Sofa]........W
|
||
W....machine....couch.........W
|
||
W..............................W
|
||
W...[Plants]......[Bookshelf]..W
|
||
W..............................W
|
||
W..............................W
|
||
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
|
||
```
|
||
|
||
- 각 에이전트 구역에 테마별 소품 (Stock: 모니터 3대, Music: 악기, Blog: 서류 등)
|
||
- 중앙: 회의 테이블 (4x2 타일)
|
||
- 하단: 휴게실 구역 (커피 머신 + 소파)
|
||
- waypoint 정의: `desk_stock`, `desk_music`, `desk_blog`, `desk_realestate`, `desk_lotto`, `meeting`, `break_room`, `coffee`
|
||
|
||
### 4.3 줌 & 패닝
|
||
|
||
- 줌 레벨: 1x, 2x, 3x, 4x (정수 배율만, 픽셀 선명도 유지)
|
||
- 데스크톱: 마우스 휠 줌, 드래그 패닝
|
||
- 모바일: 핀치 줌, 터치 패닝
|
||
- 기본값: 캔버스 크기에 맞춰 자동 fit
|
||
|
||
### 4.4 게임 루프
|
||
|
||
```javascript
|
||
function gameLoop(timestamp) {
|
||
const dt = (timestamp - lastTime) / 1000;
|
||
lastTime = timestamp;
|
||
|
||
update(dt); // 에이전트 이동, 애니메이션 프레임 업데이트
|
||
render(); // 타일맵 → 가구 → 에이전트(Y-sort) → 오버레이
|
||
|
||
requestAnimationFrame(gameLoop);
|
||
}
|
||
```
|
||
|
||
- 60fps requestAnimationFrame
|
||
- `imageSmoothingEnabled = false` (픽셀 선명도)
|
||
- devicePixelRatio 반영
|
||
|
||
---
|
||
|
||
## 5. 에이전트 캐릭터 시스템
|
||
|
||
### 5.1 프로시저럴 렌더링 (Phase 1)
|
||
|
||
- 해상도: 16 × 32px (기존 8×16에서 2배 확대)
|
||
- 에이전트별 고유 색상 (기존 유지)
|
||
- 애니메이션 프레임:
|
||
|
||
| 상태 | 프레임 수 | 속도 | 설명 |
|
||
|------|----------|------|------|
|
||
| idle | 2 | 0.8s/frame | 미세 움직임 (숨쉬기) |
|
||
| walk | 4 | 0.15s/frame | 걷기 사이클 [0,1,2,1] |
|
||
| type | 2 | 0.3s/frame | 타이핑 (팔 움직임) |
|
||
| wait | 2 | 0.5s/frame | 좌우 흔들림 (wobble) |
|
||
| break | 2 | 1.0s/frame | 커피 마시기 / 졸기 |
|
||
|
||
- 4방향 스프라이트: DOWN, UP, RIGHT, LEFT (LEFT = RIGHT 좌우반전)
|
||
|
||
### 5.2 스프라이트 로더 (Phase 2 준비)
|
||
|
||
```javascript
|
||
class SpriteLoader {
|
||
constructor() {
|
||
this.sprites = new Map(); // agent_id → spritesheet Image
|
||
this.fallback = 'procedural';
|
||
}
|
||
|
||
async load(agentId, sheetUrl) { /* PNG 로드 */ }
|
||
|
||
draw(ctx, agentId, state, direction, frame, x, y) {
|
||
if (this.sprites.has(agentId)) {
|
||
// 스프라이트시트에서 프레임 추출하여 그리기
|
||
} else {
|
||
// 프로시저럴 폴백
|
||
ProceduralSprite.draw(ctx, agentId, state, direction, frame, x, y);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
- 스프라이트시트 규격: 각 프레임 16×32px, 가로로 프레임 나열
|
||
- 행: 방향 (DOWN/UP/RIGHT), 열: 상태별 프레임
|
||
- PNG 없으면 프로시저럴 폴백 → 에셋 제작 전에도 완전 동작
|
||
|
||
---
|
||
|
||
## 6. 이동 시스템
|
||
|
||
### 6.1 BFS 경로 탐색
|
||
|
||
```javascript
|
||
function findPath(grid, start, goal) {
|
||
// 4방향 BFS (상하좌우, 대각선 없음)
|
||
// blocked 타일(가구, 벽) 회피
|
||
// 반환: [{col, row}, ...] 경로 배열
|
||
}
|
||
```
|
||
|
||
- 가구 footprint → `blocked[]` 배열로 타일 마킹
|
||
- 의자/책상 뒤 타일은 walkable (backgroundTiles 개념)
|
||
- 경로 없으면 제자리 유지
|
||
|
||
### 6.2 이동 파라미터
|
||
|
||
| 파라미터 | 값 | 설명 |
|
||
|---------|-----|------|
|
||
| WALK_SPEED | 48 px/sec | pixel-agents 참고 |
|
||
| moveProgress | 0~1 | 현재 타일 → 다음 타일 선형 보간 |
|
||
| direction | DOWN/UP/RIGHT/LEFT | 이동 방향 → 스프라이트 방향 결정 |
|
||
|
||
### 6.3 배회 로직 (idle 상태)
|
||
|
||
```
|
||
idle 진입
|
||
→ 3~8초 대기 (seatTimer)
|
||
→ 자리에서 일어남
|
||
→ 인접 floor 타일로 랜덤 이동
|
||
→ 3~6회 반복 (wanderCount)
|
||
→ 자리로 BFS 복귀
|
||
→ 2~20초 자리에서 휴식 (restTimer)
|
||
→ 반복
|
||
```
|
||
|
||
### 6.4 상태 전환 시 이동 시퀀스
|
||
|
||
| 전환 | 동작 |
|
||
|------|------|
|
||
| `* → working` | 배회 중단, 자기 책상으로 BFS 이동 → 도착 후 type 애니메이션 |
|
||
| `* → waiting` | 자기 책상에서 wobble 애니메이션 + 말풍선 |
|
||
| `* → reporting` | 자기 책상에서 빠른 type 애니메이션 |
|
||
| `idle (배회 중)` | 랜덤 floor 타일로 이동, wanderCount 소진 시 복귀 |
|
||
| `* → break` | 휴게실(break_room/coffee) waypoint로 BFS 이동 → break 애니메이션 |
|
||
| `break → idle` | 자기 책상으로 BFS 이동 → idle 루프 시작 |
|
||
|
||
---
|
||
|
||
## 7. 오버레이 시스템
|
||
|
||
캔버스 위에 HTML이 아닌 Canvas 2D로 직접 렌더링.
|
||
|
||
### 7.1 항상 표시
|
||
|
||
- **이름 라벨**: 에이전트 아래, 에이전트 색상 텍스트, 12px
|
||
- **상태 배지**: 이름 아래, 배경색 + 텍스트 ("working", "idle", "break")
|
||
|
||
### 7.2 조건부 표시
|
||
|
||
- **말풍선**: `waiting` 상태에서만, 에이전트 위에 "승인 대기!" 텍스트
|
||
- 둥근 사각형 배경 (#fbbf24), 아래 삼각형 꼬리
|
||
- 2초 페이드인, 상태 변경 시 즉시 사라짐
|
||
- **알림 배지**: 미확인 notification 있을 때, 에이전트 우상단에 빨간 원 + 숫자
|
||
|
||
### 7.3 렌더링 순서
|
||
|
||
```
|
||
1. 타일맵 (바닥 + 벽)
|
||
2. 가구 (Y-sort)
|
||
3. 에이전트 (Y-sort, 가구와 혼합)
|
||
4. 오버레이 (말풍선, 이름, 배지) — 항상 최상위
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 테마 시스템
|
||
|
||
### 8.1 테마 데이터 구조
|
||
|
||
```javascript
|
||
const THEMES = {
|
||
modern: {
|
||
name: 'Modern',
|
||
wall: { color: '#1a1a2e', border: '#333', accent: '#8b5cf6' },
|
||
floor: { color1: '#2a2a3e', color2: '#323248' },
|
||
furniture: { desk: '#3a3a5a', chair: '#4c1d95', monitor: '#5555aa', shelf: '#2a2a4e' },
|
||
decor: { plant: '#22c55e', pot: '#4a3a2a', lamp: '#fbbf24', ledStrip: '#8b5cf6' },
|
||
lighting: { ambient: 'rgba(139,92,246,0.05)', glow: 'rgba(139,92,246,0.15)' }
|
||
},
|
||
retro: {
|
||
name: 'Retro',
|
||
wall: { color: '#2a1a0a', border: '#6a4a2a', accent: '#cc8844' },
|
||
floor: { color1: '#4a3a1a', color2: '#3a2a10' },
|
||
furniture: { desk: '#6a4a1a', chair: '#8a5a2a', monitor: '#555555', shelf: '#5a3a1a' },
|
||
decor: { plant: '#44aa44', pot: '#6a4a2a', lamp: '#ffcc66', brick: '#8a5a2a' },
|
||
lighting: { ambient: 'rgba(255,200,100,0.05)', glow: 'rgba(255,200,100,0.2)' }
|
||
},
|
||
minimal: {
|
||
name: 'Minimal',
|
||
wall: { color: '#fafafa', border: '#ddd', accent: '#3b82f6' },
|
||
floor: { color1: '#e8e8e8', color2: '#f0f0f0' },
|
||
furniture: { desk: '#ffffff', chair: '#e0e0e0', monitor: '#333333', shelf: '#f5f5f5' },
|
||
decor: { plant: '#86efac', pot: '#ffffff', lamp: '#fbbf24', window: '#e0f0ff' },
|
||
lighting: { ambient: 'rgba(59,130,246,0.03)', glow: 'rgba(255,255,255,0.1)' }
|
||
}
|
||
};
|
||
```
|
||
|
||
### 8.2 테마 적용 방식
|
||
|
||
- `TileMap.render(theme)` — 바닥/벽 색상을 theme에서 읽어 렌더링
|
||
- `FurnitureRenderer.draw(type, theme)` — 가구별 프로시저럴 렌더링에 theme 팔레트 적용
|
||
- 테마 전환 시 전체 캔버스 리렌더 (레이아웃 변경 없음)
|
||
- 사용자 선택은 `localStorage`에 저장, 기본값: `modern`
|
||
|
||
### 8.3 테마별 고유 데코
|
||
|
||
| 테마 | 고유 요소 |
|
||
|------|----------|
|
||
| Modern | LED 스트립 (벽 하단), 네온 글로우, 미니멀 화분 |
|
||
| Retro | 벽돌 텍스처, CRT 모니터, 책장(컬러풀 책), 탁상 램프 |
|
||
| Minimal | 창문(자연광), 다육이, 깔끔한 화이트 선반 |
|
||
|
||
---
|
||
|
||
## 9. 히트 테스팅 & 인터랙션
|
||
|
||
### 9.1 클릭 처리
|
||
|
||
```javascript
|
||
canvas.onclick = (e) => {
|
||
const {col, row} = screenToTile(e.offsetX, e.offsetY, zoom, pan);
|
||
|
||
// 1. 에이전트 히트 테스트 (역순, 최상위 우선)
|
||
const agent = agents.findLast(a =>
|
||
Math.abs(a.x - col) < 1 && Math.abs(a.y - row) < 1.5
|
||
);
|
||
|
||
if (agent) {
|
||
openSidePanel(agent.id);
|
||
return;
|
||
}
|
||
|
||
// 2. 빈 공간 → 패널 닫기
|
||
closeSidePanel();
|
||
};
|
||
```
|
||
|
||
### 9.2 호버 (데스크톱만)
|
||
|
||
- 에이전트 위 호버 시 커서 `pointer`로 변경
|
||
- 툴팁 불필요 (이름+배지가 항상 표시되므로)
|
||
|
||
---
|
||
|
||
## 10. WebSocket 연동
|
||
|
||
기존 프로토콜 100% 유지. 프론트엔드에서 메시지 수신 시 캔버스 상태만 추가 업데이트.
|
||
|
||
| 메시지 타입 | 캔버스 반응 |
|
||
|------------|-----------|
|
||
| `agent_state` | 해당 에이전트 FSM 상태 전환 → 애니메이션/위치 변경 트리거 |
|
||
| `agent_move` | target에 따라 BFS 경로 계산 → 이동 시작 |
|
||
| `task_complete` | 에이전트 상태를 idle로 전환 |
|
||
| `notification` | 에이전트 위 알림 배지 카운트 증가 |
|
||
| `init` | 모든 에이전트 초기 위치/상태 설정 |
|
||
|
||
### agent_state 수신 시 이동 로직
|
||
|
||
```javascript
|
||
function onAgentState(agentId, newState) {
|
||
const agent = agents.get(agentId);
|
||
|
||
switch (newState) {
|
||
case 'working':
|
||
case 'waiting':
|
||
case 'reporting':
|
||
// 자리에 있지 않으면 자리로 이동
|
||
if (!agent.isAtDesk()) agent.moveTo(agent.deskWaypoint);
|
||
break;
|
||
case 'break':
|
||
agent.moveTo('break_room');
|
||
break;
|
||
case 'idle':
|
||
// 배회 루프 시작
|
||
agent.startWandering();
|
||
break;
|
||
}
|
||
|
||
agent.setState(newState);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 파일 구조 (프론트엔드)
|
||
|
||
```
|
||
src/pages/agent-office/
|
||
├── AgentOffice.jsx # 루트 컴포넌트 (재작성)
|
||
├── AgentOffice.css # 스타일 (재작성)
|
||
├── hooks/
|
||
│ ├── useAgentManager.js # WebSocket + 상태 (기존 확장)
|
||
│ └── useOfficeCanvas.js # 캔버스 셋업 (재작성)
|
||
├── components/
|
||
│ ├── TopBar.jsx # 상단 바 (신규)
|
||
│ ├── SidePanel.jsx # 사이드 패널 컨테이너 (신규)
|
||
│ ├── CommandTab.jsx # Commands 탭 (AgentColumn 리팩토링)
|
||
│ ├── TaskTab.jsx # Tasks 탭 (AgentColumn에서 분리)
|
||
│ ├── TokenTab.jsx # Tokens 탭 (신규)
|
||
│ ├── LogTab.jsx # Logs 탭 (신규)
|
||
│ ├── ApprovalCard.jsx # 승인 UI 카드 (신규)
|
||
│ └── MobileBottomSheet.jsx # 모바일 바텀 시트 (신규)
|
||
├── canvas/
|
||
│ ├── OfficeRenderer.js # 게임 루프 + 렌더 파이프라인 (재작성)
|
||
│ ├── TileMap.js # 타일맵 렌더링 + 테마 적용 (재작성)
|
||
│ ├── FurnitureRenderer.js # 가구 프로시저럴 렌더링 (신규)
|
||
│ ├── AgentSprite.js # 에이전트 이동 + 애니메이션 (재작성)
|
||
│ ├── ProceduralSprite.js # 프로시저럴 캐릭터 렌더링 (SpriteSheet 리팩토링)
|
||
│ ├── SpriteLoader.js # 스프라이트시트 로더 + 폴백 (신규)
|
||
│ ├── Pathfinder.js # BFS 경로 탐색 (신규)
|
||
│ ├── OverlayRenderer.js # 이름, 배지, 말풍선 (신규)
|
||
│ └── themes.js # 테마 데이터 (신규)
|
||
├── assets/
|
||
│ ├── office-map.json # 32x20 맵 데이터 (재작성)
|
||
│ └── sprites/ # Phase 2 스프라이트시트 PNG (빈 디렉토리)
|
||
```
|
||
|
||
### 삭제 대상
|
||
|
||
- `components/AgentColumn.jsx` → CommandTab + TaskTab으로 분리
|
||
- `components/CommandColumn.jsx` → SidePanel 내 CommandTab으로 통합
|
||
- `components/ChatPanel.jsx` → 미사용, 삭제
|
||
- `components/DocumentPanel.jsx` → LogTab으로 대체
|
||
- `canvas/SpriteSheet.js` → ProceduralSprite.js로 리팩토링
|
||
|
||
---
|
||
|
||
## 12. 백엔드 변경사항
|
||
|
||
**없음.** 기존 WebSocket 프로토콜과 REST API를 그대로 사용한다.
|
||
|
||
단, `agent_move` 메시지가 break 전환 시에도 정확히 발송되는지 확인 필요:
|
||
- `base.py`의 `check_idle_break()` → `transition('break')` → WebSocket broadcast에 `agent_move` 포함 여부 확인
|
||
- 필요 시 `transition()` 메서드에서 break 상태 전환 시 `agent_move` 메시지 추가
|
||
|
||
---
|
||
|
||
## 13. 구현 순서 (Phase 개요)
|
||
|
||
| Phase | 내용 | 의존성 |
|
||
|-------|------|--------|
|
||
| **1. 캔버스 엔진** | 게임 루프, 타일맵, 줌/팬, 테마 시스템 | 없음 |
|
||
| **2. 에이전트 시스템** | 프로시저럴 캐릭터, BFS 경로 탐색, 상태별 애니메이션, 배회 로직 | Phase 1 |
|
||
| **3. 오버레이** | 이름 라벨, 상태 배지, 말풍선, 알림 배지 | Phase 2 |
|
||
| **4. 사이드 패널** | 4탭 구성, Quick Actions, Approval UI | Phase 1 |
|
||
| **5. 페이지 통합** | AgentOffice.jsx 재작성, WebSocket 연동, 히트 테스팅 | Phase 1-4 |
|
||
| **6. 모바일 대응** | 바텀 시트, 핀치 줌, 터치 이벤트, 반응형 | Phase 5 |
|
||
| **7. 스프라이트 로더** | SpriteLoader 구현, 폴백 연결 | Phase 2 |
|
||
|
||
---
|
||
|
||
## 14. 성공 기준
|
||
|
||
- [ ] 전체 화면 캔버스에서 5명의 에이전트가 상태에 맞게 애니메이션
|
||
- [ ] idle 에이전트가 사무실을 배회하다 자리로 복귀
|
||
- [ ] break 에이전트가 휴게실로 이동하여 휴식
|
||
- [ ] 에이전트 클릭 시 사이드 패널 열림, 4탭 모두 동작
|
||
- [ ] Commands 탭에서 명령 전송 + 승인/거부 동작
|
||
- [ ] 3가지 테마 전환 동작, localStorage에 저장
|
||
- [ ] 모바일에서 바텀 시트 + 핀치 줌 동작
|
||
- [ ] 기존 WebSocket 프로토콜과 100% 호환
|