cc-haha/desktop/src/components/chat/composerUtils.test.ts
程序员阿江(Relakkes) b4142c8221 Make desktop slash commands useful outside the terminal
Desktop slash commands now separate local UI panels from CLI turn execution. The session inspector exposes status, usage, and context data from the active session, including transcript and context fallbacks, so /status, /cost, and /context can render structured desktop UI instead of raw terminal text. The inspector and help surfaces now use the existing desktop i18n catalogs for English and Chinese labels.

Constraint: Desktop read-only slash commands must not spawn duplicate CLI processes or depend on submitting a normal user turn.

Rejected: Render raw CLI command text in chat | it keeps terminal-specific layout constraints and does not fit the desktop panel UX.

Confidence: high

Scope-risk: moderate

Directive: Keep /status, /cost, and /context routed through the local inspector unless the CLI exposes a structured interactive command protocol.

Tested: cd desktop && bun run lint

Tested: cd desktop && bun test src/components/chat/composerUtils.test.ts

Tested: bun test src/server/__tests__/conversations.test.ts

Tested: cd desktop && bun run build

Tested: agent-browser smoke test on http://127.0.0.1:2024/ for /context localized inspector

Tested: git diff --check

Not-tested: Full packaged Tauri desktop build.
2026-04-28 16:38:16 +08:00

70 lines
2.5 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
findSlashToken,
insertSlashTrigger,
mergeSlashCommands,
replaceSlashCommand,
resolveSlashUiAction,
} from './composerUtils'
describe('composerUtils', () => {
it('finds slash token without trailing space', () => {
expect(findSlashToken('/rev', 4)).toEqual({ start: 0, filter: 'rev' })
expect(findSlashToken('hello /rev', 10)).toEqual({ start: 6, filter: 'rev' })
})
it('does not treat slash followed by a space as an active token', () => {
expect(findSlashToken('/ review', 8)).toBeNull()
})
it('inserts a slash trigger without appending a trailing space', () => {
expect(insertSlashTrigger('', 0)).toEqual({ value: '/', cursorPos: 1 })
expect(insertSlashTrigger('hello', 5)).toEqual({ value: 'hello /', cursorPos: 7 })
})
it('replaces the current slash token with a command and one trailing separator', () => {
expect(replaceSlashCommand('/rev', 4, 'review')).toEqual({
value: '/review ',
cursorPos: 8,
})
})
it('merges fallback commands so built-in entries like /clear remain visible', () => {
expect(
mergeSlashCommands([
{ name: 'help', description: '' },
]),
).toEqual(
expect.arrayContaining([
{ name: 'help', description: 'Show available desktop and agent commands' },
{ name: 'clear', description: 'Clear conversation history' },
{ name: 'context', description: 'Show current context usage' },
]),
)
})
it('keeps server-provided descriptions when they exist', () => {
expect(
mergeSlashCommands([
{ name: 'clear', description: 'Server description' },
]),
).toEqual(
expect.arrayContaining([
{ name: 'clear', description: 'Server description' },
]),
)
})
it('resolves hidden settings aliases without displaying duplicate fallback rows', () => {
expect(resolveSlashUiAction('plugins')).toEqual({ type: 'settings', tab: 'plugins' })
expect(mergeSlashCommands([]).map((command) => command.name)).toContain('plugin')
expect(mergeSlashCommands([]).map((command) => command.name)).not.toContain('plugins')
})
it('routes session inspection commands to the desktop panel', () => {
expect(resolveSlashUiAction('cost')).toEqual({ type: 'panel', command: 'cost' })
expect(resolveSlashUiAction('context')).toEqual({ type: 'panel', command: 'context' })
expect(resolveSlashUiAction('status')).toEqual({ type: 'panel', command: 'status' })
})
})