refactor: structured command/run payload {commandId, name, args, source}
The line field is deleted (pre-release, no shim): name and args are parseCommand's own split — name plus verbatim rawInput with its separator whitespace — so a consumer (a projection unit folding its own command records, a rich command card) never re-parses a line. CommandNode mirrors the split (name/args, both null on a run-less cross-window node); the generic card rebuilds its display line as /name + args. The connection fixture logs the same structured payload.
This commit is contained in:
@@ -20,12 +20,15 @@ export function GenericCommandCard({ node }: CommandRowOwnerProps) {
|
||||
const summary = node.outcome === null
|
||||
? '执行中…'
|
||||
: text ?? (node.outcome.kind === 'error' ? '命令失败' : '已完成')
|
||||
// Display line rebuilt from the structured payload (args carries its own
|
||||
// separator whitespace verbatim); a cross-window node whose run page fell
|
||||
// out of the window has neither.
|
||||
const title = node.name === null ? '命令' : `/${node.name}${node.args ?? ''}`
|
||||
return (
|
||||
<ToolRow
|
||||
variant="others"
|
||||
icon={<IconApiOutline14 size={16} />}
|
||||
// A cross-window node whose run page fell out of the window has no line.
|
||||
title={node.line ?? '命令'}
|
||||
title={title}
|
||||
summary={summary}
|
||||
// Expandable only when the outcome text overflows a one-line summary.
|
||||
body={text !== undefined && text.includes('\n') ? text : null}
|
||||
|
||||
@@ -168,7 +168,8 @@ export type ToolRowProps = PropsRuntime<'conversation.chat.toolview'>
|
||||
/**
|
||||
* Owner share of the per-command row slot: the frozen {@link CommandNode}
|
||||
* slice off the snapshot (cache-stable reference — memo premise). The node
|
||||
* carries the whole lifecycle (line, pairing id, outcome-or-executing), so a
|
||||
* carries the whole lifecycle (structured name/args, pairing id,
|
||||
* outcome-or-executing), so a
|
||||
* registrant needs no second data channel; domain state arrives through its
|
||||
* own projection cell.
|
||||
*/
|
||||
|
||||
@@ -367,7 +367,7 @@ describe('ChatView', () => {
|
||||
it('renders command nodes as durable rows: settled text, error state, executing spinner, run-less soft-fall', () => {
|
||||
const command = (over: Partial<CommandNode>): CommandNode => ({
|
||||
kind: 'command', seq: 5, time: 5_000, commandId: 'cmd-1',
|
||||
name: 'plan', line: '/plan', outcome: { kind: 'success', text: '已进入 plan mode' },
|
||||
name: 'plan', args: '', outcome: { kind: 'success', text: '已进入 plan mode' },
|
||||
...over,
|
||||
})
|
||||
// Settled success: the command line is the title, the outcome text the summary.
|
||||
@@ -394,7 +394,7 @@ describe('ChatView', () => {
|
||||
|
||||
// Cross-window soft-fall (run page truncated): generic title, outcome preserved.
|
||||
const orphan = makeHarness({
|
||||
nodes: [command({ seq: 8, commandId: 'cmd-4', name: null, line: null, outcome: { kind: 'success' } })],
|
||||
nodes: [command({ seq: 8, commandId: 'cmd-4', name: null, args: null, outcome: { kind: 'success' } })],
|
||||
})
|
||||
const ov = render(<orphan.ChatView {...orphan.props} />)
|
||||
expect(ov.getByText('命令')).toBeTruthy()
|
||||
|
||||
Reference in New Issue
Block a user