cvc-tui 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entry.js +71148 -61
- package/package.json +2 -2
- package/dist/app/completion.js +0 -102
- package/dist/app/createGatewayEventHandler.js +0 -508
- package/dist/app/createSlashHandler.js +0 -101
- package/dist/app/delegationStore.js +0 -51
- package/dist/app/gatewayContext.js +0 -17
- package/dist/app/historyStore.js +0 -123
- package/dist/app/inputBuffer.js +0 -120
- package/dist/app/inputSelectionStore.js +0 -8
- package/dist/app/inputStore.js +0 -28
- package/dist/app/interfaces.js +0 -6
- package/dist/app/overlayStore.js +0 -40
- package/dist/app/promptStore.js +0 -44
- package/dist/app/queueStore.js +0 -25
- package/dist/app/scroll.js +0 -44
- package/dist/app/setupHandoff.js +0 -28
- package/dist/app/slash/commands/core.js +0 -479
- package/dist/app/slash/commands/debug.js +0 -44
- package/dist/app/slash/commands/ops.js +0 -498
- package/dist/app/slash/commands/session.js +0 -431
- package/dist/app/slash/commands/setup.js +0 -20
- package/dist/app/slash/commands/toggles.js +0 -40
- package/dist/app/slash/registry.js +0 -18
- package/dist/app/slash/types.js +0 -1
- package/dist/app/spawnHistoryStore.js +0 -105
- package/dist/app/turnController.js +0 -650
- package/dist/app/turnStore.js +0 -48
- package/dist/app/uiStore.js +0 -36
- package/dist/app/useComposerState.js +0 -265
- package/dist/app/useConfigSync.js +0 -144
- package/dist/app/useInputHandlers.js +0 -403
- package/dist/app/useLongRunToolCharms.js +0 -50
- package/dist/app/useMainApp.js +0 -629
- package/dist/app/useSessionLifecycle.js +0 -175
- package/dist/app/useSubmission.js +0 -287
- package/dist/app.js +0 -15
- package/dist/banner.js +0 -57
- package/dist/components/agentsOverlay.js +0 -474
- package/dist/components/appChrome.js +0 -252
- package/dist/components/appLayout.js +0 -121
- package/dist/components/appOverlays.js +0 -65
- package/dist/components/branding.js +0 -97
- package/dist/components/fpsOverlay.js +0 -22
- package/dist/components/helpHint.js +0 -21
- package/dist/components/markdown.js +0 -501
- package/dist/components/maskedPrompt.js +0 -12
- package/dist/components/messageLine.js +0 -82
- package/dist/components/modelPicker.js +0 -254
- package/dist/components/overlayControls.js +0 -30
- package/dist/components/overlays/confirmPrompt.js +0 -25
- package/dist/components/overlays/helpOverlay.js +0 -76
- package/dist/components/overlays/historySearch.js +0 -49
- package/dist/components/overlays/modelPicker.js +0 -60
- package/dist/components/overlays/overlayUtils.js +0 -19
- package/dist/components/overlays/secretPrompt.js +0 -36
- package/dist/components/overlays/sessionPicker.js +0 -93
- package/dist/components/overlays/skillsHub.js +0 -71
- package/dist/components/prompts.js +0 -95
- package/dist/components/queuedMessages.js +0 -24
- package/dist/components/sessionPicker.js +0 -130
- package/dist/components/skillsHub.js +0 -165
- package/dist/components/streamingAssistant.js +0 -35
- package/dist/components/streamingMarkdown.js +0 -144
- package/dist/components/textInput.js +0 -794
- package/dist/components/themed.js +0 -12
- package/dist/components/thinking.js +0 -496
- package/dist/components/todoPanel.js +0 -40
- package/dist/components/transcript.js +0 -22
- package/dist/config/env.js +0 -18
- package/dist/config/limits.js +0 -22
- package/dist/config/timing.js +0 -18
- package/dist/content/charms.js +0 -5
- package/dist/content/faces.js +0 -21
- package/dist/content/fortunes.js +0 -29
- package/dist/content/hotkeys.js +0 -38
- package/dist/content/placeholders.js +0 -15
- package/dist/content/setup.js +0 -14
- package/dist/content/verbs.js +0 -41
- package/dist/domain/details.js +0 -53
- package/dist/domain/messages.js +0 -63
- package/dist/domain/paths.js +0 -16
- package/dist/domain/providers.js +0 -11
- package/dist/domain/roles.js +0 -6
- package/dist/domain/slash.js +0 -11
- package/dist/domain/usage.js +0 -1
- package/dist/domain/viewport.js +0 -33
- package/dist/gateway/client.js +0 -312
- package/dist/gatewayClient.js +0 -574
- package/dist/gatewayTypes.js +0 -1
- package/dist/hooks/useCompletion.js +0 -86
- package/dist/hooks/useGitBranch.js +0 -58
- package/dist/hooks/useInputHistory.js +0 -12
- package/dist/hooks/useQueue.js +0 -57
- package/dist/hooks/useVirtualHistory.js +0 -401
- package/dist/lib/circularBuffer.js +0 -43
- package/dist/lib/clipboard.js +0 -126
- package/dist/lib/editor.js +0 -41
- package/dist/lib/editor.test.js +0 -58
- package/dist/lib/emoji.js +0 -49
- package/dist/lib/externalCli.js +0 -11
- package/dist/lib/forceTruecolor.js +0 -26
- package/dist/lib/fpsStore.js +0 -36
- package/dist/lib/gracefulExit.js +0 -29
- package/dist/lib/history.js +0 -69
- package/dist/lib/inputMetrics.js +0 -143
- package/dist/lib/liveProgress.js +0 -51
- package/dist/lib/liveProgress.test.js +0 -89
- package/dist/lib/mathUnicode.js +0 -685
- package/dist/lib/memory.js +0 -123
- package/dist/lib/memoryMonitor.js +0 -76
- package/dist/lib/messages.js +0 -3
- package/dist/lib/messages.test.js +0 -25
- package/dist/lib/osc52.js +0 -53
- package/dist/lib/perfPane.js +0 -94
- package/dist/lib/platform.js +0 -312
- package/dist/lib/precisionWheel.js +0 -25
- package/dist/lib/reasoning.js +0 -39
- package/dist/lib/rpc.js +0 -26
- package/dist/lib/subagentTree.js +0 -287
- package/dist/lib/syntax.js +0 -89
- package/dist/lib/terminalModes.js +0 -46
- package/dist/lib/terminalParity.js +0 -48
- package/dist/lib/terminalSetup.js +0 -321
- package/dist/lib/text.js +0 -203
- package/dist/lib/text.test.js +0 -18
- package/dist/lib/todo.js +0 -2
- package/dist/lib/todo.test.js +0 -22
- package/dist/lib/viewportStore.js +0 -82
- package/dist/lib/virtualHeights.js +0 -61
- package/dist/lib/wheelAccel.js +0 -143
- package/dist/theme.js +0 -398
- package/dist/types.js +0 -1
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// SPDX-License-Identifier: MIT
|
|
4
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
5
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
6
|
-
import { Box, Link, Text } from '@cvc/ink';
|
|
7
|
-
import { Fragment, memo, useMemo } from 'react';
|
|
8
|
-
import { ensureEmojiPresentation } from '../lib/emoji.js';
|
|
9
|
-
import { BOX_CLOSE, BOX_OPEN, texToUnicode } from '../lib/mathUnicode.js';
|
|
10
|
-
import { highlightLine, isHighlightable } from '../lib/syntax.js';
|
|
11
|
-
// `\boxed{X}` regions in `texToUnicode` output are marked with the
|
|
12
|
-
// non-printable U+0001 / U+0002 sentinels. Split on them and render the
|
|
13
|
-
// boxed segment with `inverse + bold` so it reads as a highlighter-pen
|
|
14
|
-
// emphasis on top of whatever color the parent `<Text>` is using (the
|
|
15
|
-
// theme accent for math). The leading / trailing space inside the
|
|
16
|
-
// highlight gives a one-cell visual margin so the highlight reads as a
|
|
17
|
-
// block, not a hug.
|
|
18
|
-
const renderMath = (text) => {
|
|
19
|
-
if (!text.includes(BOX_OPEN)) {
|
|
20
|
-
return text;
|
|
21
|
-
}
|
|
22
|
-
const out = [];
|
|
23
|
-
let i = 0;
|
|
24
|
-
let key = 0;
|
|
25
|
-
while (i < text.length) {
|
|
26
|
-
const start = text.indexOf(BOX_OPEN, i);
|
|
27
|
-
if (start < 0) {
|
|
28
|
-
out.push(text.slice(i));
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
if (start > i) {
|
|
32
|
-
out.push(text.slice(i, start));
|
|
33
|
-
}
|
|
34
|
-
const end = text.indexOf(BOX_CLOSE, start + 1);
|
|
35
|
-
if (end < 0) {
|
|
36
|
-
out.push(text.slice(start));
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
out.push(_jsxs(Text, { bold: true, inverse: true, children: [' ', text.slice(start + 1, end), ' '] }, key++));
|
|
40
|
-
i = end + 1;
|
|
41
|
-
}
|
|
42
|
-
return out;
|
|
43
|
-
};
|
|
44
|
-
const FENCE_RE = /^\s*(`{3,}|~{3,})(.*)$/;
|
|
45
|
-
const FENCE_CLOSE_RE = /^\s*(`{3,}|~{3,})\s*$/;
|
|
46
|
-
const HR_RE = /^ {0,3}([-*_])(?:\s*\1){2,}\s*$/;
|
|
47
|
-
const HEADING_RE = /^\s{0,3}(#{1,6})\s+(.*?)(?:\s+#+\s*)?$/;
|
|
48
|
-
const SETEXT_RE = /^\s{0,3}(=+|-+)\s*$/;
|
|
49
|
-
const FOOTNOTE_RE = /^\[\^([^\]]+)\]:\s*(.*)$/;
|
|
50
|
-
const DEF_RE = /^\s*:\s+(.+)$/;
|
|
51
|
-
const BULLET_RE = /^(\s*)[-+*]\s+(.*)$/;
|
|
52
|
-
const TASK_RE = /^\[( |x|X)\]\s+(.*)$/;
|
|
53
|
-
const NUMBERED_RE = /^(\s*)(\d+)[.)]\s+(.*)$/;
|
|
54
|
-
const QUOTE_RE = /^\s*(?:>\s*)+/;
|
|
55
|
-
const TABLE_DIVIDER_CELL_RE = /^:?-{3,}:?$/;
|
|
56
|
-
const MD_URL_RE = '((?:[^\\s()]|\\([^\\s()]*\\))+?)';
|
|
57
|
-
// Display math openers: `$$ ... $$` (TeX) and `\[ ... \]` (LaTeX). The
|
|
58
|
-
// opener is matched only when `$$` / `\[` appears at the very start of the
|
|
59
|
-
// trimmed line — `startsWith('$$')` used to fire on prose like
|
|
60
|
-
// `$$x+y$$ followed by more`, opening a block that never closed because the
|
|
61
|
-
// trailing `$$` on the same line was invisible to the close-scan loop.
|
|
62
|
-
const MATH_BLOCK_OPEN_RE = /^\s*(\$\$|\\\[)(.*)$/;
|
|
63
|
-
const MATH_BLOCK_CLOSE_DOLLAR_RE = /^(.*?)\$\$\s*$/;
|
|
64
|
-
const MATH_BLOCK_CLOSE_BRACKET_RE = /^(.*?)\\\]\s*$/;
|
|
65
|
-
export const MEDIA_LINE_RE = /^\s*[`"']?MEDIA:\s*(\S+?)[`"']?\s*$/;
|
|
66
|
-
export const AUDIO_DIRECTIVE_RE = /^\s*\[\[audio_as_voice\]\]\s*$/;
|
|
67
|
-
// Inline markdown tokens, in priority order. The outer regex picks the
|
|
68
|
-
// leftmost match at each position, preferring earlier alternatives on tie —
|
|
69
|
-
// so `**` must come before `*`, `__` before `_`, etc. Each pattern owns its
|
|
70
|
-
// own capture groups; MdInline dispatches on which group matched.
|
|
71
|
-
//
|
|
72
|
-
// Subscript (`~x~`) is restricted to short alphanumeric runs so prose like
|
|
73
|
-
// `thing ~! more ~?` from Kimi / Qwen / GLM (kaomoji-style decorators)
|
|
74
|
-
// doesn't pair up the first `~` with the next one on the line and swallow
|
|
75
|
-
// the text between them as a dim `_`-prefixed span.
|
|
76
|
-
//
|
|
77
|
-
// Inline math (`$x$` and `\(x\)`) takes precedence over emphasis at the
|
|
78
|
-
// same start position because regex alternation is leftmost-first; a
|
|
79
|
-
// dollar-delimited span at column N wins over a `*` at column N+1, so
|
|
80
|
-
// `$P=a*b*c$` renders as math instead of having `*b*` corrupted into
|
|
81
|
-
// italics. Single-character minimums and "no space adjacent to delimiter"
|
|
82
|
-
// rules keep currency prose like `$5 to $10` from being swallowed.
|
|
83
|
-
export const INLINE_RE = new RegExp([
|
|
84
|
-
`!\\[(.*?)\\]\\(${MD_URL_RE}\\)`, // 1,2 image
|
|
85
|
-
`\\[(.+?)\\]\\(${MD_URL_RE}\\)`, // 3,4 link
|
|
86
|
-
`<((?:https?:\\/\\/|mailto:)[^>\\s]+|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,})>`, // 5 autolink
|
|
87
|
-
`~~(.+?)~~`, // 6 strike
|
|
88
|
-
`\`([^\\\`]+)\``, // 7 code
|
|
89
|
-
`\\*\\*(.+?)\\*\\*`, // 8 bold *
|
|
90
|
-
`(?<!\\w)__(.+?)__(?!\\w)`, // 9 bold _
|
|
91
|
-
`\\*(.+?)\\*`, // 10 italic *
|
|
92
|
-
`(?<!\\w)_(.+?)_(?!\\w)`, // 11 italic _
|
|
93
|
-
`==(.+?)==`, // 12 highlight
|
|
94
|
-
`\\[\\^([^\\]]+)\\]`, // 13 footnote ref
|
|
95
|
-
`\\^([^^\\s][^^]*?)\\^`, // 14 superscript
|
|
96
|
-
`~([A-Za-z0-9]{1,8})~`, // 15 subscript
|
|
97
|
-
`(https?:\\/\\/[^\\s<]+)`, // 16 bare URL — wrapped so it owns its own
|
|
98
|
-
// capture group; without this, the math
|
|
99
|
-
// spans below would land in m[16] and the
|
|
100
|
-
// MdInline dispatcher would treat them as
|
|
101
|
-
// bare URLs and render them as autolinks.
|
|
102
|
-
`(?<!\\$)\\$([^\\s$](?:[^$\\n]*?[^\\s$])?)\\$(?!\\$)`, // 17 inline math $...$
|
|
103
|
-
`\\\\\\(([^\\n]+?)\\\\\\)` // 18 inline math \(...\)
|
|
104
|
-
].join('|'), 'g');
|
|
105
|
-
const indentDepth = (s) => Math.floor(s.replace(/\t/g, ' ').length / 2);
|
|
106
|
-
const splitRow = (row) => row
|
|
107
|
-
.trim()
|
|
108
|
-
.replace(/^\|/, '')
|
|
109
|
-
.replace(/\|$/, '')
|
|
110
|
-
.split('|')
|
|
111
|
-
.map(c => c.trim());
|
|
112
|
-
const isTableDivider = (row) => {
|
|
113
|
-
const cells = splitRow(row);
|
|
114
|
-
return cells.length > 1 && cells.every(c => TABLE_DIVIDER_CELL_RE.test(c));
|
|
115
|
-
};
|
|
116
|
-
const autolinkUrl = (raw) => raw.startsWith('mailto:') || raw.startsWith('http') || !raw.includes('@') ? raw : `mailto:${raw}`;
|
|
117
|
-
const renderAutolink = (k, t, raw) => (_jsx(Link, { url: autolinkUrl(raw), children: _jsx(Text, { color: t.color.accent, underline: true, children: raw.replace(/^mailto:/, '') }) }, k));
|
|
118
|
-
export const stripInlineMarkup = (v) => v
|
|
119
|
-
.replace(/!\[(.*?)\]\(((?:[^\s()]|\([^\s()]*\))+?)\)/g, '[image: $1] $2')
|
|
120
|
-
.replace(/\[(.+?)\]\(((?:[^\s()]|\([^\s()]*\))+?)\)/g, '$1')
|
|
121
|
-
.replace(/<((?:https?:\/\/|mailto:)[^>\s]+|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})>/g, '$1')
|
|
122
|
-
.replace(/~~(.+?)~~/g, '$1')
|
|
123
|
-
.replace(/`([^`]+)`/g, '$1')
|
|
124
|
-
.replace(/\*\*(.+?)\*\*/g, '$1')
|
|
125
|
-
.replace(/(?<!\w)__(.+?)__(?!\w)/g, '$1')
|
|
126
|
-
.replace(/\*(.+?)\*/g, '$1')
|
|
127
|
-
.replace(/(?<!\w)_(.+?)_(?!\w)/g, '$1')
|
|
128
|
-
.replace(/==(.+?)==/g, '$1')
|
|
129
|
-
.replace(/\[\^([^\]]+)\]/g, '[$1]')
|
|
130
|
-
.replace(/\^([^^\s][^^]*?)\^/g, '^$1')
|
|
131
|
-
.replace(/~([A-Za-z0-9]{1,8})~/g, '_$1')
|
|
132
|
-
.replace(/(?<!\$)\$([^\s$](?:[^$\n]*?[^\s$])?)\$(?!\$)/g, '$1')
|
|
133
|
-
.replace(/\\\(([^\n]+?)\\\)/g, '$1');
|
|
134
|
-
const renderTable = (k, rows, t) => {
|
|
135
|
-
const widths = rows[0].map((_, ci) => Math.max(...rows.map(r => stripInlineMarkup(r[ci] ?? '').length)));
|
|
136
|
-
// Thin divider under the header. Without it tables look like prose
|
|
137
|
-
// with extra spacing because the header is just accent-coloured text
|
|
138
|
-
// (#15534). We avoid full borders on purpose — column widths come
|
|
139
|
-
// from `stripInlineMarkup(...).length` (UTF-16 code units, not
|
|
140
|
-
// display width), so a real outline often misaligns on emoji and
|
|
141
|
-
// East-Asian wide characters; one dim solid rule (`─`) under row 0
|
|
142
|
-
// plus tab-style column gaps reads cleanly on every terminal we
|
|
143
|
-
// tested.
|
|
144
|
-
const sep = widths.map(w => '─'.repeat(Math.max(1, w))).join(' ');
|
|
145
|
-
return (_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: rows.map((row, ri) => (_jsxs(Fragment, { children: [_jsx(Box, { children: widths.map((w, ci) => (_jsxs(Text, { bold: ri === 0, color: ri === 0 ? t.color.accent : undefined, children: [_jsx(MdInline, { t: t, text: row[ci] ?? '' }), ' '.repeat(Math.max(0, w - stripInlineMarkup(row[ci] ?? '').length)), ci < widths.length - 1 ? ' ' : ''] }, ci))) }), ri === 0 && rows.length > 1 ? (_jsx(Text, { color: t.color.muted, dimColor: true, children: sep })) : null] }, ri))) }, k));
|
|
146
|
-
};
|
|
147
|
-
function MdInline({ t, text }) {
|
|
148
|
-
const parts = [];
|
|
149
|
-
let last = 0;
|
|
150
|
-
for (const m of text.matchAll(INLINE_RE)) {
|
|
151
|
-
const i = m.index ?? 0;
|
|
152
|
-
const k = parts.length;
|
|
153
|
-
if (i > last) {
|
|
154
|
-
parts.push(_jsx(Text, { children: text.slice(last, i) }, k));
|
|
155
|
-
}
|
|
156
|
-
if (m[1] && m[2]) {
|
|
157
|
-
parts.push(_jsxs(Text, { color: t.color.muted, children: ["[image: ", m[1], "] ", m[2]] }, parts.length));
|
|
158
|
-
}
|
|
159
|
-
else if (m[3] && m[4]) {
|
|
160
|
-
parts.push(_jsx(Link, { url: m[4], children: _jsx(Text, { color: t.color.accent, underline: true, children: m[3] }) }, parts.length));
|
|
161
|
-
}
|
|
162
|
-
else if (m[5]) {
|
|
163
|
-
parts.push(renderAutolink(parts.length, t, m[5]));
|
|
164
|
-
}
|
|
165
|
-
else if (m[6]) {
|
|
166
|
-
parts.push(_jsx(Text, { strikethrough: true, children: _jsx(MdInline, { t: t, text: m[6] }) }, parts.length));
|
|
167
|
-
}
|
|
168
|
-
else if (m[7]) {
|
|
169
|
-
// Code is the one wrap that does NOT recurse — inline `code` spans
|
|
170
|
-
// are verbatim by definition. Letting MdInline reprocess them
|
|
171
|
-
// would corrupt regex examples and shell snippets.
|
|
172
|
-
parts.push(_jsx(Text, { color: t.color.accent, dimColor: true, children: m[7] }, parts.length));
|
|
173
|
-
}
|
|
174
|
-
else if (m[8] ?? m[9]) {
|
|
175
|
-
// Recurse into bold / italic / strike / highlight so nested
|
|
176
|
-
// `$...$` math (and other inline tokens) inside a `**bolded
|
|
177
|
-
// statement with $\mathbb{Z}$ math**` actually render. Without
|
|
178
|
-
// this the inner content is dropped into a single `<Text bold>`
|
|
179
|
-
// verbatim and the math renderer never sees it.
|
|
180
|
-
parts.push(_jsx(Text, { bold: true, children: _jsx(MdInline, { t: t, text: m[8] ?? m[9] }) }, parts.length));
|
|
181
|
-
}
|
|
182
|
-
else if (m[10] ?? m[11]) {
|
|
183
|
-
parts.push(_jsx(Text, { italic: true, children: _jsx(MdInline, { t: t, text: m[10] ?? m[11] }) }, parts.length));
|
|
184
|
-
}
|
|
185
|
-
else if (m[12]) {
|
|
186
|
-
parts.push(_jsx(Text, { backgroundColor: t.color.diffAdded, color: t.color.diffAddedWord, children: _jsx(MdInline, { t: t, text: m[12] }) }, parts.length));
|
|
187
|
-
}
|
|
188
|
-
else if (m[13]) {
|
|
189
|
-
parts.push(_jsxs(Text, { color: t.color.muted, children: ["[", m[13], "]"] }, parts.length));
|
|
190
|
-
}
|
|
191
|
-
else if (m[14]) {
|
|
192
|
-
parts.push(_jsxs(Text, { color: t.color.muted, children: ["^", m[14]] }, parts.length));
|
|
193
|
-
}
|
|
194
|
-
else if (m[15]) {
|
|
195
|
-
parts.push(_jsxs(Text, { color: t.color.muted, children: ["_", m[15]] }, parts.length));
|
|
196
|
-
}
|
|
197
|
-
else if (m[16]) {
|
|
198
|
-
// Bare URL — trim trailing prose punctuation into a sibling text node
|
|
199
|
-
// so `see https://x.com/, which…` keeps the comma outside the link.
|
|
200
|
-
const url = m[16].replace(/[),.;:!?]+$/g, '');
|
|
201
|
-
parts.push(renderAutolink(parts.length, t, url));
|
|
202
|
-
if (url.length < m[16].length) {
|
|
203
|
-
parts.push(_jsx(Text, { children: m[16].slice(url.length) }, parts.length));
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else if (m[17] ?? m[18]) {
|
|
207
|
-
// Inline math is run through `texToUnicode` (Greek letters, ℕℤℚℝ,
|
|
208
|
-
// operators, sub/superscripts, fractions) and rendered in italic
|
|
209
|
-
// accent. Italic is the disambiguator — links use accent+underline,
|
|
210
|
-
// so without italic readers can't tell `\mathbb{R}` (math) from a
|
|
211
|
-
// hyperlinked word. Anything `texToUnicode` doesn't recognise is
|
|
212
|
-
// preserved verbatim, so unfamiliar commands just look like their
|
|
213
|
-
// raw LaTeX rather than vanishing.
|
|
214
|
-
parts.push(_jsx(Text, { color: t.color.accent, italic: true, children: renderMath(texToUnicode(m[17] ?? m[18])) }, parts.length));
|
|
215
|
-
}
|
|
216
|
-
last = i + m[0].length;
|
|
217
|
-
}
|
|
218
|
-
if (last < text.length) {
|
|
219
|
-
parts.push(_jsx(Text, { children: text.slice(last) }, parts.length));
|
|
220
|
-
}
|
|
221
|
-
return _jsx(Text, { wrap: "wrap-trim", children: parts.length ? parts : text });
|
|
222
|
-
}
|
|
223
|
-
// Cross-instance parsed-children cache: useMemo's per-instance cache dies
|
|
224
|
-
// on remount, so virtualization re-parses every row that scrolls back into
|
|
225
|
-
// view. Theme-keyed WeakMap drops stale palettes; inner Map is LRU-bounded.
|
|
226
|
-
const MD_CACHE_LIMIT = 512;
|
|
227
|
-
const mdCache = new WeakMap();
|
|
228
|
-
const cacheBucket = (t) => {
|
|
229
|
-
const b = mdCache.get(t);
|
|
230
|
-
if (b) {
|
|
231
|
-
return b;
|
|
232
|
-
}
|
|
233
|
-
const fresh = new Map();
|
|
234
|
-
mdCache.set(t, fresh);
|
|
235
|
-
return fresh;
|
|
236
|
-
};
|
|
237
|
-
const cacheGet = (b, key) => {
|
|
238
|
-
const v = b.get(key);
|
|
239
|
-
if (v) {
|
|
240
|
-
b.delete(key);
|
|
241
|
-
b.set(key, v);
|
|
242
|
-
}
|
|
243
|
-
return v;
|
|
244
|
-
};
|
|
245
|
-
const cacheSet = (b, key, v) => {
|
|
246
|
-
b.set(key, v);
|
|
247
|
-
if (b.size > MD_CACHE_LIMIT) {
|
|
248
|
-
b.delete(b.keys().next().value);
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
function MdImpl({ compact, t, text }) {
|
|
252
|
-
const nodes = useMemo(() => {
|
|
253
|
-
const bucket = cacheBucket(t);
|
|
254
|
-
const cacheKey = `${compact ? '1' : '0'}|${text}`;
|
|
255
|
-
const cached = cacheGet(bucket, cacheKey);
|
|
256
|
-
if (cached) {
|
|
257
|
-
return cached;
|
|
258
|
-
}
|
|
259
|
-
const lines = ensureEmojiPresentation(text).split('\n');
|
|
260
|
-
const nodes = [];
|
|
261
|
-
let prevKind = null;
|
|
262
|
-
let i = 0;
|
|
263
|
-
const gap = () => {
|
|
264
|
-
if (nodes.length && prevKind !== 'blank') {
|
|
265
|
-
nodes.push(_jsx(Text, { children: " " }, `gap-${nodes.length}`));
|
|
266
|
-
prevKind = 'blank';
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
const start = (kind) => {
|
|
270
|
-
if (prevKind && prevKind !== 'blank' && prevKind !== kind) {
|
|
271
|
-
gap();
|
|
272
|
-
}
|
|
273
|
-
prevKind = kind;
|
|
274
|
-
};
|
|
275
|
-
while (i < lines.length) {
|
|
276
|
-
const line = lines[i];
|
|
277
|
-
const key = nodes.length;
|
|
278
|
-
if (!line.trim()) {
|
|
279
|
-
if (!compact) {
|
|
280
|
-
gap();
|
|
281
|
-
}
|
|
282
|
-
i++;
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
if (AUDIO_DIRECTIVE_RE.test(line)) {
|
|
286
|
-
i++;
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
const media = line.match(MEDIA_LINE_RE)?.[1];
|
|
290
|
-
if (media) {
|
|
291
|
-
start('paragraph');
|
|
292
|
-
nodes.push(_jsxs(Text, { color: t.color.muted, wrap: "wrap-trim", children: ['▸ ', _jsx(Link, { url: /^(?:\/|[a-z]:[\\/])/i.test(media) ? `file://${media}` : media, children: _jsx(Text, { color: t.color.accent, underline: true, children: media }) })] }, key));
|
|
293
|
-
i++;
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
const fence = line.match(FENCE_RE);
|
|
297
|
-
if (fence) {
|
|
298
|
-
const char = fence[1][0];
|
|
299
|
-
const len = fence[1].length;
|
|
300
|
-
const lang = fence[2].trim().toLowerCase();
|
|
301
|
-
const block = [];
|
|
302
|
-
for (i++; i < lines.length; i++) {
|
|
303
|
-
const close = lines[i].match(FENCE_CLOSE_RE)?.[1];
|
|
304
|
-
if (close && close[0] === char && close.length >= len) {
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
block.push(lines[i]);
|
|
308
|
-
}
|
|
309
|
-
if (i < lines.length) {
|
|
310
|
-
i++;
|
|
311
|
-
}
|
|
312
|
-
if (['md', 'markdown'].includes(lang)) {
|
|
313
|
-
start('paragraph');
|
|
314
|
-
nodes.push(_jsx(Md, { compact: compact, t: t, text: block.join('\n') }, key));
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
start('code');
|
|
318
|
-
const isDiff = lang === 'diff';
|
|
319
|
-
const highlighted = !isDiff && isHighlightable(lang);
|
|
320
|
-
nodes.push(_jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [lang && !isDiff && _jsx(Text, { color: t.color.muted, children: '─ ' + lang }), block.map((l, j) => {
|
|
321
|
-
if (highlighted) {
|
|
322
|
-
return (_jsx(Text, { children: highlightLine(l, lang, t).map(([color, text], kk) => color ? (_jsx(Text, { color: color, children: text }, kk)) : (_jsx(Text, { children: text }, kk))) }, j));
|
|
323
|
-
}
|
|
324
|
-
const add = isDiff && l.startsWith('+');
|
|
325
|
-
const del = isDiff && l.startsWith('-');
|
|
326
|
-
const hunk = isDiff && l.startsWith('@@');
|
|
327
|
-
return (_jsx(Text, { backgroundColor: add ? t.color.diffAdded : del ? t.color.diffRemoved : undefined, color: add ? t.color.diffAddedWord : del ? t.color.diffRemovedWord : hunk ? t.color.muted : undefined, dimColor: isDiff && !add && !del && !hunk && l.startsWith(' '), children: l }, j));
|
|
328
|
-
})] }, key));
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
const mathOpen = line.match(MATH_BLOCK_OPEN_RE);
|
|
332
|
-
if (mathOpen) {
|
|
333
|
-
const opener = mathOpen[1];
|
|
334
|
-
const closeRe = opener === '$$' ? MATH_BLOCK_CLOSE_DOLLAR_RE : MATH_BLOCK_CLOSE_BRACKET_RE;
|
|
335
|
-
const headRest = mathOpen[2] ?? '';
|
|
336
|
-
const block = [];
|
|
337
|
-
// Single-line block: `$$x + y = z$$` or `\[x\]`. Capture inner content
|
|
338
|
-
// and emit the block immediately. Without this, the close-scan loop
|
|
339
|
-
// skips line `i` and treats the next opener as our closer, swallowing
|
|
340
|
-
// every paragraph in between.
|
|
341
|
-
const sameLineClose = headRest.match(closeRe);
|
|
342
|
-
if (sameLineClose) {
|
|
343
|
-
const inner = sameLineClose[1].trim();
|
|
344
|
-
start('code');
|
|
345
|
-
nodes.push(_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: inner ? _jsx(Text, { color: t.color.accent, children: renderMath(texToUnicode(inner)) }) : null }, key));
|
|
346
|
-
i++;
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
// Multi-line block: scan ahead for a real closer before committing.
|
|
350
|
-
// If none exists in the rest of the document, render this line as a
|
|
351
|
-
// paragraph instead of consuming everything that follows.
|
|
352
|
-
let closeIdx = -1;
|
|
353
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
354
|
-
if (closeRe.test(lines[j])) {
|
|
355
|
-
closeIdx = j;
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (closeIdx < 0) {
|
|
360
|
-
start('paragraph');
|
|
361
|
-
nodes.push(_jsx(MdInline, { t: t, text: line }, key));
|
|
362
|
-
i++;
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
if (headRest.trim()) {
|
|
366
|
-
block.push(headRest);
|
|
367
|
-
}
|
|
368
|
-
for (let j = i + 1; j < closeIdx; j++) {
|
|
369
|
-
block.push(lines[j]);
|
|
370
|
-
}
|
|
371
|
-
const tail = lines[closeIdx].match(closeRe)[1].trimEnd();
|
|
372
|
-
if (tail.trim()) {
|
|
373
|
-
block.push(tail);
|
|
374
|
-
}
|
|
375
|
-
start('code');
|
|
376
|
-
nodes.push(_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: block.map((l, j) => (_jsx(Text, { color: t.color.accent, children: renderMath(texToUnicode(l)) }, j))) }, key));
|
|
377
|
-
i = closeIdx + 1;
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
const heading = line.match(HEADING_RE)?.[2];
|
|
381
|
-
if (heading) {
|
|
382
|
-
start('heading');
|
|
383
|
-
nodes.push(_jsx(Text, { bold: true, color: t.color.accent, wrap: "wrap-trim", children: _jsx(MdInline, { t: t, text: heading }) }, key));
|
|
384
|
-
i++;
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
if (i + 1 < lines.length && SETEXT_RE.test(lines[i + 1])) {
|
|
388
|
-
start('heading');
|
|
389
|
-
nodes.push(_jsx(Text, { bold: true, color: t.color.accent, wrap: "wrap-trim", children: _jsx(MdInline, { t: t, text: line.trim() }) }, key));
|
|
390
|
-
i += 2;
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (HR_RE.test(line)) {
|
|
394
|
-
start('rule');
|
|
395
|
-
nodes.push(_jsx(Text, { color: t.color.muted, children: '─'.repeat(36) }, key));
|
|
396
|
-
i++;
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
const footnote = line.match(FOOTNOTE_RE);
|
|
400
|
-
if (footnote) {
|
|
401
|
-
start('list');
|
|
402
|
-
nodes.push(_jsxs(Text, { color: t.color.muted, wrap: "wrap-trim", children: ["[", footnote[1], "] ", _jsx(MdInline, { t: t, text: footnote[2] ?? '' })] }, key));
|
|
403
|
-
i++;
|
|
404
|
-
while (i < lines.length && /^\s{2,}\S/.test(lines[i])) {
|
|
405
|
-
nodes.push(_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: t.color.muted, wrap: "wrap-trim", children: _jsx(MdInline, { t: t, text: lines[i].trim() }) }) }, `${key}-cont-${i}`));
|
|
406
|
-
i++;
|
|
407
|
-
}
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
if (i + 1 < lines.length && DEF_RE.test(lines[i + 1])) {
|
|
411
|
-
start('list');
|
|
412
|
-
nodes.push(_jsx(Text, { bold: true, wrap: "wrap-trim", children: line.trim() }, key));
|
|
413
|
-
i++;
|
|
414
|
-
while (i < lines.length) {
|
|
415
|
-
const def = lines[i].match(DEF_RE)?.[1];
|
|
416
|
-
if (!def) {
|
|
417
|
-
break;
|
|
418
|
-
}
|
|
419
|
-
nodes.push(_jsxs(Text, { wrap: "wrap-trim", children: [_jsx(Text, { color: t.color.muted, children: " \u00B7 " }), _jsx(MdInline, { t: t, text: def })] }, `${key}-def-${i}`));
|
|
420
|
-
i++;
|
|
421
|
-
}
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
const bullet = line.match(BULLET_RE);
|
|
425
|
-
if (bullet) {
|
|
426
|
-
start('list');
|
|
427
|
-
const task = bullet[2].match(TASK_RE);
|
|
428
|
-
const marker = task ? (task[1].toLowerCase() === 'x' ? '☑' : '☐') : '•';
|
|
429
|
-
nodes.push(_jsx(Box, { paddingLeft: indentDepth(bullet[1]) * 2, children: _jsxs(Text, { wrap: "wrap-trim", children: [_jsxs(Text, { color: t.color.muted, children: [marker, " "] }), _jsx(MdInline, { t: t, text: task ? task[2] : bullet[2] })] }) }, key));
|
|
430
|
-
i++;
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
const numbered = line.match(NUMBERED_RE);
|
|
434
|
-
if (numbered) {
|
|
435
|
-
start('list');
|
|
436
|
-
nodes.push(_jsx(Box, { paddingLeft: indentDepth(numbered[1]) * 2, children: _jsxs(Text, { wrap: "wrap-trim", children: [_jsxs(Text, { color: t.color.muted, children: [numbered[2], ". "] }), _jsx(MdInline, { t: t, text: numbered[3] })] }) }, key));
|
|
437
|
-
i++;
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
440
|
-
if (QUOTE_RE.test(line)) {
|
|
441
|
-
start('quote');
|
|
442
|
-
const quoteLines = [];
|
|
443
|
-
while (i < lines.length && QUOTE_RE.test(lines[i])) {
|
|
444
|
-
const prefix = lines[i].match(QUOTE_RE)?.[0] ?? '';
|
|
445
|
-
quoteLines.push({ depth: (prefix.match(/>/g) ?? []).length, text: lines[i].slice(prefix.length) });
|
|
446
|
-
i++;
|
|
447
|
-
}
|
|
448
|
-
nodes.push(_jsx(Box, { flexDirection: "column", children: quoteLines.map((ql, qi) => (_jsx(Box, { paddingLeft: Math.max(0, ql.depth - 1) * 2, children: _jsxs(Text, { color: t.color.muted, wrap: "wrap-trim", children: ["\u2502 ", _jsx(MdInline, { t: t, text: ql.text })] }) }, qi))) }, key));
|
|
449
|
-
continue;
|
|
450
|
-
}
|
|
451
|
-
if (line.includes('|') && i + 1 < lines.length && isTableDivider(lines[i + 1])) {
|
|
452
|
-
start('table');
|
|
453
|
-
const rows = [splitRow(line)];
|
|
454
|
-
for (i += 2; i < lines.length && lines[i].includes('|') && lines[i].trim(); i++) {
|
|
455
|
-
rows.push(splitRow(lines[i]));
|
|
456
|
-
}
|
|
457
|
-
nodes.push(renderTable(key, rows, t));
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
if (/^<\/?details\b/i.test(line)) {
|
|
461
|
-
i++;
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
|
-
const summary = line.match(/^<summary>(.*?)<\/summary>$/i)?.[1];
|
|
465
|
-
if (summary) {
|
|
466
|
-
start('paragraph');
|
|
467
|
-
nodes.push(_jsxs(Text, { color: t.color.muted, wrap: "wrap-trim", children: ["\u25B6 ", summary] }, key));
|
|
468
|
-
i++;
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
if (/^<\/?[^>]+>$/.test(line.trim())) {
|
|
472
|
-
start('paragraph');
|
|
473
|
-
nodes.push(_jsx(Text, { color: t.color.muted, wrap: "wrap-trim", children: line.trim() }, key));
|
|
474
|
-
i++;
|
|
475
|
-
continue;
|
|
476
|
-
}
|
|
477
|
-
if (line.includes('|') && line.trim().startsWith('|')) {
|
|
478
|
-
start('table');
|
|
479
|
-
const rows = [];
|
|
480
|
-
while (i < lines.length && lines[i].trim().startsWith('|')) {
|
|
481
|
-
const row = lines[i].trim();
|
|
482
|
-
if (!/^[|\s:-]+$/.test(row)) {
|
|
483
|
-
rows.push(splitRow(row));
|
|
484
|
-
}
|
|
485
|
-
i++;
|
|
486
|
-
}
|
|
487
|
-
if (rows.length) {
|
|
488
|
-
nodes.push(renderTable(key, rows, t));
|
|
489
|
-
}
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
start('paragraph');
|
|
493
|
-
nodes.push(_jsx(MdInline, { t: t, text: line }, key));
|
|
494
|
-
i++;
|
|
495
|
-
}
|
|
496
|
-
cacheSet(bucket, cacheKey, nodes);
|
|
497
|
-
return nodes;
|
|
498
|
-
}, [compact, t, text]);
|
|
499
|
-
return _jsx(Box, { flexDirection: "column", children: nodes });
|
|
500
|
-
}
|
|
501
|
-
export const Md = memo(MdImpl);
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// SPDX-License-Identifier: MIT
|
|
4
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
5
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
6
|
-
import { Box, Text } from '@cvc/ink';
|
|
7
|
-
import { useState } from 'react';
|
|
8
|
-
import { TextInput } from './textInput.js';
|
|
9
|
-
export function MaskedPrompt({ cols = 80, icon, label, onSubmit, sub, t }) {
|
|
10
|
-
const [value, setValue] = useState('');
|
|
11
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: t.color.warn, children: [icon, " ", label] }), sub && _jsxs(Text, { color: t.color.muted, children: [" ", sub] }), _jsxs(Box, { children: [_jsx(Text, { color: t.color.label, children: '> ' }), _jsx(TextInput, { columns: Math.max(20, cols - 6), mask: "*", onChange: setValue, onSubmit: onSubmit, value: value })] })] }));
|
|
12
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// SPDX-License-Identifier: MIT
|
|
4
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
5
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
6
|
-
import { Ansi, Box, NoSelect, Text } from '@cvc/ink';
|
|
7
|
-
import { memo, useState } from 'react';
|
|
8
|
-
import { LONG_MSG } from '../config/limits.js';
|
|
9
|
-
import { sectionMode } from '../domain/details.js';
|
|
10
|
-
import { userDisplay } from '../domain/messages.js';
|
|
11
|
-
import { ROLE } from '../domain/roles.js';
|
|
12
|
-
import { transcriptBodyWidth, transcriptGutterWidth } from '../lib/inputMetrics.js';
|
|
13
|
-
import { boundedHistoryRenderText, boundedLiveRenderText, compactPreview, hasAnsi, isPasteBackedText, stripAnsi } from '../lib/text.js';
|
|
14
|
-
import { Md } from './markdown.js';
|
|
15
|
-
import { StreamingMd } from './streamingMarkdown.js';
|
|
16
|
-
import { ToolTrail } from './thinking.js';
|
|
17
|
-
import { TodoPanel } from './todoPanel.js';
|
|
18
|
-
// Collapse threshold for long system messages (system prompt etc.)
|
|
19
|
-
const SYSTEM_COLLAPSE_CHARS = 400;
|
|
20
|
-
export const MessageLine = memo(function MessageLine({ cols, compact, detailsMode = 'collapsed', detailsModeCommandOverride = false, isStreaming = false, limitHistoryRender = false, msg, sections, t, tools = [] }) {
|
|
21
|
-
// Per-section overrides win over the global mode, so resolve each section
|
|
22
|
-
// we might consume here once and gate visibility on the *content-bearing*
|
|
23
|
-
// sections only — never on the global mode. A `trail` message feeds Tool
|
|
24
|
-
// calls + Activity; an assistant message with thinking/tools metadata
|
|
25
|
-
// feeds Thinking + Tool calls. Gating on every section would let
|
|
26
|
-
// `thinking` (expanded by default) keep an empty wrapper alive when only
|
|
27
|
-
// `tools` is hidden — exactly the empty-Box bug Copilot caught.
|
|
28
|
-
const thinkingMode = sectionMode('thinking', detailsMode, sections, detailsModeCommandOverride);
|
|
29
|
-
const toolsMode = sectionMode('tools', detailsMode, sections, detailsModeCommandOverride);
|
|
30
|
-
const activityMode = sectionMode('activity', detailsMode, sections, detailsModeCommandOverride);
|
|
31
|
-
const thinking = msg.thinking?.trim() ?? '';
|
|
32
|
-
// Collapse toggle for long system messages
|
|
33
|
-
const systemIsLong = msg.role === 'system' && msg.text.length > SYSTEM_COLLAPSE_CHARS;
|
|
34
|
-
const [systemOpen, setSystemOpen] = useState(false);
|
|
35
|
-
if (msg.kind === 'trail' && msg.todos?.length) {
|
|
36
|
-
return (_jsx(TodoPanel, { defaultCollapsed: msg.todoCollapsedByDefault, incomplete: msg.todoIncomplete, t: t, todos: msg.todos }));
|
|
37
|
-
}
|
|
38
|
-
if (msg.kind === 'trail' && (msg.tools?.length || tools.length || thinking)) {
|
|
39
|
-
return thinkingMode !== 'hidden' || toolsMode !== 'hidden' || activityMode !== 'hidden' ? (_jsx(Box, { flexDirection: "column", children: _jsx(ToolTrail, { commandOverride: detailsModeCommandOverride, detailsMode: detailsMode, reasoning: thinking, reasoningTokens: msg.thinkingTokens, sections: sections, t: t, tools: tools, toolTokens: msg.toolTokens, trail: msg.tools ?? [] }) })) : null;
|
|
40
|
-
}
|
|
41
|
-
if (msg.role === 'tool') {
|
|
42
|
-
const maxChars = Math.max(24, cols - 14);
|
|
43
|
-
const stripped = hasAnsi(msg.text) ? stripAnsi(msg.text) : msg.text;
|
|
44
|
-
const preview = compactPreview(stripped, maxChars) || '(empty tool result)';
|
|
45
|
-
return (_jsx(Box, { alignSelf: "flex-start", borderColor: t.color.muted, borderStyle: "round", marginLeft: 3, paddingX: 1, children: hasAnsi(msg.text) ? (_jsx(Text, { wrap: "truncate-end", children: _jsx(Ansi, { children: msg.text }) })) : (_jsx(Text, { color: t.color.muted, wrap: "truncate-end", children: preview })) }));
|
|
46
|
-
}
|
|
47
|
-
const { body, glyph, prefix } = ROLE[msg.role](t);
|
|
48
|
-
const gutterWidth = transcriptGutterWidth(msg.role, t.brand.prompt);
|
|
49
|
-
const showDetails = (toolsMode !== 'hidden' && Boolean(msg.tools?.length)) || (thinkingMode !== 'hidden' && Boolean(thinking));
|
|
50
|
-
const content = (() => {
|
|
51
|
-
if (msg.kind === 'slash') {
|
|
52
|
-
return _jsx(Text, { color: t.color.muted, children: msg.text });
|
|
53
|
-
}
|
|
54
|
-
// ── Collapsible long system message (system prompt, AGENTS.md, etc.) ──
|
|
55
|
-
// MUST come before the hasAnsi check — system messages from the backend
|
|
56
|
-
// contain Rich markup escape codes that would otherwise hit <Ansi> full render.
|
|
57
|
-
if (systemIsLong) {
|
|
58
|
-
const firstLine = (msg.text.split('\n')[0] ?? '').trim().slice(0, 120) || '(system message)';
|
|
59
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { onClick: () => setSystemOpen(v => !v), children: [_jsx(Text, { color: t.color.accent, children: systemOpen ? '▾ ' : '▸ ' }), _jsx(Text, { color: t.color.muted, children: firstLine }), _jsxs(Text, { color: t.color.muted, dimColor: true, children: [' — ', msg.text.length.toLocaleString(), " chars"] })] }), systemOpen && _jsx(Ansi, { children: msg.text })] }));
|
|
60
|
-
}
|
|
61
|
-
if (msg.role !== 'user' && hasAnsi(msg.text)) {
|
|
62
|
-
return _jsx(Ansi, { children: msg.text });
|
|
63
|
-
}
|
|
64
|
-
if (msg.role === 'assistant') {
|
|
65
|
-
return isStreaming ? (
|
|
66
|
-
// Incremental markdown: split at the last stable block boundary so
|
|
67
|
-
// only the in-flight tail re-tokenizes per delta. See
|
|
68
|
-
// streamingMarkdown.tsx for the cost model.
|
|
69
|
-
_jsx(StreamingMd, { compact: compact, t: t, text: boundedLiveRenderText(msg.text) })) : (_jsx(Md, { compact: compact, t: t, text: limitHistoryRender ? boundedHistoryRenderText(msg.text) : msg.text }));
|
|
70
|
-
}
|
|
71
|
-
if (msg.role === 'user' && msg.text.length > LONG_MSG && isPasteBackedText(msg.text)) {
|
|
72
|
-
const [head, ...rest] = userDisplay(msg.text).split('[long message]');
|
|
73
|
-
return (_jsxs(Text, { color: body, children: [head, _jsx(Text, { color: t.color.muted, dimColor: true, children: "[long message]" }), rest.join('')] }));
|
|
74
|
-
}
|
|
75
|
-
return _jsx(Text, { ...(body ? { color: body } : {}), children: msg.text });
|
|
76
|
-
})();
|
|
77
|
-
// Diff segments (emitted by pushInlineDiffSegment between narration
|
|
78
|
-
// segments) need a blank line on both sides so the patch doesn't butt up
|
|
79
|
-
// against the prose around it.
|
|
80
|
-
const isDiffSegment = msg.kind === 'diff';
|
|
81
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: msg.role === 'user' || isDiffSegment ? 1 : 0, marginTop: msg.role === 'user' || msg.kind === 'slash' || isDiffSegment ? 1 : 0, children: [showDetails && (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsx(ToolTrail, { commandOverride: detailsModeCommandOverride, detailsMode: detailsMode, reasoning: thinking, reasoningTokens: msg.thinkingTokens, sections: sections, t: t, toolTokens: msg.toolTokens, trail: msg.tools }) })), _jsxs(Box, { children: [_jsx(NoSelect, { flexShrink: 0, fromLeftEdge: true, width: gutterWidth, children: _jsxs(Text, { bold: msg.role === 'user', color: prefix, children: [glyph, ' '] }) }), _jsx(Box, { width: transcriptBodyWidth(cols, msg.role, t.brand.prompt), children: content })] })] }));
|
|
82
|
-
});
|