gsd-pi 2.16.0 → 2.17.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 (89) hide show
  1. package/dist/resources/extensions/gsd/auto-dashboard.ts +4 -0
  2. package/dist/resources/extensions/gsd/auto-dispatch.ts +9 -3
  3. package/dist/resources/extensions/gsd/auto-prompts.ts +71 -41
  4. package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
  5. package/dist/resources/extensions/gsd/auto.ts +54 -15
  6. package/dist/resources/extensions/gsd/commands.ts +20 -2
  7. package/dist/resources/extensions/gsd/complexity.ts +236 -0
  8. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  9. package/dist/resources/extensions/gsd/files.ts +6 -2
  10. package/dist/resources/extensions/gsd/git-service.ts +19 -8
  11. package/dist/resources/extensions/gsd/gitignore.ts +41 -2
  12. package/dist/resources/extensions/gsd/guided-flow.ts +10 -6
  13. package/dist/resources/extensions/gsd/metrics.ts +44 -0
  14. package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -0
  15. package/dist/resources/extensions/gsd/native-parser-bridge.ts +5 -0
  16. package/dist/resources/extensions/gsd/preferences.ts +122 -1
  17. package/dist/resources/extensions/gsd/routing-history.ts +290 -0
  18. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  19. package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  20. package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  21. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  22. package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  23. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
  24. package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  25. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  26. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  27. package/dist/resources/extensions/gsd/types.ts +28 -0
  28. package/dist/resources/extensions/gsd/worktree.ts +2 -2
  29. package/package.json +1 -1
  30. package/packages/pi-ai/dist/models.generated.d.ts +493 -13
  31. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  32. package/packages/pi-ai/dist/models.generated.js +422 -62
  33. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  34. package/packages/pi-ai/dist/providers/google-shared.d.ts +12 -0
  35. package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
  36. package/packages/pi-ai/dist/providers/google-shared.js +9 -22
  37. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
  38. package/packages/pi-ai/dist/providers/google-shared.test.d.ts +2 -0
  39. package/packages/pi-ai/dist/providers/google-shared.test.d.ts.map +1 -0
  40. package/packages/pi-ai/dist/providers/google-shared.test.js +125 -0
  41. package/packages/pi-ai/dist/providers/google-shared.test.js.map +1 -0
  42. package/packages/pi-ai/src/models.generated.ts +422 -62
  43. package/packages/pi-ai/src/providers/google-shared.test.ts +137 -0
  44. package/packages/pi-ai/src/providers/google-shared.ts +10 -19
  45. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +7 -7
  46. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  47. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
  48. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  49. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
  50. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
  51. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
  52. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
  53. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  54. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
  55. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  56. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +85 -0
  57. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
  58. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
  59. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  60. package/pkg/dist/modes/interactive/theme/theme.js +10 -0
  61. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  62. package/src/resources/extensions/gsd/auto-dashboard.ts +4 -0
  63. package/src/resources/extensions/gsd/auto-dispatch.ts +9 -3
  64. package/src/resources/extensions/gsd/auto-prompts.ts +71 -41
  65. package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
  66. package/src/resources/extensions/gsd/auto.ts +54 -15
  67. package/src/resources/extensions/gsd/commands.ts +20 -2
  68. package/src/resources/extensions/gsd/complexity.ts +236 -0
  69. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  70. package/src/resources/extensions/gsd/files.ts +6 -2
  71. package/src/resources/extensions/gsd/git-service.ts +19 -8
  72. package/src/resources/extensions/gsd/gitignore.ts +41 -2
  73. package/src/resources/extensions/gsd/guided-flow.ts +10 -6
  74. package/src/resources/extensions/gsd/metrics.ts +44 -0
  75. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -0
  76. package/src/resources/extensions/gsd/native-parser-bridge.ts +5 -0
  77. package/src/resources/extensions/gsd/preferences.ts +122 -1
  78. package/src/resources/extensions/gsd/routing-history.ts +290 -0
  79. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  80. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  81. package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  82. package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  83. package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  84. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
  85. package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  86. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  87. package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  88. package/src/resources/extensions/gsd/types.ts +28 -0
  89. package/src/resources/extensions/gsd/worktree.ts +2 -2
@@ -2,15 +2,11 @@
2
2
  * Shared diff computation utilities for the edit tool.
