fix(web): hide session lineage in header

This commit is contained in:
kingwl
2026-07-31 21:08:57 +08:00
parent 5c4b701afe
commit 07b0efc49e
32 changed files with 46 additions and 155 deletions
@@ -228,12 +228,9 @@ describe('conversation slot inject surface', () => {
await b.runtime.dispose()
})
it('routes navigation and workspace switching through the runtime owners, carrying the draft', async () => {
it('routes workspace switching through the runtime owner, carrying the draft', async () => {
const b = await bench()
const { injected } = b.conversationSurface(ROOT)
const resident = b.residentSurface(ROOT)
injected.open(ROOT)
expect(b.runtime.sessions.calls).toContainEqual({ method: 'open', args: [ROOT] })
// Same-session connect (the picked workspace resolves to this session):
// no draft movement, plain re-open.
b.runtime.workspaces.stub('connectWorkspace', () => Promise.resolve(ROOT))
@@ -241,7 +238,7 @@ describe('conversation slot inject surface', () => {
actions.setDraft('carry me')
void resident.selectWorkspace('workspace-1' as never)
await vi.waitFor(() => {
expect(b.runtime.sessions.calls.filter(c => c.method === 'open')).toHaveLength(2)
expect(b.runtime.sessions.calls.filter(c => c.method === 'open')).toHaveLength(1)
})
expect(b.runtime.workspaces.calls).toContainEqual({ method: 'connectWorkspace', args: ['workspace-1'] })
expect(state.getSnapshot().draft).toBe('carry me')
@@ -20,7 +20,7 @@
* suite only proves the assembled wiring.
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, waitFor, within } from '@testing-library/react'
import { cleanup, fireEvent, waitFor } from '@testing-library/react'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import type { ISession, SessionId, TodoItem, ToolResultNode } from '@deepseek-ai/dsh-client-runtime/client'
import type { PropsRenderSlots } from '@deepseek-ai/dsh-client-ui-slots'
@@ -256,16 +256,14 @@ describe('prompt rejection through the assembled composer', () => {
})
describe('title projection across assembled surfaces', () => {
it('one summary update re-labels the breadcrumb and document.title consumers together', async () => {
it('one summary update re-labels the current-session heading', async () => {
const runtime = await bench([])
const view = runtime.renderRoot()
// The strict session header breadcrumb reads useSessions ancestry.
const crumb = within(view.container.querySelector('[aria-label="会话层级"]') as HTMLElement)
expect(crumb.getByText('S')).toBeTruthy()
expect(view.getByRole('heading', { name: 'S', level: 1 })).toBeTruthy()
await runtime.sessions.updateSummary(SID, { displayTitle: '修订标题', title: '修订标题' })
await waitFor(() => { expect(crumb.getByText('修订标题')).toBeTruthy() })
expect(crumb.queryByText('S')).toBeNull()
await waitFor(() => { expect(view.getByRole('heading', { name: '修订标题', level: 1 })).toBeTruthy() })
expect(view.queryByRole('heading', { name: 'S', level: 1 })).toBeNull()
await runtime.dispose()
})
})
@@ -111,7 +111,6 @@ function mount(
const useInput = bindSnapshotSelector(wiring.state)
const inputActions = wiring.actions
const stop = vi.fn()
const open = vi.fn()
const slotCalls: string[] = []
let pickerOwner: unknown
const renderSlot = ((key: string, owner: object, opts?: { only?: string }) => {
@@ -140,8 +139,6 @@ function mount(
version: () => 1,
}}
bindDraftMirror={write => wiring.bindMirror(write)}
open={open}
t={t}
{...owner}
/>
)
@@ -203,7 +200,7 @@ function mount(
}
const view = render(<ConversationRoot {...props} />)
return {
view, chat, sink, open, retargetWorkspace, session, slotCalls,
view, chat, sink, retargetWorkspace, session, slotCalls,
pickerOwner: () => pickerOwner,
rerender: () => { view.rerender(<ConversationRoot {...props} />) },
}
@@ -218,8 +215,8 @@ describe('ConversationRoot resident composer', () => {
expect(b.chat.store.getSnapshot().draft).toBe('ordinary revised')
fireEvent.keyDown(box, { key: 'Enter' })
expect(b.sink).toHaveBeenCalledWith('ordinary revised')
fireEvent.click(b.view.getByRole('button', { name: 'Root' }))
expect(b.open).toHaveBeenCalledWith(sid('root'))
expect(b.view.getByRole('heading', { name: 'Child', level: 1 })).toBeTruthy()
expect(b.view.queryByText('Root')).toBeNull()
})
it('active phase: fixed header outside the scrollport; sticky composer seat inside it', () => {