@yinan_chen/pi-claude-code-ui 0.0.0-stage → 0.2.0

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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -2
  3. package/package.json +34 -5
  4. package/src/claude-tools.ts +274 -0
  5. package/src/claude-view.ts +775 -0
  6. package/src/index.ts +566 -0
  7. package/src/pi-runtime.ts +232 -0
  8. package/src/preview.ts +147 -0
  9. package/src/running-effect.ts +29 -0
  10. package/src/skill-block-recollapse.ts +362 -0
  11. package/src/sticky-first-message.ts +1114 -0
  12. package/src/subagent-panel.ts +144 -0
  13. package/src/vendor/ATTRIBUTION.md +26 -0
  14. package/src/vendor/ansi-color.ts +44 -0
  15. package/src/vendor/ansi-text.ts +47 -0
  16. package/src/vendor/diff/ATTRIBUTION.md +28 -0
  17. package/src/vendor/diff/ansi-utils.ts +113 -0
  18. package/src/vendor/diff/diff-component.ts +125 -0
  19. package/src/vendor/diff/diff-edit-render.ts +237 -0
  20. package/src/vendor/diff/diff-header.ts +137 -0
  21. package/src/vendor/diff/diff-highlight.ts +115 -0
  22. package/src/vendor/diff/diff-inline.ts +324 -0
  23. package/src/vendor/diff/diff-layout.ts +762 -0
  24. package/src/vendor/diff/diff-limits.ts +229 -0
  25. package/src/vendor/diff/diff-palette.ts +410 -0
  26. package/src/vendor/diff/diff-parse.ts +535 -0
  27. package/src/vendor/diff/diff-presentation.ts +70 -0
  28. package/src/vendor/diff/diff-renderer.ts +23 -0
  29. package/src/vendor/diff/diff-text.ts +45 -0
  30. package/src/vendor/diff/diff-write-render.ts +572 -0
  31. package/src/vendor/diff/index.ts +126 -0
  32. package/src/vendor/diff/line-width-safety.ts +92 -0
  33. package/src/vendor/diff/shiki-highlight.ts +92 -0
  34. package/src/vendor/diff/write-execution.ts +113 -0
  35. package/src/vendor/diff-config.ts +30 -0
  36. package/src/vendor/show-more-hint.ts +38 -0
  37. package/src/vendor/tool-result-sanitize.ts +24 -0
  38. package/src/working-message.ts +140 -0