3
3
  * Used by both edit.ts (for execution) and tool-execution.ts (for preview rendering).
4
4
  *
5
- * Hot-path functions (fuzzyFindText, normalizeForFuzzyMatch, generateDiffString)
6
- * delegate to the native Rust engine for performance on large files.
5
+ * These helpers intentionally stay in JavaScript. Issue #453 showed that
6
+ * post-tool preview paths must not depend on the native addon because a native
7
+ * hang there can wedge the entire interactive session after a successful tool run.
7
8
  */
8
9
 
9
- import {
10
- fuzzyFindText as nativeFuzzyFindText,
11
- generateDiff as nativeGenerateDiff,
12
- normalizeForFuzzyMatch as nativeNormalizeForFuzzyMatch,
13
- } from "@gsd/native";
14
10
  import { constants } from "fs";
15
11
  import { access, readFile } from "fs/promises";
16
12
  import { resolveToCwd } from "./path-utils.js";
@@ -32,14 +28,23 @@ export function restoreLineEndings(text: string, ending: "\r\n" | "\n"): string
32
28
  }
33
29
 
34
30
  /**
35
- * Normalize text for fuzzy matching (native Rust implementation).
31
+ * Normalize text for fuzzy matching.
36
32
  * - Strip trailing whitespace from each line
37
33
  * - Normalize smart quotes to ASCII equivalents
38
34
  * - Normalize Unicode dashes/hyphens to ASCII hyphen
39
35
  * - Normalize special Unicode spaces to regular space
40
36
  */
