From 38ba5b87fae8fd472aba7af9f7636872ed338a94 Mon Sep 17 00:00:00 2001 From: ejclaw Date: Tue, 28 Apr 2026 19:53:00 +0900 Subject: [PATCH] Show full dashboard room content --- apps/dashboard/src/RoomCardV2.test.ts | 38 +++++++++++++++++++++++++++ apps/dashboard/src/RoomCardV2.tsx | 2 +- apps/dashboard/src/styles.css | 3 ++- src/web-dashboard-data.test.ts | 24 ++++++++--------- src/web-dashboard-data.ts | 18 ++++--------- 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/apps/dashboard/src/RoomCardV2.test.ts b/apps/dashboard/src/RoomCardV2.test.ts index 6da276c..16cbb4c 100644 --- a/apps/dashboard/src/RoomCardV2.test.ts +++ b/apps/dashboard/src/RoomCardV2.test.ts @@ -208,4 +208,42 @@ describe('RoomCardV2', () => { ); expect(html).toContain('bar-chart-label-fit-playwright.png'); }); + + it('renders expanded watcher messages without truncating content', () => { + const watcherTail = 'WATCHER_TAIL_VISIBLE'; + const html = renderToStaticMarkup( + createElement(RoomCardV2, { + activity: activity({ + messages: [ + { + id: 'watcher-1', + sender: 'bot-1', + senderName: 'reviewer', + content: `[Watcher] ${'검증 로그 '.repeat(40)} ${watcherTail}`, + timestamp: '2026-04-28T02:00:00.000Z', + isFromMe: false, + isBotMessage: true, + sourceKind: 'bot', + }, + ], + }), + activityLoading: false, + busy: false, + draft: '', + entry, + expanded: true, + inboxItems: [], + locale: 'ko', + onDraftChange: () => {}, + onSendMessage: () => {}, + onToggle: () => {}, + pinned: true, + t, + ...formatters, + }), + ); + + expect(html).toContain('class="room-watcher-fold"'); + expect(html).toContain(watcherTail); + }); }); diff --git a/apps/dashboard/src/RoomCardV2.tsx b/apps/dashboard/src/RoomCardV2.tsx index c3edd5a..8caad08 100644 --- a/apps/dashboard/src/RoomCardV2.tsx +++ b/apps/dashboard/src/RoomCardV2.tsx @@ -870,7 +870,7 @@ function RoomWatcherFold({ - + ))} diff --git a/apps/dashboard/src/styles.css b/apps/dashboard/src/styles.css index bf286dc..6d2075b 100644 --- a/apps/dashboard/src/styles.css +++ b/apps/dashboard/src/styles.css @@ -249,6 +249,7 @@ body:has(.view-stack.view-rooms) .error-card { background: var(--panel); overflow-y: auto; overflow-x: hidden; + scroll-padding-bottom: 96px; } .view-stack.view-rooms .rooms-detail .room-card-v2.is-pinned { @@ -290,7 +291,7 @@ body:has(.view-stack.view-rooms) .error-card { } .view-stack.view-rooms .rooms-detail .room-thread-section { - padding: 4px 12px 4px 0; + padding: 4px 12px 96px 0; } .view-stack.view-rooms .rooms-detail .room-card-head { diff --git a/src/web-dashboard-data.test.ts b/src/web-dashboard-data.test.ts index 7960cb4..35e1cd2 100644 --- a/src/web-dashboard-data.test.ts +++ b/src/web-dashboard-data.test.ts @@ -238,6 +238,8 @@ describe('web dashboard data', () => { }); it('builds redacted room activity from messages and paired turn output', () => { + const longMessageTail = 'TAIL_MESSAGE_VISIBLE'; + const longOutputTail = 'TAIL_OUTPUT_VISIBLE'; const task = makePairedTask({ id: 'paired-room-1', chat_jid: 'dc:ops', @@ -281,21 +283,12 @@ describe('web dashboard data', () => { last_error: 'OPENAI_API_KEY=plain-secret-value', }; const outputs: PairedTurnOutput[] = [ - { - id: 1, - task_id: task.id, - turn_number: 1, - role: 'owner', - output_text: 'owner output', - verdict: 'step_done', - created_at: '2026-04-26T05:25:00.000Z', - }, { id: 2, task_id: task.id, turn_number: 2, role: 'reviewer', - output_text: 'reviewer output with BOT_TOKEN=plain-secret-value', + output_text: `reviewer output with BOT_TOKEN=plain-secret-value ${'x'.repeat(1900)} ${longOutputTail}`, verdict: null, created_at: '2026-04-26T05:30:00.000Z', }, @@ -306,7 +299,7 @@ describe('web dashboard data', () => { chat_jid: 'dc:ops', sender: 'user-1', sender_name: '눈쟁이', - content: 'latest message', + content: `latest message ${'y'.repeat(950)} ${longMessageTail}`, timestamp: '2026-04-26T05:29:00.000Z', is_from_me: false, is_bot_message: false, @@ -347,12 +340,17 @@ describe('web dashboard data', () => { { turnNumber: 2, role: 'reviewer', - outputText: 'reviewer output with BOT_TOKEN=', + outputText: expect.stringMatching( + new RegExp(`BOT_TOKEN=[\\s\\S]*${longOutputTail}`), + ), }, ], }); expect(activity.messages).toMatchObject([ - { senderName: '눈쟁이', content: 'latest message' }, + { + senderName: '눈쟁이', + content: expect.stringContaining(longMessageTail), + }, ]); }); diff --git a/src/web-dashboard-data.ts b/src/web-dashboard-data.ts index ab0ae00..71f5e45 100644 --- a/src/web-dashboard-data.ts +++ b/src/web-dashboard-data.ts @@ -163,8 +163,6 @@ const SECRET_ASSIGNMENT_RE = /\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|AUTH|PRIVATE_KEY)[A-Z0-9_]*)\s*=\s*([^\s"'`]+)/gi; const SECRET_VALUE_RE = /\b(?:sk-[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9_]{12,}|xox[baprs]-[A-Za-z0-9-]{12,}|eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{10,})\b/g; -const ROOM_MESSAGE_PREVIEW_MAX_LENGTH = 900; -const ROOM_OUTPUT_PREVIEW_MAX_LENGTH = 1800; function redactSensitiveText(value: string): string { return value @@ -185,8 +183,8 @@ function buildInboxPreview(value: string): string { return truncateText(redactSensitiveText(value).replace(/\s+/g, ' ').trim()); } -function buildRoomPreview(value: string, maxLength: number): string { - return truncateText(redactSensitiveText(value).trim(), maxLength); +function buildRoomBody(value: string): string { + return redactSensitiveText(value).trim(); } function decodeCommonHtmlEntities(value: string): string { @@ -485,7 +483,7 @@ function sanitizeRoomMessage( id: message.id, sender: message.sender, senderName: message.sender_name || message.sender, - content: buildRoomPreview(normalized.text, ROOM_MESSAGE_PREVIEW_MAX_LENGTH), + content: buildRoomBody(normalized.text), attachments: normalized.attachments, timestamp: message.timestamp, isFromMe: !!message.is_from_me, @@ -576,10 +574,7 @@ function sanitizeRoomTurn( completedAt, lastError: (attempt?.last_error ?? turn.last_error) - ? buildRoomPreview( - attempt?.last_error ?? turn.last_error ?? '', - ROOM_MESSAGE_PREVIEW_MAX_LENGTH, - ) + ? buildRoomBody(attempt?.last_error ?? turn.last_error ?? '') : null, progressText, progressUpdatedAt, @@ -598,10 +593,7 @@ function sanitizeRoomTurnOutput( role: output.role, verdict: output.verdict ?? null, createdAt: output.created_at, - outputText: buildRoomPreview( - normalized.text, - ROOM_OUTPUT_PREVIEW_MAX_LENGTH, - ), + outputText: buildRoomBody(normalized.text), attachments: normalized.attachments, }; }