fix(tui): collapse long system messages in transcript with expand toggle
System messages over 400 chars (system prompt, AGENTS.md, etc.) now render as a collapsed \u25b8/\u25be toggle line in the transcript, matching the Chevron convention used for runtime details. The summary shows the first line + char count; clicking expands to full content.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Ansi, Box, NoSelect, Text } from '@hermes/ink'
|
import { Ansi, Box, NoSelect, Text } from '@hermes/ink'
|
||||||
import { memo } from 'react'
|
import { memo, useState } from 'react'
|
||||||
|
|
||||||
import { LONG_MSG } from '../config/limits.js'
|
import { LONG_MSG } from '../config/limits.js'
|
||||||
import { sectionMode } from '../domain/details.js'
|
import { sectionMode } from '../domain/details.js'
|
||||||
@@ -22,6 +22,9 @@ import { StreamingMd } from './streamingMarkdown.js'
|
|||||||
import { ToolTrail } from './thinking.js'
|
import { ToolTrail } from './thinking.js'
|
||||||
import { TodoPanel } from './todoPanel.js'
|
import { TodoPanel } from './todoPanel.js'
|
||||||
|
|
||||||
|
// Collapse threshold for long system messages (system prompt etc.)
|
||||||
|
const SYSTEM_COLLAPSE_CHARS = 400
|
||||||
|
|
||||||
export const MessageLine = memo(function MessageLine({
|
export const MessageLine = memo(function MessageLine({
|
||||||
cols,
|
cols,
|
||||||
compact,
|
compact,
|
||||||
@@ -46,6 +49,10 @@ export const MessageLine = memo(function MessageLine({
|
|||||||
const activityMode = sectionMode('activity', detailsMode, sections, detailsModeCommandOverride)
|
const activityMode = sectionMode('activity', detailsMode, sections, detailsModeCommandOverride)
|
||||||
const thinking = msg.thinking?.trim() ?? ''
|
const thinking = msg.thinking?.trim() ?? ''
|
||||||
|
|
||||||
|
// Collapse toggle for long system messages
|
||||||
|
const systemIsLong = msg.role === 'system' && msg.text.length > SYSTEM_COLLAPSE_CHARS
|
||||||
|
const [systemOpen, setSystemOpen] = useState(false)
|
||||||
|
|
||||||
if (msg.kind === 'trail' && msg.todos?.length) {
|
if (msg.kind === 'trail' && msg.todos?.length) {
|
||||||
return (
|
return (
|
||||||
<TodoPanel
|
<TodoPanel
|
||||||
@@ -106,6 +113,27 @@ export const MessageLine = memo(function MessageLine({
|
|||||||
return <Text color={t.color.muted}>{msg.text}</Text>
|
return <Text color={t.color.muted}>{msg.text}</Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Collapsible long system message (system prompt, AGENTS.md, etc.) ──
|
||||||
|
// MUST come before the hasAnsi check — system messages from the backend
|
||||||
|
// contain Rich markup escape codes that would otherwise hit <Ansi> full render.
|
||||||
|
if (systemIsLong) {
|
||||||
|
const firstLine = (msg.text.split('\n')[0] ?? '').trim().slice(0, 120) || '(system message)'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box flexDirection="column">
|
||||||
|
<Box onClick={() => setSystemOpen(v => !v)}>
|
||||||
|
<Text color={t.color.accent}>{systemOpen ? '▾ ' : '▸ '}</Text>
|
||||||
|
<Text color={t.color.muted}>{firstLine}</Text>
|
||||||
|
<Text color={t.color.muted} dimColor>
|
||||||
|
{' — '}
|
||||||
|
{msg.text.length.toLocaleString()} chars
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
{systemOpen && <Ansi>{msg.text}</Ansi>}
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.role !== 'user' && hasAnsi(msg.text)) {
|
if (msg.role !== 'user' && hasAnsi(msg.text)) {
|
||||||
return <Ansi>{msg.text}</Ansi>
|
return <Ansi>{msg.text}</Ansi>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user