From eaf1a96a3ec41d07ccb08f0e68b05e2bf59cb7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Mon, 27 Jul 2026 02:43:13 +0800 Subject: [PATCH] fix(desktop): move the session metadata back under the title Pinning the metadata to the right end of the title row left a wide gap between them, so the two read as unrelated blocks rather than one header. Put it back on its own line under the title at 11px, and give the header back some of the height it lost: a 17px title (15px in the workspace layout) and py-3. Measured 66.75px on a real session, between the 123.5px original and the 35.75px single-line version. The title still truncates to one line with the full text in a hover tooltip, so a long title cannot push the transcript down again. --- desktop/src/pages/ActiveSession.test.tsx | 15 +++--- desktop/src/pages/ActiveSession.tsx | 64 ++++++++++++------------ 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/desktop/src/pages/ActiveSession.test.tsx b/desktop/src/pages/ActiveSession.test.tsx index 3f3bd69c..837e7807 100644 --- a/desktop/src/pages/ActiveSession.test.tsx +++ b/desktop/src/pages/ActiveSession.test.tsx @@ -1949,7 +1949,7 @@ describe('ActiveSession task polling', () => { describe('ActiveSession header', () => { // 回归锚点:标题曾经是 text-[22px] 且不截断,长标题会折成两行再加一行元数据, - // 连同 pt-6/pb-4 把聊天区顶掉约 90px。标题必须单行截断,元数据与它同一行。 + // 连同 pt-6/pb-4 把聊天区顶掉约 120px。标题必须单行截断,元数据留在它下面那行。 const longTitle = 'Create a todo_cccc-ccccbb directory, write a throwaway todo app with react + vite + tailwindcss, then start it' function mountSessionWithLongTitle(sessionId: string) { @@ -2013,7 +2013,7 @@ describe('ActiveSession header', () => { expect(heading.className).not.toMatch(/text-\[22px\]/) }) - it('puts the session metadata on the title row instead of a second line', () => { + it('gives the metadata its own line under the title', () => { const sessionId = 'header-meta-session' mountSessionWithLongTitle(sessionId) @@ -2022,10 +2022,13 @@ describe('ActiveSession header', () => { const header = screen.getByTestId('session-header') const heading = within(header).getByRole('heading', { level: 1 }) const titleRow = heading.parentElement as HTMLElement + const meta = titleRow.nextElementSibling as HTMLElement - expect(within(titleRow).getByText('2 messages')).toBeInTheDocument() - expect(within(titleRow).getByText('15k tokens')).toBeInTheDocument() - expect(header).toHaveClass('py-2') + // 元数据挤在标题右侧时会离标题很远,读起来像飘在角落的另一块内容。 + expect(within(titleRow).queryByText('2 messages')).not.toBeInTheDocument() + expect(within(meta).getByText('2 messages')).toBeInTheDocument() + expect(within(meta).getByText('15k tokens')).toBeInTheDocument() + expect(header).toHaveClass('py-3') }) it('keeps the separators between metadata items, never in front of them', () => { @@ -2035,7 +2038,7 @@ describe('ActiveSession header', () => { render() const heading = within(screen.getByTestId('session-header')).getByRole('heading', { level: 1 }) - const meta = (heading.parentElement as HTMLElement).lastElementChild as HTMLElement + const meta = (heading.parentElement as HTMLElement).nextElementSibling as HTMLElement // 空闲会话只有三项元数据(tokens / 更新时间 / 消息数),之间两个「·」,开头不该有。 // 分隔符是纯装饰,读屏时不该被念出来。 diff --git a/desktop/src/pages/ActiveSession.tsx b/desktop/src/pages/ActiveSession.tsx index acce0332..5e004874 100644 --- a/desktop/src/pages/ActiveSession.tsx +++ b/desktop/src/pages/ActiveSession.tsx @@ -624,47 +624,49 @@ export function ActiveSession() { data-testid="session-header" className={ showRightPanel - ? 'flex w-full items-center border-b border-[var(--color-border)] px-4 py-2' - : 'w-full border-b border-[var(--color-border)] px-9 py-2' + ? 'flex w-full items-center border-b border-[var(--color-border)] px-4 py-2.5' + : 'w-full border-b border-[var(--color-border)] px-9 py-3' } >

{headerTitle}

-
- {[ - isActive && ( - - - {t('session.active')} - - ), - totalTokens > 0 && ( - - {t('common.tokens', { count: formatTokenCount(totalTokens) })} - - ), - lastUpdated && ( - {t('session.lastUpdated', { time: lastUpdated })} - ), - !showRightPanel && visibleMessageCount > 0 && ( - {t('session.messages', { count: visibleMessageCount })} - ), - ] - .filter((part): part is ReactElement => Boolean(part)) - .map((part, index) => ( - - {index > 0 && } - {part} - - ))} -
+
+
+ {[ + isActive && ( + + + {t('session.active')} + + ), + totalTokens > 0 && ( + + {t('common.tokens', { count: formatTokenCount(totalTokens) })} + + ), + lastUpdated && ( + {t('session.lastUpdated', { time: lastUpdated })} + ), + !showRightPanel && visibleMessageCount > 0 && ( + {t('session.messages', { count: visibleMessageCount })} + ), + ] + .filter((part): part is ReactElement => Boolean(part)) + .map((part, index) => ( + + {index > 0 && } + {part} + + ))}
{session && getSessionWorkspaceState(session) !== 'available' && (