@@ -0,0 +1,237 @@
1
+ import { Text, type Component } from "@earendil-works/pi-tui";
2
+ import type { EditToolDetails } from "@earendil-works/pi-coding-agent";
3
+ import { sanitizeToolResultText } from "../tool-result-sanitize.ts";
4
+ import {
5
+ getLineNumberWidth,
6
+ parseDiff,
7
+ type ParsedDiff,
8
+ type ParsedDiffEntry,
9
+ } from "./diff-parse.ts";
10
+ import { buildInlineHighlightMap, buildSplitRows } from "./diff-inline.ts";
11
+ import { resolveDiffPalette, type DiffTheme } from "./diff-palette.ts";
12
+ import { createCodeLineHighlighter, resolveLanguageFromPath } from "./diff-highlight.ts";
13
+ import {
14
+ canRenderSplitLayout,
15
+ renderCompact,
16
+ renderSplit,
17
+ renderUnified,
18
+ type DiffRenderContext,
19
+ } from "./diff-layout.ts";
20
+ import { renderDiffFrameLine, renderHeaderRows, renderSingleDiffRow } from "./diff-header.ts";
21
+ import {
22
+ applyLineLimit,
23
+ clampDiffLineToWidth,
24
+ clampDiffLinesToWidth,
25
+ compactDiffContext,
26
+ resolveDiffDisplayLimit,
27
+ resolveDiffProcessBudget,
28
+ takeEntriesForLineBudget,
29
+ takeSplitRowsForBudget,
30
+ SPLIT_DECOR_ROWS,
31
+ } from "./diff-limits.ts";
32
+ import {
33
+ RICH_DIFF_COMPONENT,
34
+ createDiffRenderCache,
35
+ displayConfigCacheKey,
36
+ resolveDiffIndicatorMode,
37
+ resolveLiveDisplayConfig,
38
+ normalizeCollapsedHintLine,
39
+ type DiffRenderOptions,
40
+ type DisplayConfigInput,
41
+ } from "./diff-component.ts";
42
+ import {
43
+ buildDiffSummaryText,
44
+ normalizeDiffRenderWidth,
45
+ resolveDiffPresentationMode,
46
+ } from "./diff-presentation.ts";
47
+
48
+ function safeGetDiff(details: unknown): string {
49
+ if (!details || typeof details !== "object") {
50
+ return "";
51
+ }
52
+ const typed = details as Partial<EditToolDetails>;
53
+ return typeof typed.diff === "string" ? typed.diff : "";
54
+ }
55
+
56
+ // Pi appends an omission marker after final context to say the file continues.
57
+ // It does not separate visible diff regions, so keep only omissions before a rendered line.
58
+ function omitTerminalOmissions(entries: ParsedDiffEntry[]): ParsedDiffEntry[] {
59
+ let lastLineIndex = -1;
60
+ for (let index = entries.length - 1; index >= 0; index--) {
61
+ if (entries[index]?.kind === "line") {
62
+ lastLineIndex = index;
63
+ break;
64
+ }
65
+ }
66
+
67
+ return entries.filter((entry, index) => entry.kind !== "omission" || index < lastLineIndex);
68
+ }
69
+
70
+ /**
71
+ * edit 变更行统计(compact 单行 `(+A -D)` 用):复用现有 parser,diff 文件头不计入。
72
+ * 无 diff 或解析失败时返回 undefined,不把未知状态误报为零变更。
73
+ */
74
+ export function countEditDiffStats(
75
+ details: unknown,
76
+ ): { added: number; removed: number } | undefined {
77
+ const diffText = sanitizeToolResultText(safeGetDiff(details));
78
+ if (!diffText.trim()) return undefined;
79
+ try {
80
+ const parsed = parseDiff(diffText);
81
+ return { added: parsed.stats.added, removed: parsed.stats.removed };
82
+ } catch {
83
+ return undefined;
84
+ }
85
+ }
86
+
87
+ export function renderEditDiffResult(
88
+ details: unknown,
89
+ options: DiffRenderOptions,
90
+ config: DisplayConfigInput,
91
+ theme: DiffTheme,
92
+ fallbackText: string,
93
+ ): Component {
94
+ const diffText = sanitizeToolResultText(safeGetDiff(details));
95
+ const safeFallbackText = sanitizeToolResultText(fallbackText);
96
+ if (!diffText.trim()) {
97
+ if (!safeFallbackText.trim()) {
98
+ return new Text(theme.fg("muted", "↳ edit completed (no diff payload)"), 0, 0);
99
+ }
100
+ return new Text(theme.fg("toolOutput", safeFallbackText), 0, 0);
101
+ }
102
+
103
+ let parsed: ParsedDiff;
104
+ try {
105
+ parsed = parseDiff(diffText);
106
+ } catch (error) {
107
+ const message = error instanceof Error ? error.message : String(error);
108
+ return new Text(theme.fg("warning", `↳ unable to render diff: ${message}`), 0, 0);
109
+ }
110
+
111
+ const renderEntries = omitTerminalOmissions(parsed.entries);
112
+ if (renderEntries.length === 0) {
113
+ return new Text(theme.fg("muted", "↳ no diff data"), 0, 0);
114
+ }
115
+
116
+ const splitRows = buildSplitRows(renderEntries);
117
+ const showHashlineAnchors =
118
+ options.expanded === true &&
119
+ renderEntries.some((entry) => entry.kind === "line" && !!entry.hashlineAnchorContent);
120
+ const lineNumberWidth = getLineNumberWidth(renderEntries, showHashlineAnchors);
121
+ const palette = resolveDiffPalette(theme);
122
+ // Rich diffs use ccstyle's self shell. Keep the panel transparent so the
123
+ // separator cannot leak toolSuccessBg across the entire new column.
124
+ const containerBgAnsi = undefined;
125
+ const language = resolveLanguageFromPath(options.filePath);
126
+ const cache = createDiffRenderCache();
127
+ const highlightLine = createCodeLineHighlighter(language, theme, renderEntries, () => {
128
+ cache.invalidate();
129
+ options.invalidate?.();
130
+ });
131
+
132
+ return {
133
+ [RICH_DIFF_COMPONENT]: true,
134
+ /** 鼠标层只在真正的 remainder 行上提供展开入口。 */
135
+ isCollapsedHintLine(plainLine: string): boolean {
136
+ const hint = cache.getHintLine();
137
+ return hint !== undefined && normalizeCollapsedHintLine(plainLine) === hint;
138
+ },
139
+ render(width: number): string[] {
140
+ // Live config: panel can change indicator/wrap/limits after this component is created.
141
+ const live = resolveLiveDisplayConfig(config);
142
+ const wordWrap = live.diffWordWrap;
143
+ const indicatorMode = resolveDiffIndicatorMode(live);
144
+ const configKey = displayConfigCacheKey(live);
145
+ const safeWidth = normalizeDiffRenderWidth(width);
146
+ const mode = resolveDiffPresentationMode(live, safeWidth, canRenderSplitLayout(safeWidth));
147
+ const hovered = options.isHovered?.() ?? false;
148
+ const cached = cache.get(safeWidth, options.expanded, mode, configKey, hovered);
149
+ if (cached) {
150
+ return cached;
151
+ }
152
+
153
+ if (mode === "summary") {
154
+ return cache.set(
155
+ safeWidth,
156
+ options.expanded,
157
+ mode,
158
+ configKey,
159
+ hovered,
160
+ clampDiffLinesToWidth(
161
+ renderSingleDiffRow(
162
+ buildDiffSummaryText(parsed.stats, safeWidth),
163
+ "toolOutput",
164
+ safeWidth,
165
+ theme,
166
+ ),
167
+ safeWidth,
168
+ ),
169
+ );
170
+ }
171
+
172
+ const headerRows = renderHeaderRows(parsed.stats, mode, safeWidth, theme);
173
+ const collapsed = options.expanded !== true;
174
+ // Collapsed: compact pure context runs so every change stays inside the budget.
175
+ const sourceEntries = collapsed ? compactDiffContext(renderEntries) : renderEntries;
176
+ const sourceSplitRows = collapsed ? buildSplitRows(sourceEntries) : splitRows;
177
+ const displayLimit = resolveDiffDisplayLimit(options.expanded, live.editDiffCollapsedLines);
178
+ const processBudget = resolveDiffProcessBudget(displayLimit, wordWrap);
179
+ // Only highlight/render a prefix that can fill the display limit; full-diff
180
+ // LCS + syntax highlight on thousands of hidden lines is pure waste when collapsed.
181
+ const entryBudget = takeEntriesForLineBudget(sourceEntries, processBudget);
182
+ const splitBudget = takeSplitRowsForBudget(sourceSplitRows, processBudget);
183
+ const inlineHighlights = buildInlineHighlightMap(splitBudget.rows);
184
+ const renderCtx: DiffRenderContext = {
185
+ width: safeWidth,
186
+ theme,
187
+ inlineHighlights,
188
+ palette,
189
+ highlightLine,
190
+ containerBgAnsi,
191
+ wordWrap,
192
+ indicatorMode,
193
+ showHashlineAnchors,
194
+ };
195
+ const bodyRows =
196
+ mode === "split"
197
+ ? renderSplit(splitBudget.rows, renderCtx, lineNumberWidth)
198
+ : mode === "compact"
199
+ ? renderCompact(entryBudget.entries, renderCtx)
200
+ : renderUnified(entryBudget.entries, renderCtx, lineNumberWidth);
201
+ const unprocessedLogicalRows =
202
+ mode === "split"
203
+ ? Math.max(0, splitBudget.totalRows - splitBudget.processedRows)
204
+ : Math.max(0, entryBudget.totalLineEntries - entryBudget.processedLineEntries);
205
+ // Split decor rows (column rule + old/new labels) must not consume the
206
+ // collapsed body budget: the limit is meant for content rows.
207
+ const collapsedBodyLimit = mode === "split" && live.editDiffCollapsedLines > 0 ? live.editDiffCollapsedLines + SPLIT_DECOR_ROWS : live.editDiffCollapsedLines;
208
+ const bodyWithLimit = applyLineLimit(
209
+ bodyRows,
210
+ safeWidth,
211
+ options.expanded,
212
+ collapsedBodyLimit,
213
+ parsed.stats.hunks,
214
+ theme,
215
+ unprocessedLogicalRows,
216
+ hovered,
217
+ );
218
+ const frame = renderDiffFrameLine(safeWidth, theme);
219
+ const renderedLines =
220
+ mode === "unified"
221
+ ? [...headerRows.map((row) => row.text), frame, ...bodyWithLimit.lines, frame]
222
+ : [...headerRows.map((row) => row.text), ...bodyWithLimit.lines];
223
+
224
+ const clampedLines = clampDiffLinesToWidth(renderedLines, safeWidth);
225
+ return cache.set(
226
+ safeWidth,
227
+ options.expanded,
228
+ mode,
229
+ configKey,
230
+ hovered,
231
+ clampedLines,
232
+ normalizeCollapsedHintLine(bodyWithLimit.hintLine),
233
+ );
234
+ },
235
+ invalidate: cache.invalidate,
236
+ } as Component;
237
+ }
@@ -0,0 +1,137 @@
1
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { sanitizeAnsiForThemedOutput } from "./ansi-utils.ts";
3
+ import { normalizeCodeWhitespace, wrapToWidth } from "./diff-text.ts";
4
+ import { emphasis, stabilizeBackgroundResets, type DiffTheme } from "./diff-palette.ts";
5
+ import { clampDiffLineToWidth } from "./diff-limits.ts";
6
+ import type { DiffMetaEntry, DiffStats } from "./diff-parse.ts";
7
+ import type { DiffPresentationMode } from "./diff-presentation.ts";
8
+ import type { RenderedRow } from "./diff-layout.ts";
9
+
10
+ export function formatMetaEntryRows(
11
+ entry: DiffMetaEntry,
12
+ width: number,
13
+ theme: DiffTheme,
14
+ wordWrap: boolean,
15
+ ): RenderedRow[] {
16
+ const normalized = sanitizeAnsiForThemedOutput(normalizeCodeWhitespace(entry.raw));
17
+ const lines = wordWrap
18
+ ? wrapToWidth(normalized, width, true)
19
+ : [truncateToWidth(normalized, width)];
20
+
21
+ const mapColor = (line: string): string => {
22
+ if (entry.kind === "hunk") {
23
+ return stabilizeBackgroundResets(theme.fg("accent", line));
24
+ }
25
+ if (entry.kind === "file") {
26
+ return stabilizeBackgroundResets(theme.fg("muted", line));
27
+ }
28
+ return stabilizeBackgroundResets(theme.fg("toolDiffContext", line));
29
+ };
30
+
31
+ return lines.map((line) => ({
32
+ text: mapColor(line),
33
+ hunkIndex: entry.kind === "file" ? null : entry.hunkIndex || null,
34
+ }));
35
+ }
36
+
37
+ function renderDiffStatBar(stats: DiffStats, width: number, theme: DiffTheme): string | null {
38
+ const totalChanges = stats.added + stats.removed;
39
+ if (totalChanges === 0 || width < 20) {
40
+ return null;
41
+ }
42
+
43
+ const barSlots = Math.max(8, Math.min(24, Math.floor(width / 12)));
44
+ let addedSlots = Math.max(
45
+ 0,
46
+ Math.min(barSlots, Math.round((stats.added / totalChanges) * barSlots)),
47
+ );
48
+ if (stats.added > 0 && addedSlots === 0) {
49
+ addedSlots = 1;
50
+ }
51
+ if (stats.removed > 0 && addedSlots >= barSlots) {
52
+ addedSlots = barSlots - 1;
53
+ }
54
+ const removedSlots = Math.max(0, barSlots - addedSlots);
55
+
56
+ const addedBar = addedSlots > 0 ? theme.fg("toolDiffAdded", "━".repeat(addedSlots)) : "";
57
+ const removedBar = removedSlots > 0 ? theme.fg("toolDiffRemoved", "━".repeat(removedSlots)) : "";
58
+ return stabilizeBackgroundResets(
59
+ `${theme.fg("dim", "[")}${addedBar}${removedBar}${theme.fg("dim", "]")}`,
60
+ );
61
+ }
62
+
63
+ function buildDiffSummaryBasePieces(stats: DiffStats, theme: DiffTheme): string[] {
64
+ // ↳ 前缀已按一南要求移除(2026-10-02):与 ⎿ 缩进引导符叠着读起来重复。
65
+ return [
66
+ theme.fg("toolOutput", emphasis(theme, "diff")),
67
+ theme.fg("toolDiffAdded", `+${stats.added}`),
68
+ theme.fg("toolDiffRemoved", `-${stats.removed}`),
69
+ ];
70
+ }
71
+
72
+ export function renderHeaderRows(
73
+ stats: DiffStats,
74
+ mode: Exclude<DiffPresentationMode, "summary">,
75
+ width: number,
76
+ theme: DiffTheme,
77
+ ): RenderedRow[] {
78
+ if (mode === "compact") {
79
+ const summary = buildDiffSummaryBasePieces(stats, theme).join(" ");
80
+ return [{ text: stabilizeBackgroundResets(truncateToWidth(summary, width)), hunkIndex: null }];
81
+ }
82
+
83
+ const summaryPieces = [...buildDiffSummaryBasePieces(stats, theme), theme.fg("muted", mode)];
84
+
85
+ const summary = summaryPieces.join(mode === "split" ? " " : theme.fg("muted", " • "));
86
+ const meter = renderDiffStatBar(stats, width, theme);
87
+ if (!meter) {
88
+ return [{ text: stabilizeBackgroundResets(truncateToWidth(summary, width)), hunkIndex: null }];
89
+ }
90
+
91
+ const meterSeparator = " ";
92
+ const meterWidth = visibleWidth(meterSeparator) + visibleWidth(meter);
93
+ if (meterWidth >= width) {
94
+ return [{ text: stabilizeBackgroundResets(truncateToWidth(summary, width)), hunkIndex: null }];
95
+ }
96
+
97
+ const summaryWidth = Math.max(0, width - meterWidth);
98
+ const fittedSummary = truncateToWidth(summary, summaryWidth);
99
+ return [
100
+ {
101
+ text: stabilizeBackgroundResets(`${fittedSummary}${meterSeparator}${meter}`),
102
+ hunkIndex: null,
103
+ },
104
+ ];
105
+ }
106
+
107
+ export function renderDiffFrameLine(width: number, theme: DiffTheme): string {
108
+ const frameWidth = Math.max(0, width);
109
+ if (frameWidth === 0) {
110
+ return "";
111
+ }
112
+ return stabilizeBackgroundResets(theme.fg("dim", "─".repeat(frameWidth)));
113
+ }
114
+
115
+ export function renderSingleDiffRow(
116
+ text: string,
117
+ color: string,
118
+ width: number,
119
+ theme: DiffTheme,
120
+ ): string[] {
121
+ if (width <= 0) {
122
+ return [""];
123
+ }
124
+ return [clampDiffLineToWidth(stabilizeBackgroundResets(theme.fg(color, text)), width)];
125
+ }
126
+
127
+ export function renderWriteHeader(
128
+ wasOverwrite: boolean,
129
+ width: number,
130
+ theme: DiffTheme,
131
+ headerLabel?: string,
132
+ ): string {
133
+ const actionLabel = headerLabel?.trim() || (wasOverwrite ? "overwritten" : "created");
134
+ return stabilizeBackgroundResets(
135
+ truncateToWidth(theme.fg("toolOutput", `↳ ${emphasis(theme, actionLabel)}`), width),
136
+ );
137
+ }
@@ -0,0 +1,115 @@
1
+ import { getLanguageFromPath, highlightCode } from "@earendil-works/pi-coding-agent";
2
+ import { sanitizeAnsiForThemedOutput } from "./ansi-utils.ts";
3
+ import { sanitizeToolResultText } from "../tool-result-sanitize.ts";
4
+ import { MAX_HL_CHARS, shikiHighlightCache } from "./shiki-highlight.ts";
5
+ import { normalizeCodeWhitespace } from "./diff-text.ts";
6
+ import {
7
+ applyInlineSpanHighlight,
8
+ getLineBackground,
9
+ resolveShikiTheme,
10
+ type DiffPalette,
11
+ type DiffTheme,
12
+ } from "./diff-palette.ts";
13
+ import type { DiffLineEntry, ParsedDiffEntry } from "./diff-parse.ts";
14
+ import type { DiffSpan } from "./diff-inline.ts";
15
+
16
+ export type CodeLineHighlighter = (line: string, entry: DiffLineEntry) => string;
17
+
18
+ function cleanCodeLine(line: string): string {
19
+ return sanitizeToolResultText(line).replace(/\n/g, "");
20
+ }
21
+
22
+ export function shouldHighlightCodeBlock(code: string): boolean {
23
+ return code.length <= MAX_HL_CHARS;
24
+ }
25
+
26
+ export function resolveLanguageFromPath(rawPath: string | undefined): string | undefined {
27
+ if (!rawPath || !rawPath.trim()) {
28
+ return undefined;
29
+ }
30
+ const normalizedPath = rawPath.replace(/^@/, "").trim();
31
+ if (!normalizedPath) {
32
+ return undefined;
33
+ }
34
+ try {
35
+ return getLanguageFromPath(normalizedPath);
36
+ } catch {
37
+ return undefined;
38
+ }
39
+ }
40
+
41
+ export function createCodeLineHighlighter(
42
+ language: string | undefined,
43
+ theme: DiffTheme,
44
+ entries: readonly ParsedDiffEntry[],
45
+ invalidate?: () => void,
46
+ ): CodeLineHighlighter {
47
+ const codeEntries = entries.filter((entry): entry is DiffLineEntry => entry.kind === "line");
48
+ const cleanLines = codeEntries.map((entry) =>
49
+ cleanCodeLine(normalizeCodeWhitespace(entry.content)),
50
+ );
51
+ const code = cleanLines.join("\n");
52
+ const shouldHighlight = !!language && shouldHighlightCodeBlock(code);
53
+ const fallbackLines = shouldHighlight
54
+ ? (() => {
55
+ try {
56
+ return highlightCode(code, language).map(sanitizeAnsiForThemedOutput);
57
+ } catch {
58
+ return cleanLines.map(sanitizeAnsiForThemedOutput);
59
+ }
60
+ })()
61
+ : cleanLines.map(sanitizeAnsiForThemedOutput);
62
+ const fallbackByEntry = new WeakMap(
63
+ codeEntries.map((entry, index) => [entry, fallbackLines[index] ?? cleanLines[index] ?? ""]),
64
+ );
65
+ if (!shouldHighlight) return (_line, entry) => fallbackByEntry.get(entry) ?? "";
66
+
67
+ const shikiTheme = resolveShikiTheme(theme);
68
+ let highlightedByEntry: WeakMap<DiffLineEntry, string> | undefined;
69
+ const resolveHighlighted = () => {
70
+ if (highlightedByEntry) return;
71
+ const highlighted = shikiHighlightCache.get(
72
+ code,
73
+ language,
74
+ shikiTheme,
75
+ fallbackLines,
76
+ invalidate,
77
+ );
78
+ if (highlighted) {
79
+ highlightedByEntry = new WeakMap(
80
+ codeEntries.map((entry, index) => [
81
+ entry,
82
+ sanitizeAnsiForThemedOutput(highlighted[index] ?? fallbackByEntry.get(entry) ?? ""),
83
+ ]),
84
+ );
85
+ }
86
+ };
87
+ resolveHighlighted();
88
+ return (_line, entry) => {
89
+ resolveHighlighted();
90
+ return highlightedByEntry?.get(entry) ?? fallbackByEntry.get(entry) ?? "";
91
+ };
92
+ }
93
+
94
+ export function highlightDiffLine(
95
+ codeText: string,
96
+ entry: DiffLineEntry,
97
+ inlineHighlights: WeakMap<DiffLineEntry, DiffSpan[]>,
98
+ palette: DiffPalette,
99
+ highlightLine: CodeLineHighlighter,
100
+ containerBgAnsi: string | undefined,
101
+ ): { highlighted: string; rowBg: string | undefined } {
102
+ const syntaxHighlighted = highlightLine(codeText, entry);
103
+ const rowBg = getLineBackground(entry.lineKind, palette, false);
104
+ const emphasisBg = getLineBackground(entry.lineKind, palette, true);
105
+ const inlineSpans = inlineHighlights.get(entry) ?? [];
106
+ const highlighted = applyInlineSpanHighlight(
107
+ codeText,
108
+ syntaxHighlighted,
109
+ inlineSpans,
110
+ emphasisBg,
111
+ rowBg,
112
+ containerBgAnsi,
113
+ );
114
+ return { highlighted, rowBg };
115
+ }