@bubblebrain-ai/bubble 0.0.7 → 0.0.8
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/agent.d.ts +6 -0
- package/dist/agent.js +36 -3
- package/dist/context/budget.d.ts +1 -0
- package/dist/context/budget.js +1 -1
- package/dist/context/usage.d.ts +34 -0
- package/dist/context/usage.js +213 -0
- package/dist/diff-stats.d.ts +5 -0
- package/dist/diff-stats.js +21 -0
- package/dist/main.js +28 -4
- package/dist/mcp/transports.d.ts +1 -0
- package/dist/mcp/transports.js +8 -0
- package/dist/model-catalog.js +1 -1
- package/dist/orchestrator/default-hooks.js +6 -18
- package/dist/prompt/compose.js +2 -1
- package/dist/prompt/provider-prompts/kimi.js +3 -1
- package/dist/provider-registry.js +3 -3
- package/dist/provider-transform.d.ts +3 -1
- package/dist/provider-transform.js +15 -0
- package/dist/provider.d.ts +4 -1
- package/dist/provider.js +89 -4
- package/dist/reasoning-debug.d.ts +7 -0
- package/dist/reasoning-debug.js +30 -0
- package/dist/session-log.js +13 -2
- package/dist/session-types.d.ts +1 -1
- package/dist/slash-commands/commands.js +36 -2
- package/dist/tools/edit.js +5 -0
- package/dist/tools/file-state.d.ts +19 -0
- package/dist/tools/file-state.js +15 -0
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.js +92 -11
- package/dist/tui/escape-confirmation.d.ts +15 -0
- package/dist/tui/escape-confirmation.js +30 -0
- package/dist/tui/run.js +93 -23
- package/dist/tui-ink/app.d.ts +43 -0
- package/dist/tui-ink/app.js +1016 -0
- package/dist/tui-ink/approval/approval-dialog.d.ts +13 -0
- package/dist/tui-ink/approval/approval-dialog.js +129 -0
- package/dist/tui-ink/approval/diff-view.d.ts +7 -0
- package/dist/tui-ink/approval/diff-view.js +43 -0
- package/dist/tui-ink/approval/select.d.ts +35 -0
- package/dist/tui-ink/approval/select.js +87 -0
- package/dist/tui-ink/code-highlight.d.ts +6 -0
- package/dist/tui-ink/code-highlight.js +94 -0
- package/dist/tui-ink/display-history.d.ts +38 -0
- package/dist/tui-ink/display-history.js +130 -0
- package/dist/tui-ink/edit-diff.d.ts +11 -0
- package/dist/tui-ink/edit-diff.js +52 -0
- package/dist/tui-ink/file-mentions.d.ts +29 -0
- package/dist/tui-ink/file-mentions.js +174 -0
- package/dist/tui-ink/footer.d.ts +19 -0
- package/dist/tui-ink/footer.js +44 -0
- package/dist/tui-ink/image-paste.d.ts +54 -0
- package/dist/tui-ink/image-paste.js +288 -0
- package/dist/tui-ink/input-box.d.ts +41 -0
- package/dist/tui-ink/input-box.js +637 -0
- package/dist/tui-ink/markdown.d.ts +38 -0
- package/dist/tui-ink/markdown.js +384 -0
- package/dist/tui-ink/message-list.d.ts +33 -0
- package/dist/tui-ink/message-list.js +571 -0
- package/dist/tui-ink/model-picker.d.ts +43 -0
- package/dist/tui-ink/model-picker.js +326 -0
- package/dist/tui-ink/plan-confirm.d.ts +7 -0
- package/dist/tui-ink/plan-confirm.js +104 -0
- package/dist/tui-ink/question-dialog.d.ts +8 -0
- package/dist/tui-ink/question-dialog.js +98 -0
- package/dist/tui-ink/recent-activity.d.ts +8 -0
- package/dist/tui-ink/recent-activity.js +71 -0
- package/dist/tui-ink/run.d.ts +33 -0
- package/dist/tui-ink/run.js +25 -0
- package/dist/tui-ink/theme.d.ts +37 -0
- package/dist/tui-ink/theme.js +42 -0
- package/dist/tui-ink/todos.d.ts +7 -0
- package/dist/tui-ink/todos.js +44 -0
- package/dist/tui-ink/trace-groups.d.ts +25 -0
- package/dist/tui-ink/trace-groups.js +310 -0
- package/dist/tui-ink/use-terminal-size.d.ts +4 -0
- package/dist/tui-ink/use-terminal-size.js +21 -0
- package/dist/tui-ink/welcome.d.ts +18 -0
- package/dist/tui-ink/welcome.js +119 -0
- package/dist/types.d.ts +4 -0
- package/package.json +6 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight Markdown renderer for Ink TUI.
|
|
4
|
+
* Supports code blocks, inline formatting, and tables.
|
|
5
|
+
*/
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { Box, Text } from "ink";
|
|
8
|
+
import stringWidth from "string-width";
|
|
9
|
+
import { useTerminalSize } from "./use-terminal-size.js";
|
|
10
|
+
import { theme } from "./theme.js";
|
|
11
|
+
import { highlightCode } from "./code-highlight.js";
|
|
12
|
+
const graphemeSegmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function"
|
|
13
|
+
? new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
14
|
+
: null;
|
|
15
|
+
function splitGraphemes(text) {
|
|
16
|
+
if (!text)
|
|
17
|
+
return [];
|
|
18
|
+
if (graphemeSegmenter) {
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const { segment } of graphemeSegmenter.segment(text))
|
|
21
|
+
out.push(segment);
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
return Array.from(text);
|
|
25
|
+
}
|
|
26
|
+
export function parseMarkdownBlocks(text) {
|
|
27
|
+
const lines = text.split("\n");
|
|
28
|
+
const blocks = [];
|
|
29
|
+
let i = 0;
|
|
30
|
+
while (i < lines.length) {
|
|
31
|
+
const line = lines[i];
|
|
32
|
+
// Code block
|
|
33
|
+
if (line.startsWith("```")) {
|
|
34
|
+
const lang = line.slice(3).trim();
|
|
35
|
+
i++;
|
|
36
|
+
const codeLines = [];
|
|
37
|
+
while (i < lines.length && !lines[i].startsWith("```")) {
|
|
38
|
+
codeLines.push(lines[i]);
|
|
39
|
+
i++;
|
|
40
|
+
}
|
|
41
|
+
blocks.push({ type: "code", lang, lines: codeLines });
|
|
42
|
+
i++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Table
|
|
46
|
+
if (line.trim().startsWith("|")) {
|
|
47
|
+
const tableLines = [];
|
|
48
|
+
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
49
|
+
tableLines.push(lines[i]);
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
52
|
+
if (tableLines.length >= 2) {
|
|
53
|
+
const headers = parseTableRow(tableLines[0]);
|
|
54
|
+
if (headers.length > 0 && isTableSeparatorRow(tableLines[1], headers.length)) {
|
|
55
|
+
const rows = tableLines.slice(2).map((rowLine) => normalizeTableRow(parseTableRow(rowLine), headers.length));
|
|
56
|
+
blocks.push({ type: "table", headers, rows });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
blocks.push({ type: "paragraph", lines: tableLines });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
blocks.push({ type: "paragraph", lines: tableLines });
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Heading
|
|
68
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
|
|
69
|
+
if (headingMatch) {
|
|
70
|
+
blocks.push({ type: "heading", level: headingMatch[1].length, text: headingMatch[2].trim() });
|
|
71
|
+
i++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Empty line -> skip
|
|
75
|
+
if (line.trim() === "") {
|
|
76
|
+
i++;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// Paragraph
|
|
80
|
+
const paraLines = [];
|
|
81
|
+
while (i < lines.length &&
|
|
82
|
+
lines[i].trim() !== "" &&
|
|
83
|
+
!lines[i].startsWith("```") &&
|
|
84
|
+
!lines[i].trim().startsWith("|")) {
|
|
85
|
+
paraLines.push(lines[i]);
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
blocks.push({ type: "paragraph", lines: paraLines });
|
|
89
|
+
}
|
|
90
|
+
return blocks;
|
|
91
|
+
}
|
|
92
|
+
function parseTableRow(line) {
|
|
93
|
+
let body = line.trim();
|
|
94
|
+
if (body.startsWith("|"))
|
|
95
|
+
body = body.slice(1);
|
|
96
|
+
if (endsWithUnescapedPipe(body))
|
|
97
|
+
body = body.slice(0, -1);
|
|
98
|
+
const cells = [];
|
|
99
|
+
let current = "";
|
|
100
|
+
let inCode = false;
|
|
101
|
+
for (let i = 0; i < body.length; i++) {
|
|
102
|
+
const char = body[i];
|
|
103
|
+
if (char === "\\" && i + 1 < body.length) {
|
|
104
|
+
current += body[i + 1];
|
|
105
|
+
i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (char === "`") {
|
|
109
|
+
inCode = !inCode;
|
|
110
|
+
current += char;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (char === "|" && !inCode) {
|
|
114
|
+
cells.push(current.trim());
|
|
115
|
+
current = "";
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
current += char;
|
|
119
|
+
}
|
|
120
|
+
cells.push(current.trim());
|
|
121
|
+
return cells;
|
|
122
|
+
}
|
|
123
|
+
function endsWithUnescapedPipe(text) {
|
|
124
|
+
if (!text.endsWith("|"))
|
|
125
|
+
return false;
|
|
126
|
+
let slashCount = 0;
|
|
127
|
+
for (let i = text.length - 2; i >= 0 && text[i] === "\\"; i--)
|
|
128
|
+
slashCount++;
|
|
129
|
+
return slashCount % 2 === 0;
|
|
130
|
+
}
|
|
131
|
+
function isTableSeparatorRow(line, expectedColumns) {
|
|
132
|
+
const cells = parseTableRow(line);
|
|
133
|
+
if (cells.length !== expectedColumns)
|
|
134
|
+
return false;
|
|
135
|
+
return cells.every((cell) => /^:?-{3,}:?$/.test(cell.replace(/\s+/g, "")));
|
|
136
|
+
}
|
|
137
|
+
function normalizeTableRow(row, colCount) {
|
|
138
|
+
const normalized = row.slice(0, colCount);
|
|
139
|
+
while (normalized.length < colCount)
|
|
140
|
+
normalized.push("");
|
|
141
|
+
return normalized;
|
|
142
|
+
}
|
|
143
|
+
function visualWidth(str) {
|
|
144
|
+
if (!str)
|
|
145
|
+
return 0;
|
|
146
|
+
return stringWidth(str);
|
|
147
|
+
}
|
|
148
|
+
function graphemeWidth(grapheme) {
|
|
149
|
+
if (!grapheme)
|
|
150
|
+
return 0;
|
|
151
|
+
return stringWidth(grapheme);
|
|
152
|
+
}
|
|
153
|
+
// Inline formatting: bold, italic, inline code
|
|
154
|
+
export function parseMarkdownInlineSegments(text, style = {}) {
|
|
155
|
+
const segments = [];
|
|
156
|
+
let buffer = "";
|
|
157
|
+
let i = 0;
|
|
158
|
+
const flush = () => {
|
|
159
|
+
appendInlineSegment(segments, buffer, style);
|
|
160
|
+
buffer = "";
|
|
161
|
+
};
|
|
162
|
+
while (i < text.length) {
|
|
163
|
+
const char = text[i];
|
|
164
|
+
if (char === "\\" && i + 1 < text.length) {
|
|
165
|
+
buffer += text[i + 1];
|
|
166
|
+
i += 2;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (char === "`") {
|
|
170
|
+
const close = findClosingMarker(text, "`", i + 1);
|
|
171
|
+
if (close !== -1) {
|
|
172
|
+
flush();
|
|
173
|
+
appendInlineSegment(segments, text.slice(i + 1, close), { ...style, code: true });
|
|
174
|
+
i = close + 1;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const marker = inlineMarkerAt(text, i);
|
|
179
|
+
if (marker) {
|
|
180
|
+
const close = findClosingMarker(text, marker, i + marker.length);
|
|
181
|
+
if (close !== -1 && close > i + marker.length) {
|
|
182
|
+
flush();
|
|
183
|
+
const inner = text.slice(i + marker.length, close);
|
|
184
|
+
const nextStyle = marker === "***"
|
|
185
|
+
? { ...style, bold: true, italic: true }
|
|
186
|
+
: marker === "**" || marker === "__"
|
|
187
|
+
? { ...style, bold: true }
|
|
188
|
+
: { ...style, italic: true };
|
|
189
|
+
for (const segment of parseMarkdownInlineSegments(inner, nextStyle)) {
|
|
190
|
+
appendInlineSegment(segments, segment.text, segment);
|
|
191
|
+
}
|
|
192
|
+
i = close + marker.length;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
buffer += char;
|
|
197
|
+
i++;
|
|
198
|
+
}
|
|
199
|
+
flush();
|
|
200
|
+
return segments.length > 0 ? segments : [{ text, ...style }];
|
|
201
|
+
}
|
|
202
|
+
function inlineMarkerAt(text, index) {
|
|
203
|
+
for (const marker of ["***", "**", "__", "*", "_"]) {
|
|
204
|
+
if (!text.startsWith(marker, index))
|
|
205
|
+
continue;
|
|
206
|
+
if (marker.includes("_") && isIntraWordUnderscore(text, index, marker.length))
|
|
207
|
+
continue;
|
|
208
|
+
return marker;
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
function findClosingMarker(text, marker, start) {
|
|
213
|
+
for (let i = start; i <= text.length - marker.length; i++) {
|
|
214
|
+
if (text[i] === "\\") {
|
|
215
|
+
i++;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (!text.startsWith(marker, i))
|
|
219
|
+
continue;
|
|
220
|
+
if (marker.includes("_") && isIntraWordUnderscore(text, i, marker.length))
|
|
221
|
+
continue;
|
|
222
|
+
return i;
|
|
223
|
+
}
|
|
224
|
+
return -1;
|
|
225
|
+
}
|
|
226
|
+
function isIntraWordUnderscore(text, index, markerLength) {
|
|
227
|
+
const before = text[index - 1];
|
|
228
|
+
const after = text[index + markerLength];
|
|
229
|
+
return isWordChar(before) && isWordChar(after);
|
|
230
|
+
}
|
|
231
|
+
function isWordChar(char) {
|
|
232
|
+
return !!char && /[A-Za-z0-9]/.test(char);
|
|
233
|
+
}
|
|
234
|
+
function appendInlineSegment(segments, text, style) {
|
|
235
|
+
if (!text)
|
|
236
|
+
return;
|
|
237
|
+
const previous = segments[segments.length - 1];
|
|
238
|
+
const next = {
|
|
239
|
+
text,
|
|
240
|
+
bold: style.bold || undefined,
|
|
241
|
+
italic: style.italic || undefined,
|
|
242
|
+
code: style.code || undefined,
|
|
243
|
+
};
|
|
244
|
+
if (previous &&
|
|
245
|
+
previous.bold === next.bold &&
|
|
246
|
+
previous.italic === next.italic &&
|
|
247
|
+
previous.code === next.code) {
|
|
248
|
+
previous.text += text;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
segments.push(next);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function renderInlineSegments(text, keyPrefix, style = {}) {
|
|
255
|
+
return parseMarkdownInlineSegments(text, style).map((segment, index) => (_jsx(Text, { bold: segment.bold, italic: segment.italic, color: segment.code ? "#a78bfa" : undefined, children: segment.text }, `${keyPrefix}-${index}`)));
|
|
256
|
+
}
|
|
257
|
+
function inlinePlainText(text) {
|
|
258
|
+
return parseMarkdownInlineSegments(text).map((segment) => segment.text).join("");
|
|
259
|
+
}
|
|
260
|
+
function InlineText({ text }) {
|
|
261
|
+
return _jsx(Text, { children: renderInlineSegments(text, "inline") });
|
|
262
|
+
}
|
|
263
|
+
function CodeBlock({ lang, lines }) {
|
|
264
|
+
const [highlighted, setHighlighted] = React.useState(null);
|
|
265
|
+
React.useEffect(() => {
|
|
266
|
+
let cancelled = false;
|
|
267
|
+
async function run() {
|
|
268
|
+
const code = lines.join("\n");
|
|
269
|
+
if (!code)
|
|
270
|
+
return;
|
|
271
|
+
try {
|
|
272
|
+
const ansi = await highlightCode(code, lang || "text");
|
|
273
|
+
if (!cancelled)
|
|
274
|
+
setHighlighted(ansi.split("\n"));
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
if (!cancelled)
|
|
278
|
+
setHighlighted(lines);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Show plain text immediately while highlighting loads
|
|
282
|
+
setHighlighted(lines);
|
|
283
|
+
run();
|
|
284
|
+
return () => {
|
|
285
|
+
cancelled = true;
|
|
286
|
+
};
|
|
287
|
+
}, [lang, lines]);
|
|
288
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [lang && _jsx(Text, { color: theme.muted, children: lang }), _jsx(Box, { flexDirection: "column", children: highlighted?.map((line, i) => (_jsx(Text, { children: line || " " }, i))) })] }));
|
|
289
|
+
}
|
|
290
|
+
function TableBlock({ headers, rows, maxWidth, }) {
|
|
291
|
+
const { columns: termWidth } = useTerminalSize();
|
|
292
|
+
const colCount = headers.length;
|
|
293
|
+
// Reserve a buffer so the table fits even when wrapped inside an indented
|
|
294
|
+
// box (e.g. the timeline gutter contributes marginLeft + "⛬ " = 5 cells).
|
|
295
|
+
const budget = Math.max(20, (maxWidth ?? termWidth) - 8);
|
|
296
|
+
const maxWidths = headers.map((h, i) => {
|
|
297
|
+
let max = visualWidth(inlinePlainText(h));
|
|
298
|
+
for (const row of rows) {
|
|
299
|
+
const cell = row[i] || "";
|
|
300
|
+
max = Math.max(max, visualWidth(inlinePlainText(cell)));
|
|
301
|
+
}
|
|
302
|
+
return max;
|
|
303
|
+
});
|
|
304
|
+
const totalInnerWidth = maxWidths.reduce((a, b) => a + b, 0);
|
|
305
|
+
const separatorsWidth = colCount * 3 + 1; // " │ " separators + outer edges
|
|
306
|
+
const totalWidth = totalInnerWidth + separatorsWidth;
|
|
307
|
+
let widths = [...maxWidths];
|
|
308
|
+
if (totalWidth > budget) {
|
|
309
|
+
const available = Math.max(budget - separatorsWidth, colCount * 4);
|
|
310
|
+
const ratio = totalInnerWidth > 0 ? available / totalInnerWidth : 1;
|
|
311
|
+
widths = maxWidths.map((w) => Math.max(4, Math.floor(w * ratio)));
|
|
312
|
+
}
|
|
313
|
+
const top = "┌" + widths.map((w) => "─".repeat(w + 2)).join("┬") + "┐";
|
|
314
|
+
const mid = "├" + widths.map((w) => "─".repeat(w + 2)).join("┼") + "┤";
|
|
315
|
+
const bot = "└" + widths.map((w) => "─".repeat(w + 2)).join("┴") + "┘";
|
|
316
|
+
const renderRow = (cells, keyPrefix, isHeader = false) => (_jsxs(Text, { children: ["│ ", cells.map((c, i) => (_jsxs(React.Fragment, { children: [renderTableCell(c, widths[i] ?? 4, isHeader, `${keyPrefix}-cell-${i}`), i < colCount - 1 ? " │ " : " │"] }, i)))] }, keyPrefix));
|
|
317
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: top }), renderRow(headers, "header", true), _jsx(Text, { children: mid }), rows.map((row, ri) => renderRow(row, `row-${ri}`)), _jsx(Text, { children: bot })] }));
|
|
318
|
+
}
|
|
319
|
+
function renderTableCell(cell, width, isHeader, keyPrefix) {
|
|
320
|
+
const segments = truncateInlineSegments(parseMarkdownInlineSegments(cell, { bold: isHeader }), width);
|
|
321
|
+
const padding = " ".repeat(Math.max(0, width - inlineSegmentsWidth(segments)));
|
|
322
|
+
return [
|
|
323
|
+
...segments.map((segment, index) => (_jsx(Text, { bold: segment.bold, italic: segment.italic, color: segment.code ? "#a78bfa" : undefined, children: segment.text }, `${keyPrefix}-${index}`))),
|
|
324
|
+
_jsx(Text, { children: padding }, `${keyPrefix}-pad`),
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
function truncateInlineSegments(segments, width) {
|
|
328
|
+
if (inlineSegmentsWidth(segments) <= width)
|
|
329
|
+
return segments;
|
|
330
|
+
if (width <= 1)
|
|
331
|
+
return [{ text: "…" }];
|
|
332
|
+
const target = width - 1;
|
|
333
|
+
const output = [];
|
|
334
|
+
let used = 0;
|
|
335
|
+
for (const segment of segments) {
|
|
336
|
+
let text = "";
|
|
337
|
+
for (const grapheme of splitGraphemes(segment.text)) {
|
|
338
|
+
const gWidth = graphemeWidth(grapheme);
|
|
339
|
+
if (used + gWidth > target) {
|
|
340
|
+
if (text)
|
|
341
|
+
appendInlineSegment(output, text, segment);
|
|
342
|
+
appendInlineSegment(output, "…", {});
|
|
343
|
+
return output;
|
|
344
|
+
}
|
|
345
|
+
text += grapheme;
|
|
346
|
+
used += gWidth;
|
|
347
|
+
}
|
|
348
|
+
appendInlineSegment(output, text, segment);
|
|
349
|
+
}
|
|
350
|
+
appendInlineSegment(output, "…", {});
|
|
351
|
+
return output;
|
|
352
|
+
}
|
|
353
|
+
function inlineSegmentsWidth(segments) {
|
|
354
|
+
return segments.reduce((sum, segment) => sum + visualWidth(segment.text), 0);
|
|
355
|
+
}
|
|
356
|
+
function HeadingBlock({ level, text }) {
|
|
357
|
+
const props = { bold: true };
|
|
358
|
+
if (level === 1) {
|
|
359
|
+
props.underline = true;
|
|
360
|
+
props.color = theme.accent;
|
|
361
|
+
}
|
|
362
|
+
else if (level === 2) {
|
|
363
|
+
props.color = theme.accent;
|
|
364
|
+
}
|
|
365
|
+
else if (level === 3) {
|
|
366
|
+
props.color = theme.warning;
|
|
367
|
+
}
|
|
368
|
+
return (_jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { ...props, children: text }) }));
|
|
369
|
+
}
|
|
370
|
+
export function MarkdownContent({ content, maxWidth, }) {
|
|
371
|
+
const blocks = React.useMemo(() => parseMarkdownBlocks(content), [content]);
|
|
372
|
+
return (_jsx(Box, { flexDirection: "column", children: blocks.map((block, i) => {
|
|
373
|
+
if (block.type === "code") {
|
|
374
|
+
return _jsx(CodeBlock, { lang: block.lang, lines: block.lines }, i);
|
|
375
|
+
}
|
|
376
|
+
if (block.type === "table") {
|
|
377
|
+
return (_jsx(TableBlock, { headers: block.headers, rows: block.rows, maxWidth: maxWidth }, i));
|
|
378
|
+
}
|
|
379
|
+
if (block.type === "heading") {
|
|
380
|
+
return _jsx(HeadingBlock, { level: block.level, text: block.text }, i);
|
|
381
|
+
}
|
|
382
|
+
return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: block.lines.map((line, li) => (_jsx(InlineText, { text: line }, li))) }, i));
|
|
383
|
+
}) }));
|
|
384
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { DisplayMessage, DisplayMessagePart, DisplayToolCall } from "./display-history.js";
|
|
3
|
+
/**
|
|
4
|
+
* Hint surfaced when the user can interrupt the currently-running pending tool
|
|
5
|
+
* via the approval dialog. The match is loose (by request type → tool name),
|
|
6
|
+
* since ApprovalRequest does not carry a toolCallId today.
|
|
7
|
+
*/
|
|
8
|
+
export interface PendingApprovalHint {
|
|
9
|
+
toolName: "edit" | "write" | "bash";
|
|
10
|
+
path?: string;
|
|
11
|
+
command?: string;
|
|
12
|
+
}
|
|
13
|
+
interface MessageListProps {
|
|
14
|
+
messages: DisplayMessage[];
|
|
15
|
+
streamingContent: string;
|
|
16
|
+
streamingReasoning: string;
|
|
17
|
+
streamingTools: DisplayToolCall[];
|
|
18
|
+
streamingParts: DisplayMessagePart[];
|
|
19
|
+
terminalColumns: number;
|
|
20
|
+
verboseTrace: boolean;
|
|
21
|
+
pendingApproval?: PendingApprovalHint | null;
|
|
22
|
+
/** Animation tick used to refresh in-progress elapsed counters. */
|
|
23
|
+
nowTick?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Optional banner rendered as the first item of the scrollback Static
|
|
26
|
+
* stream. Committed to scrollback once on initial mount so it doesn't
|
|
27
|
+
* float between older messages and the live tail as the conversation
|
|
28
|
+
* progresses.
|
|
29
|
+
*/
|
|
30
|
+
welcomeBanner?: React.ReactNode;
|
|
31
|
+
}
|
|
32
|
+
export declare function MessageList({ messages, streamingContent, streamingReasoning, streamingTools, streamingParts, terminalColumns, verboseTrace, pendingApproval, nowTick, welcomeBanner, }: MessageListProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export {};
|