140 lines
4.7 KiB
JavaScript
140 lines
4.7 KiB
JavaScript
/**
|
|
* IntentTrace Component - Premium thinking display
|
|
*
|
|
* DESIGN:
|
|
* - Default: hidden or 1-line summary
|
|
* - When shown: Intent / Next / Why + "+N more"
|
|
* - Never spam raw logs into transcript
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import { Box, Text } from 'ink';
|
|
import Spinner from 'ink-spinner';
|
|
import { colors } from '../../tui-theme.mjs';
|
|
import { icon } from '../../icons.mjs';
|
|
import { getCapabilities } from '../../terminal-profile.mjs';
|
|
|
|
const h = React.createElement;
|
|
|
|
/**
|
|
* IntentTrace - Collapsible thinking summary
|
|
*
|
|
* Props:
|
|
* - intent: current intent (1 line)
|
|
* - next: next action (1 line)
|
|
* - why: optional reasoning (1 line)
|
|
* - steps: array of step strings
|
|
* - isThinking: show spinner
|
|
* - verbosity: 'off' | 'brief' | 'detailed'
|
|
*/
|
|
const IntentTrace = ({
|
|
intent = null,
|
|
next = null,
|
|
why = null,
|
|
steps = [],
|
|
isThinking = false,
|
|
verbosity = 'brief',
|
|
width = 80
|
|
}) => {
|
|
const caps = getCapabilities();
|
|
const [expanded, setExpanded] = useState(false);
|
|
|
|
// Off mode = nothing shown
|
|
if (verbosity === 'off' && !isThinking) return null;
|
|
|
|
const railChar = caps.unicodeOK ? '┊' : ':';
|
|
const railColor = colors.muted;
|
|
|
|
// Brief mode: just intent + next
|
|
if (verbosity === 'brief' || !expanded) {
|
|
return h(Box, {
|
|
flexDirection: 'column',
|
|
marginY: 0
|
|
},
|
|
// Header with spinner
|
|
h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
isThinking ? h(Spinner, { type: 'dots' }) : null,
|
|
isThinking ? h(Text, {}, ' ') : null,
|
|
h(Text, { color: colors.muted, dimColor: true },
|
|
isThinking ? 'thinking...' : 'thought'
|
|
)
|
|
),
|
|
|
|
// Intent line
|
|
intent ? h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.muted, bold: true }, 'Intent: '),
|
|
h(Text, { color: colors.muted },
|
|
intent.length > width - 15 ? intent.slice(0, width - 18) + '...' : intent
|
|
)
|
|
) : null,
|
|
|
|
// Next line
|
|
next ? h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.muted, bold: true }, 'Next: '),
|
|
h(Text, { color: colors.muted },
|
|
next.length > width - 13 ? next.slice(0, width - 16) + '...' : next
|
|
)
|
|
) : null,
|
|
|
|
// Expand hint (if more steps)
|
|
steps.length > 0 ? h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.muted, dimColor: true },
|
|
`+${steps.length} more`
|
|
)
|
|
) : null
|
|
);
|
|
}
|
|
|
|
// Detailed mode: show all
|
|
return h(Box, { flexDirection: 'column', marginY: 0 },
|
|
// Header
|
|
h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
isThinking ? h(Spinner, { type: 'dots' }) : null,
|
|
isThinking ? h(Text, {}, ' ') : null,
|
|
h(Text, { color: colors.muted }, 'Intent Trace')
|
|
),
|
|
|
|
// Intent
|
|
intent ? h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.accent }, 'Intent: '),
|
|
h(Text, { color: colors.fg }, intent)
|
|
) : null,
|
|
|
|
// Next
|
|
next ? h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.accent }, 'Next: '),
|
|
h(Text, { color: colors.fg }, next)
|
|
) : null,
|
|
|
|
// Why
|
|
why ? h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.muted }, 'Why: '),
|
|
h(Text, { color: colors.muted }, why)
|
|
) : null,
|
|
|
|
// Steps
|
|
...steps.map((step, i) =>
|
|
h(Box, { key: i, flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.muted, dimColor: true }, `${i + 1}. ${step}`)
|
|
)
|
|
),
|
|
// Collapse hint
|
|
h(Box, { flexDirection: 'row' },
|
|
h(Text, { color: railColor, dimColor: true }, railChar + ' '),
|
|
h(Text, { color: colors.muted, dimColor: true }, '[collapse]')
|
|
)
|
|
);
|
|
};
|
|
|
|
export default IntentTrace;
|
|
export { IntentTrace };
|