pi-ui-extend 1.0.36 → 1.0.37
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.
|
@@ -28,10 +28,15 @@ export function renderConversationEntry(entry, width, options) {
|
|
|
28
28
|
const lines = renderMarkdownTextLines(userEntry.text, userContentWidth, userContentLeft).map((line) => ({
|
|
29
29
|
...userLine(line.text, userEntry.id, line.syntaxHighlight, [
|
|
30
30
|
...(line.segments ?? []),
|
|
31
|
+
...diagramStyledSegments(line.diagramSegments, options.colors),
|
|
31
32
|
...(line.links?.map((link) => ({ ...link, foreground: options.colors.info, underline: true })) ?? []),
|
|
32
33
|
], line.links),
|
|
33
34
|
...(line.copyText === undefined ? {} : { copyText: line.copyText }),
|
|
34
35
|
...(line.continuesOnNextLine ? { continuesOnNextLine: true } : {}),
|
|
36
|
+
...(line.codeBlock ? {
|
|
37
|
+
colorOverride: options.colors.codeBlockForeground,
|
|
38
|
+
backgroundOverride: options.colors.codeBlockBackground,
|
|
39
|
+
} : {}),
|
|
35
40
|
}));
|
|
36
41
|
return attachImageClickTargets(lines, userEntry.id, userEntry.images, { foreground: options.colors.info, underline: true });
|
|
37
42
|
};
|
|
@@ -94,13 +99,16 @@ function renderAssistantLines(text, width, options) {
|
|
|
94
99
|
const existingSegments = line.segments?.map((segment) => ({ ...segment, start: segment.start + contentLeft, end: segment.end + contentLeft })) ?? [];
|
|
95
100
|
const links = line.links?.map((link) => ({ ...link, start: link.start + contentLeft, end: link.end + contentLeft })) ?? [];
|
|
96
101
|
const linkSegments = links.map((link) => ({ start: link.start, end: link.end, foreground: options.colors.info, underline: true }));
|
|
97
|
-
const
|
|
102
|
+
const diagramSegments = diagramStyledSegments(line.diagramSegments, options.colors, contentLeft);
|
|
103
|
+
const allSegments = headingSegment
|
|
104
|
+
? [headingSegment, ...existingSegments, ...diagramSegments, ...linkSegments]
|
|
105
|
+
: [...existingSegments, ...diagramSegments, ...linkSegments];
|
|
98
106
|
lines.push({
|
|
99
107
|
text: padHorizontalText(line.text, width),
|
|
100
108
|
...(line.copyText === undefined ? {} : { copyText: line.copyText }),
|
|
101
109
|
...(line.continuesOnNextLine ? { continuesOnNextLine: true } : {}),
|
|
102
|
-
colorOverride: options.colors.assistantForeground,
|
|
103
|
-
backgroundOverride: options.colors.assistantMessageBackground,
|
|
110
|
+
colorOverride: line.codeBlock ? options.colors.codeBlockForeground : options.colors.assistantForeground,
|
|
111
|
+
backgroundOverride: line.codeBlock ? options.colors.codeBlockBackground : options.colors.assistantMessageBackground,
|
|
104
112
|
...(allSegments.length > 0 ? { segments: allSegments } : {}),
|
|
105
113
|
...(links.length > 0 ? { links } : {}),
|
|
106
114
|
...(line.syntaxHighlight ? { syntaxHighlight: line.syntaxHighlight } : {}),
|
|
@@ -108,3 +116,17 @@ function renderAssistantLines(text, width, options) {
|
|
|
108
116
|
}
|
|
109
117
|
return lines;
|
|
110
118
|
}
|
|
119
|
+
function diagramStyledSegments(segments, colors, offset = 0) {
|
|
120
|
+
return (segments ?? []).map((segment) => {
|
|
121
|
+
const range = { start: segment.start + offset, end: segment.end + offset };
|
|
122
|
+
switch (segment.class) {
|
|
123
|
+
case "border":
|
|
124
|
+
case "edgeLabel":
|
|
125
|
+
return { ...range, foreground: colors.muted };
|
|
126
|
+
case "edge":
|
|
127
|
+
return { ...range, foreground: colors.accent };
|
|
128
|
+
case "title":
|
|
129
|
+
return { ...range, foreground: colors.accent, bold: true };
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Cls as MermaidSpanClass } from "grok-mermaid";
|
|
1
2
|
import { type SyntaxLineHighlight, type ToolBodySyntaxHighlights } from "./syntax-highlight.js";
|
|
2
3
|
export type RenderedMarkdownLine = {
|
|
3
4
|
text: string;
|
|
@@ -22,6 +23,7 @@ export type RenderedMarkdownTextLine = {
|
|
|
22
23
|
text: string;
|
|
23
24
|
copyText?: string;
|
|
24
25
|
continuesOnNextLine?: boolean;
|
|
26
|
+
codeBlock?: true;
|
|
25
27
|
segments?: readonly {
|
|
26
28
|
start: number;
|
|
27
29
|
end: number;
|
|
@@ -29,8 +31,14 @@ export type RenderedMarkdownTextLine = {
|
|
|
29
31
|
}[] | undefined;
|
|
30
32
|
links?: readonly RenderedMarkdownLink[] | undefined;
|
|
31
33
|
syntaxHighlight?: SyntaxLineHighlight | undefined;
|
|
34
|
+
diagramSegments?: readonly RenderedMarkdownDiagramSegment[] | undefined;
|
|
32
35
|
heading?: boolean;
|
|
33
36
|
};
|
|
37
|
+
export type RenderedMarkdownDiagramSegment = {
|
|
38
|
+
start: number;
|
|
39
|
+
end: number;
|
|
40
|
+
class: Exclude<MermaidSpanClass, "none" | "text">;
|
|
41
|
+
};
|
|
34
42
|
export type RenderMarkdownTextLinesOptions = {
|
|
35
43
|
preserveWrappedWordSeparator?: boolean;
|
|
36
44
|
};
|
package/dist/markdown-format.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { render as renderMermaid } from "grok-mermaid";
|
|
1
2
|
import { displayGraphemes, expandTabs, stringDisplayWidth } from "./terminal-width.js";
|
|
2
3
|
import { syntaxHighlightLanguageForMarkdownFence, } from "./syntax-highlight.js";
|
|
3
4
|
const MIN_TRAILING_WORD_WIDTH_TO_REBALANCE = 5;
|
|
5
|
+
const MERMAID_CACHE_LIMIT = 100;
|
|
6
|
+
const MERMAID_MAX_PANELS = 4;
|
|
7
|
+
const MERMAID_MIN_PANEL_WIDTH = 40;
|
|
8
|
+
const MERMAID_SEQUENCE_MESSAGE_OPERATORS = ["-->>", "->>", "--x", "-x", "--)", "-)", "-->", "->"];
|
|
9
|
+
const mermaidRenderCache = new Map();
|
|
4
10
|
export function formatMarkdownTables(text, maxWidth) {
|
|
5
11
|
const lines = text.split("\n");
|
|
6
12
|
const formatted = [];
|
|
@@ -85,14 +91,40 @@ export function renderMarkdownLine(text, start = 0) {
|
|
|
85
91
|
export function renderMarkdownTextLines(text, width, start = 0, options = {}) {
|
|
86
92
|
const lines = [];
|
|
87
93
|
let fence;
|
|
94
|
+
let fenceHasBodyLine = false;
|
|
88
95
|
const formattedText = formatMarkdownTables(sanitizeMarkdownText(text), width);
|
|
89
96
|
if (formattedText.length === 0)
|
|
90
97
|
return [];
|
|
91
|
-
|
|
98
|
+
const rawLines = formattedText.split("\n");
|
|
99
|
+
for (let lineIndex = 0; lineIndex < rawLines.length;) {
|
|
100
|
+
const rawLine = rawLines[lineIndex] ?? "";
|
|
92
101
|
const nextFence = markdownFence(rawLine);
|
|
102
|
+
if (!fence && nextFence && isMermaidFence(nextFence)) {
|
|
103
|
+
const diagram = renderMermaidBlock(rawLines, lineIndex, nextFence, width);
|
|
104
|
+
if (diagram) {
|
|
105
|
+
lines.push(...diagram.lines);
|
|
106
|
+
lineIndex = diagram.nextLineIndex;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
93
110
|
const closesFence = Boolean(fence && nextFence && fence.marker === nextFence.marker && nextFence.length >= fence.length);
|
|
94
111
|
const opensFence = !fence && nextFence !== undefined;
|
|
95
|
-
|
|
112
|
+
if (opensFence && nextFence) {
|
|
113
|
+
fence = { ...nextFence, language: syntaxHighlightLanguageForMarkdownFence(nextFence.info) };
|
|
114
|
+
fenceHasBodyLine = false;
|
|
115
|
+
lineIndex += 1;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (closesFence) {
|
|
119
|
+
if (!fenceHasBodyLine)
|
|
120
|
+
lines.push({ text: "", codeBlock: true });
|
|
121
|
+
fence = undefined;
|
|
122
|
+
fenceHasBodyLine = false;
|
|
123
|
+
lineIndex += 1;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const syntaxHighlight = markdownLineSyntaxHighlight(fence, false, start);
|
|
127
|
+
const codeBlock = fence !== undefined;
|
|
96
128
|
const isHeadingLine = !fence && /^\s{0,3}#{1,6}\s/.test(rawLine);
|
|
97
129
|
const markdownLine = syntaxHighlight?.language === "markdown" || isHeadingLine ? renderMarkdownLine(rawLine) : undefined;
|
|
98
130
|
const logicalLine = markdownLine ?? { text: rawLine, segments: [], links: [] };
|
|
@@ -113,21 +145,245 @@ export function renderMarkdownTextLines(text, width, start = 0, options = {}) {
|
|
|
113
145
|
text: wrapped.text,
|
|
114
146
|
...(wrapped.copyText === undefined ? {} : { copyText: wrapped.copyText }),
|
|
115
147
|
...(wrapped.continuesOnNextLine ? { continuesOnNextLine: true } : {}),
|
|
148
|
+
...(codeBlock ? { codeBlock: true } : {}),
|
|
116
149
|
...(wrapped.segments.length > 0 ? { segments: wrapped.segments } : {}),
|
|
117
150
|
...(wrapped.links && wrapped.links.length > 0 ? { links: wrapped.links } : {}),
|
|
118
151
|
...(wrappedSyntaxHighlight ? { syntaxHighlight: wrappedSyntaxHighlight } : {}),
|
|
119
152
|
...(isHeadingLine ? { heading: true } : {}),
|
|
120
153
|
});
|
|
121
154
|
}
|
|
122
|
-
if (
|
|
123
|
-
|
|
155
|
+
if (codeBlock)
|
|
156
|
+
fenceHasBodyLine = true;
|
|
157
|
+
lineIndex += 1;
|
|
158
|
+
}
|
|
159
|
+
if (fence && !fenceHasBodyLine)
|
|
160
|
+
lines.push({ text: "", codeBlock: true });
|
|
161
|
+
return lines;
|
|
162
|
+
}
|
|
163
|
+
function renderMermaidBlock(rawLines, openingLineIndex, openingFence, width) {
|
|
164
|
+
let closingLineIndex;
|
|
165
|
+
for (let index = openingLineIndex + 1; index < rawLines.length; index += 1) {
|
|
166
|
+
const candidate = markdownFence(rawLines[index] ?? "");
|
|
167
|
+
if (candidate && candidate.marker === openingFence.marker && candidate.length >= openingFence.length) {
|
|
168
|
+
closingLineIndex = index;
|
|
169
|
+
break;
|
|
124
170
|
}
|
|
125
|
-
|
|
126
|
-
|
|
171
|
+
}
|
|
172
|
+
const sourceEnd = closingLineIndex ?? rawLines.length;
|
|
173
|
+
const source = rawLines.slice(openingLineIndex + 1, sourceEnd).join("\n");
|
|
174
|
+
let art = cachedMermaidRender(source);
|
|
175
|
+
if (!art)
|
|
176
|
+
return undefined;
|
|
177
|
+
const availableWidth = Math.max(1, width);
|
|
178
|
+
let participantLegend = [];
|
|
179
|
+
let messageLegend = [];
|
|
180
|
+
if (art.width > availableWidth) {
|
|
181
|
+
const compactSequence = compactMermaidSequence(source);
|
|
182
|
+
if (compactSequence) {
|
|
183
|
+
const compactArt = cachedMermaidRender(compactSequence.source);
|
|
184
|
+
if (compactArt && compactArt.width < art.width) {
|
|
185
|
+
art = compactArt;
|
|
186
|
+
participantLegend = compactSequence.participantLegend;
|
|
187
|
+
messageLegend = compactSequence.messageLegend;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const panelStarts = mermaidPanelStarts(art.width, availableWidth);
|
|
192
|
+
if (panelStarts.length > 1 && (availableWidth < MERMAID_MIN_PANEL_WIDTH || panelStarts.length > MERMAID_MAX_PANELS)) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
lines: [
|
|
197
|
+
...mermaidArtLines(art, availableWidth, panelStarts),
|
|
198
|
+
...mermaidLegendLines("Participants", participantLegend, availableWidth),
|
|
199
|
+
...mermaidLegendLines("Messages", messageLegend, availableWidth),
|
|
200
|
+
],
|
|
201
|
+
nextLineIndex: closingLineIndex === undefined ? rawLines.length : closingLineIndex + 1,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function compactMermaidSequence(source) {
|
|
205
|
+
const lines = source.split("\n");
|
|
206
|
+
const header = lines.find((line) => line.trim().length > 0 && !line.trimStart().startsWith("%%"));
|
|
207
|
+
if (!header || !/^sequenceDiagram\s*$/iu.test(header.trim()))
|
|
208
|
+
return undefined;
|
|
209
|
+
const participantLegend = [];
|
|
210
|
+
const messageLegend = [];
|
|
211
|
+
const autonumber = lines.some((line) => /^\s*autonumber(?:\s|$)/iu.test(line));
|
|
212
|
+
let participantNumber = 0;
|
|
213
|
+
let messageNumber = 0;
|
|
214
|
+
const compactLines = lines.map((line) => {
|
|
215
|
+
const match = /^(\s*)(participant|actor)\s+(\S+?)(?:\s+as\s+(.+?))?\s*$/iu.exec(line);
|
|
216
|
+
if (match) {
|
|
217
|
+
const [, indent = "", declaration = "participant", id = "", rawLabel] = match;
|
|
218
|
+
participantNumber += 1;
|
|
219
|
+
const marker = `P${participantNumber}`;
|
|
220
|
+
const label = rawLabel ? normalizeMermaidLegendLabel(rawLabel) : id;
|
|
221
|
+
participantLegend.push(`${marker} (${id}) — ${label}`);
|
|
222
|
+
return `${indent}${declaration} ${id} as ${marker}`;
|
|
223
|
+
}
|
|
224
|
+
const message = mermaidSequenceMessage(line);
|
|
225
|
+
if (!message)
|
|
226
|
+
return line;
|
|
227
|
+
messageNumber += 1;
|
|
228
|
+
if (message.label.length === 0)
|
|
229
|
+
return line;
|
|
230
|
+
messageLegend.push(`${messageNumber}. ${normalizeMermaidLegendLabel(message.label)}`);
|
|
231
|
+
return autonumber ? message.prefix : `${message.prefix}: [${messageNumber}]`;
|
|
232
|
+
});
|
|
233
|
+
return participantLegend.length > 0 || messageLegend.length > 0
|
|
234
|
+
? { source: compactLines.join("\n"), participantLegend, messageLegend }
|
|
235
|
+
: undefined;
|
|
236
|
+
}
|
|
237
|
+
function mermaidSequenceMessage(line) {
|
|
238
|
+
for (const operator of MERMAID_SEQUENCE_MESSAGE_OPERATORS) {
|
|
239
|
+
const operatorIndex = line.indexOf(operator);
|
|
240
|
+
if (operatorIndex < 0)
|
|
241
|
+
continue;
|
|
242
|
+
const colonIndex = line.indexOf(":", operatorIndex + operator.length);
|
|
243
|
+
const targetEnd = colonIndex < 0 ? line.length : colonIndex;
|
|
244
|
+
const from = line.slice(0, operatorIndex).trim();
|
|
245
|
+
const to = line.slice(operatorIndex + operator.length, targetEnd).trim();
|
|
246
|
+
if (!from || !to || /\s/u.test(from) || /\s/u.test(to))
|
|
247
|
+
return undefined;
|
|
248
|
+
return {
|
|
249
|
+
prefix: line.slice(0, targetEnd).trimEnd(),
|
|
250
|
+
label: colonIndex < 0 ? "" : line.slice(colonIndex + 1).trim(),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
function normalizeMermaidLegendLabel(label) {
|
|
256
|
+
const normalized = label
|
|
257
|
+
.replace(/<br\s*\/?\s*>/giu, " ")
|
|
258
|
+
.replace(/<\/?[A-Za-z][^>]*>/gu, "")
|
|
259
|
+
.replace(/^(?:"([\s\S]*)"|'([\s\S]*)')$/u, "$1$2")
|
|
260
|
+
.trim();
|
|
261
|
+
return normalized || label.trim();
|
|
262
|
+
}
|
|
263
|
+
function mermaidPanelStarts(artWidth, panelWidth) {
|
|
264
|
+
if (artWidth <= panelWidth)
|
|
265
|
+
return [0];
|
|
266
|
+
const overlap = Math.min(24, Math.max(8, Math.floor(panelWidth / 5)));
|
|
267
|
+
const stride = Math.max(1, panelWidth - overlap);
|
|
268
|
+
const panelCount = 1 + Math.ceil((artWidth - panelWidth) / stride);
|
|
269
|
+
return Array.from({ length: panelCount }, (_, index) => Math.min(index * stride, artWidth - panelWidth));
|
|
270
|
+
}
|
|
271
|
+
function mermaidArtLines(art, width, panelStarts) {
|
|
272
|
+
if (panelStarts.length === 1) {
|
|
273
|
+
return art.plain.map((text, rowIndex) => {
|
|
274
|
+
const diagramSegments = mermaidDiagramSegments(text, art.styled[rowIndex] ?? []);
|
|
275
|
+
return { text, ...(diagramSegments.length > 0 ? { diagramSegments } : {}) };
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
const lines = [];
|
|
279
|
+
for (const [panelIndex, startColumn] of panelStarts.entries()) {
|
|
280
|
+
const continuesLeft = startColumn > 0;
|
|
281
|
+
const continuesRight = startColumn + width < art.width;
|
|
282
|
+
const title = `Mermaid ${panelIndex + 1}/${panelStarts.length}${continuesLeft ? " ←" : ""}${continuesRight ? " →" : ""}`;
|
|
283
|
+
lines.push({
|
|
284
|
+
text: title,
|
|
285
|
+
diagramSegments: [{ start: 0, end: title.length, class: "title" }],
|
|
286
|
+
});
|
|
287
|
+
for (const [rowIndex, text] of art.plain.entries()) {
|
|
288
|
+
lines.push(sliceMermaidDiagramRow(text, art.styled[rowIndex] ?? [], startColumn, width));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return lines;
|
|
292
|
+
}
|
|
293
|
+
function mermaidLegendLines(title, entries, width) {
|
|
294
|
+
if (entries.length === 0)
|
|
295
|
+
return [];
|
|
296
|
+
const lines = [{
|
|
297
|
+
text: title,
|
|
298
|
+
diagramSegments: [{ start: 0, end: title.length, class: "title" }],
|
|
299
|
+
}];
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
for (const wrapped of wrapRenderedMarkdownLine({ text: entry, segments: [], links: [] }, width, {})) {
|
|
302
|
+
lines.push({
|
|
303
|
+
text: wrapped.text,
|
|
304
|
+
...(wrapped.continuesOnNextLine ? { continuesOnNextLine: true } : {}),
|
|
305
|
+
});
|
|
127
306
|
}
|
|
128
307
|
}
|
|
129
308
|
return lines;
|
|
130
309
|
}
|
|
310
|
+
function sliceMermaidDiagramRow(text, spans, startColumn, width) {
|
|
311
|
+
const endColumn = startColumn + width;
|
|
312
|
+
const diagramSegments = [];
|
|
313
|
+
let result = "";
|
|
314
|
+
let column = 0;
|
|
315
|
+
let spanIndex = 0;
|
|
316
|
+
let spanEnd = spans[0]?.text.length ?? 0;
|
|
317
|
+
for (const grapheme of displayGraphemes(text)) {
|
|
318
|
+
const nextColumn = column + grapheme.width;
|
|
319
|
+
if (nextColumn <= startColumn) {
|
|
320
|
+
column = nextColumn;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (column >= endColumn)
|
|
324
|
+
break;
|
|
325
|
+
while (spanIndex < spans.length - 1 && grapheme.start >= spanEnd) {
|
|
326
|
+
spanIndex += 1;
|
|
327
|
+
spanEnd += spans[spanIndex]?.text.length ?? 0;
|
|
328
|
+
}
|
|
329
|
+
const fullyVisible = column >= startColumn && nextColumn <= endColumn;
|
|
330
|
+
const chunk = fullyVisible
|
|
331
|
+
? grapheme.text
|
|
332
|
+
: " ".repeat(Math.max(0, Math.min(nextColumn, endColumn) - Math.max(column, startColumn)));
|
|
333
|
+
const chunkStart = result.length;
|
|
334
|
+
result += chunk;
|
|
335
|
+
const spanClass = spans[spanIndex]?.cls;
|
|
336
|
+
if (fullyVisible && spanClass && spanClass !== "none" && spanClass !== "text") {
|
|
337
|
+
const previous = diagramSegments.at(-1);
|
|
338
|
+
if (previous && previous.class === spanClass && previous.end === chunkStart) {
|
|
339
|
+
previous.end = result.length;
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
diagramSegments.push({ start: chunkStart, end: result.length, class: spanClass });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
column = nextColumn;
|
|
346
|
+
}
|
|
347
|
+
return { text: result, ...(diagramSegments.length > 0 ? { diagramSegments } : {}) };
|
|
348
|
+
}
|
|
349
|
+
function cachedMermaidRender(source) {
|
|
350
|
+
if (mermaidRenderCache.has(source)) {
|
|
351
|
+
const cached = mermaidRenderCache.get(source) ?? null;
|
|
352
|
+
mermaidRenderCache.delete(source);
|
|
353
|
+
mermaidRenderCache.set(source, cached);
|
|
354
|
+
return cached;
|
|
355
|
+
}
|
|
356
|
+
let art;
|
|
357
|
+
try {
|
|
358
|
+
art = renderMermaid(source.replace(/<br\s*\/?\s*>/giu, " "));
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
art = null;
|
|
362
|
+
}
|
|
363
|
+
mermaidRenderCache.set(source, art);
|
|
364
|
+
if (mermaidRenderCache.size > MERMAID_CACHE_LIMIT) {
|
|
365
|
+
const oldestKey = mermaidRenderCache.keys().next().value;
|
|
366
|
+
if (oldestKey !== undefined)
|
|
367
|
+
mermaidRenderCache.delete(oldestKey);
|
|
368
|
+
}
|
|
369
|
+
return art;
|
|
370
|
+
}
|
|
371
|
+
function mermaidDiagramSegments(text, spans) {
|
|
372
|
+
const segments = [];
|
|
373
|
+
let start = 0;
|
|
374
|
+
for (const span of spans) {
|
|
375
|
+
const end = start + span.text.length;
|
|
376
|
+
if (start < text.length && span.cls !== "none" && span.cls !== "text") {
|
|
377
|
+
segments.push({ start, end: Math.min(end, text.length), class: span.cls });
|
|
378
|
+
}
|
|
379
|
+
start = end;
|
|
380
|
+
}
|
|
381
|
+
return segments;
|
|
382
|
+
}
|
|
383
|
+
function isMermaidFence(fence) {
|
|
384
|
+
const token = fence.info.trim().split(/\s+/, 1)[0]?.toLowerCase().replace(/^[{.]+|[}.]+$/gu, "") ?? "";
|
|
385
|
+
return token === "mermaid";
|
|
386
|
+
}
|
|
131
387
|
export function markdownSyntaxHighlightsForText(text, startColumn = 0) {
|
|
132
388
|
const highlights = [];
|
|
133
389
|
let fence;
|
package/dist/theme.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export type Theme = {
|
|
|
18
18
|
assistantMessageBackground: string;
|
|
19
19
|
userMessageBackground: string;
|
|
20
20
|
thinkingMessageBackground: string;
|
|
21
|
+
codeBlockForeground: string;
|
|
22
|
+
codeBlockBackground: string;
|
|
21
23
|
inputCursorBackground: string;
|
|
22
24
|
popupForeground: string;
|
|
23
25
|
popupBackground: string;
|
package/dist/theme.js
CHANGED
|
@@ -19,6 +19,8 @@ export const THEMES = {
|
|
|
19
19
|
assistantMessageBackground: "",
|
|
20
20
|
userMessageBackground: "",
|
|
21
21
|
thinkingMessageBackground: "",
|
|
22
|
+
codeBlockForeground: "#e6edf3",
|
|
23
|
+
codeBlockBackground: "#2a2f36",
|
|
22
24
|
inputCursorBackground: "#7fb3c8",
|
|
23
25
|
popupForeground: "#e6edf3",
|
|
24
26
|
popupBackground: "#1e1e1e",
|
|
@@ -75,6 +77,8 @@ export const THEMES = {
|
|
|
75
77
|
assistantMessageBackground: "",
|
|
76
78
|
userMessageBackground: "",
|
|
77
79
|
thinkingMessageBackground: "",
|
|
80
|
+
codeBlockForeground: "#0f172a",
|
|
81
|
+
codeBlockBackground: "#f1f5f9",
|
|
78
82
|
inputCursorBackground: "#0284c7",
|
|
79
83
|
popupForeground: "#0f172a",
|
|
80
84
|
popupBackground: "#ffffff",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-ui-extend",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.37",
|
|
4
4
|
"description": "Pix: a workspace-first terminal UI for Pi with tabs, readable tool activity, voice input, and bundled agent tools.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
83
83
|
"@earendil-works/pi-tui": "0.84.4",
|
|
84
84
|
"@mariozechner/clipboard": "^0.3.9",
|
|
85
|
+
"grok-mermaid": "0.2.2",
|
|
85
86
|
"jsonc-parser": "3.3.1",
|
|
86
87
|
"typebox": "1.1.38",
|
|
87
88
|
"vscode-jsonrpc": "^8.2.1",
|