요약카드(백엔드 매입가×수량)와 증권사별(매입가 단순 합) 총 매입이 서로
달라 혼란. 박재오 정의대로 총 매입 = Σ매입가(수량 미곱산)로 통일.
getBrokerSummary를 stockUtils.computeBrokerSummary로 추출(테스트 5건),
usePortfolio가 portfolioSummary.total_buy를 프론트 단순 합으로 override해
요약카드·증권사별·AI 프롬프트가 동일 값 사용. 손익은 avg_price×수량 유지.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TAB_AI 탭과 관련 컴포넌트(AiTradeTab)·훅(useAiBalance) 삭제. 헤더 카드는
aib 모의투자 요약 분기를 제거하고 항상 포트폴리오 요약을 표시.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
모바일 바텀시트(Commands/Tasks)가 55vh로 작아 내용 확인이 불편 → 헤더에
전체화면 토글 버튼 추가(100dvh 확장, 데스크톱은 숨김). music 에이전트
이미지를 agent_music_2로 교체.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend no longer emits the 'break' state (see web-backend
de8adae). Remove the matching entry from STATE_COLORS and the
.ao-card-dot.break CSS rule. Safe because AgentCard's unknown-state
fallback (DEFAULT_STATE_COLOR) handles any stray legacy value.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Old code assumed result_data was a JSON string and ran JSON.parse on it,
falling back to returning the value verbatim on parse error. When the
backend ships result_data as a dict (e.g. compose tasks return
{music_task_id, tracks}), JSON.parse threw, the catch returned the raw
object, and React threw error #31 'Objects are not valid as a React
child' the moment the user expanded the task row.
Extract formatResultData helper: object → JSON.stringify, JSON string
→ parse then pretty-print, plain string → as-is.
Regression tests cover all three input shapes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend returns {"tasks": [...]} and {"logs": [...]} but TaskTab and
LogTab stored the raw object and called .map on it, throwing
'l.map is not a function' the moment a user opened the Tasks or
Logs tab. Unwrap via Array.isArray check (also covers theoretical
bare-array responses).
Regression test for TaskTab covers all three response shapes:
wrapped object, bare array, and empty.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace fixed 3s reconnect with exponential backoff
(1s/2s/4s/8s/16s/30s, capped). Reduces console noise when
upstream WebSocket is blocked (e.g. DSM reverse proxy without
WS upgrade headers).
- ws.onerror swallowed (onclose still schedules reconnect) so the
browser stops printing an unhandled-error pair per attempt.
- Expose reconnectAttempt in hook; TopBar shows 'Connecting…'
pre-first-attempt and 'Disconnected · 재연결 시도 #N' after.
Root cause of WS failure is upstream (curl proves the endpoint
itself is fine — see DSM reverse proxy WebSocket headers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ffmpeg libwebp quality=85 compression_level=6.
Total: 11.8MB → 875KB (~11MB saved). Visually indistinguishable on
the card grid at the 9:16 image aspect.
PNG removals were already staged in the previous CommandTab commit;
this commit adds the 6 .webp replacements and points constants.js
imports at .webp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously connect's onclose handler referenced connect itself before
the useCallback declaration, triggering react-hooks/immutability. Hold
the latest connect in a ref (updated in useEffect) and call through it
on reconnect. Same runtime behavior, lint-clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was hardcoded #22c55e (green) regardless of actual state, making
error/break states look healthy. Switch to muted #94a3b8.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Approval card gated on 'waiting_approval' (was 'waiting'), matching
the state useAgentManager emits — previously the approval UI was
silently suppressed and pendingTask buttons unreachable
- QUICK_ACTIONS/PARAM_ACTIONS: drop blog (agent removed),
add insta (extract / collect_trends / render)
- Regression test covers the three approval-card branches
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
문제:
- 페이지 1~10 미리보기가 가로 overflow인데 시각 affordance 없어서 page 2~10 못 봄
- 슬레이트 목록(.ic-slates-grid)이 모바일에서 어색 + 카드 자체가 viewport 밖으로 밀림
수정:
- PagesStrip 컴포넌트 신설: 좌/우 chevron + page 인디케이터(3/10) + 양옆 fade gradient
+ 키보드 ←/→ + scroll-snap + 클릭 페이지 이동 + 활성 카드 핑크 테두리/scale
- .ic-page-img width를 clamp(140px, 42vw, 220px)로 viewport 비례
- .ic-slates-grid 모바일 2칸 강제, 640px+ 부터 auto-fill
- .ic-detail에 min-width: 0 + max-width: 100% (자식이 부모 안 밀게)
- .ic-layout grid-template-columns에 minmax(0, 1fr) — 자식 overflow 시 부모 안정
- .ic 모바일 좌우 padding 12px (768px+ 16px)
- Card image aspect-ratio 1/1 → 941/1672 (real image ratio, no crop)
- object-fit cover → contain (defensive against rounding)
- Drop card aspect-ratio so it grows from natural image+name height
- Drop grid max-width 720px so grid fills the viewport width
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 PNGs for 5 active agents + 1 shared placeholder. Required by
constants.js imports; without these the build resolves them from
local disk but a clean clone or NAS deploy would 404.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two tests verifying that working state adds the pulse class and
idle state does not. Pulse animation is part of the design spec §5
but was not covered by the original 8 tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- state→color mapping via STATE_COLORS
- notification badge with 9+ overflow
- active prop for selected card border
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eliminates dual-write drift risk between ACTIVE_AGENT_IDS list
and GRID_SLOTS slot ordering. Single source of truth is now
GRID_SLOTS; ACTIVE_AGENT_IDS is computed from it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 5 active agents (stock/music/insta/realestate/lotto) + 4 placeholders
- AGENT_META, GRID_SLOTS, STATE_COLORS in single constants module
- blog removed (replaced by insta)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trends 탭의 🎴 버튼 클릭이 silent로 끝나 사용자에게 무동작처럼 보이던
이슈 fix. handleCreateSlate를 3초 간격 폴링으로 확장 (최대 8분):
- 시작/진행/성공/실패 상단 배너로 시각화
- 카드 생성 완료 시 자동으로 Cards 탭 전환 + 새 슬레이트 자동 선택
→ SlateDetail이 카피·이미지 미리보기 즉시 표시
- 실패 시 에러 메시지 + 클릭으로 dismiss
- "Claude 카피 추론 + Playwright 카드 10장 생성 중 (3~7분)" 안내 문구
빈 catch 블록 3곳에 의도 주석 추가, test-setup.js 의 beforeEach
명시적 import. 우리 신규 코드의 lint error 0으로 정리.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- useScreenerRun: 실행 시마다 previewHistory에 누적 (최대 10, 메모리만 —
새로고침 시 사라짐, DB 부하 없음). top_ticker/score 요약 포함.
- RunHistoryList: '이번 세션 미리보기'와 '저장된 실행' 두 섹션으로 분리.
미리보기 항목은 클릭으로 결과 표 로드 + '비교' 버튼으로 비교 대상 지정.
- ResultTable: compareWith prop으로 비교 모드. 순위Δ(▲▼NEW)·점수Δ
컬럼 추가, 이번엔 빠진 종목은 'OUT'으로 별도 섹션에 회색 표시.
- 헤더에 'vs HH:MM:SS (통과 X)' 라벨로 비교 대상 명시.
- ScoreChips: 아이콘 제거, 풀 라벨 표시 (외국인/거래량급증/20일모멘텀/
52주신고가/RS레이팅/이평선정배열/VCP수축). title에 노드 의미 + 70점
강조 안내.
- ResultTable: 각 컬럼 헤더에 ⓘ 마커 + 의미 hover 설명. 진입/손절/익절
컬럼명에 '(원)' 명시. 상단에 hover 가이드 한 줄 추가.