mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-08-01 16:43:37 +08:00
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.
This commit is contained in:
parent
5b891151ae
commit
eaf1a96a3e
@ -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(<ActiveSession />)
|
||||
|
||||
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 / 更新时间 / 消息数),之间两个「·」,开头不该有。
|
||||
// 分隔符是纯装饰,读屏时不该被念出来。
|
||||
|
||||
@ -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'
|
||||
}
|
||||
>
|
||||
<div className={showRightPanel ? 'min-w-0 flex-1' : 'mx-auto w-full max-w-[900px] min-w-0'}>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<h1
|
||||
className="min-w-0 flex-1 truncate text-[15px] font-bold leading-tight tracking-[-0.2px] text-[var(--color-text-primary)]"
|
||||
className={`min-w-0 flex-1 truncate font-bold leading-tight tracking-[-0.2px] text-[var(--color-text-primary)] ${
|
||||
showRightPanel ? 'text-[15px]' : 'text-[17px]'
|
||||
}`}
|
||||
style={{ fontFamily: 'var(--font-headline)' }}
|
||||
title={headerTitle}
|
||||
>
|
||||
{headerTitle}
|
||||
</h1>
|
||||
<div className="flex min-w-0 shrink items-center gap-1.5 overflow-hidden whitespace-nowrap text-[11px] text-[var(--color-text-tertiary)]">
|
||||
{[
|
||||
isActive && (
|
||||
<span key="active" className="flex shrink-0 items-center gap-1.5 text-[var(--color-text-secondary)]">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-success)] animate-pulse-dot" />
|
||||
{t('session.active')}
|
||||
</span>
|
||||
),
|
||||
totalTokens > 0 && (
|
||||
<span key="tokens" className="shrink-0" title={t('common.tokens', { count: totalTokens.toLocaleString() })}>
|
||||
{t('common.tokens', { count: formatTokenCount(totalTokens) })}
|
||||
</span>
|
||||
),
|
||||
lastUpdated && (
|
||||
<span key="updated" className="truncate">{t('session.lastUpdated', { time: lastUpdated })}</span>
|
||||
),
|
||||
!showRightPanel && visibleMessageCount > 0 && (
|
||||
<span key="messages" className="shrink-0">{t('session.messages', { count: visibleMessageCount })}</span>
|
||||
),
|
||||
]
|
||||
.filter((part): part is ReactElement => Boolean(part))
|
||||
.map((part, index) => (
|
||||
<Fragment key={part.key}>
|
||||
{index > 0 && <span aria-hidden="true" className="shrink-0">·</span>}
|
||||
{part}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 flex min-w-0 items-center gap-1.5 overflow-hidden whitespace-nowrap text-[11px] text-[var(--color-text-tertiary)]">
|
||||
{[
|
||||
isActive && (
|
||||
<span key="active" className="flex shrink-0 items-center gap-1.5 text-[var(--color-text-secondary)]">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[var(--color-success)] animate-pulse-dot" />
|
||||
{t('session.active')}
|
||||
</span>
|
||||
),
|
||||
totalTokens > 0 && (
|
||||
<span key="tokens" className="shrink-0" title={t('common.tokens', { count: totalTokens.toLocaleString() })}>
|
||||
{t('common.tokens', { count: formatTokenCount(totalTokens) })}
|
||||
</span>
|
||||
),
|
||||
lastUpdated && (
|
||||
<span key="updated" className="truncate">{t('session.lastUpdated', { time: lastUpdated })}</span>
|
||||
),
|
||||
!showRightPanel && visibleMessageCount > 0 && (
|
||||
<span key="messages" className="shrink-0">{t('session.messages', { count: visibleMessageCount })}</span>
|
||||
),
|
||||
]
|
||||
.filter((part): part is ReactElement => Boolean(part))
|
||||
.map((part, index) => (
|
||||
<Fragment key={part.key}>
|
||||
{index > 0 && <span aria-hidden="true" className="shrink-0">·</span>}
|
||||
{part}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
{session && getSessionWorkspaceState(session) !== 'available' && (
|
||||
<div className={`mt-2 inline-flex max-w-full items-center gap-2 rounded-[var(--radius-md)] border px-3 py-1.5 text-[11px] ${
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user