feat(tui)!: consolidate terminal UI improvements

This commit is contained in:
Turtle
2026-07-29 10:00:29 +08:00
parent 1f242753ec
commit 31cb340c36
57 changed files with 1875 additions and 896 deletions

View File

@@ -7,7 +7,7 @@ import type { Context } from 'cordis'
import { agentEvents } from '@deepseek-ai/dsh-agent'
import { createUserMessage, CallId, type ContentBlock , createMessage, createToolResultMessage } from '@deepseek-ai/dsh-llm'
import type {} from '@deepseek-ai/dsh-llm-retry'
import { SessionId, type JsonValue, type Session } from '@deepseek-ai/dsh-session'
import { SessionId, type JsonValue, type Session, type SessionEvent } from '@deepseek-ai/dsh-session'
import SessionReferenceService from '@deepseek-ai/dsh-session-reference'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { type ToolDefinition, type ToolResultView } from '@deepseek-ai/dsh-tools'
@@ -51,10 +51,12 @@ const CHECKPOINTS = [
'surface-after-compaction-narrow',
'surface-after-compaction-wide',
'model-selector',
'model-selector-filtered',
'model-switching',
'errors-and-help',
'disposed-terminal',
'resume-sessions',
'resume-sessions-all-workspaces',
'status-diagnostics',
'status-diagnostics-narrow',
'todo-plan-cleared',
@@ -801,6 +803,10 @@ describe('TUI terminal-state snapshots', () => {
harness.terminal.send('\r')
})
await checkpoint('model-selector', harness.terminal, { includeScrollback: true })
await renderAfter(harness, () => {
harness.terminal.send('pro')
})
await checkpoint('model-selector-filtered', harness.terminal, { includeScrollback: true })
await renderAfter(harness, () => {
harness.terminal.send('\x1b[B')
harness.terminal.send('\r')
@@ -812,35 +818,33 @@ describe('TUI terminal-state snapshots', () => {
it('opens the searchable resume selector with log-backed session summaries', async () => {
const dateNow = vi.spyOn(Date, 'now').mockReturnValue(Date.parse('2026-07-23T08:00:00.000Z'))
const earlier = { version: 0, id: SessionId('earlier-session'), createdAt: Date.parse('2024-01-01T00:00:00Z'), cwd: '/workspace/project' }
const elsewhere = { version: 0, id: SessionId('elsewhere-session'), createdAt: Date.parse('2024-02-02T00:00:00Z'), cwd: '/workspace/other' }
const log = (meta: typeof earlier, title: string, day: string): { meta: typeof earlier; events: SessionEvent[] } => ({
meta,
events: [
{ type: 'turn/start', seq: 0, time: Date.parse(`${day}T00:00:01Z`), data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } } },
{ type: 'user/message', seq: 1, time: Date.parse(`${day}T00:00:02Z`), data: createUserMessage({ content: [{ type: 'text', text: 'restore the selector' }], source: { kind: 'user' } }), surfaceOp: 'append' },
{ type: 'step/start', seq: 2, time: Date.parse(`${day}T00:00:03Z`), data: { turn: 1, step: 1 } },
{ type: 'request/header', seq: 3, time: Date.parse(`${day}T00:00:04Z`), data: { header: { config: { provider: 'deepseek', model: 'deepseek-v4-pro' } }, reason: 'initial' } },
{ type: 'assistant/message', seq: 4, time: Date.parse(`${day}T00:00:05Z`), data: {
turn: 1, step: 1,
message: createMessage({
role: 'assistant',
content: [{ type: 'text', text: 'ready' }],
source: { kind: 'model', provider: 'deepseek', model: 'deepseek-v4-pro' },
}),
}, surfaceOp: 'append' },
{ type: 'step/end', seq: 5, time: Date.parse(`${day}T00:00:06Z`), data: { turn: 1, step: 1 } },
{ type: 'turn/end', seq: 6, time: Date.parse(`${day}T00:00:07Z`), data: { turn: 1, reason: { kind: 'completed' } } },
{ type: 'session/title', seq: 7, time: Date.parse(`${day}T00:00:08Z`), data: { title, messageSeqs: [1], source: { kind: 'fallback' } } },
],
})
const harness = await setupSnapshot({
config: { resumeCommand: 'dsh --resume {session}' },
sessionPersistence: {
list: async () => [earlier],
load: async () => ({
meta: earlier,
events: [
{ type: 'turn/start', seq: 0, time: Date.parse('2024-01-01T00:00:01Z'), data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } } },
{ type: 'user/message', seq: 1, time: Date.parse('2024-01-01T00:00:02Z'), data: createUserMessage({
content: [{ type: 'text', text: 'restore the selector' }], source: { kind: 'user' },
}), surfaceOp: 'append' },
{ type: 'step/start', seq: 2, time: Date.parse('2024-01-01T00:00:03Z'), data: { turn: 1, step: 1 } },
{ type: 'request/header', seq: 3, time: Date.parse('2024-01-01T00:00:04Z'), data: { header: { config: { provider: 'deepseek', model: 'deepseek-v4-pro' } }, reason: 'initial' } },
{ type: 'assistant/message', seq: 4, time: Date.parse('2024-01-01T00:00:05Z'), data: {
turn: 1, step: 1,
message: createMessage({
role: 'assistant',
content: [{ type: 'text', text: 'ready' }],
source: {
kind: 'model',
...{ provider: 'deepseek', model: 'deepseek-v4-pro' },
},
}),
}, surfaceOp: 'append' },
{ type: 'step/end', seq: 5, time: Date.parse('2024-01-01T00:00:06Z'), data: { turn: 1, step: 1 } },
{ type: 'turn/end', seq: 6, time: Date.parse('2024-01-01T00:00:07Z'), data: { turn: 1, reason: { kind: 'completed' } } },
{ type: 'session/title', seq: 7, time: Date.parse('2024-01-01T00:00:08Z'), data: { title: 'Resume selector design', messageSeqs: [1], source: { kind: 'fallback' } } },
],
}),
list: async () => [earlier, elsewhere],
load: async id => id === elsewhere.id
? log(elsewhere, 'Other workspace work', '2024-02-02')
: log(earlier, 'Resume selector design', '2024-01-01'),
},
}, { columns: 92, rows: 32 })
harness.terminal.send('/resume')
@@ -850,6 +854,12 @@ describe('TUI terminal-state snapshots', () => {
await new Promise(resolve => setTimeout(resolve, 60))
await harness.terminal.flush()
await checkpoint('resume-sessions', harness.terminal, { includeScrollback: true })
// Tab switches to the all-workspaces scope, which adds the other workspace's
// session and labels every row with the directory it belongs to.
harness.terminal.send('\t')
await new Promise(resolve => setTimeout(resolve, 60))
await harness.terminal.flush()
await checkpoint('resume-sessions-all-workspaces', harness.terminal, { includeScrollback: true })
await disposeSnapshot(harness)
dateNow.mockRestore()
})