41
37
  export function normalizeForFuzzyMatch(text: string): string {
42
- return nativeNormalizeForFuzzyMatch(text);
38
+ return text
39
+ .replace(/\r\n/g, "\n")
40
+ .replace(/\r/g, "\n")
41
+ .replace(/[“”]/g, '"')
42
+ .replace(/[‘’]/g, "'")
43
+ .replace(/[‐‑‒–—−]/g, "-")
44
+ .replace(/[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g, " ")
45
+ .split("\n")
46
+ .map((line) => line.replace(/[ \t]+$/g, ""))
47
+ .join("\n");
43
48
  }
44
49
 
45
50
  export interface FuzzyMatchResult {
@@ -59,14 +64,44 @@ export interface FuzzyMatchResult {
59
64
  }
60
65
 
61
66
  /**
62
- * Find oldText in content, trying exact match first, then fuzzy match
63
- * (native Rust implementation).
67
+ * Find oldText in content, trying exact match first, then fuzzy match.
64
68
  *
65
69
  * When fuzzy matching is used, the returned contentForReplacement is the
66
70
  * fuzzy-normalized version of the content.
67
71
  */
68
72
  export function fuzzyFindText(content: string, oldText: string): FuzzyMatchResult {
69
- return nativeFuzzyFindText(content, oldText);
73
+ const exactIndex = content.indexOf(oldText);
74
+ if (exactIndex !== -1) {
75
+ return {
76
+ found: true,
77
+ index: exactIndex,
78
+ matchLength: oldText.length,
79
+ usedFuzzyMatch: false,
80
+ contentForReplacement: content,
81
+ };
82
+ }
83
+
84
+ const normalizedContent = normalizeForFuzzyMatch(content);
85
+ const normalizedOldText = normalizeForFuzzyMatch(oldText);
86
+ const fuzzyIndex = normalizedContent.indexOf(normalizedOldText);
87
+
88
+ if (fuzzyIndex === -1) {
89
+ return {
90
+ found: false,
91
+ index: -1,
92
+ matchLength: 0,
93
+ usedFuzzyMatch: false,
94
+ contentForReplacement: content,
95
+ };
96
+ }
97
+
98
+ return {
99
+ found: true,
100
+ index: fuzzyIndex,
101
+ matchLength: normalizedOldText.length,
102
+ usedFuzzyMatch: true,
103
+ contentForReplacement: normalizedContent,
104
+ };
70
105
  }
71
106
 
72
107
  /** Strip UTF-8 BOM if present, return both the BOM (if any) and the text without it */
@@ -75,20 +110,81 @@ export function stripBom(content: string): { bom: string; text: string } {
75
110
  }
76
111
 
77
112
  /**
78
- * Generate a unified diff string with line numbers and context
79
- * (native Rust implementation using Myers' algorithm via the `similar` crate).
113
+ * Generate a unified diff string with line numbers and context.
80
114
  *
81
115
  * Returns both the diff string and the first changed line number (in the new file).
116
+ * Only lines within `contextLines` of a change are included (like unified diff).
82
117
  */
83
118
  export function generateDiffString(
84
119
  oldContent: string,
85
120
  newContent: string,
86
121
  contextLines = 4,
87
122
  ): { diff: string; firstChangedLine: number | undefined } {
88
- const result = nativeGenerateDiff(oldContent, newContent, contextLines);
123
+ const ops = buildLineDiff(oldContent, newContent);
124
+ let firstChangedLine: number | undefined;
125
+
126
+ // First pass: assign line numbers and find changed indices
127
+ const annotated: { op: LineDiffOp; oldLine: number; newLine: number }[] = [];
128
+ let oldLine = 1;
129
+ let newLine = 1;
130
+ const changedIndices: number[] = [];
131
+
132
+ for (let idx = 0; idx < ops.length; idx++) {
133
+ const op = ops[idx];
134
+ annotated.push({ op, oldLine, newLine });
135
+
136
+ if (op.type !== "context") {
137
+ changedIndices.push(idx);
138
+ if (firstChangedLine === undefined) {
139
+ firstChangedLine = newLine;
140
+ }
141
+ }
142
+
143
+ if (op.type === "remove") {
144
+ oldLine += 1;
145
+ } else if (op.type === "add") {
146
+ newLine += 1;
147
+ } else {
148
+ oldLine += 1;
149
+ newLine += 1;
150
+ }
151
+ }
152
+
153
+ // Build set of indices to include (changes + surrounding context)
154
+ const includeSet = new Set<number>();
155
+ for (const ci of changedIndices) {
156
+ for (let k = Math.max(0, ci - contextLines); k <= Math.min(ops.length - 1, ci + contextLines); k++) {
157
+ includeSet.add(k);
158
+ }
159
+ }
160
+
161
+ const maxLine = Math.max(oldLine - 1, newLine - 1, 1);
162
+ const lineNumberWidth = String(maxLine).length;
163
+ const rendered: string[] = [];
164
+ let lastIncluded = -1;
165
+
166
+ for (let idx = 0; idx < annotated.length; idx++) {
167
+ if (!includeSet.has(idx)) continue;
168
+
169
+ // Insert separator when there's a gap between included regions
170
+ if (lastIncluded !== -1 && idx > lastIncluded + 1) {
171
+ rendered.push("...");
172
+ }
173
+ lastIncluded = idx;
174
+
175
+ const { op, oldLine: ol, newLine: nl } = annotated[idx];
176
+ if (op.type === "context") {
177
+ rendered.push(` ${String(nl).padStart(lineNumberWidth, " ")} ${op.line}`);
178
+ } else if (op.type === "remove") {
179
+ rendered.push(`-${String(ol).padStart(lineNumberWidth, " ")} ${op.line}`);
180
+ } else {
181
+ rendered.push(`+${String(nl).padStart(lineNumberWidth, " ")} ${op.line}`);
182
+ }
183
+ }
184
+
89
185
  return {
90
- diff: result.diff,
91
- firstChangedLine: result.firstChangedLine ?? undefined,
186
+ diff: rendered.join("\n"),
187
+ firstChangedLine,
92
188
  };
93
189
  }
94
190
 
@@ -101,6 +197,138 @@ export interface EditDiffError {
101
197
  error: string;
102
198
  }
103
199
 
200
+ type LineDiffOp =
201
+ | { type: "context"; line: string }
202
+ | { type: "remove"; line: string }
203
+ | { type: "add"; line: string };
204
+
205
+ function splitLines(text: string): string[] {
206
+ const lines = text.split("\n");
207
+ if (lines.length > 0 && lines.at(-1) === "") {
208
+ lines.pop();
209
+ }
210
+ return lines;
211
+ }
212
+
213
+ /**
214
+ * Maximum number of cells (oldLines * newLines) before we switch from the
215
+ * full LCS DP algorithm to a simpler linear-scan diff. This prevents OOM
216
+ * on large files (e.g. 10k lines would need a 100M-cell matrix).
217
+ */
218
+ const MAX_DP_CELLS = 4_000_000; // ~32 MB for 64-bit numbers
219
+
220
+ function buildLineDiff(oldContent: string, newContent: string): LineDiffOp[] {
221
+ const oldLines = splitLines(oldContent);
222
+ const newLines = splitLines(newContent);
223
+
224
+ const cells = (oldLines.length + 1) * (newLines.length + 1);
225
+ if (cells > MAX_DP_CELLS) {
226
+ return buildLineDiffLinear(oldLines, newLines);
227
+ }
228
+
229
+ return buildLineDiffLCS(oldLines, newLines);
230
+ }
231
+
232
+ /**
233
+ * Full LCS-based diff using O(n*m) DP table. Produces optimal diffs but
234
+ * is only safe for files where n*m <= MAX_DP_CELLS.
235
+ */
236
+ function buildLineDiffLCS(oldLines: string[], newLines: string[]): LineDiffOp[] {
237
+ const dp: number[][] = Array.from({ length: oldLines.length + 1 }, () =>
238
+ Array<number>(newLines.length + 1).fill(0),
239
+ );
240
+
241
+ for (let i = oldLines.length - 1; i >= 0; i--) {
242
+ for (let j = newLines.length - 1; j >= 0; j--) {
243
+ if (oldLines[i] === newLines[j]) {
244
+ dp[i][j] = dp[i + 1][j + 1] + 1;
245
+ } else {
246
+ dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
247
+ }
248
+ }
249
+ }
250
+
251
+ const ops: LineDiffOp[] = [];
252
+ let i = 0;
253
+ let j = 0;
254
+
255
+ while (i < oldLines.length && j < newLines.length) {
256
+ if (oldLines[i] === newLines[j]) {
257
+ ops.push({ type: "context", line: oldLines[i] });
258
+ i += 1;
259
+ j += 1;
260
+ continue;
261
+ }
262
+
263
+ if (dp[i + 1][j] >= dp[i][j + 1]) {
264
+ ops.push({ type: "remove", line: oldLines[i] });
265
+ i += 1;
266
+ } else {
267
+ ops.push({ type: "add", line: newLines[j] });
268
+ j += 1;
269
+ }
270
+ }
271
+
272
+ while (i < oldLines.length) {
273
+ ops.push({ type: "remove", line: oldLines[i] });
274
+ i += 1;
275
+ }
276
+
277
+ while (j < newLines.length) {
278
+ ops.push({ type: "add", line: newLines[j] });
279
+ j += 1;
280
+ }
281
+
282
+ return ops;
283
+ }
284
+
285
+ /**
286
+ * Linear-time fallback diff for large files. Matches common prefix/suffix,
287
+ * then treats the remaining middle as a bulk remove+add. Not optimal but
288
+ * O(n+m) in both time and space.
289
+ */
290
+ function buildLineDiffLinear(oldLines: string[], newLines: string[]): LineDiffOp[] {
291
+ const ops: LineDiffOp[] = [];
292
+
293
+ // Match common prefix
294
+ let prefixLen = 0;
295
+ const minLen = Math.min(oldLines.length, newLines.length);
296
+ while (prefixLen < minLen && oldLines[prefixLen] === newLines[prefixLen]) {
297
+ prefixLen++;
298
+ }
299
+
300
+ // Match common suffix (not overlapping with prefix)
301
+ let suffixLen = 0;
302
+ while (
303
+ suffixLen < minLen - prefixLen &&
304
+ oldLines[oldLines.length - 1 - suffixLen] === newLines[newLines.length - 1 - suffixLen]
305
+ ) {
306
+ suffixLen++;
307
+ }
308
+
309
+ // Emit prefix context
310
+ for (let i = 0; i < prefixLen; i++) {
311
+ ops.push({ type: "context", line: oldLines[i] });
312
+ }
313
+
314
+ // Emit removed lines from the middle
315
+ for (let i = prefixLen; i < oldLines.length - suffixLen; i++) {
316
+ ops.push({ type: "remove", line: oldLines[i] });
317
+ }
318
+
319
+ // Emit added lines from the middle
320
+ for (let j = prefixLen; j < newLines.length - suffixLen; j++) {
321
+ ops.push({ type: "add", line: newLines[j] });
322
+ }
323
+
324
+ // Emit suffix context
325
+ for (let i = oldLines.length - suffixLen; i < oldLines.length; i++) {
326
+ ops.push({ type: "context", line: oldLines[i] });
327
+ }
328
+
329
+ return ops;
330
+ }
331
+
104
332
  /**
105
333
  * Compute the diff for an edit operation without applying it.
106
334
  * Used for preview rendering in the TUI before the tool executes.
@@ -11,6 +11,11 @@ import {
11
11
  } from "@gsd/native";
12
12
  import { getCustomThemesDir, getThemesDir } from "../../../config.js";
13
13
 
14
+ // Issue #453: native preview highlighting can wedge the entire interactive
15
+ // session after a successful file tool. Keep the safer plain-text path as the
16
+ // default and allow native highlighting only as an explicit opt-in.
17
+ const NATIVE_TUI_HIGHLIGHT_ENABLED = process.env.GSD_ENABLE_NATIVE_TUI_HIGHLIGHT === "1";
18
+
14
19
  // ============================================================================
15
20
  // Types & Schema
16
21
  // ============================================================================
@@ -955,6 +960,10 @@ function getHighlightColors(t: Theme): HighlightColors {
955
960
  * Returns array of highlighted lines.
956
961
  */
957
962
  export function highlightCode(code: string, lang?: string): string[] {
963
+ if (!NATIVE_TUI_HIGHLIGHT_ENABLED) {
964
+ return code.split("\n");
965
+ }
966
+
958
967
  const validLang = lang && supportsLanguage(lang) ? lang : null;
959
968
  try {
960
969
  return nativeHighlightCode(code, validLang, getHighlightColors(theme)).split("\n");
@@ -1051,6 +1060,10 @@ export function getMarkdownTheme(): MarkdownTheme {
1051
1060
  underline: (text: string) => theme.underline(text),
1052
1061
  strikethrough: (text: string) => chalk.strikethrough(text),
1053
1062
  highlightCode: (code: string, lang?: string): string[] => {
1063
+ if (!NATIVE_TUI_HIGHLIGHT_ENABLED) {
1064
+ return code.split("\n").map((line) => theme.fg("mdCodeBlock", line));
1065
+ }
1066
+
1054
1067
  const validLang = lang && supportsLanguage(lang) ? lang : null;
1055
1068
  try {
1056
1069
  return nativeHighlightCode(code, validLang, getHighlightColors(theme)).split("\n");
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/theme/theme.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/theme/theme.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,10 @@ import { TypeCompiler } from "@sinclair/typebox/compiler";
5
5
  import chalk from "chalk";
6
6
  import { highlightCode as nativeHighlightCode, supportsLanguage, } from "@gsd/native";
7
7
  import { getCustomThemesDir, getThemesDir } from "../../../config.js";
8
+ // Issue #453: native preview highlighting can wedge the entire interactive
9
+ // session after a successful file tool. Keep the safer plain-text path as the
10
+ // default and allow native highlighting only as an explicit opt-in.
11
+ const NATIVE_TUI_HIGHLIGHT_ENABLED = process.env.GSD_ENABLE_NATIVE_TUI_HIGHLIGHT === "1";
8
12
  // ============================================================================
9
13
  // Types & Schema
10
14
  // ============================================================================
@@ -798,6 +802,9 @@ function getHighlightColors(t) {
798
802
  * Returns array of highlighted lines.
799
803
  */
800
804
  export function highlightCode(code, lang) {
805
+ if (!NATIVE_TUI_HIGHLIGHT_ENABLED) {
806
+ return code.split("\n");
807
+ }
801
808
  const validLang = lang && supportsLanguage(lang) ? lang : null;
802
809
  try {
803
810
  return nativeHighlightCode(code, validLang, getHighlightColors(theme)).split("\n");
@@ -892,6 +899,9 @@ export function getMarkdownTheme() {
892
899
  underline: (text) => theme.underline(text),
893
900
  strikethrough: (text) => chalk.strikethrough(text),
894
901
  highlightCode: (code, lang) => {
902
+ if (!NATIVE_TUI_HIGHLIGHT_ENABLED) {
903
+ return code.split("\n").map((line) => theme.fg("mdCodeBlock", line));
904
+ }
895
905
  const validLang = lang && supportsLanguage(lang) ? lang : null;
896
906
  try {
897
907
  return nativeHighlightCode(code, validLang, getHighlightColors(theme)).split("\n");