@dsh-plus/llm-pi 0.1.23 → 0.1.24

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 (3) hide show
  1. package/lib/client.js +4 -2620
  2. package/package.json +3 -3
  3. package/lib/style.css +0 -471
package/lib/client.js CHANGED
@@ -4,2625 +4,9 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- //#region \0rolldown/runtime.js
8
- var __create = Object.create;
9
- var __defProp = Object.defineProperty;
10
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
- var __getOwnPropNames = Object.getOwnPropertyNames;
12
- var __getProtoOf = Object.getPrototypeOf;
13
- var __hasOwnProp = Object.prototype.hasOwnProperty;
14
- var __copyProps = (to, from, except, desc) => {
15
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
- key = keys[i];
17
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
- get: ((k) => from[k]).bind(null, key),
19
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
- });
21
- }
22
- return to;
23
- };
24
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
- value: mod,
26
- enumerable: true
27
- }) : target, mod));
28
- //#endregion
29
7
  let react = require("react");
30
8
  let react_jsx_runtime = require("react/jsx-runtime");
31
- let clsx = require("clsx");
32
- clsx = __toESM(clsx, 1);
33
- require("react-dom");
34
- require("anser");
35
- let shiki_core = require("shiki/core");
36
- let shiki_engine_javascript = require("shiki/engine/javascript");
37
- let _shikijs_langs_typescript = require("@shikijs/langs/typescript");
38
- _shikijs_langs_typescript = __toESM(_shikijs_langs_typescript, 1);
39
- let _shikijs_langs_shellscript = require("@shikijs/langs/shellscript");
40
- _shikijs_langs_shellscript = __toESM(_shikijs_langs_shellscript, 1);
41
- let _shikijs_langs_json = require("@shikijs/langs/json");
42
- _shikijs_langs_json = __toESM(_shikijs_langs_json, 1);
43
- let mdast_util_from_markdown = require("mdast-util-from-markdown");
44
- let mdast_util_gfm = require("mdast-util-gfm");
45
- let mdast_util_math = require("mdast-util-math");
46
- let micromark_extension_gfm = require("micromark-extension-gfm");
47
- let micromark_extension_math = require("micromark-extension-math");
48
- let micromark_core_commonmark = require("micromark-core-commonmark");
49
- let micromark_util_character = require("micromark-util-character");
50
- let micromark_util_classify_character = require("micromark-util-classify-character");
51
- let micromark_util_symbol = require("micromark-util-symbol");
52
- let micromark_factory_space = require("micromark-factory-space");
53
- let micromark_util_sanitize_uri = require("micromark-util-sanitize-uri");
54
- let katex = require("katex");
55
- katex = __toESM(katex, 1);
56
- require("katex/dist/katex.min.css");
57
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/DiffBlock.module.css
58
- var DiffBlock_module_default = {
59
- "add": "Bz5CTG_add",
60
- "block": "Bz5CTG_block",
61
- "body": "Bz5CTG_body",
62
- "copyButton": "Bz5CTG_copyButton",
63
- "del": "Bz5CTG_del",
64
- "expand": "Bz5CTG_expand",
65
- "footer": "Bz5CTG_footer",
66
- "gap": "Bz5CTG_gap",
67
- "line": "Bz5CTG_line",
68
- "path": "Bz5CTG_path"
69
- };
70
- //#endregion
71
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/markdown/CodeBlock.module.css
72
- var CodeBlock_module_default = {
73
- "action": "gcpIYW_action",
74
- "banner": "gcpIYW_banner",
75
- "bannerWrap": "gcpIYW_bannerWrap",
76
- "block": "gcpIYW_block",
77
- "copyButton": "gcpIYW_copyButton",
78
- "infostring": "gcpIYW_infostring",
79
- "numbered": "gcpIYW_numbered",
80
- "plain": "gcpIYW_plain",
81
- "shiki": "gcpIYW_shiki"
82
- };
83
- //#endregion
84
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/markdown/MarkdownText.module.css
85
- var MarkdownText_module_default = {
86
- "fileMention": "fdgmDq_fileMention",
87
- "image": "fdgmDq_image",
88
- "imageAlt": "fdgmDq_imageAlt",
89
- "linkIcon": "fdgmDq_linkIcon",
90
- "markdown": "fdgmDq_markdown",
91
- "tableFill": "fdgmDq_tableFill",
92
- "tableScroll": "fdgmDq_tableScroll"
93
- };
94
- //#endregion
95
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/index.js
96
- /** ic_ds_chevron_down_outline_14 */
97
- const IconChevronDownOutline14 = ({ size = 14, className }) => (0, react_jsx_runtime.jsx)("svg", {
98
- width: size,
99
- height: size,
100
- className,
101
- viewBox: "0 0 14 14",
102
- fill: "none",
103
- xmlns: "http://www.w3.org/2000/svg",
104
- children: (0, react_jsx_runtime.jsx)("path", {
105
- d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
106
- fill: "currentColor"
107
- })
108
- });
109
- /**
110
- * Write text to the host clipboard, preferring the async Clipboard API and
111
- * falling back to `execCommand('copy')` on hosts (jsdom, insecure contexts)
112
- * that omit it.
113
- * @param text - the exact text to place on the clipboard.
114
- * @returns true only when the host accepted the write.
115
- */
116
- async function writeClipboard(text) {
117
- if (navigator.clipboard?.writeText) try {
118
- await navigator.clipboard.writeText(text);
119
- return true;
120
- } catch {
121
- return false;
122
- }
123
- const exec = typeof document.execCommand === "function" ? document.execCommand.bind(document) : void 0;
124
- if (exec === void 0) return false;
125
- const el = document.createElement("textarea");
126
- el.value = text;
127
- el.setAttribute("readonly", "");
128
- el.style.position = "fixed";
129
- el.style.left = "-9999px";
130
- document.body.appendChild(el);
131
- el.select();
132
- try {
133
- return exec("copy");
134
- } catch {
135
- return false;
136
- } finally {
137
- el.remove();
138
- }
139
- }
140
- const CODE_FILE_TYPE_SET = /* @__PURE__ */ new Set([
141
- "angular",
142
- "c",
143
- "clojure",
144
- "cmake",
145
- "cpp",
146
- "csharp",
147
- "css",
148
- "dart",
149
- "docker",
150
- "elixir",
151
- "env",
152
- "erlang",
153
- "flutter",
154
- "git",
155
- "go",
156
- "graphql",
157
- "haskell",
158
- "ini",
159
- "java",
160
- "javascript",
161
- "json",
162
- "kotlin",
163
- "lua",
164
- "makefile",
165
- "node",
166
- "objective-c",
167
- "perl",
168
- "php",
169
- "powershell",
170
- "protobuf",
171
- "python",
172
- "r",
173
- "react",
174
- "ruby",
175
- "rust",
176
- "scala",
177
- "shell",
178
- "solidity",
179
- "sql",
180
- "svelte",
181
- "swift",
182
- "toml",
183
- "typescript",
184
- "vue",
185
- "wasm",
186
- "xml",
187
- "yaml",
188
- "zig"
189
- ]);
190
- const FILE_NAME_TYPES = {
191
- ".bash_profile": "shell",
192
- ".bashrc": "shell",
193
- ".env": "env",
194
- ".gitattributes": "git",
195
- ".gitconfig": "git",
196
- ".gitignore": "git",
197
- ".gitmodules": "git",
198
- ".mailmap": "git",
199
- ".profile": "shell",
200
- ".zprofile": "shell",
201
- ".zshrc": "shell",
202
- "bsdmakefile": "makefile",
203
- "cmakelists.txt": "cmake",
204
- "commit_editmsg": "git",
205
- "compose.yaml": "docker",
206
- "compose.yml": "docker",
207
- "docker-compose.yaml": "docker",
208
- "docker-compose.yml": "docker",
209
- "dockerfile": "docker",
210
- "gemfile": "ruby",
211
- "gnumakefile": "makefile",
212
- "guardfile": "ruby",
213
- "makefile": "makefile",
214
- "npm-shrinkwrap.json": "node",
215
- "package-lock.json": "node",
216
- "package.json": "node",
217
- "podfile": "ruby",
218
- "rakefile": "ruby"
219
- };
220
- const FILE_NAME_PREFIX_TYPES = [["dockerfile.", "docker"], [".env.", "env"]];
221
- const FILE_NAME_SUFFIX_TYPES = [
222
- [".component.ts", "angular"],
223
- [".component.html", "angular"],
224
- [".directive.ts", "angular"],
225
- [".service.ts", "angular"],
226
- [".module.ts", "angular"],
227
- [".pipe.ts", "angular"],
228
- [".guard.ts", "angular"],
229
- [".interceptor.ts", "angular"],
230
- [".dockerfile", "docker"]
231
- ];
232
- const EXTENSION_TYPES$1 = {
233
- "bash": "shell",
234
- "c": "c",
235
- "c++": "cpp",
236
- "cc": "cpp",
237
- "cfg": "ini",
238
- "cjs": "javascript",
239
- "clj": "clojure",
240
- "cljc": "clojure",
241
- "cljs": "clojure",
242
- "cmake": "cmake",
243
- "cpp": "cpp",
244
- "cs": "csharp",
245
- "csh": "shell",
246
- "css": "css",
247
- "csx": "csharp",
248
- "cts": "typescript",
249
- "cxx": "cpp",
250
- "dart": "dart",
251
- "dtd": "xml",
252
- "edn": "clojure",
253
- "env": "env",
254
- "erl": "erlang",
255
- "es6": "javascript",
256
- "escript": "erlang",
257
- "ex": "elixir",
258
- "exs": "elixir",
259
- "fish": "shell",
260
- "gemspec": "ruby",
261
- "go": "go",
262
- "gql": "graphql",
263
- "graphql": "graphql",
264
- "h": "c",
265
- "h++": "cpp",
266
- "hh": "cpp",
267
- "hpp": "cpp",
268
- "hrl": "erlang",
269
- "hs": "haskell",
270
- "hxx": "cpp",
271
- "ini": "ini",
272
- "ipp": "cpp",
273
- "java": "java",
274
- "js": "javascript",
275
- "json": "json",
276
- "json5": "json",
277
- "jsonc": "json",
278
- "jsx": "react",
279
- "ksh": "shell",
280
- "kt": "kotlin",
281
- "kts": "kotlin",
282
- "lhs": "haskell",
283
- "lua": "lua",
284
- "m": "objective-c",
285
- "mak": "makefile",
286
- "mjs": "javascript",
287
- "mk": "makefile",
288
- "mm": "objective-c",
289
- "mts": "typescript",
290
- "node": "node",
291
- "pch": "objective-c",
292
- "php": "php",
293
- "php3": "php",
294
- "php4": "php",
295
- "php5": "php",
296
- "phps": "php",
297
- "phtml": "php",
298
- "pl": "perl",
299
- "plist": "xml",
300
- "pm": "perl",
301
- "pod": "perl",
302
- "proto": "protobuf",
303
- "ps1": "powershell",
304
- "psd1": "powershell",
305
- "psm1": "powershell",
306
- "py": "python",
307
- "pyi": "python",
308
- "pyw": "python",
309
- "pyx": "python",
310
- "r": "r",
311
- "rake": "ruby",
312
- "rb": "ruby",
313
- "rmd": "r",
314
- "rs": "rust",
315
- "sc": "scala",
316
- "scala": "scala",
317
- "sh": "shell",
318
- "sol": "solidity",
319
- "sql": "sql",
320
- "svelte": "svelte",
321
- "swift": "swift",
322
- "t": "perl",
323
- "tcsh": "shell",
324
- "toml": "toml",
325
- "tpp": "cpp",
326
- "ts": "typescript",
327
- "tsx": "react",
328
- "vue": "vue",
329
- "wasm": "wasm",
330
- "wast": "wasm",
331
- "wat": "wasm",
332
- "xml": "xml",
333
- "xsd": "xml",
334
- "xsl": "xml",
335
- "xslt": "xml",
336
- "yaml": "yaml",
337
- "yml": "yaml",
338
- "zig": "zig",
339
- "zsh": "shell"
340
- };
341
- const LINK_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
342
- "ts",
343
- "tsx",
344
- "js",
345
- "jsx",
346
- "mjs",
347
- "cjs",
348
- "cts",
349
- "mts",
350
- "css",
351
- "scss",
352
- "sass",
353
- "less",
354
- "html",
355
- "htm",
356
- "vue",
357
- "svelte",
358
- "astro",
359
- "json",
360
- "jsonc",
361
- "json5",
362
- "yaml",
363
- "yml",
364
- "toml",
365
- "xml",
366
- "ini",
367
- "env",
368
- "sh",
369
- "bash",
370
- "zsh",
371
- "fish",
372
- "ps1",
373
- "bat",
374
- "cmd",
375
- "py",
376
- "pyi",
377
- "rb",
378
- "rs",
379
- "go",
380
- "java",
381
- "kt",
382
- "kts",
383
- "c",
384
- "cc",
385
- "cpp",
386
- "cxx",
387
- "h",
388
- "hh",
389
- "hpp",
390
- "cs",
391
- "php",
392
- "swift",
393
- "sql",
394
- "csv",
395
- "tsv",
396
- "proto",
397
- "graphql",
398
- "gql",
399
- "lua",
400
- "r",
401
- "pl",
402
- "scala",
403
- "clj",
404
- "cljs",
405
- "ex",
406
- "exs",
407
- "erl",
408
- "hs",
409
- "dart"
410
- ]);
411
- function basename$1(path) {
412
- return path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
413
- }
414
- /**
415
- * Test whether a resolved file type uses the full-color code-icon set.
416
- * @param type - Resolved file-type string.
417
- * @returns Whether the value is a detailed code-file type.
418
- */
419
- function isCodeFileType(type) {
420
- return CODE_FILE_TYPE_SET.has(type);
421
- }
422
- /**
423
- * Test whether an extension belonged to the established coarse LinkIcon code category.
424
- * @param extension - Extension without a leading dot.
425
- * @returns Whether clickable links keep the code glyph for this extension.
426
- */
427
- function isLinkCodeExtension(extension) {
428
- return LINK_CODE_EXTENSIONS.has(extension.toLowerCase());
429
- }
430
- /**
431
- * Resolve the most specific code/configuration icon according to the supplied map priority.
432
- * @param name - Lowercase basename.
433
- * @param extension - Lowercase extension without the leading dot.
434
- * @param context - Optional project-file snapshot for context-sensitive matches.
435
- * @returns The detailed code type, or null when the traditional file classifier owns the path.
436
- */
437
- function classifyCodeFileType(name, extension, context) {
438
- const exact = FILE_NAME_TYPES[name];
439
- if (exact !== void 0) return exact;
440
- for (const [prefix, type] of FILE_NAME_PREFIX_TYPES) if (name.startsWith(prefix)) return type;
441
- for (const [suffix, type] of FILE_NAME_SUFFIX_TYPES) if (name.endsWith(suffix)) return type;
442
- if (extension === "dart" && context !== void 0) {
443
- if ((Object.entries(context.files).find(([path]) => basename$1(path).toLowerCase() === "pubspec.yaml")?.[1])?.includes("flutter:") === true) return "flutter";
444
- }
445
- return EXTENSION_TYPES$1[extension] ?? null;
446
- }
447
- const EXTENSION_TYPES = {
448
- scss: "code",
449
- sass: "code",
450
- less: "code",
451
- vue: "code",
452
- svelte: "code",
453
- astro: "code",
454
- bat: "code",
455
- cmd: "code",
456
- csv: "code",
457
- tsv: "code",
458
- html: "html",
459
- htm: "html",
460
- png: "image",
461
- jpg: "image",
462
- jpeg: "image",
463
- gif: "image",
464
- svg: "image",
465
- webp: "image",
466
- avif: "image",
467
- bmp: "image",
468
- ico: "image",
469
- tif: "image",
470
- tiff: "image",
471
- heic: "image",
472
- heif: "image",
473
- md: "markdown",
474
- mdx: "markdown",
475
- markdown: "markdown",
476
- pdf: "pdf",
477
- ppt: "ppt",
478
- pptx: "ppt",
479
- key: "ppt",
480
- mp4: "video",
481
- mov: "video",
482
- m4v: "video",
483
- webm: "video",
484
- mkv: "video",
485
- avi: "video",
486
- mpg: "video",
487
- mpeg: "video",
488
- doc: "word",
489
- docx: "word",
490
- rtf: "word",
491
- odt: "word",
492
- pages: "word",
493
- xls: "excel",
494
- xlsx: "excel",
495
- xlsm: "excel",
496
- numbers: "excel"
497
- };
498
- const NAME_TYPES = {
499
- changelog: "markdown",
500
- contributing: "markdown",
501
- readme: "markdown"
502
- };
503
- function basename(path) {
504
- return path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
505
- }
506
- /**
507
- * Extract the final suffix from a file path without changing its case.
508
- * A leading dot starts a suffix, while a missing or trailing dot returns an empty string.
509
- * @param path - File path or basename using either path separator.
510
- * @returns The characters after the basename's final dot.
511
- */
512
- function fileExtension(path) {
513
- const name = basename(path);
514
- const dot = name.lastIndexOf(".");
515
- return dot < 0 ? "" : name.slice(dot + 1);
516
- }
517
- /**
518
- * Classify a file path or name for file-card presentation.
519
- * Matching is case-insensitive and applies code filename rules before extension rules;
520
- * unknown names fall back to `other`.
521
- * @param path - File path or basename using either path separator.
522
- * @param context - Optional project files used by context-sensitive code mappings.
523
- * @returns The file's closed presentation category.
524
- */
525
- function classifyFileType(path, context) {
526
- const name = basename(path).toLowerCase();
527
- const extension = fileExtension(name).toLowerCase();
528
- return classifyCodeFileType(name, extension, context) ?? NAME_TYPES[name] ?? EXTENSION_TYPES[extension] ?? "other";
529
- }
530
- /**
531
- * Derive a file path's link-icon category from its extension. Unknown and
532
- * missing extensions fall to `other` (the plain-paper glyph).
533
- * @param path - File path as the producing tool spelled it (either separator).
534
- * @returns The file's glyph category; never `url` or `folder`.
535
- */
536
- function classifyLinkPath(path) {
537
- const type = classifyFileType(path);
538
- const extension = fileExtension(path);
539
- if (isCodeFileType(type)) return isLinkCodeExtension(extension) ? "code" : "other";
540
- if (extension === "") return "other";
541
- switch (type) {
542
- case "code":
543
- case "html": return "code";
544
- case "image": return "image";
545
- case "excel":
546
- case "pdf":
547
- case "ppt":
548
- case "word": return "document";
549
- case "markdown":
550
- case "other":
551
- case "video": return "other";
552
- /* v8 ignore next -- classifyFileType returns a closed union exhausted above */
553
- default: return assertNever$1(type);
554
- }
555
- }
556
- const GlobeGlyph = ({ size, className }) => (0, react_jsx_runtime.jsx)("svg", {
557
- width: size,
558
- height: size,
559
- className,
560
- viewBox: "0 0 20 20",
561
- fill: "none",
562
- xmlns: "http://www.w3.org/2000/svg",
563
- "aria-hidden": true,
564
- children: (0, react_jsx_runtime.jsx)("path", {
565
- fillRule: "evenodd",
566
- clipRule: "evenodd",
567
- d: "M9.99951 1.68994C14.5889 1.68994 18.3098 5.41022 18.3101 9.99951C18.3101 14.589 14.589 18.3101 9.99951 18.3101C5.41022 18.3098 1.68994 14.5889 1.68994 9.99951C1.69017 5.41037 5.41037 1.69017 9.99951 1.68994ZM3.23193 10.7495C3.52924 13.463 5.41981 15.6965 7.94873 16.4946C7.74323 16.1395 7.56487 15.736 7.41162 15.3022C6.97892 14.0773 6.70459 12.4906 6.63232 10.7495H3.23193ZM13.3667 10.7495C13.2944 12.4906 13.0211 14.0773 12.5884 15.3022C12.4352 15.7359 12.2557 16.1386 12.0503 16.4937C14.5794 15.6956 16.4707 13.4631 16.7681 10.7495H13.3667ZM8.13428 10.7495C8.20587 12.3512 8.45916 13.7619 8.82666 14.8022C9.046 15.4231 9.29031 15.8714 9.52686 16.1499C9.76321 16.428 9.92505 16.4651 9.99951 16.4653C10.0737 16.4653 10.2362 16.4288 10.4731 16.1499C10.7097 15.8714 10.954 15.4231 11.1733 14.8022C11.5408 13.7619 11.7941 12.3512 11.8657 10.7495H8.13428ZM9.99951 3.18994C9.92505 3.19013 9.76321 3.22823 9.52686 3.50635C9.29035 3.78486 9.04595 4.23325 8.82666 4.854C8.43314 5.96808 8.16996 7.50638 8.12158 9.24951H11.8784C11.83 7.50638 11.5669 5.96808 11.1733 4.854C10.954 4.23324 10.7097 3.78486 10.4731 3.50635C10.2362 3.22748 10.0737 3.18994 9.99951 3.18994ZM12.27 3.57861C12.3862 3.82232 12.4924 4.0822 12.5884 4.354C13.0494 5.65902 13.33 7.37415 13.3784 9.24951H16.7681C16.4792 6.61525 14.6885 4.43369 12.27 3.57861ZM7.729 3.57861C5.3109 4.4338 3.52072 6.61541 3.23193 9.24951H6.62158C6.67001 7.37415 6.95063 5.65902 7.41162 4.354C7.50759 4.08233 7.61287 3.82221 7.729 3.57861Z",
568
- fill: "currentColor"
569
- })
570
- });
571
- const CodeGlyph = ({ size, className }) => (0, react_jsx_runtime.jsxs)("svg", {
572
- width: size,
573
- height: size,
574
- className,
575
- viewBox: "0 0 20 20",
576
- fill: "none",
577
- xmlns: "http://www.w3.org/2000/svg",
578
- "aria-hidden": true,
579
- children: [
580
- (0, react_jsx_runtime.jsx)("path", {
581
- d: "M5 15.2021L1.67188 11.874C1.42017 11.6223 1.1823 11.3858 1.00781 11.1689C0.823288 10.9396 0.645016 10.6559 0.586914 10.2891C0.556619 10.0975 0.556619 9.90245 0.586914 9.71094C0.645016 9.3441 0.823288 9.06042 1.00781 8.83105C1.1823 8.61419 1.42017 8.37768 1.67188 8.12598L5 4.79785L6.20215 6L2.87402 9.32813C2.59555 9.6066 2.43539 9.76802 2.33203 9.89648C2.29246 9.94567 2.27406 9.97638 2.26563 9.99121C2.26528 9.99711 2.26528 10.0029 2.26563 10.0088C2.27406 10.0236 2.29246 10.0543 2.33203 10.1035C2.43539 10.232 2.59555 10.3934 2.87402 10.6719L6.20215 14L5 15.2021Z",
582
- fill: "currentColor"
583
- }),
584
- (0, react_jsx_runtime.jsx)("path", {
585
- d: "M15 15.2021L18.3281 11.874C18.5798 11.6223 18.8177 11.3858 18.9922 11.1689C19.1767 10.9396 19.355 10.6559 19.4131 10.2891C19.4434 10.0975 19.4434 9.90245 19.4131 9.71094C19.355 9.3441 19.1767 9.06042 18.9922 8.83105C18.8177 8.61419 18.5798 8.37768 18.3281 8.12598L15 4.79785L13.7979 6L17.126 9.32813C17.4045 9.6066 17.5646 9.76802 17.668 9.89648C17.7075 9.94567 17.7259 9.97638 17.7344 9.99121C17.7347 9.99711 17.7347 10.0029 17.7344 10.0088C17.7259 10.0236 17.7075 10.0543 17.668 10.1035C17.5646 10.232 17.4045 10.3934 17.126 10.6719L13.7979 14L15 15.2021Z",
586
- fill: "currentColor"
587
- }),
588
- (0, react_jsx_runtime.jsx)("path", {
589
- d: "M9.41181 15.0993L7.76955 14.6593L10.3577 5.00002L12 5.44006L9.41181 15.0993Z",
590
- fill: "currentColor"
591
- })
592
- ]
593
- });
594
- const FolderGlyph = ({ size, className }) => (0, react_jsx_runtime.jsxs)("svg", {
595
- width: size,
596
- height: size,
597
- className,
598
- viewBox: "0 0 20 20",
599
- fill: "none",
600
- xmlns: "http://www.w3.org/2000/svg",
601
- "aria-hidden": true,
602
- children: [(0, react_jsx_runtime.jsx)("path", {
603
- d: "M1.2002 11.4178V6.7244C1.2002 6.47036 1.19957 6.2962 1.21387 6.12773C1.29746 5.14425 1.76052 4.23195 2.50489 3.58378C2.63247 3.47269 2.77337 3.37059 2.97852 3.2205C3.11259 3.12241 3.20444 3.05471 3.29981 2.99296C3.85318 2.6347 4.49029 2.42604 5.14844 2.38847C5.26177 2.38201 5.37601 2.38261 5.542 2.38261H7.91211C8.63453 2.38261 9.13199 2.37701 9.61329 2.49882C9.7656 2.53739 9.91613 2.58526 10.0625 2.64237C10.525 2.8229 10.9267 3.11712 11.5147 3.5369L12.0859 3.94413C12.1927 4.02032 12.2509 4.06163 12.2959 4.09062C12.3336 4.11488 12.3394 4.11605 12.332 4.11308C12.3403 4.11633 12.3488 4.11875 12.3574 4.12089C12.3627 4.12161 12.3757 4.12265 12.4004 4.12382C12.4538 4.12635 12.5252 4.12675 12.6563 4.12675C13.7369 4.12675 14.6105 4.12608 15.3086 4.19999C16.0209 4.27545 16.643 4.43585 17.1885 4.82011C17.5449 5.07115 17.8554 5.38173 18.1064 5.73808C18.4908 6.28368 18.6521 6.90645 18.7275 7.61894C18.8014 8.31701 18.7998 9.19066 18.7998 10.2713V11.4178C18.7998 12.5248 18.801 13.4195 18.7236 14.1336C18.6446 14.8626 18.4763 15.4982 18.0742 16.0516C17.8399 16.374 17.5559 16.6571 17.2334 16.8914C16.68 17.2935 16.0445 17.4628 15.3154 17.5418C14.6014 17.6191 13.7065 17.618 12.5996 17.618H7.40039C6.29352 17.618 5.39863 17.6191 4.68457 17.5418C3.95547 17.4628 3.32001 17.2935 2.76661 16.8914C2.44414 16.6571 2.16011 16.374 1.92579 16.0516C1.52375 15.4982 1.35537 14.8626 1.27637 14.1336C1.199 13.4195 1.2002 12.5248 1.2002 11.4178ZM2.79981 11.4178C2.79981 12.5602 2.80136 13.3529 2.86719 13.9607C2.93144 14.5537 3.04885 14.8759 3.21973 15.1111C3.35542 15.2979 3.52029 15.4618 3.70704 15.5975C3.94221 15.7683 4.26361 15.8867 4.85645 15.951C5.46439 16.0168 6.25766 16.0174 7.40039 16.0174H12.5996C13.7424 16.0174 14.5356 16.0168 15.1436 15.951C15.7364 15.8867 16.0578 15.7683 16.293 15.5975C16.4797 15.4618 16.6446 15.2979 16.7803 15.1111C16.9512 14.8759 17.0686 14.5537 17.1328 13.9607C17.1986 13.3529 17.2002 12.5602 17.2002 11.4178V10.2713C17.2002 9.15571 17.1987 8.38124 17.1357 7.7869C17.0743 7.20716 16.9618 6.8914 16.7988 6.65995C16.6534 6.45353 16.473 6.2741 16.2666 6.1287C16.0351 5.96565 15.7196 5.85222 15.1397 5.79081C14.5454 5.7279 13.7716 5.72733 12.6563 5.72733C12.4465 5.72733 12.2081 5.73253 11.9736 5.6746C11.8947 5.65509 11.8168 5.63037 11.7412 5.60038C11.517 5.51135 11.3268 5.36863 11.1563 5.24687L10.585 4.83866C9.93302 4.37321 9.71454 4.22394 9.48047 4.13261C9.39579 4.09958 9.30882 4.07191 9.22071 4.0496C8.97721 3.98798 8.71286 3.98222 7.91211 3.98222H5.542C5.35809 3.98222 5.29665 3.98285 5.23926 3.98612C4.85821 4.00788 4.48933 4.12829 4.16895 4.33573C4.12071 4.36697 4.07125 4.40295 3.92286 4.51151C3.69569 4.67771 3.61995 4.73389 3.55567 4.78983C3.1248 5.16501 2.8561 5.69324 2.80762 6.26249C2.80039 6.34752 2.79981 6.44244 2.79981 6.7244V11.4178Z",
604
- fill: "currentColor"
605
- }), (0, react_jsx_runtime.jsx)("path", {
606
- d: "M15.4913 8.11793V9.71754H4.50892V8.11793H15.4913Z",
607
- fill: "currentColor"
608
- })]
609
- });
610
- const PhotoGlyph = ({ size, className }) => (0, react_jsx_runtime.jsxs)("svg", {
611
- width: size,
612
- height: size,
613
- className,
614
- viewBox: "0 0 20 20",
615
- fill: "none",
616
- xmlns: "http://www.w3.org/2000/svg",
617
- "aria-hidden": true,
618
- children: [(0, react_jsx_runtime.jsx)("path", {
619
- fillRule: "evenodd",
620
- clipRule: "evenodd",
621
- d: "M13 5.15039C14.574 5.15039 15.8496 6.42599 15.8496 8C15.8496 9.57401 14.574 10.8496 13 10.8496C11.426 10.8496 10.1504 9.57401 10.1504 8C10.1504 6.42599 11.426 5.1504 13 5.15039ZM13 6.84961C12.3649 6.84961 11.8496 7.36488 11.8496 8C11.8496 8.63513 12.3649 9.15039 13 9.15039C13.6351 9.15039 14.1504 8.63513 14.1504 8C14.1504 7.36488 13.6351 6.84961 13 6.84961Z",
622
- fill: "currentColor"
623
- }), (0, react_jsx_runtime.jsx)("path", {
624
- fillRule: "evenodd",
625
- clipRule: "evenodd",
626
- d: "M12.5996 2.15039C13.7056 2.15039 14.6038 2.14883 15.3213 2.22656C16.0546 2.30602 16.6994 2.47647 17.2627 2.88574C17.5895 3.1232 17.8768 3.41049 18.1143 3.73731C18.5235 4.30063 18.694 4.9454 18.7734 5.67871C18.8512 6.39616 18.8496 7.29441 18.8496 8.40039V11.5996C18.8496 12.7056 18.8512 13.6038 18.7734 14.3213C18.694 15.0546 18.5235 15.6994 18.1143 16.2627C17.8768 16.5895 17.5895 16.8768 17.2627 17.1143C16.6994 17.5235 16.0546 17.694 15.3213 17.7734C14.6038 17.8512 13.7056 17.8496 12.5996 17.8496H7.40039C6.29441 17.8496 5.39616 17.8512 4.67871 17.7734C3.9454 17.694 3.30063 17.5235 2.73731 17.1143C2.41049 16.8768 2.1232 16.5895 1.88574 16.2627C1.47647 15.6994 1.30602 15.0546 1.22656 14.3213C1.14883 13.6038 1.15039 12.7056 1.15039 11.5996V8.40039C1.15039 7.29441 1.14883 6.39616 1.22656 5.67871C1.30602 4.9454 1.47647 4.30063 1.88574 3.73731C2.1232 3.41049 2.41049 3.1232 2.73731 2.88574C3.30063 2.47647 3.9454 2.30602 4.67871 2.22656C5.39616 2.14883 6.29441 2.15039 7.40039 2.15039H12.5996ZM5.56348 10.8164C5.30962 10.8348 5.02517 10.9291 4.58008 11.1973C4.13813 11.4635 3.60989 11.8565 2.85449 12.4229C2.85831 13.1414 2.86855 13.6903 2.91699 14.1377C2.98078 14.7264 3.09709 15.0384 3.26074 15.2637C3.39335 15.4462 3.55382 15.6067 3.73633 15.7393C3.96158 15.9029 4.2736 16.0192 4.86231 16.083C5.46688 16.1485 6.25677 16.1504 7.40039 16.1504H11.9492L8.71094 12.9131C8.00039 12.2025 7.51129 11.7149 7.09571 11.376C6.69274 11.0474 6.42364 10.9144 6.17481 10.8604C5.97432 10.8168 5.7681 10.8017 5.56348 10.8164ZM7.40039 3.84961C6.25677 3.84961 5.46688 3.85153 4.86231 3.91699C4.2736 3.98078 3.96158 4.09709 3.73633 4.26074C3.55382 4.39335 3.39335 4.55382 3.26074 4.73633C3.09709 4.96158 2.98078 5.2736 2.91699 5.86231C2.85153 6.46688 2.84961 7.25677 2.84961 8.40039V10.3105C3.15698 10.0902 3.43896 9.8988 3.70215 9.74024C4.26649 9.40025 4.81639 9.16621 5.44141 9.1211C5.80775 9.0947 6.17623 9.1213 6.53516 9.19922C7.14769 9.33224 7.65922 9.64212 8.16992 10.0586C8.66793 10.4648 9.22718 11.025 9.91309 11.7109L14.333 16.1318C14.6363 16.122 14.9012 16.1086 15.1377 16.083C15.7264 16.0192 16.0384 15.9029 16.2637 15.7393C16.4462 15.6067 16.6067 15.4462 16.7393 15.2637C16.9029 15.0384 17.0192 14.7264 17.083 14.1377C17.1485 13.5331 17.1504 12.7432 17.1504 11.5996V8.40039C17.1504 7.25677 17.1485 6.46688 17.083 5.86231C17.0192 5.2736 16.9029 4.96158 16.7393 4.73633C16.6067 4.55382 16.4462 4.39335 16.2637 4.26074C16.0384 4.09709 15.7264 3.98078 15.1377 3.91699C14.5331 3.85153 13.7432 3.84961 12.5996 3.84961H7.40039Z",
627
- fill: "currentColor"
628
- })]
629
- });
630
- const PaperGlyph = ({ size, className }) => (0, react_jsx_runtime.jsx)("svg", {
631
- width: size,
632
- height: size,
633
- className,
634
- viewBox: "0 0 20 20",
635
- fill: "none",
636
- xmlns: "http://www.w3.org/2000/svg",
637
- "aria-hidden": true,
638
- children: (0, react_jsx_runtime.jsx)("path", {
639
- d: "M11.3467 4.59607C11.3469 5.40284 11.3499 5.9609 11.3965 6.39099C11.444 6.82964 11.5288 7.03981 11.6328 7.18298C11.7253 7.3102 11.8376 7.42158 11.9648 7.51404C12.108 7.61807 12.3181 7.70283 12.7568 7.75037C13.1851 7.79675 13.7399 7.79992 14.541 7.80017L11.3467 4.59607ZM17.2695 12.7455C17.2695 13.5901 17.2706 14.2881 17.21 14.848C17.1477 15.4228 17.0119 15.9463 16.6768 16.4076C16.4856 16.6707 16.2543 16.902 15.9912 17.0931C15.5298 17.4284 15.0065 17.5641 14.4316 17.6263C13.8718 17.687 13.1737 17.6859 12.3291 17.6859H8.23926C7.39498 17.6859 6.69745 17.687 6.1377 17.6263C5.56284 17.5641 5.03855 17.4284 4.57715 17.0931C4.31413 16.902 4.08272 16.6706 3.8916 16.4076C3.55647 15.9463 3.42167 15.4227 3.35938 14.848C3.29872 14.2881 3.29981 13.5901 3.29981 12.7455V6.80017C3.29981 5.95586 3.29876 5.25837 3.35938 4.69861C3.42166 4.12375 3.55638 3.59946 3.8916 3.13806C4.08274 2.87503 4.31412 2.64365 4.57715 2.45252C5.03855 2.11729 5.56284 1.98257 6.1377 1.92029C6.69746 1.85967 7.39495 1.86072 8.23926 1.86072H9.38468C10.039 1.86072 10.3661 1.86072 10.6679 1.95896C10.7677 1.99146 10.8648 2.03176 10.9583 2.0795C11.241 2.22382 11.472 2.45549 11.934 2.91883L16.2188 7.21612C16.679 7.67761 16.909 7.90836 17.0523 8.1902C17.0997 8.28342 17.1397 8.38022 17.172 8.4797C17.2695 8.78045 17.2695 9.10631 17.2695 9.75801V12.7455ZM4.89942 12.7455C4.89942 13.6256 4.90106 14.2215 4.9502 14.6752C4.99773 15.1139 5.08249 15.324 5.18652 15.4672C5.27898 15.5944 5.39037 15.7067 5.51758 15.7992C5.66076 15.9032 5.87093 15.988 6.30957 16.0355C6.76321 16.0847 7.35919 16.0853 8.23926 16.0853H12.3291C13.2091 16.0853 13.8051 16.0846 14.2588 16.0355C14.6973 15.988 14.9076 15.9032 15.0508 15.7992C15.1781 15.7067 15.2903 15.5945 15.3828 15.4672C15.4868 15.324 15.5716 15.1139 15.6191 14.6752C15.6683 14.2215 15.6689 13.6256 15.6689 12.7455V9.40076H14.6865C13.8419 9.40076 13.1439 9.40185 12.584 9.34119C12.0093 9.27889 11.4857 9.14411 11.0244 8.80896C10.7614 8.61784 10.53 8.38645 10.3389 8.12341C10.0036 7.66202 9.86795 7.13773 9.80567 6.56287C9.74505 6.00312 9.74609 5.30558 9.7461 4.4613V3.46033H8.23926C7.35919 3.46033 6.76321 3.46196 6.30957 3.51111C5.87097 3.55865 5.66075 3.64342 5.51758 3.74744C5.39057 3.83979 5.27888 3.95148 5.18652 4.07849C5.0825 4.22166 4.99773 4.43188 4.9502 4.87048C4.90105 5.32412 4.89942 5.92011 4.89942 6.80017V12.7455Z",
640
- fill: "currentColor"
641
- })
642
- });
643
- const PaperDocGlyph = ({ size, className }) => (0, react_jsx_runtime.jsxs)("svg", {
644
- width: size,
645
- height: size,
646
- className,
647
- viewBox: "0 0 20 20",
648
- fill: "none",
649
- xmlns: "http://www.w3.org/2000/svg",
650
- "aria-hidden": true,
651
- children: [
652
- (0, react_jsx_runtime.jsx)("path", {
653
- d: "M11.3457 4.5957C11.3459 5.40247 11.3489 5.96054 11.3955 6.39063C11.443 6.82927 11.5278 7.03944 11.6318 7.18262C11.7243 7.30983 11.8367 7.42122 11.9639 7.51367C12.1071 7.61771 12.3171 7.70247 12.7559 7.75C13.1841 7.79639 13.739 7.79955 14.54 7.79981L11.3457 4.5957ZM17.2686 12.7451C17.2686 13.5897 17.2696 14.2878 17.209 14.8477C17.1467 15.4224 17.011 15.9459 16.6758 16.4072C16.4846 16.6703 16.2533 16.9016 15.9902 17.0928C15.5289 17.428 15.0055 17.5637 14.4307 17.626C13.8708 17.6866 13.1727 17.6855 12.3281 17.6855H8.23828C7.394 17.6855 6.69647 17.6866 6.13672 17.626C5.56186 17.5637 5.03757 17.428 4.57617 17.0928C4.31315 16.9016 4.08175 16.6703 3.89063 16.4072C3.55549 15.9459 3.42069 15.4224 3.3584 14.8477C3.29774 14.2878 3.29883 13.5897 3.29883 12.7451V6.79981C3.29883 5.95549 3.29778 5.258 3.3584 4.69824C3.42068 4.12338 3.5554 3.5991 3.89063 3.1377C4.08176 2.87466 4.31314 2.64328 4.57617 2.45215C5.03757 2.11692 5.56186 1.98221 6.13672 1.91992C6.69648 1.8593 7.39397 1.86035 8.23828 1.86035H9.3837C10.038 1.86035 10.3652 1.86035 10.6669 1.9586C10.7668 1.99109 10.8639 2.03139 10.9573 2.07913C11.24 2.22346 11.471 2.45512 11.933 2.91846L16.2178 7.21575C16.678 7.67725 16.9081 7.90799 17.0514 8.18983C17.0988 8.28305 17.1388 8.37985 17.171 8.47933C17.2686 8.78009 17.2686 9.10594 17.2686 9.75765V12.7451ZM4.89844 12.7451C4.89844 13.6253 4.90008 14.2211 4.94922 14.6748C4.99675 15.1135 5.08151 15.3236 5.18555 15.4668C5.278 15.594 5.38939 15.7064 5.5166 15.7988C5.65978 15.9029 5.86995 15.9876 6.30859 16.0352C6.76223 16.0843 7.35822 16.085 8.23828 16.085H12.3281C13.2081 16.085 13.8042 16.0843 14.2578 16.0352C14.6963 15.9876 14.9066 15.9028 15.0498 15.7988C15.1771 15.7063 15.2893 15.5941 15.3818 15.4668C15.4859 15.3236 15.5706 15.1135 15.6182 14.6748C15.6673 14.2211 15.668 13.6252 15.668 12.7451V9.40039H14.6855C13.841 9.40039 13.1429 9.40148 12.583 9.34082C12.0083 9.27852 11.4847 9.14374 11.0234 8.8086C10.7604 8.61747 10.5291 8.38608 10.3379 8.12305C10.0027 7.66165 9.86697 7.13736 9.80469 6.5625C9.74408 6.00275 9.74512 5.30522 9.74512 4.46094V3.45996H8.23828C7.35822 3.45996 6.76223 3.46159 6.30859 3.51074C5.86999 3.55828 5.65977 3.64305 5.5166 3.74707C5.38959 3.83942 5.2779 3.95111 5.18555 4.07813C5.08153 4.2213 4.99676 4.43152 4.94922 4.87012C4.90007 5.32376 4.89844 5.91974 4.89844 6.79981V12.7451Z",
654
- fill: "currentColor"
655
- }),
656
- (0, react_jsx_runtime.jsx)("path", {
657
- d: "M11.1523 13.0995V14.4999H6.30273V13.0995H11.1523Z",
658
- fill: "currentColor"
659
- }),
660
- (0, react_jsx_runtime.jsx)("path", {
661
- d: "M10.2148 10V11.4004H6.30273V10H10.2148Z",
662
- fill: "currentColor"
663
- })
664
- ]
665
- });
666
- /** Local exhaustiveness helper — this package does not depend on `dsh-llm`. */
667
- /* v8 ignore next 3 -- closed-union backstop; only reached if a kind is forged */
668
- function assertNever$1(value) {
669
- throw new Error(`unreachable link icon kind: ${String(value)}`);
670
- }
671
- /**
672
- * Render the leading glyph for one clickable artifact link.
673
- * @param props - The link category, optional size (default 14px — the inline
674
- * link text size these glyphs sit beside), and optional CSS class.
675
- * @returns The category's SVG glyph, riding currentColor.
676
- */
677
- function LinkIcon({ kind, size = 14, className }) {
678
- switch (kind) {
679
- case "url": return (0, react_jsx_runtime.jsx)(GlobeGlyph, {
680
- size,
681
- className
682
- });
683
- case "folder": return (0, react_jsx_runtime.jsx)(FolderGlyph, {
684
- size,
685
- className
686
- });
687
- case "code": return (0, react_jsx_runtime.jsx)(CodeGlyph, {
688
- size,
689
- className
690
- });
691
- case "image": return (0, react_jsx_runtime.jsx)(PhotoGlyph, {
692
- size,
693
- className
694
- });
695
- case "document": return (0, react_jsx_runtime.jsx)(PaperDocGlyph, {
696
- size,
697
- className
698
- });
699
- case "other": return (0, react_jsx_runtime.jsx)(PaperGlyph, {
700
- size,
701
- className
702
- });
703
- /* v8 ignore next -- closed-union backstop; only reached if a kind is forged */
704
- default: return assertNever$1(kind);
705
- }
706
- }
707
- /**
708
- * The client's ONE syntax highlighter: a synchronous fine-grained shiki core
709
- * (JavaScript regex engine — no oniguruma WASM, bundle-friendly) with an
710
- * explicit grammar allowlist and a CSS-variables theme. Colors live in the
711
- * theme package's token sheets as `--shiki-*` custom properties (light and
712
- * dark blocks), never here — the repo's tokens-only styling rule.
713
- *
714
- * Only the three markdown-fence and `run_code` grammars (TypeScript, shell,
715
- * JSON) load into the singleton at boot — the set every session renders. The
716
- * read card's wider extension set (the file-extension language hints the read
717
- * tool's `langFromPath` emits — `packages/fs/tool-fs`: python, rust, yaml,
718
- * markup, …) is imported lazily and registered the first time such a language
719
- * is requested, so a session that never opens a read card in one of those
720
- * languages pays neither the ~1.6 MB of grammar modules nor their synchronous
721
- * init. The first render of a lazy language falls back to plain text while its
722
- * grammar loads, then {@link onGrammarLoaded} notifies subscribers to re-render
723
- * with highlighting. An unknown or absent language falls back to plain text (no
724
- * highlighting, still monospace) — never an error.
725
- */
726
- /**
727
- * Grammars the singleton loads at boot; each entry's own `name` is the id
728
- * `codeToTokens`/`codeToHtml` resolve. The JS-family aliases (js/jsx/ts/tsx)
729
- * resolve to the TypeScript grammar rather than a separate one: it tokenizes
730
- * plain TS/JS exactly, and JSX/TSX approximately (shiki's TS grammar is not the
731
- * dedicated TSX grammar, so JSX elements tokenize imperfectly) — an accepted
732
- * trade to keep the boot set to one JS-family grammar. The read card's wider
733
- * set loads lazily through {@link LAZY_GRAMMARS}.
734
- */
735
- const LANGS = [
736
- _shikijs_langs_typescript.default,
737
- _shikijs_langs_shellscript.default,
738
- _shikijs_langs_json.default
739
- ];
740
- /**
741
- * The read card's extension grammars, each behind a dynamic import so its
742
- * module stays out of the boot chunk until a read of that language renders.
743
- * Keyed by the grammar id (`LanguageRegistration.name`) the aliases resolve to.
744
- * `@shikijs/langs`' default export is a `LanguageRegistration[]`; the loader
745
- * hands the whole array to `loadLanguageSync`, which registers each entry
746
- * (including embedded sub-grammars). The three boot grammars are absent —
747
- * already loaded, so no alias value ever points at a missing entry here.
748
- */
749
- const LAZY_GRAMMARS = /* @__PURE__ */ new Map([
750
- ["python", () => import("@shikijs/langs/python")],
751
- ["ruby", () => import("@shikijs/langs/ruby")],
752
- ["go", () => import("@shikijs/langs/go")],
753
- ["rust", () => import("@shikijs/langs/rust")],
754
- ["java", () => import("@shikijs/langs/java")],
755
- ["c", () => import("@shikijs/langs/c")],
756
- ["cpp", () => import("@shikijs/langs/cpp")],
757
- ["csharp", () => import("@shikijs/langs/csharp")],
758
- ["kotlin", () => import("@shikijs/langs/kotlin")],
759
- ["swift", () => import("@shikijs/langs/swift")],
760
- ["php", () => import("@shikijs/langs/php")],
761
- ["yaml", () => import("@shikijs/langs/yaml")],
762
- ["toml", () => import("@shikijs/langs/toml")],
763
- ["ini", () => import("@shikijs/langs/ini")],
764
- ["markdown", () => import("@shikijs/langs/markdown")],
765
- ["mdx", () => import("@shikijs/langs/mdx")],
766
- ["html", () => import("@shikijs/langs/html")],
767
- ["css", () => import("@shikijs/langs/css")],
768
- ["scss", () => import("@shikijs/langs/scss")],
769
- ["less", () => import("@shikijs/langs/less")],
770
- ["sql", () => import("@shikijs/langs/sql")],
771
- ["xml", () => import("@shikijs/langs/xml")],
772
- ["lua", () => import("@shikijs/langs/lua")]
773
- ]);
774
- /**
775
- * Language ids (and aliases) the highlighter accepts; everything else renders
776
- * plain. A Map, not an object: fence info strings are assistant-authored, so
777
- * a label like `constructor` or `__proto__` must miss instead of resolving an
778
- * inherited property and crashing the renderer inside shiki. Keys cover both
779
- * the markdown-fence aliases `CodeBlock` uses and the file-extension hint ids
780
- * the read tool's `langFromPath` emits, so both callers resolve the same
781
- * grammars. The JS family maps to the TypeScript grammar (see {@link LANGS} for
782
- * the JSX/TSX approximation). A value not in {@link LANGS} names a
783
- * {@link LAZY_GRAMMARS} entry loaded on first use.
784
- */
785
- const LANG_ALIASES = /* @__PURE__ */ new Map([
786
- ["typescript", "typescript"],
787
- ["ts", "typescript"],
788
- ["tsx", "typescript"],
789
- ["javascript", "typescript"],
790
- ["js", "typescript"],
791
- ["jsx", "typescript"],
792
- ["shellscript", "shellscript"],
793
- ["bash", "shellscript"],
794
- ["sh", "shellscript"],
795
- ["shell", "shellscript"],
796
- ["zsh", "shellscript"],
797
- ["json", "json"],
798
- ["jsonc", "json"],
799
- ["py", "python"],
800
- ["python", "python"],
801
- ["rb", "ruby"],
802
- ["ruby", "ruby"],
803
- ["go", "go"],
804
- ["rs", "rust"],
805
- ["rust", "rust"],
806
- ["java", "java"],
807
- ["c", "c"],
808
- ["cpp", "cpp"],
809
- ["cs", "csharp"],
810
- ["csharp", "csharp"],
811
- ["kotlin", "kotlin"],
812
- ["swift", "swift"],
813
- ["php", "php"],
814
- ["yaml", "yaml"],
815
- ["yml", "yaml"],
816
- ["toml", "toml"],
817
- ["ini", "ini"],
818
- ["md", "markdown"],
819
- ["markdown", "markdown"],
820
- ["mdx", "mdx"],
821
- ["html", "html"],
822
- ["css", "css"],
823
- ["scss", "scss"],
824
- ["less", "less"],
825
- ["sql", "sql"],
826
- ["xml", "xml"],
827
- ["lua", "lua"]
828
- ]);
829
- /**
830
- * Whether a language hint can use the shared syntax highlighter.
831
- * @param lang - Language hint from a code surface.
832
- * @returns Whether the hint resolves to a supported grammar.
833
- */
834
- function supportsHighlighting(lang) {
835
- return lang !== void 0 && LANG_ALIASES.has(lang.toLowerCase());
836
- }
837
- /** All token colors resolve through `--shiki-*` custom properties (theme package sheets). */
838
- const cssVariablesTheme = (0, shiki_core.createCssVariablesTheme)({
839
- name: "css-variables",
840
- variablePrefix: "--shiki-",
841
- fontStyle: true
842
- });
843
- /**
844
- * The client regex engine compiles each TextMate pattern when its scanner is
845
- * created. Shiki otherwise defers patterns longer than 3,000 characters until
846
- * their first match; that compilation counts against Shiki's 500 ms per-line
847
- * budget and can return a partial token stream under host contention. Eager
848
- * compilation leaves the same budget in place for scanning user content.
849
- */
850
- const regexEngine = (0, shiki_engine_javascript.createJavaScriptRegexEngine)({
851
- forgiving: true,
852
- regexConstructor: (pattern) => (0, shiki_engine_javascript.defaultJavaScriptRegexConstructor)(pattern, { lazyCompileLength: Number.POSITIVE_INFINITY })
853
- });
854
- let singleton;
855
- /** Representative paths through every boot grammar, compiled before user content is timed. */
856
- const BOOT_GRAMMAR_WARMUPS = [
857
- {
858
- lang: "typescript",
859
- code: "const answer: number = 42"
860
- },
861
- {
862
- lang: "shellscript",
863
- code: "printf '%s\\n' \"$HOME\""
864
- },
865
- {
866
- lang: "json",
867
- code: "{\"ready\":true}"
868
- }
869
- ];
870
- /** Construct and pre-tokenize the boot grammars outside the user-content scan budget. */
871
- function createHighlighter() {
872
- const instance = (0, shiki_core.createHighlighterCoreSync)({
873
- themes: [cssVariablesTheme],
874
- langs: LANGS,
875
- engine: regexEngine
876
- });
877
- for (const sample of BOOT_GRAMMAR_WARMUPS) instance.codeToTokens(sample.code, {
878
- lang: sample.lang,
879
- theme: "css-variables",
880
- tokenizeTimeLimit: 0
881
- });
882
- return instance;
883
- }
884
- /** The synchronous highlighter (one instance per document); pre-warmed below, lazy as the fallback. */
885
- function highlighter() {
886
- singleton ??= createHighlighter();
887
- return singleton;
888
- }
889
- /** Grammar ids whose lazy import is in flight or done, so it is requested once. */
890
- const requested = /* @__PURE__ */ new Set();
891
- /** Subscribers re-rendered after a lazy grammar registers (React callers). */
892
- const listeners = /* @__PURE__ */ new Set();
893
- /** Bumped on each lazy-grammar load; the `useSyncExternalStore` snapshot. */
894
- let loadCount = 0;
895
- /**
896
- * Subscribe to lazy-grammar load completions; `listener` fires after a
897
- * {@link LAZY_GRAMMARS} grammar finishes registering on the singleton, so a
898
- * caller that rendered its plain fallback while the grammar loaded can
899
- * re-highlight. Uses the `useSyncExternalStore` subscribe signature; pair it with
900
- * {@link grammarLoadCount} as the snapshot. Returns an unsubscribe function.
901
- * @param listener - invoked (no args) on each grammar-load completion.
902
- * @returns a disposer that removes the listener.
903
- */
904
- function subscribeGrammarLoaded(listener) {
905
- listeners.add(listener);
906
- return () => {
907
- listeners.delete(listener);
908
- };
909
- }
910
- /**
911
- * The lazy-grammar load counter — a value that changes on every load, so a
912
- * `useSyncExternalStore` snapshot re-renders the subscriber when a grammar
913
- * registers. Opaque: only its identity across renders matters.
914
- * @returns the current load count.
915
- */
916
- function grammarLoadCount() {
917
- return loadCount;
918
- }
919
- /**
920
- * Ensure the grammar `resolved` names is registered. A boot grammar (not in
921
- * {@link LAZY_GRAMMARS}) and an already-loaded lazy grammar report ready
922
- * synchronously; a lazy grammar not yet loaded starts its import (once) and
923
- * reports not-ready, so the caller renders plain until a
924
- * {@link subscribeGrammarLoaded} listener fires.
925
- * @param resolved - the grammar id an alias resolved to.
926
- * @returns whether the grammar is registered and ready to tokenize now.
927
- */
928
- function ensureGrammar(resolved) {
929
- const load = LAZY_GRAMMARS.get(resolved);
930
- if (load === void 0) return true;
931
- if (highlighter().getLoadedLanguages().includes(resolved)) return true;
932
- if (!requested.has(resolved)) {
933
- requested.add(resolved);
934
- load().then((mod) => {
935
- highlighter().loadLanguageSync(mod.default);
936
- loadCount += 1;
937
- for (const listener of listeners) listener();
938
- });
939
- }
940
- return false;
941
- }
942
- setTimeout(() => {
943
- highlighter();
944
- }, 0).unref?.();
945
- /**
946
- * Highlight `code` into shiki's HTML (a single `<pre class="shiki">` tree)
947
- * when `lang` maps to a registered grammar; `undefined` means the caller
948
- * renders its plain fallback. A lazy grammar not yet loaded returns `undefined`
949
- * for this call and loads in the background; subscribe with
950
- * {@link onGrammarLoaded} to re-highlight once it registers.
951
- * @param code - the source text.
952
- * @param lang - the language hint (a markdown fence info string or a fixed caller id).
953
- * @returns the highlighted HTML, or `undefined` for unknown or not-yet-loaded languages.
954
- */
955
- function highlightToHtml(code, lang) {
956
- const resolved = lang === void 0 ? void 0 : LANG_ALIASES.get(lang.toLowerCase());
957
- if (resolved === void 0) return void 0;
958
- if (!ensureGrammar(resolved)) return void 0;
959
- return highlighter().codeToHtml(code, {
960
- lang: resolved,
961
- theme: "css-variables"
962
- });
963
- }
964
- /** vscode-textmate FontStyle bits shiki folds into `text-decoration` values. */
965
- const DECORATION_BITS = [[4, "underline"], [8, "line-through"]];
966
- /**
967
- * The inline style shiki's HTML arm assigns one token (`getTokenStyleObject`
968
- * mirrored onto React style keys): the css-variables color plus the
969
- * vscode-textmate font-style bits the theme lets through — italic (1), bold
970
- * (2), and the {@link DECORATION_BITS} decorations (the theme injects bold,
971
- * italic, and underline rules for markup scopes, so markdown fences carry
972
- * them). The theme has no per-scope backgrounds, so `background-color` never
973
- * occurs; the arm-parity tests fail loud if a shiki upgrade changes that.
974
- */
975
- function spanStyle(token) {
976
- const style = { color: token.color };
977
- /* v8 ignore next -- fontStyle is optional in ThemedToken's type; tokenizeWithTheme always stamps it. */
978
- const bits = token.fontStyle ?? 0;
979
- if ((bits & 1) !== 0) style.fontStyle = "italic";
980
- if ((bits & 2) !== 0) style.fontWeight = "bold";
981
- const decorations = DECORATION_BITS.filter(([bit]) => (bits & bit) !== 0);
982
- if (decorations.length > 0) style.textDecoration = decorations.map(([, value]) => value).join(" ");
983
- return style;
984
- }
985
- /**
986
- * Narrow one tokenized line to the runs a `<span style>` renders, folding a
987
- * whitespace-only run into the token that follows it — shiki's default
988
- * `mergeWhitespaces` HTML behavior — with each run styled through
989
- * {@link spanStyle}, so the streaming spans and the settled `codeToHtml`
990
- * swap render one identical span tree. shiki exempts underlined/struck
991
- * whitespace from the fold; under the css-variables theme that case cannot
992
- * occur — its only underline rule styles inline-link scopes, whose spaced
993
- * text tokenizes as one run, and it injects no strikethrough rule — so the
994
- * unconditional fold here stays equivalent (the markdown arm-parity test
995
- * pins it). A line-trailing whitespace-only run has no follower and keeps
996
- * its own span, as in shiki.
997
- */
998
- function lineSpans(line) {
999
- const spans = [];
1000
- let pendingWhitespace = "";
1001
- for (const [index, token] of line.entries()) {
1002
- if (/^\s+$/.test(token.content) && index + 1 < line.length) {
1003
- pendingWhitespace += token.content;
1004
- continue;
1005
- }
1006
- spans.push({
1007
- text: pendingWhitespace + token.content,
1008
- style: spanStyle(token)
1009
- });
1010
- pendingWhitespace = "";
1011
- }
1012
- return spans;
1013
- }
1014
- /**
1015
- * Incremental highlighter for one growing streaming fence. TextMate
1016
- * tokenization is line-based and forward-only — a line's tokens depend only on
1017
- * its own text and the grammar state entering it — so appended text never
1018
- * changes a completed line's tokens. The session caches the spans of every
1019
- * completed line together with the grammar state after them;
1020
- * {@link updateFrame} reports only newly completed lines plus the still-growing
1021
- * last line, while {@link update} materializes the complete compatibility
1022
- * result. Per-call tokenization cost therefore excludes the completed prefix,
1023
- * and the result equals a from-scratch tokenization of the same code.
1024
- * Non-append input and a change of resolved grammar reset the cache and
1025
- * re-tokenize fully, so any input stays correct.
1026
- */
1027
- var StreamingHighlightSession = class {
1028
- /** Grammar id the cache was built with; a different resolution resets it. */
1029
- resolved;
1030
- /** Newline-terminated source prefix covered by {@link spans}. */
1031
- prefix = "";
1032
- /** Cached spans, one entry per completed line of {@link prefix}. */
1033
- spans = [];
1034
- /** Grammar state after {@link prefix}; undefined = the grammar's initial state. */
1035
- state;
1036
- lastCode;
1037
- lastLang;
1038
- lastResult;
1039
- generation = 0;
1040
- lastFrame;
1041
- reset(resolved) {
1042
- this.resolved = resolved;
1043
- this.prefix = "";
1044
- this.spans = [];
1045
- this.state = void 0;
1046
- this.generation += 1;
1047
- this.lastFrame = void 0;
1048
- }
1049
- /** Tokenize `text` with `resolved`, resuming from the cached grammar state when one exists. */
1050
- tokenize(resolved, text) {
1051
- return highlighter().codeToTokensBase(text, {
1052
- lang: resolved,
1053
- theme: "css-variables",
1054
- ...this.state === void 0 ? {} : { grammarState: this.state }
1055
- });
1056
- }
1057
- /**
1058
- * Tokenize one update as a delta for a retained renderer.
1059
- * @param code - the fence text accumulated so far.
1060
- * @param lang - the language hint.
1061
- * @returns Newly completed lines plus the current tail, or `undefined` for the plain arm.
1062
- */
1063
- updateFrame(code, lang) {
1064
- if (code === this.lastCode && lang === this.lastLang && this.lastFrame !== void 0) return this.lastFrame;
1065
- this.lastCode = code;
1066
- this.lastLang = lang;
1067
- this.lastResult = void 0;
1068
- const resolved = lang === void 0 ? void 0 : LANG_ALIASES.get(lang.toLowerCase());
1069
- if (resolved === void 0 || !ensureGrammar(resolved)) {
1070
- this.reset(void 0);
1071
- return;
1072
- }
1073
- if (resolved !== this.resolved || !code.startsWith(this.prefix)) this.reset(resolved);
1074
- const firstNewLine = this.spans.length;
1075
- const rest = code.slice(this.prefix.length);
1076
- const lastNewline = rest.lastIndexOf("\n");
1077
- if (lastNewline >= 0) {
1078
- const grownEnd = rest[lastNewline - 1] === "\r" ? lastNewline - 1 : lastNewline;
1079
- const tokens = this.tokenize(resolved, rest.slice(0, grownEnd));
1080
- for (const line of tokens) this.spans.push(lineSpans(line));
1081
- this.state = highlighter().getLastGrammarState(tokens);
1082
- this.prefix = code.slice(0, this.prefix.length + lastNewline + 1);
1083
- }
1084
- this.lastFrame = {
1085
- generation: this.generation,
1086
- appended: this.spans.slice(firstNewLine),
1087
- tail: this.tokenize(resolved, rest.slice(lastNewline + 1)).map(lineSpans)
1088
- };
1089
- return this.lastFrame;
1090
- }
1091
- /**
1092
- * Tokenize the fence's current text into per-line highlighted runs;
1093
- * `undefined` means the caller renders its plain fallback. Idempotent per
1094
- * (`code`, `lang`) input — repeated calls return the identical result array —
1095
- * and a retained line keeps its span-array identity across growing calls, so
1096
- * a React caller can reuse cached line elements. A lazy grammar not yet
1097
- * loaded returns `undefined` and loads in the background exactly as
1098
- * {@link highlightToHtml} does; the next call after it registers highlights.
1099
- * @param code - the fence text accumulated so far (display-trimmed, no synthetic trailing newline).
1100
- * @param lang - the language hint (a markdown fence info string).
1101
- * @returns one entry per line of `code` (each an array of runs), or `undefined` for unknown or not-yet-loaded languages.
1102
- */
1103
- update(code, lang) {
1104
- if (code === this.lastCode && lang === this.lastLang && this.lastResult !== void 0) return this.lastResult;
1105
- const frame = this.updateFrame(code, lang);
1106
- if (frame === void 0) return void 0;
1107
- this.lastResult = [...this.spans, ...frame.tail];
1108
- return this.lastResult;
1109
- }
1110
- };
1111
- const noop = () => {};
1112
- /** One document-wide observer; activated elements leave it permanently. */
1113
- var HighlightViewport = class {
1114
- observer;
1115
- activators = /* @__PURE__ */ new Map();
1116
- observe(element, activate) {
1117
- if (typeof IntersectionObserver === "undefined") {
1118
- activate();
1119
- return noop;
1120
- }
1121
- this.observer ??= new IntersectionObserver((entries) => {
1122
- for (const entry of entries) {
1123
- if (!entry.isIntersecting) continue;
1124
- const current = this.activators.get(entry.target);
1125
- /* v8 ignore next -- the observer reports only elements still registered with it. */
1126
- if (current === void 0) continue;
1127
- this.activators.delete(entry.target);
1128
- this.observer?.unobserve(entry.target);
1129
- current();
1130
- }
1131
- this.releaseEmptyObserver();
1132
- });
1133
- this.activators.set(element, activate);
1134
- this.observer.observe(element);
1135
- return () => {
1136
- this.activators.delete(element);
1137
- this.observer?.unobserve(element);
1138
- this.releaseEmptyObserver();
1139
- };
1140
- }
1141
- releaseEmptyObserver() {
1142
- if (this.activators.size > 0) return;
1143
- this.observer?.disconnect();
1144
- this.observer = void 0;
1145
- }
1146
- };
1147
- const highlightViewport = new HighlightViewport();
1148
- /**
1149
- * Activate one supported code surface when it first intersects the viewport.
1150
- * Activation lasts for the component lifetime; browsers without
1151
- * IntersectionObserver activate immediately.
1152
- * @param target - Code surface whose plain rendering reserves its geometry.
1153
- * @param lang - Optional language hint.
1154
- * @returns Whether this component may build highlighted output.
1155
- */
1156
- function useViewportHighlighting(target, lang) {
1157
- const supported = supportsHighlighting(lang);
1158
- const [activated, setActivated] = (0, react.useState)(false);
1159
- const activate = (0, react.useCallback)(() => {
1160
- setActivated(true);
1161
- }, []);
1162
- (0, react.useEffect)(() => {
1163
- if (activated || !supported) return;
1164
- const element = target.current;
1165
- /* v8 ignore next -- React attaches the host ref before running effects. */
1166
- if (element === null) return;
1167
- return highlightViewport.observe(element, activate);
1168
- }, [
1169
- activate,
1170
- activated,
1171
- supported,
1172
- target
1173
- ]);
1174
- return activated && supported;
1175
- }
1176
- DiffBlock_module_default.path, DiffBlock_module_default.del, DiffBlock_module_default.add, DiffBlock_module_default.gap;
1177
- /**
1178
- * Incremental block-level markdown parsing for an append-only text stream.
1179
- *
1180
- * Re-parsing the whole accumulated document on every streaming chunk is
1181
- * quadratic in the final reply length. CommonMark block parsing is line-based
1182
- * and appended text can only reshape the parse frontier — the last top-level
1183
- * block (a paragraph becoming a setext heading or a table, or a list
1184
- * continuing after a blank line) — so earlier blocks are final. This parser
1185
- * therefore freezes all but the trailing {@link UNSTABLE_TAIL_BLOCKS} blocks
1186
- * and re-parses only the source tail behind them. A final unclosed top-level
1187
- * fence cannot freeze as a block, so its completed content lines use a second
1188
- * frontier: only the last completed line and current partial line return
1189
- * through the caller's grammar. Each source region is therefore parsed a
1190
- * bounded number of times over the stream instead of once per chunk.
1191
- *
1192
- * The block freeze boundary comes from the parser's own `position` offsets.
1193
- * The cut sits at the *end offset* of the last frozen block (not the next
1194
- * block's start): a following block's start offset excludes up to three spaces
1195
- * of insignificant leading indentation, which is harmless to drop, but
1196
- * cutting at the previous end also keeps the inter-block blank lines in the
1197
- * tail so the sliced source stays verbatim. Fence scanning only recognizes a
1198
- * parser-confirmed code node and closing delimiter; ambiguous input returns to
1199
- * the normal tail parse.
1200
- *
1201
- * Known deviation, shared with any prefix-freeze scheme: micromark resolves
1202
- * reference-style links and footnotes document-wide at parse time, so a
1203
- * reference whose definition lands on the other side of the freeze boundary
1204
- * renders literally until the settled full parse self-heals it.
1205
- */
1206
- /**
1207
- * Trailing blocks kept unstable. Appended text reshapes at most the last
1208
- * block; the second-to-last is retained as safety margin so a freeze decision
1209
- * never has to reason about the parse frontier.
1210
- */
1211
- const UNSTABLE_TAIL_BLOCKS = 2;
1212
- /**
1213
- * A block's render key: its absolute source start offset. A position-less
1214
- * node (a grammar is free to omit positions) falls back to a negative
1215
- * list-index key — unique within one update's tail, which is the only place
1216
- * the fallback can occur: freezing requires the cut block's position, so a
1217
- * position-less parse keeps every block in the tail (real grammars always
1218
- * stamp positions and never take this path).
1219
- */
1220
- function blockKey(node, base, index) {
1221
- const offset = node.position?.start.offset;
1222
- return offset === void 0 ? -(index + 1) : base + offset;
1223
- }
1224
- /** Return the first line terminator at or after `start`, including a CRLF pair. */
1225
- function lineTerminatorEnd(text, start) {
1226
- for (let index = start; index < text.length; index += 1) {
1227
- const char = text[index];
1228
- if (char === "\n") return index + 1;
1229
- if (char === "\r") return text[index + 1] === "\n" ? index + 2 : index + 1;
1230
- }
1231
- }
1232
- /**
1233
- * Source prefix before the last completed line. Keeping that line beside the
1234
- * current partial line lets the grammar retain its trailing-newline semantics.
1235
- */
1236
- function committableLinePrefixLength(text) {
1237
- let previousEnd = 0;
1238
- let end = 0;
1239
- for (let index = 0; index < text.length; index += 1) {
1240
- const char = text[index];
1241
- if (char === "\n") {
1242
- previousEnd = end;
1243
- end = index + 1;
1244
- continue;
1245
- }
1246
- if (char !== "\r" || index + 1 >= text.length) continue;
1247
- if (text[index + 1] === "\n") index += 1;
1248
- previousEnd = end;
1249
- end = index + 1;
1250
- }
1251
- return previousEnd;
1252
- }
1253
- /** Exact source terminator ending a non-empty committable prefix. */
1254
- function trailingLineTerminator(text) {
1255
- return text.endsWith("\r\n") ? "\r\n" : text.endsWith("\r") ? "\r" : "\n";
1256
- }
1257
- /** Whether `text` contains a CommonMark closing fence on one of its logical lines. */
1258
- function containsClosingFence(text, marker, markerLength) {
1259
- let start = 0;
1260
- while (start <= text.length) {
1261
- let end = start;
1262
- while (end < text.length && text[end] !== "\n" && text[end] !== "\r") end += 1;
1263
- const line = text.slice(start, end);
1264
- let indent = 0;
1265
- while (indent < 3 && line[indent] === " ") indent += 1;
1266
- let run = indent;
1267
- while (line[run] === marker) run += 1;
1268
- if (run - indent >= markerLength && /^[ \t]*$/.test(line.slice(run))) return true;
1269
- if (end === text.length) return false;
1270
- start = text[end] === "\r" && text[end + 1] === "\n" ? end + 2 : end + 1;
1271
- }
1272
- /* v8 ignore next -- each loop iteration returns at EOF or advances past a line terminator. */
1273
- return false;
1274
- }
1275
- /** Advance an mdast point across one append while treating a split CRLF as one line ending. */
1276
- function advancePoint(point, appended, precededByCarriageReturn) {
1277
- let line = point.line;
1278
- let column = point.column;
1279
- let afterCarriageReturn = precededByCarriageReturn;
1280
- for (const char of appended) {
1281
- if (char === "\n") {
1282
- if (!afterCarriageReturn) line += 1;
1283
- column = 1;
1284
- afterCarriageReturn = false;
1285
- continue;
1286
- }
1287
- if (char === "\r") {
1288
- line += 1;
1289
- column = 1;
1290
- afterCarriageReturn = true;
1291
- continue;
1292
- }
1293
- column += 1;
1294
- afterCarriageReturn = false;
1295
- }
1296
- return {
1297
- line,
1298
- column,
1299
- offset: point.offset + appended.length
1300
- };
1301
- }
1302
- /**
1303
- * Append-only incremental parser over a caller-supplied grammar. One instance
1304
- * accumulates one streaming document; non-append input resets it.
1305
- */
1306
- var IncrementalMarkdownParser = class {
1307
- parse;
1308
- prevText = "";
1309
- tailStart = 0;
1310
- frozen = [];
1311
- generation = 0;
1312
- cached = null;
1313
- openFence = null;
1314
- /** @param parse - Grammar shared with whatever renders the blocks, so boundaries agree. */
1315
- constructor(parse) {
1316
- this.parse = parse;
1317
- }
1318
- /** Parse one unclosed-fence content slice through the caller's grammar. */
1319
- fenceValue(state, text) {
1320
- const root = this.parse(`${state.syntheticPrefix}${text}`);
1321
- if (root.children.length !== 1) return void 0;
1322
- const node = root.children[0];
1323
- return node.type === "code" ? node.value : void 0;
1324
- }
1325
- /** Recognize the parsed tail's final unclosed fence and prepare its incremental content frontier. */
1326
- openFenceState(text, base, tail, frozen) {
1327
- const codeIndex = tail.length - 1;
1328
- const block = tail[codeIndex];
1329
- if (block?.node.type !== "code") return null;
1330
- const node = block.node;
1331
- const startOffset = node.position?.start.offset;
1332
- const end = node.position?.end;
1333
- if (startOffset === void 0 || end?.offset === void 0) return null;
1334
- /* v8 ignore next -- the caller's parse slice ends at text.length, so its final node ends there. */
1335
- if (base + end.offset !== text.length) return null;
1336
- const source = text.slice(base);
1337
- const previousLf = source.lastIndexOf("\n", startOffset - 1);
1338
- const previousCr = source.lastIndexOf("\r", startOffset - 1);
1339
- const lineStart = Math.max(previousLf, previousCr) + 1;
1340
- const terminatorEnd = lineTerminatorEnd(source, startOffset);
1341
- /* v8 ignore next -- a parser-confirmed fenced code node requires its opening line terminator. */
1342
- if (terminatorEnd === void 0) return null;
1343
- if (terminatorEnd === source.length && source.endsWith("\r")) return null;
1344
- const openingLine = source.slice(lineStart, terminatorEnd).replace(/[\r\n]+$/, "");
1345
- const opening = /^( {0,3})(`{3,}|~{3,})/.exec(openingLine);
1346
- if (opening === null) return null;
1347
- const indent = opening[1];
1348
- const run = opening[2];
1349
- /* v8 ignore next -- mdast positions a fenced code node at the matched delimiter after indentation. */
1350
- if (lineStart + indent.length !== startOffset) return null;
1351
- const marker = run[0];
1352
- const contentStart = base + terminatorEnd;
1353
- const content = text.slice(contentStart);
1354
- if (containsClosingFence(content, marker, run.length)) return null;
1355
- const syntheticPrefix = `${indent}${run}\n`;
1356
- const stableLength = committableLinePrefixLength(content);
1357
- const stableValue = stableLength === 0 ? "" : this.fenceValue({ syntheticPrefix }, content.slice(0, stableLength));
1358
- if (stableValue === void 0) return null;
1359
- const pendingStart = contentStart + stableLength;
1360
- const stableSource = content.slice(0, stableLength);
1361
- const valuePrefix = stableLength === 0 ? "" : `${stableValue}${trailingLineTerminator(stableSource)}`;
1362
- const pendingValue = this.fenceValue({ syntheticPrefix }, text.slice(pendingStart));
1363
- if (pendingValue === void 0 || `${valuePrefix}${pendingValue}` !== node.value) return null;
1364
- return {
1365
- marker,
1366
- markerLength: run.length,
1367
- syntheticPrefix,
1368
- codeIndex,
1369
- frozen,
1370
- tail,
1371
- pendingStart,
1372
- valuePrefix,
1373
- end: {
1374
- line: end.line,
1375
- column: end.column,
1376
- offset: end.offset
1377
- },
1378
- endedWithCarriageReturn: text.endsWith("\r")
1379
- };
1380
- }
1381
- /** Extend a recognized unclosed fence without parsing its completed content prefix again. */
1382
- updateOpenFence(state, text, previousText) {
1383
- const pending = text.slice(state.pendingStart);
1384
- if (containsClosingFence(pending, state.marker, state.markerLength)) return void 0;
1385
- const pendingValue = this.fenceValue(state, pending);
1386
- if (pendingValue === void 0) return void 0;
1387
- const stableLength = committableLinePrefixLength(pending);
1388
- const stableValue = stableLength === 0 ? "" : this.fenceValue(state, pending.slice(0, stableLength));
1389
- if (stableValue === void 0) return void 0;
1390
- const previousNode = state.tail[state.codeIndex].node;
1391
- const end = advancePoint(state.end, text.slice(previousText.length), state.endedWithCarriageReturn);
1392
- const node = {
1393
- ...previousNode,
1394
- value: `${state.valuePrefix}${pendingValue}`,
1395
- position: {
1396
- start: previousNode.position.start,
1397
- end
1398
- }
1399
- };
1400
- const tail = state.tail.map((entry, index) => index === state.codeIndex ? {
1401
- ...entry,
1402
- node
1403
- } : entry);
1404
- const cached = {
1405
- frozen: state.frozen,
1406
- tail,
1407
- generation: this.generation
1408
- };
1409
- this.openFence = {
1410
- ...state,
1411
- tail,
1412
- pendingStart: state.pendingStart + stableLength,
1413
- valuePrefix: stableLength === 0 ? state.valuePrefix : `${state.valuePrefix}${stableValue}${trailingLineTerminator(pending.slice(0, stableLength))}`,
1414
- end,
1415
- endedWithCarriageReturn: text.endsWith("\r")
1416
- };
1417
- return cached;
1418
- }
1419
- /**
1420
- * Fold the current accumulated text and return the frozen/tail split.
1421
- * Idempotent for identical input (the previous result is returned as-is),
1422
- * so callers may invoke it from render paths that re-execute.
1423
- * @param text - The full accumulated markdown source.
1424
- * @returns Frozen and tail blocks with stream-stable render keys.
1425
- */
1426
- update(text) {
1427
- if (this.cached !== null && text === this.prevText) return this.cached;
1428
- if (!text.startsWith(this.prevText)) {
1429
- this.prevText = "";
1430
- this.tailStart = 0;
1431
- this.frozen = [];
1432
- this.openFence = null;
1433
- this.generation += 1;
1434
- }
1435
- const previousText = this.prevText;
1436
- if (previousText !== "" && this.openFence !== null) {
1437
- const incremental = this.updateOpenFence(this.openFence, text, previousText);
1438
- if (incremental !== void 0) {
1439
- this.prevText = text;
1440
- this.cached = incremental;
1441
- return incremental;
1442
- }
1443
- this.openFence = null;
1444
- }
1445
- this.prevText = text;
1446
- const base = this.tailStart;
1447
- const blocks = this.parse(text.slice(base)).children;
1448
- let firstUnstable = Math.max(0, blocks.length - UNSTABLE_TAIL_BLOCKS);
1449
- if (firstUnstable > 0) {
1450
- const cutEnd = blocks[firstUnstable - 1]?.position?.end.offset;
1451
- if (cutEnd === void 0) firstUnstable = 0;
1452
- else {
1453
- for (const node of blocks.slice(0, firstUnstable)) this.frozen.push({
1454
- node,
1455
- key: blockKey(node, base, this.frozen.length)
1456
- });
1457
- this.tailStart = base + cutEnd;
1458
- }
1459
- }
1460
- const tail = blocks.slice(firstUnstable).map((node, index) => ({
1461
- node,
1462
- key: blockKey(node, base, index)
1463
- }));
1464
- this.cached = {
1465
- frozen: [...this.frozen],
1466
- tail,
1467
- generation: this.generation
1468
- };
1469
- this.openFence = this.openFenceState(text, base, tail, this.cached.frozen);
1470
- return this.cached;
1471
- }
1472
- };
1473
- /** Let asterisk strong emphasis close after punctuation when CJK prose continues without whitespace. */
1474
- const cjkCharacter = new RegExp([
1475
- "\\p{Script_Extensions=Han}",
1476
- "\\p{Script_Extensions=Hiragana}",
1477
- "\\p{Script_Extensions=Katakana}",
1478
- "\\p{Script_Extensions=Hangul}",
1479
- "\\p{Script_Extensions=Bopomofo}"
1480
- ].join("|"), "u");
1481
- function isCjkCharacter(code) {
1482
- return code !== null && code >= 0 && cjkCharacter.test(String.fromCodePoint(code));
1483
- }
1484
- const tokenizeCjkFriendlyAttention = function(effects, ok, nok) {
1485
- const configuredAttentionMarkers = this.parser.constructs.attentionMarkers.null;
1486
- if (configuredAttentionMarkers === void 0) throw new Error("micromark CommonMark attention markers are unavailable");
1487
- const attentionMarkers = configuredAttentionMarkers;
1488
- const previous = this.previous;
1489
- const before = (0, micromark_util_classify_character.classifyCharacter)(previous);
1490
- let marker = micromark_util_symbol.codes.eof;
1491
- return start;
1492
- function start(code) {
1493
- /* v8 ignore next -- this text construct is dispatched only for an asterisk. */
1494
- if (code !== micromark_util_symbol.codes.asterisk) return nok(code);
1495
- marker = code;
1496
- effects.enter("attentionSequence");
1497
- return inside(code);
1498
- }
1499
- function inside(code) {
1500
- if (code === marker) {
1501
- effects.consume(code);
1502
- return inside;
1503
- }
1504
- const token = effects.exit("attentionSequence");
1505
- const after = (0, micromark_util_classify_character.classifyCharacter)(code);
1506
- const open = !after || after === micromark_util_symbol.constants.characterGroupPunctuation && Boolean(before) || attentionMarkers.includes(code);
1507
- const commonMarkClose = !before || before === micromark_util_symbol.constants.characterGroupPunctuation && Boolean(after) || attentionMarkers.includes(previous);
1508
- const cjkStrongClose = token.end.offset - token.start.offset >= 2 && (0, micromark_util_character.unicodePunctuation)(previous) && isCjkCharacter(code);
1509
- const close = commonMarkClose || cjkStrongClose;
1510
- token._open = open;
1511
- token._close = close;
1512
- return ok(code);
1513
- }
1514
- };
1515
- const cjkFriendlyAttention = {
1516
- name: "cjkFriendlyAttention",
1517
- resolveAll: micromark_core_commonmark.attention.resolveAll,
1518
- tokenize: tokenizeCjkFriendlyAttention
1519
- };
1520
- const cjkFriendlyStrongExtension = { text: { [micromark_util_symbol.codes.asterisk]: cjkFriendlyAttention } };
1521
- /**
1522
- * Extend CommonMark asterisk strong emphasis for punctuation-delimited CJK
1523
- * prose, as a micromark syntax extension for `fromMarkdown`.
1524
- * @returns The micromark syntax extension.
1525
- */
1526
- function cjkFriendlyStrong() {
1527
- return cjkFriendlyStrongExtension;
1528
- }
1529
- /** Extend upstream dollar-only math syntax with TeX delimiters while reusing its token vocabulary. */
1530
- const previousBackslash = function(code) {
1531
- if (code !== micromark_util_symbol.codes.backslash) return true;
1532
- const tail = this.events.at(-1);
1533
- /* v8 ignore next -- a previous code necessarily has a preceding event. */
1534
- if (tail === void 0) return false;
1535
- return tail[1].type === micromark_util_symbol.types.characterEscape;
1536
- };
1537
- const tokenizeBackslashMathText = function(effects, ok, nok) {
1538
- return start;
1539
- function start(code) {
1540
- /* v8 ignore next -- the text construct is dispatched only for a backslash. */
1541
- if (code !== micromark_util_symbol.codes.backslash) return nok(code);
1542
- effects.enter("mathText");
1543
- effects.enter("mathTextSequence");
1544
- effects.consume(code);
1545
- return open;
1546
- }
1547
- function open(code) {
1548
- if (code !== micromark_util_symbol.codes.leftParenthesis) return nok(code);
1549
- effects.consume(code);
1550
- effects.exit("mathTextSequence");
1551
- return between;
1552
- }
1553
- function between(code) {
1554
- if (code === micromark_util_symbol.codes.eof) return nok(code);
1555
- if (code === micromark_util_symbol.codes.backslash) return effects.attempt({
1556
- partial: true,
1557
- tokenize: tokenizeClose
1558
- }, close, afterCloseAttempt)(code);
1559
- if ((0, micromark_util_character.markdownLineEnding)(code)) {
1560
- effects.enter(micromark_util_symbol.types.lineEnding);
1561
- effects.consume(code);
1562
- effects.exit(micromark_util_symbol.types.lineEnding);
1563
- return between;
1564
- }
1565
- return dataStart(code);
1566
- }
1567
- function afterCloseAttempt(code) {
1568
- return effects.check({
1569
- partial: true,
1570
- tokenize: tokenizeOpen
1571
- }, nok, dataStart)(code);
1572
- }
1573
- function dataStart(code) {
1574
- effects.enter("mathTextData");
1575
- effects.consume(code);
1576
- return code === micromark_util_symbol.codes.backslash ? afterDataBackslash : data;
1577
- }
1578
- function afterDataBackslash(code) {
1579
- if (code === micromark_util_symbol.codes.backslash) {
1580
- effects.consume(code);
1581
- return data;
1582
- }
1583
- return data(code);
1584
- }
1585
- function data(code) {
1586
- if (code === micromark_util_symbol.codes.eof || code === micromark_util_symbol.codes.backslash || (0, micromark_util_character.markdownLineEnding)(code)) {
1587
- effects.exit("mathTextData");
1588
- return between(code);
1589
- }
1590
- effects.consume(code);
1591
- return data;
1592
- }
1593
- function close(code) {
1594
- effects.exit("mathText");
1595
- return ok(code);
1596
- }
1597
- function tokenizeClose(closeEffects, closeOk, closeNok) {
1598
- return slash;
1599
- function slash(code) {
1600
- /* v8 ignore next -- this partial construct is attempted only at a backslash. */
1601
- if (code !== micromark_util_symbol.codes.backslash) return closeNok(code);
1602
- closeEffects.enter("mathTextSequence");
1603
- closeEffects.consume(code);
1604
- return parenthesis;
1605
- }
1606
- function parenthesis(code) {
1607
- if (code !== micromark_util_symbol.codes.rightParenthesis) return closeNok(code);
1608
- closeEffects.consume(code);
1609
- closeEffects.exit("mathTextSequence");
1610
- return closeOk;
1611
- }
1612
- }
1613
- function tokenizeOpen(openEffects, openOk, openNok) {
1614
- return slash;
1615
- function slash(code) {
1616
- /* v8 ignore next -- the opening check follows a failed close attempt at a backslash. */
1617
- if (code !== micromark_util_symbol.codes.backslash) return openNok(code);
1618
- openEffects.enter(micromark_util_symbol.types.chunkString);
1619
- openEffects.consume(code);
1620
- return parenthesis;
1621
- }
1622
- function parenthesis(code) {
1623
- if (code !== micromark_util_symbol.codes.leftParenthesis) return openNok(code);
1624
- openEffects.consume(code);
1625
- openEffects.exit(micromark_util_symbol.types.chunkString);
1626
- return openOk;
1627
- }
1628
- }
1629
- };
1630
- function createMathFlow(marker, openMarker, closeMarker, multiline) {
1631
- const tokenize = function(effects, ok, nok) {
1632
- const self = this;
1633
- let oddBackslashRun = false;
1634
- const tail = self.events.at(-1);
1635
- const initialSize = tail?.[1].type === micromark_util_symbol.types.linePrefix ? tail[2].sliceSerialize(tail[1], true).length : 0;
1636
- return start;
1637
- function start(code) {
1638
- /* v8 ignore next -- the flow construct is dispatched only for its marker. */
1639
- if (code !== marker) return nok(code);
1640
- effects.enter("mathFlow");
1641
- effects.enter("mathFlowFence");
1642
- effects.enter("mathFlowFenceSequence");
1643
- effects.consume(code);
1644
- return open;
1645
- }
1646
- function open(code) {
1647
- if (code !== openMarker) return nok(code);
1648
- effects.consume(code);
1649
- effects.exit("mathFlowFenceSequence");
1650
- effects.exit("mathFlowFence");
1651
- return marker === micromark_util_symbol.codes.dollarSign ? afterDollarOpen : content;
1652
- }
1653
- function afterDollarOpen(code) {
1654
- return code === micromark_util_symbol.codes.dollarSign ? nok(code) : content(code);
1655
- }
1656
- function content(code) {
1657
- if (code === micromark_util_symbol.codes.eof) return nok(code);
1658
- if (code === marker && (marker !== micromark_util_symbol.codes.dollarSign || !oddBackslashRun)) return effects.attempt({
1659
- partial: true,
1660
- tokenize: tokenizeClosingFence
1661
- }, closed, afterClosingFenceAttempt)(code);
1662
- if ((0, micromark_util_character.markdownLineEnding)(code)) return multiline ? effects.attempt(nonLazyContinuation, afterContinuation, nok)(code) : nok(code);
1663
- return valueStart(code);
1664
- }
1665
- function afterClosingFenceAttempt(code) {
1666
- return marker === micromark_util_symbol.codes.backslash ? effects.check({
1667
- partial: true,
1668
- tokenize: tokenizeOpeningFence
1669
- }, nok, markerValueStart)(code) : markerValueStart(code);
1670
- }
1671
- function afterContinuation(code) {
1672
- return effects.attempt({
1673
- partial: true,
1674
- tokenize: tokenizeClosingFence
1675
- }, closed, initialSize ? (0, micromark_factory_space.factorySpace)(effects, content, micromark_util_symbol.types.linePrefix, initialSize + 1) : content)(code);
1676
- }
1677
- function valueStart(code) {
1678
- effects.enter("mathFlowValue");
1679
- oddBackslashRun = code === micromark_util_symbol.codes.backslash;
1680
- effects.consume(code);
1681
- return value;
1682
- }
1683
- function markerValueStart(code) {
1684
- effects.enter("mathFlowValue");
1685
- oddBackslashRun = false;
1686
- effects.consume(code);
1687
- return valueAfterMarker;
1688
- }
1689
- function valueAfterMarker(code) {
1690
- if (code === marker) {
1691
- effects.consume(code);
1692
- return value;
1693
- }
1694
- return value(code);
1695
- }
1696
- function value(code) {
1697
- if (code === micromark_util_symbol.codes.eof || code === marker || (0, micromark_util_character.markdownLineEnding)(code)) {
1698
- effects.exit("mathFlowValue");
1699
- return content(code);
1700
- }
1701
- oddBackslashRun = code === micromark_util_symbol.codes.backslash ? !oddBackslashRun : false;
1702
- effects.consume(code);
1703
- return value;
1704
- }
1705
- function closed(code) {
1706
- effects.exit("mathFlow");
1707
- return ok(code);
1708
- }
1709
- function tokenizeClosingFence(closeEffects, closeOk, closeNok) {
1710
- return (0, micromark_factory_space.factorySpace)(closeEffects, sequenceStart, micromark_util_symbol.types.linePrefix, micromark_util_symbol.constants.tabSize);
1711
- function sequenceStart(code) {
1712
- if (code !== marker) return closeNok(code);
1713
- closeEffects.enter("mathFlowFence");
1714
- closeEffects.enter("mathFlowFenceSequence");
1715
- closeEffects.consume(code);
1716
- return sequenceEnd;
1717
- }
1718
- function sequenceEnd(code) {
1719
- if (code !== closeMarker) return closeNok(code);
1720
- closeEffects.consume(code);
1721
- closeEffects.exit("mathFlowFenceSequence");
1722
- return (0, micromark_factory_space.factorySpace)(closeEffects, after, micromark_util_symbol.types.whitespace);
1723
- }
1724
- function after(code) {
1725
- if (code !== micromark_util_symbol.codes.eof && !(0, micromark_util_character.markdownLineEnding)(code)) return closeNok(code);
1726
- closeEffects.exit("mathFlowFence");
1727
- return closeOk(code);
1728
- }
1729
- }
1730
- function tokenizeOpeningFence(openEffects, openOk, openNok) {
1731
- return sequenceStart;
1732
- function sequenceStart(code) {
1733
- /* v8 ignore next -- the opening check follows a failed close attempt at the marker. */
1734
- if (code !== marker) return openNok(code);
1735
- openEffects.enter(micromark_util_symbol.types.chunkString);
1736
- openEffects.consume(code);
1737
- return sequenceEnd;
1738
- }
1739
- function sequenceEnd(code) {
1740
- if (code !== openMarker) return openNok(code);
1741
- openEffects.consume(code);
1742
- openEffects.exit(micromark_util_symbol.types.chunkString);
1743
- return openOk;
1744
- }
1745
- }
1746
- };
1747
- return {
1748
- concrete: true,
1749
- name: marker === micromark_util_symbol.codes.dollarSign ? "sameLineDollarMathFlow" : "backslashMathFlow",
1750
- tokenize
1751
- };
1752
- }
1753
- const tokenizeNonLazyContinuation = function(effects, ok, nok) {
1754
- const self = this;
1755
- return start;
1756
- function start(code) {
1757
- /* v8 ignore next -- continuation constructs are attempted only after a line ending. */
1758
- if (code === micromark_util_symbol.codes.eof) return ok(code);
1759
- /* v8 ignore next -- continuation constructs are attempted only after a line ending. */
1760
- if (!(0, micromark_util_character.markdownLineEnding)(code)) return nok(code);
1761
- effects.enter(micromark_util_symbol.types.lineEnding);
1762
- effects.consume(code);
1763
- effects.exit(micromark_util_symbol.types.lineEnding);
1764
- return lineStart;
1765
- }
1766
- function lineStart(code) {
1767
- return self.parser.lazy[self.now().line] ? nok(code) : ok(code);
1768
- }
1769
- };
1770
- const nonLazyContinuation = {
1771
- partial: true,
1772
- tokenize: tokenizeNonLazyContinuation
1773
- };
1774
- const backslashMathText = {
1775
- name: "backslashMathText",
1776
- previous: previousBackslash,
1777
- tokenize: tokenizeBackslashMathText
1778
- };
1779
- const backslashMathFlow = createMathFlow(micromark_util_symbol.codes.backslash, micromark_util_symbol.codes.leftSquareBracket, micromark_util_symbol.codes.rightSquareBracket, true);
1780
- const sameLineDollarMathFlow = createMathFlow(micromark_util_symbol.codes.dollarSign, micromark_util_symbol.codes.dollarSign, micromark_util_symbol.codes.dollarSign, false);
1781
- const backslashMath = {
1782
- flow: {
1783
- [micromark_util_symbol.codes.backslash]: backslashMathFlow,
1784
- [micromark_util_symbol.codes.dollarSign]: sameLineDollarMathFlow
1785
- },
1786
- text: { [micromark_util_symbol.codes.backslash]: backslashMathText }
1787
- };
1788
- /**
1789
- * TeX backslash delimiters and same-line display-dollar blocks as a micromark
1790
- * syntax extension reusing `micromark-extension-math`'s token vocabulary; the
1791
- * caller must also register `math()` on the same parse so the emitted tokens
1792
- * compile to standard math nodes.
1793
- * @returns The micromark syntax extension.
1794
- */
1795
- function mathCompatibility() {
1796
- return backslashMath;
1797
- }
1798
- /**
1799
- * The markdown renderer's two mdast grammars, one per rendering arm. Each
1800
- * arm is internally consistent — the incremental tail parses, the one-shot
1801
- * parses, and the plain-text projection of a given grammar always agree on
1802
- * where blocks start and end — and the settled grammar is the streaming one
1803
- * plus the math extensions, so the arms differ only where TeX delimiters
1804
- * begin a math construct (a `$$` block is a paragraph while streaming and a
1805
- * math block once settled, by design).
1806
- */
1807
- /**
1808
- * Parse GFM markdown (the streaming arm's grammar: no math, so incomplete
1809
- * TeX never flashes KaTeX errors mid-stream).
1810
- * @param text - Markdown source.
1811
- * @returns The mdast root.
1812
- */
1813
- function parseGfm(text) {
1814
- return (0, mdast_util_from_markdown.fromMarkdown)(text, {
1815
- extensions: [(0, micromark_extension_gfm.gfm)(), cjkFriendlyStrong()],
1816
- mdastExtensions: [(0, mdast_util_gfm.gfmFromMarkdown)()]
1817
- });
1818
- }
1819
- /**
1820
- * Parse GFM markdown plus TeX math with the compatibility delimiters
1821
- * (the settled arm's grammar).
1822
- * @param text - Markdown source.
1823
- * @returns The mdast root.
1824
- */
1825
- function parseGfmWithMath(text) {
1826
- return (0, mdast_util_from_markdown.fromMarkdown)(text, {
1827
- extensions: [
1828
- (0, micromark_extension_gfm.gfm)(),
1829
- cjkFriendlyStrong(),
1830
- mathCompatibility(),
1831
- (0, micromark_extension_math.math)()
1832
- ],
1833
- mdastExtensions: [(0, mdast_util_gfm.gfmFromMarkdown)(), (0, mdast_util_math.mathFromMarkdown)()]
1834
- });
1835
- }
1836
- /**
1837
- * The `pre` attributes shiki's HTML arm emits for the css-variables theme,
1838
- * mirrored so the streaming arm's tree is interchangeable with the settled
1839
- * swap (`tests/streaming-code-block.client.spec.tsx` pins the two arms'
1840
- * parity).
1841
- */
1842
- const SHIKI_PRE_PROPS = {
1843
- className: "shiki css-variables",
1844
- style: {
1845
- backgroundColor: "var(--shiki-background)",
1846
- color: "var(--shiki-foreground)"
1847
- },
1848
- tabIndex: 0
1849
- };
1850
- /** Completed-line group size; React reconciles groups while the DOM remains line-for-line identical. */
1851
- const STREAMING_LINE_GROUP_SIZE = 32;
1852
- function renderLine(line, index) {
1853
- return (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index > 0 && "\n", (0, react_jsx_runtime.jsx)("span", {
1854
- className: "line",
1855
- children: line.map((span, spanIndex) => (0, react_jsx_runtime.jsx)("span", {
1856
- style: span.style,
1857
- children: span.text
1858
- }, spanIndex))
1859
- })] }, index);
1860
- }
1861
- function CodeBlock({ code, lang, streaming, className, lineNumbers = false, copyLabel, copiedLabel }) {
1862
- const trimmed = code.endsWith("\n") ? code.slice(0, -1) : code;
1863
- const sourceLines = lineNumbers ? trimmed.split("\n") : void 0;
1864
- const rootRef = (0, react.useRef)(null);
1865
- const highlighting = useViewportHighlighting(rootRef, lang);
1866
- const loaded = (0, react.useSyncExternalStore)(subscribeGrammarLoaded, grammarLoadCount, grammarLoadCount);
1867
- const sessionRef = (0, react.useRef)(null);
1868
- const lineCacheRef = (0, react.useRef)(null);
1869
- const settledRef = (0, react.useRef)(false);
1870
- const streamedBody = (0, react.useMemo)(() => {
1871
- if (!highlighting) {
1872
- sessionRef.current = null;
1873
- lineCacheRef.current = null;
1874
- settledRef.current = false;
1875
- return;
1876
- }
1877
- if (streaming !== true) {
1878
- const previous = lineCacheRef.current;
1879
- if (previous !== null && previous.code === trimmed && previous.lang === lang) {
1880
- settledRef.current = true;
1881
- return previous.body;
1882
- }
1883
- sessionRef.current = null;
1884
- lineCacheRef.current = null;
1885
- settledRef.current = true;
1886
- return;
1887
- }
1888
- if (settledRef.current) {
1889
- sessionRef.current = null;
1890
- lineCacheRef.current = null;
1891
- settledRef.current = false;
1892
- }
1893
- sessionRef.current ??= new StreamingHighlightSession();
1894
- const frame = sessionRef.current.updateFrame(trimmed, lang);
1895
- if (frame === void 0) {
1896
- lineCacheRef.current = null;
1897
- return;
1898
- }
1899
- const previous = lineCacheRef.current;
1900
- if (previous?.frame === frame && previous.code === trimmed && previous.lang === lang) return previous.body;
1901
- const sameGeneration = previous?.generation === frame.generation;
1902
- const groups = sameGeneration ? [...previous.groups] : [];
1903
- let pending = sameGeneration ? [...previous.pending] : [];
1904
- let nextLine = sameGeneration ? previous.nextLine : 0;
1905
- for (const line of frame.appended) {
1906
- pending.push(renderLine(line, nextLine));
1907
- nextLine += 1;
1908
- if (pending.length !== STREAMING_LINE_GROUP_SIZE) continue;
1909
- const start = nextLine - pending.length;
1910
- groups.push((0, react_jsx_runtime.jsx)(react.Fragment, { children: pending }, start));
1911
- pending = [];
1912
- }
1913
- const tail = frame.tail.map((line, index) => renderLine(line, nextLine + index));
1914
- const tailGroup = (0, react_jsx_runtime.jsx)(react.Fragment, { children: [...pending, ...tail] }, nextLine - pending.length);
1915
- const body = (0, react_jsx_runtime.jsx)("pre", {
1916
- ...SHIKI_PRE_PROPS,
1917
- children: (0, react_jsx_runtime.jsxs)("code", { children: [groups, tailGroup] })
1918
- });
1919
- lineCacheRef.current = {
1920
- code: trimmed,
1921
- lang,
1922
- generation: frame.generation,
1923
- frame,
1924
- groups,
1925
- pending,
1926
- nextLine,
1927
- body
1928
- };
1929
- return body;
1930
- }, [
1931
- streaming,
1932
- highlighting,
1933
- trimmed,
1934
- lang,
1935
- loaded
1936
- ]);
1937
- const html = (0, react.useMemo)(() => highlighting && streaming !== true && streamedBody === void 0 ? highlightToHtml(trimmed, lang) : void 0, [
1938
- streaming,
1939
- highlighting,
1940
- streamedBody,
1941
- trimmed,
1942
- lang,
1943
- loaded
1944
- ]);
1945
- const [copied, setCopied] = (0, react.useState)(false);
1946
- const onCopy = (0, react.useCallback)(() => {
1947
- if (copied) return;
1948
- writeClipboard(rootRef.current?.querySelector("pre")?.textContent ?? trimmed).then((ok) => {
1949
- if (!ok) return;
1950
- setCopied(true);
1951
- window.setTimeout(() => {
1952
- setCopied(false);
1953
- }, 1e3);
1954
- });
1955
- }, [copied, trimmed]);
1956
- const body = streamedBody !== void 0 ? streamedBody : html === void 0 ? (0, react_jsx_runtime.jsx)("pre", {
1957
- className: CodeBlock_module_default.plain,
1958
- children: (0, react_jsx_runtime.jsx)("code", { children: sourceLines === void 0 ? trimmed : sourceLines.map((line, index) => (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index > 0 && "\n", (0, react_jsx_runtime.jsx)("span", {
1959
- className: "line",
1960
- children: line
1961
- })] }, index)) })
1962
- }) : (0, react_jsx_runtime.jsx)("div", { dangerouslySetInnerHTML: { __html: html } });
1963
- return (0, react_jsx_runtime.jsxs)("div", {
1964
- ref: rootRef,
1965
- className: (0, clsx.default)(CodeBlock_module_default.block, "md-code-block", lineNumbers && CodeBlock_module_default.numbered, className),
1966
- "data-line-numbers": lineNumbers || void 0,
1967
- style: sourceLines === void 0 ? void 0 : { "--dsl-code-block-line-number-width": `${Math.max(2, String(sourceLines.length).length)}ch` },
1968
- children: [(0, react_jsx_runtime.jsx)("div", {
1969
- className: CodeBlock_module_default.bannerWrap,
1970
- children: (0, react_jsx_runtime.jsxs)("div", {
1971
- className: CodeBlock_module_default.banner,
1972
- "data-code-block-banner": true,
1973
- children: [(0, react_jsx_runtime.jsx)("div", {
1974
- className: CodeBlock_module_default.infostring,
1975
- children: lang ?? ""
1976
- }), (0, react_jsx_runtime.jsx)("div", {
1977
- className: CodeBlock_module_default.action,
1978
- children: (0, react_jsx_runtime.jsx)("button", {
1979
- type: "button",
1980
- className: CodeBlock_module_default.copyButton,
1981
- onClick: onCopy,
1982
- children: copied ? copiedLabel : copyLabel
1983
- })
1984
- })]
1985
- })
1986
- }), body]
1987
- });
1988
- }
1989
- /**
1990
- * TeX-to-React via KaTeX, replicating the rehype-katex pipeline this renderer
1991
- * replaced: the same three-arm error chain (strict render, `strict: 'ignore'`
1992
- * retry, error span) and a DOM-identical element tree, so settled math keeps
1993
- * its exact markup. KaTeX emits an HTML string; the browser's own HTML parser
1994
- * (`DOMParser`, applying the spec's SVG/MathML foreign-content attribute
1995
- * adjustments KaTeX output relies on) turns it into a tree this module maps
1996
- * onto React elements — KaTeX output is a static span/MathML/SVG vocabulary
1997
- * with no raw user HTML, the same trust shiki's tree gets in CodeBlock.
1998
- *
1999
- * React 18 has no MathML support, so the `.katex-mathml` subtree's elements
2000
- * land in the HTML namespace — exactly as they did under the replaced
2001
- * hast-util-to-jsx-runtime pipeline. The visual arm is the `.katex-html`
2002
- * span tree; the MathML arm serves assistive technology, which reads it by
2003
- * tag name regardless of namespace.
2004
- */
2005
- /**
2006
- * Convert one inline `style` attribute string into React's style object.
2007
- * KaTeX emits only plain kebab-case declarations (no custom properties and no
2008
- * nameless declarations), so camel-casing the property is the whole mapping.
2009
- */
2010
- function styleObject(css) {
2011
- const style = {};
2012
- for (const declaration of css.split(";")) {
2013
- const colon = declaration.indexOf(":");
2014
- if (colon === -1) continue;
2015
- const key = declaration.slice(0, colon).trim().replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
2016
- style[key] = declaration.slice(colon + 1).trim();
2017
- }
2018
- return style;
2019
- }
2020
- /** Map one parsed DOM node onto a React element (text nodes pass through). */
2021
- function domToReact(node, key) {
2022
- if (node.nodeType === Node.TEXT_NODE) return node.textContent;
2023
- /* v8 ignore next 2 -- KaTeX output holds only elements and text; other
2024
- node kinds cannot appear in its serialized vocabulary. */
2025
- if (node.nodeType !== Node.ELEMENT_NODE) return null;
2026
- const element = node;
2027
- const props = { key };
2028
- for (const attribute of element.attributes) if (attribute.name === "class") props["className"] = attribute.value;
2029
- else if (attribute.name === "style") props["style"] = styleObject(attribute.value);
2030
- else props[attribute.name] = attribute.value;
2031
- const children = [...element.childNodes].map(domToReact);
2032
- return children.length === 0 ? (0, react.createElement)(element.localName, props) : (0, react.createElement)(element.localName, props, ...children);
2033
- }
2034
- /**
2035
- * Render TeX source to React elements through KaTeX.
2036
- * @param value - The TeX source (math node value; fenced `math` blocks append
2037
- * their trailing newline to match the replaced pipeline's text extraction).
2038
- * @param displayMode - Display (block) versus inline rendering.
2039
- * @returns KaTeX's element tree, or the error span when the source does not
2040
- * parse (colored with KaTeX's stock `errorColor`, matching rehype-katex).
2041
- */
2042
- function renderTexToReact(value, displayMode) {
2043
- let html;
2044
- try {
2045
- html = katex.default.renderToString(value, {
2046
- displayMode,
2047
- throwOnError: true
2048
- });
2049
- } catch (error) {
2050
- try {
2051
- html = katex.default.renderToString(value, {
2052
- displayMode,
2053
- strict: "ignore",
2054
- throwOnError: false
2055
- });
2056
- } catch {
2057
- /* v8 ignore next 8 */
2058
- return (0, react_jsx_runtime.jsx)("span", {
2059
- className: "katex-error",
2060
- style: { color: "#cc0000" },
2061
- title: String(error),
2062
- children: value
2063
- });
2064
- }
2065
- }
2066
- return [...new DOMParser().parseFromString(html, "text/html").body.childNodes].map(domToReact);
2067
- }
2068
- /**
2069
- * Direct mdast→React markdown renderer. Replaces the react-markdown /
2070
- * remark-rehype pipeline with one switch over parsed nodes so streaming can
2071
- * cache frozen blocks as React elements; the rendered DOM is pinned
2072
- * byte-for-byte by `tests/fixtures/markdown-dom` and must not drift.
2073
- *
2074
- * Untrusted-output policy (unchanged from the replaced pipeline): link and
2075
- * image destinations pass a protocol allowlist, images additionally require
2076
- * absolute HTTP(S), raw HTML renders as literal text (no HTML enters the
2077
- * DOM), and KaTeX runs without trusted commands. Fragment-anchor URLs fail
2078
- * the allowlist, so footnote references and back-references render as plain
2079
- * text rather than in-page links.
2080
- *
2081
- * Merge-extensible node unions fall through the documented default (render
2082
- * nothing) rather than ending in assertNever: grammars registered elsewhere
2083
- * may add node types this renderer has no mapping for.
2084
- */
2085
- function sanitizeUrl(url) {
2086
- try {
2087
- switch (new URL(url).protocol) {
2088
- case "http:":
2089
- case "https:":
2090
- case "mailto:": return url;
2091
- default: return "";
2092
- }
2093
- } catch {
2094
- return "";
2095
- }
2096
- }
2097
- function remoteImageUrl(url) {
2098
- try {
2099
- const protocol = new URL(url).protocol;
2100
- return protocol === "http:" || protocol === "https:" ? url : void 0;
2101
- } catch {
2102
- return;
2103
- }
2104
- }
2105
- /** Protocols a vocabulary-rewritten image destination may carry. */
2106
- function vocabularyImageUrl(url) {
2107
- try {
2108
- const protocol = new URL(url).protocol;
2109
- return protocol === "http:" || protocol === "https:" || protocol === "blob:" || protocol === "data:" ? url : void 0;
2110
- } catch {
2111
- return;
2112
- }
2113
- }
2114
- /**
2115
- * The displayable source for one image destination: absolute HTTP(S) as
2116
- * authored, otherwise the context's local-path vocabulary when it vouches for
2117
- * the destination. Either miss leaves the authored fallback (alt text) to the
2118
- * caller.
2119
- * @param url - The authored markdown destination.
2120
- * @param pathImages - Rewriting vocabulary, when the render pass has one.
2121
- * @returns The displayable image URL, or undefined.
2122
- */
2123
- function imageSource(url, pathImages) {
2124
- const remote = remoteImageUrl(sanitizeUrl((0, micromark_util_sanitize_uri.normalizeUri)(url)));
2125
- if (remote !== void 0) return remote;
2126
- const rewritten = pathImages?.resolve(url);
2127
- return rewritten === void 0 ? void 0 : vocabularyImageUrl(rewritten);
2128
- }
2129
- /**
2130
- * Create an empty {@link ReferenceTargets}.
2131
- * @returns Fresh empty maps.
2132
- */
2133
- function createReferenceTargets() {
2134
- return {
2135
- definitions: /* @__PURE__ */ new Map(),
2136
- footnotes: /* @__PURE__ */ new Map()
2137
- };
2138
- }
2139
- /**
2140
- * Record every definition and footnote definition under `nodes` into
2141
- * `targets`, depth-first, keeping the first definition per identifier.
2142
- * @param nodes - Subtrees to walk (top-level blocks or any nested children).
2143
- * @param targets - Accumulator, typically shared across incremental segments.
2144
- */
2145
- function collectReferenceTargets(nodes, targets) {
2146
- for (const node of nodes) {
2147
- if (node.type === "definition") {
2148
- const id = node.identifier.toUpperCase();
2149
- if (!targets.definitions.has(id)) targets.definitions.set(id, node);
2150
- } else if (node.type === "footnoteDefinition") {
2151
- const id = node.identifier.toUpperCase();
2152
- if (!targets.footnotes.has(id)) targets.footnotes.set(id, node);
2153
- }
2154
- if ("children" in node) collectReferenceTargets(node.children, targets);
2155
- }
2156
- }
2157
- /**
2158
- * Render top-level blocks. Nodes that render nothing (definitions, unmapped
2159
- * types) are dropped rather than kept as null placeholders, matching the
2160
- * replaced pipeline's child lists so separator newlines land identically.
2161
- * @param blocks - Blocks with their stream-stable render keys.
2162
- * @param context - The pass state; footnote numbering mutates in document order.
2163
- * @returns One React node per rendered block.
2164
- */
2165
- function renderBlocks(blocks, context) {
2166
- return blocks.map((block) => renderNode(block.node, block.key, context)).filter((element) => element !== null);
2167
- }
2168
- /**
2169
- * Interleave the newline text nodes the replaced pipeline emitted between
2170
- * block-level children. They are invisible between elements but coalesce
2171
- * into adjacent literal raw-HTML text, where the DOM parity fixtures pin
2172
- * them.
2173
- * @param elements - Rendered block children with empty renders already dropped.
2174
- * @param edges - Also emit the leading and trailing newline (hast's loose wrap).
2175
- * @returns The interleaved children.
2176
- */
2177
- function wrapBlockChildren(elements, edges) {
2178
- const wrapped = [];
2179
- for (const element of elements) {
2180
- if (edges || wrapped.length > 0) wrapped.push("\n");
2181
- wrapped.push(element);
2182
- }
2183
- if (edges && elements.length > 0) wrapped.push("\n");
2184
- return wrapped;
2185
- }
2186
- /** Render container children into {@link BlockEntry} values, dropping empty renders. */
2187
- function renderBlockEntries(blocks, context) {
2188
- const entries = [];
2189
- for (const [index, block] of blocks.entries()) if (block.type === "paragraph") entries.push({ paragraph: renderChildren(block.children, context) });
2190
- else {
2191
- const element = renderNode(block, index, context);
2192
- if (element !== null) entries.push({ element });
2193
- }
2194
- return entries;
2195
- }
2196
- function renderChildren(nodes, context) {
2197
- return nodes.map((node, index) => renderNode(node, index, context));
2198
- }
2199
- function renderNode(node, key, context) {
2200
- switch (node.type) {
2201
- case "text": return node.value;
2202
- case "paragraph": return (0, react_jsx_runtime.jsx)("p", { children: renderChildren(node.children, context) }, key);
2203
- case "heading": return (0, react.createElement)(`h${node.depth}`, { key }, ...renderChildren(node.children, context));
2204
- case "blockquote": return (0, react_jsx_runtime.jsx)("blockquote", { children: wrapBlockChildren(renderChildren(node.children, {
2205
- ...context,
2206
- inBlockquote: true
2207
- }).filter((child) => child !== null), true) }, key);
2208
- case "thematicBreak": return (0, react_jsx_runtime.jsx)("hr", {}, key);
2209
- case "break": return (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [(0, react_jsx_runtime.jsx)("br", {}), "\n"] }, key);
2210
- case "strong": return (0, react_jsx_runtime.jsx)("strong", { children: renderChildren(node.children, context) }, key);
2211
- case "emphasis": return (0, react_jsx_runtime.jsx)("em", { children: renderChildren(node.children, context) }, key);
2212
- case "delete": return (0, react_jsx_runtime.jsx)("del", { children: renderChildren(node.children, context) }, key);
2213
- case "inlineCode": {
2214
- const value = node.value.replace(/\r?\n|\r/g, " ");
2215
- const href = inlineCodeHttpUrl(value);
2216
- if (href !== void 0) return (0, react_jsx_runtime.jsx)("code", { children: renderSafeLink(href, [value], "link") }, key);
2217
- const mention = context.inLink === true ? void 0 : context.fileMentions?.resolve(value);
2218
- if (mention !== void 0) return (0, react_jsx_runtime.jsx)("code", { children: (0, react_jsx_runtime.jsxs)("button", {
2219
- type: "button",
2220
- className: MarkdownText_module_default.fileMention,
2221
- title: mention.title,
2222
- "aria-label": mention.label,
2223
- onClick: mention.open,
2224
- children: [(0, react_jsx_runtime.jsx)(LinkIcon, {
2225
- kind: classifyLinkPath(value),
2226
- className: MarkdownText_module_default.linkIcon
2227
- }), value]
2228
- }) }, key);
2229
- return (0, react_jsx_runtime.jsx)("code", { children: value }, key);
2230
- }
2231
- case "html": return node.value;
2232
- case "code": return renderCode(node, key, context);
2233
- case "math": return (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderTexToReact(node.value, true) }, key);
2234
- case "inlineMath": return (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderTexToReact(node.value, false) }, key);
2235
- case "list": return renderList(node, key, context);
2236
- case "listItem": return renderListItem(node, listItemLoose(node), key, context);
2237
- case "table": return renderTable(node, key, context);
2238
- case "link": return renderAnchor(node.url, renderChildren(node.children, {
2239
- ...context,
2240
- inLink: true
2241
- }), key, !anchorWrapsOnlyImages(node.children));
2242
- case "linkReference": return renderLinkReference(node, key, context);
2243
- case "image": return renderImage(node.url, node.alt ?? "", key, context);
2244
- case "imageReference": return renderImageReference(node, key, context);
2245
- case "footnoteReference": return renderFootnoteReference(node, key, context);
2246
- case "definition":
2247
- case "footnoteDefinition": return null;
2248
- default: return null;
2249
- }
2250
- }
2251
- function renderCode(node, key, context) {
2252
- const language = node.lang ?? void 0;
2253
- if (node.value === "") return (0, react_jsx_runtime.jsx)("pre", { children: (0, react_jsx_runtime.jsx)("code", { className: language === void 0 ? void 0 : `language-${language}` }) }, key);
2254
- const lang = language === void 0 ? void 0 : /^[\w-]+/.exec(language)?.[0];
2255
- if (!context.streaming && lang === "math") return (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderTexToReact(`${node.value}\n`, true) }, key);
2256
- return (0, react_jsx_runtime.jsx)(CodeBlock, {
2257
- code: `${node.value}\n`,
2258
- lang,
2259
- streaming: context.streaming,
2260
- copyLabel: context.labels.code.copyLabel,
2261
- copiedLabel: context.labels.code.copiedLabel
2262
- }, key);
2263
- }
2264
- /** A list is loose when it or any of its items is spread; every item then keeps its paragraphs. */
2265
- function listLoose(list) {
2266
- return (list.spread ?? false) || list.children.some(listItemLoose);
2267
- }
2268
- function listItemLoose(item) {
2269
- return item.spread ?? item.children.length > 1;
2270
- }
2271
- function renderList(node, key, context) {
2272
- const loose = listLoose(node);
2273
- const properties = {};
2274
- if (typeof node.start === "number" && node.start !== 1) properties.start = node.start;
2275
- if (node.children.some((item) => typeof item.checked === "boolean")) properties.className = "contains-task-list";
2276
- return (0, react.createElement)(node.ordered === true ? "ol" : "ul", {
2277
- key,
2278
- ...properties
2279
- }, ...node.children.map((item, index) => renderListItem(item, loose, index, context)));
2280
- }
2281
- function renderListItem(item, loose, key, context) {
2282
- const entries = renderBlockEntries(item.children, context);
2283
- const task = typeof item.checked === "boolean";
2284
- if (task) {
2285
- const checkbox = (0, react_jsx_runtime.jsx)("input", {
2286
- type: "checkbox",
2287
- checked: item.checked === true,
2288
- disabled: true
2289
- }, "task-checkbox");
2290
- const head = entries[0];
2291
- if (head !== void 0 && "paragraph" in head) head.paragraph = head.paragraph.length > 0 ? [
2292
- checkbox,
2293
- " ",
2294
- ...head.paragraph
2295
- ] : [checkbox];
2296
- else entries.unshift({ paragraph: [checkbox] });
2297
- }
2298
- const parts = [];
2299
- for (const [index, entry] of entries.entries()) {
2300
- const isParagraph = "paragraph" in entry;
2301
- if (loose || index !== 0 || !isParagraph) parts.push("\n");
2302
- if (!isParagraph) parts.push(entry.element);
2303
- else if (loose) parts.push((0, react_jsx_runtime.jsx)("p", { children: entry.paragraph }, `p-${index}`));
2304
- else parts.push((0, react_jsx_runtime.jsx)(react.Fragment, { children: entry.paragraph }, `p-${index}`));
2305
- }
2306
- const tail = entries[entries.length - 1];
2307
- if (tail !== void 0 && (loose || !("paragraph" in tail))) parts.push("\n");
2308
- return (0, react_jsx_runtime.jsx)("li", {
2309
- className: task ? "task-list-item" : void 0,
2310
- children: parts
2311
- }, key);
2312
- }
2313
- function renderTable(node, key, context) {
2314
- const align = node.align ?? null;
2315
- const [headRow, ...bodyRows] = node.children;
2316
- const wide = (align === null ? headRow?.children.length ?? 0 : align.length) >= 4 && context.inBlockquote !== true;
2317
- return (0, react_jsx_runtime.jsx)("div", {
2318
- className: (0, clsx.default)(MarkdownText_module_default.tableScroll, wide ? "md-table-wide" : MarkdownText_module_default.tableFill),
2319
- tabIndex: wide ? 0 : void 0,
2320
- children: (0, react_jsx_runtime.jsxs)("table", { children: [headRow !== void 0 && (0, react_jsx_runtime.jsx)("thead", { children: renderTableRow(headRow, "th", align, 0, context) }), bodyRows.length > 0 && (0, react_jsx_runtime.jsx)("tbody", { children: bodyRows.map((row, index) => renderTableRow(row, "td", align, index + 1, context)) })] })
2321
- }, key);
2322
- }
2323
- function renderTableRow(row, cellTag, align, key, context) {
2324
- const length = align === null ? row.children.length : align.length;
2325
- const cells = [];
2326
- for (let index = 0; index < length; index++) {
2327
- const cell = row.children[index];
2328
- const alignValue = align?.[index];
2329
- cells.push((0, react.createElement)(cellTag, {
2330
- key: index,
2331
- style: alignValue == null ? void 0 : { textAlign: alignValue }
2332
- }, ...cell === void 0 ? [] : renderChildren(cell.children, context)));
2333
- }
2334
- return (0, react_jsx_runtime.jsx)("tr", { children: cells }, key);
2335
- }
2336
- /**
2337
- * True when an anchor's markdown children are all images, so the anchor is a
2338
- * clickable picture (badge, thumbnail): the leading URL glyph would dangle
2339
- * beside the image instead of leading link text, so those anchors skip it.
2340
- */
2341
- function anchorWrapsOnlyImages(children) {
2342
- return children.length > 0 && children.every((child) => child.type === "image" || child.type === "imageReference");
2343
- }
2344
- /** Anchor over an already-authored href: allowlisted or unwrapped, external links get the safe attributes. */
2345
- function renderSafeLink(href, children, key, glyph = true) {
2346
- const safeHref = sanitizeUrl(href);
2347
- if (safeHref === "") return (0, react_jsx_runtime.jsx)(react.Fragment, { children }, key);
2348
- return (0, react_jsx_runtime.jsxs)("a", {
2349
- href: safeHref,
2350
- ...["http:", "https:"].includes(new URL(safeHref).protocol) ? {
2351
- target: "_blank",
2352
- rel: "noopener noreferrer"
2353
- } : {},
2354
- children: [glyph && (0, react_jsx_runtime.jsx)(LinkIcon, {
2355
- kind: "url",
2356
- className: MarkdownText_module_default.linkIcon
2357
- }), children]
2358
- }, key);
2359
- }
2360
- /** Anchor over a parsed markdown destination, which hast normalized before the allowlist saw it. */
2361
- function renderAnchor(url, children, key, glyph = true) {
2362
- return renderSafeLink((0, micromark_util_sanitize_uri.normalizeUri)(url), children, key, glyph);
2363
- }
2364
- /**
2365
- * The complete inline-code value when it is exactly an absolute HTTP(S) URL
2366
- * (no surrounding whitespace); anything else stays inert code.
2367
- */
2368
- function inlineCodeHttpUrl(value) {
2369
- if (value.trim() !== value) return void 0;
2370
- try {
2371
- const protocol = new URL(value).protocol;
2372
- return protocol === "http:" || protocol === "https:" ? value : void 0;
2373
- } catch {
2374
- return;
2375
- }
2376
- }
2377
- function renderImage(url, alt, key, context) {
2378
- const imageSrc = imageSource(url, context.pathImages);
2379
- if (imageSrc === void 0) return (0, react_jsx_runtime.jsx)("span", {
2380
- className: MarkdownText_module_default.imageAlt,
2381
- children: alt
2382
- }, key);
2383
- return (0, react_jsx_runtime.jsx)(MarkdownImage, {
2384
- src: imageSrc,
2385
- alt,
2386
- destination: url
2387
- }, `${key}:${imageSrc}`);
2388
- }
2389
- /** Failed loads retain the authored alt or destination; a new source remounts the image. */
2390
- function MarkdownImage({ src, alt, destination }) {
2391
- const [failed, setFailed] = (0, react.useState)(false);
2392
- if (failed) return (0, react_jsx_runtime.jsx)("span", {
2393
- className: MarkdownText_module_default.imageAlt,
2394
- children: alt || destination
2395
- });
2396
- return (0, react_jsx_runtime.jsx)("img", {
2397
- className: MarkdownText_module_default.image,
2398
- src,
2399
- alt,
2400
- onError: () => {
2401
- setFailed(true);
2402
- },
2403
- loading: "lazy",
2404
- decoding: "async",
2405
- referrerPolicy: "no-referrer"
2406
- });
2407
- }
2408
- /** The bracketed source text a reference reverts to when its definition is missing. */
2409
- function referenceSuffix(node) {
2410
- if (node.referenceType === "collapsed") return "][]";
2411
- if (node.referenceType === "full") return `][${node.label ?? node.identifier}]`;
2412
- return "]";
2413
- }
2414
- function renderLinkReference(node, key, context) {
2415
- const definition = context.targets.definitions.get(node.identifier.toUpperCase());
2416
- if (definition === void 0) return (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
2417
- "[",
2418
- renderChildren(node.children, context),
2419
- referenceSuffix(node)
2420
- ] }, key);
2421
- const rendered = renderChildren(node.children, {
2422
- ...context,
2423
- inLink: true
2424
- });
2425
- return renderAnchor(definition.url, rendered, key, !anchorWrapsOnlyImages(node.children));
2426
- }
2427
- function renderImageReference(node, key, context) {
2428
- const definition = context.targets.definitions.get(node.identifier.toUpperCase());
2429
- if (definition === void 0) return `![${node.alt ?? ""}${referenceSuffix(node)}`;
2430
- return renderImage(definition.url, node.alt ?? "", key, context);
2431
- }
2432
- function renderFootnoteReference(node, key, context) {
2433
- const id = node.identifier.toUpperCase();
2434
- const seen = context.footnoteCounts.get(id);
2435
- if (seen === void 0) context.footnoteOrder.push(id);
2436
- context.footnoteCounts.set(id, (seen ?? 0) + 1);
2437
- return (0, react_jsx_runtime.jsx)("sup", { children: String(context.footnoteOrder.indexOf(id) + 1) }, key);
2438
- }
2439
- /**
2440
- * Render the trailing footnote section for every footnote referenced during
2441
- * the pass, in first-reference order, with one plain-text back-reference
2442
- * marker per rendered reference.
2443
- * @param context - The pass state after all blocks rendered.
2444
- * @returns The section, or null when no referenced footnote has a definition.
2445
- */
2446
- function renderFootnoteSection(context) {
2447
- const items = [];
2448
- for (const id of context.footnoteOrder) {
2449
- const definition = context.targets.footnotes.get(id);
2450
- if (definition === void 0) continue;
2451
- const count = context.footnoteCounts.get(id) ?? 0;
2452
- const backrefs = [];
2453
- for (let reference = 1; reference <= count; reference++) {
2454
- if (backrefs.length > 0) backrefs.push(" ");
2455
- backrefs.push("↩");
2456
- if (reference > 1) backrefs.push((0, react_jsx_runtime.jsx)("sup", { children: String(reference) }, `re-${reference}`));
2457
- }
2458
- const entries = renderBlockEntries(definition.children, context);
2459
- const tail = entries[entries.length - 1];
2460
- const body = entries.map((entry, index) => "paragraph" in entry ? (0, react_jsx_runtime.jsxs)("p", { children: [entry.paragraph, entry === tail && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" ", backrefs] })] }, `p-${index}`) : entry.element);
2461
- if (tail === void 0 || !("paragraph" in tail)) body.push(...backrefs);
2462
- items.push((0, react_jsx_runtime.jsx)("li", {
2463
- id: `user-content-fn-${(0, micromark_util_sanitize_uri.normalizeUri)(id.toLowerCase())}`,
2464
- children: wrapBlockChildren(body, true)
2465
- }, id));
2466
- }
2467
- if (items.length === 0) return null;
2468
- return (0, react_jsx_runtime.jsxs)("section", {
2469
- "data-footnotes": true,
2470
- className: "footnotes",
2471
- children: [(0, react_jsx_runtime.jsx)("h2", {
2472
- id: "footnote-label",
2473
- className: "sr-only",
2474
- children: context.labels.footnotes
2475
- }), (0, react_jsx_runtime.jsx)("ol", { children: items })]
2476
- }, "footnotes");
2477
- }
2478
- /**
2479
- * Untrusted assistant-Markdown renderer over the direct mdast pipeline:
2480
- * `parse.ts` grammars, the incremental streaming parser, and `render.tsx`.
2481
- * While a message streams, all but the trailing two blocks freeze as cached
2482
- * React elements and only the source tail behind them re-parses per chunk,
2483
- * so per-chunk work tracks the tail size instead of the whole reply. Frozen
2484
- * blocks keep their source-offset keys when they cross the freeze boundary,
2485
- * so React reconciles instead of remounting. Known deviation while
2486
- * streaming: a reference-style link or footnote whose definition sits on the
2487
- * other side of the freeze boundary renders literally until the settled
2488
- * full parse self-heals it.
2489
- */
2490
- /** One settled full render: parse with math, resolve references, append the footnote section. */
2491
- function renderSettled(text, labels, fileMentions, pathImages) {
2492
- const root = parseGfmWithMath(text);
2493
- const targets = createReferenceTargets();
2494
- collectReferenceTargets(root.children, targets);
2495
- const context = {
2496
- streaming: false,
2497
- labels,
2498
- fileMentions,
2499
- pathImages,
2500
- targets,
2501
- footnoteOrder: [],
2502
- footnoteCounts: /* @__PURE__ */ new Map()
2503
- };
2504
- const blocks = wrapBlockChildren(renderBlocks(root.children.map((node, index) => ({
2505
- node,
2506
- /* v8 ignore next -- parseFull uses parseGfm, which stamps every top-level node. */
2507
- key: node.position?.start.offset ?? -(index + 1)
2508
- })), context), false);
2509
- const section = renderFootnoteSection(context);
2510
- return section === null ? blocks : [
2511
- ...blocks,
2512
- "\n",
2513
- section
2514
- ];
2515
- }
2516
- /**
2517
- * Streaming render state for one growing message: the incremental parser,
2518
- * the frozen blocks' cached elements, and the reference/footnote state their
2519
- * rendering consumed (footnote numbering assigned to frozen references is
2520
- * final, so the tail continues from a copy of it each frame).
2521
- */
2522
- var StreamingRenderer = class {
2523
- labels;
2524
- parser = new IncrementalMarkdownParser(parseGfm);
2525
- generation = -1;
2526
- frozenCount = 0;
2527
- frozenElements = [];
2528
- frozenTargets = createReferenceTargets();
2529
- frozenFootnoteOrder = [];
2530
- frozenFootnoteCounts = /* @__PURE__ */ new Map();
2531
- lastText = null;
2532
- lastRendered = [];
2533
- /** @param labels - Localized Markdown chrome baked into cached elements; the owner replaces the renderer when it changes. */
2534
- constructor(labels) {
2535
- this.labels = labels;
2536
- }
2537
- /**
2538
- * Render the current accumulated text. Idempotent per text value, so React
2539
- * may re-execute the calling render freely.
2540
- * @param text - The full accumulated markdown source.
2541
- * @returns Frozen elements, re-rendered tail, and the footnote section.
2542
- */
2543
- render(text) {
2544
- if (text === this.lastText) return this.lastRendered;
2545
- const { frozen, tail, generation } = this.parser.update(text);
2546
- if (generation !== this.generation) {
2547
- this.generation = generation;
2548
- this.frozenCount = 0;
2549
- this.frozenElements = [];
2550
- this.frozenTargets = createReferenceTargets();
2551
- this.frozenFootnoteOrder = [];
2552
- this.frozenFootnoteCounts = /* @__PURE__ */ new Map();
2553
- }
2554
- const newlyFrozen = frozen.slice(this.frozenCount);
2555
- collectReferenceTargets(newlyFrozen.map((block) => block.node), this.frozenTargets);
2556
- const frameTargets = {
2557
- definitions: new Map(this.frozenTargets.definitions),
2558
- footnotes: new Map(this.frozenTargets.footnotes)
2559
- };
2560
- collectReferenceTargets(tail.map((block) => block.node), frameTargets);
2561
- if (newlyFrozen.length > 0) {
2562
- const frozenContext = {
2563
- streaming: true,
2564
- labels: this.labels,
2565
- fileMentions: void 0,
2566
- pathImages: void 0,
2567
- targets: frameTargets,
2568
- footnoteOrder: this.frozenFootnoteOrder,
2569
- footnoteCounts: this.frozenFootnoteCounts
2570
- };
2571
- const batch = [...this.frozenElements];
2572
- for (const element of renderBlocks(newlyFrozen, frozenContext)) {
2573
- if (batch.length > 0) batch.push("\n");
2574
- batch.push(element);
2575
- }
2576
- this.frozenElements = batch;
2577
- this.frozenCount = frozen.length;
2578
- }
2579
- const tailContext = {
2580
- streaming: true,
2581
- labels: this.labels,
2582
- fileMentions: void 0,
2583
- pathImages: void 0,
2584
- targets: frameTargets,
2585
- footnoteOrder: [...this.frozenFootnoteOrder],
2586
- footnoteCounts: new Map(this.frozenFootnoteCounts)
2587
- };
2588
- const children = [...this.frozenElements];
2589
- for (const element of renderBlocks(tail, tailContext)) {
2590
- if (children.length > 0) children.push("\n");
2591
- children.push(element);
2592
- }
2593
- const section = renderFootnoteSection(tailContext);
2594
- if (section !== null) children.push("\n", section);
2595
- this.lastText = text;
2596
- this.lastRendered = children;
2597
- return this.lastRendered;
2598
- }
2599
- };
2600
- (0, react.memo)(function MarkdownText({ text, streaming = false, labels, fileMentions, pathImages }) {
2601
- const streamRef = (0, react.useRef)(null);
2602
- const streamLabelsRef = (0, react.useRef)(labels);
2603
- const children = (0, react.useMemo)(() => {
2604
- if (!streaming) {
2605
- streamRef.current = null;
2606
- return renderSettled(text, labels, fileMentions, pathImages);
2607
- }
2608
- if (streamRef.current === null || streamLabelsRef.current !== labels) {
2609
- streamRef.current = new StreamingRenderer(labels);
2610
- streamLabelsRef.current = labels;
2611
- }
2612
- return streamRef.current.render(text);
2613
- }, [
2614
- text,
2615
- streaming,
2616
- labels,
2617
- fileMentions,
2618
- pathImages
2619
- ]);
2620
- return (0, react_jsx_runtime.jsx)("div", {
2621
- className: MarkdownText_module_default.markdown,
2622
- children
2623
- });
2624
- });
2625
- //#endregion
9
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
2626
10
  //#region ../shared/src/client/card.tsx
2627
11
  const IDLE_STATUS = {
2628
12
  kind: "idle",
@@ -2658,7 +42,7 @@ window.__ModuleLoader__.load({
2658
42
  className: `${p}-pending`,
2659
43
  children: props.dirtyLabel
2660
44
  }) : null,
2661
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
45
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: `${p}-chevron${props.open ? ` ${p}-chevronOpen` : ""}` })
2662
46
  ]
2663
47
  }), props.open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2664
48
  className: `${p}-body`,
@@ -3308,7 +692,7 @@ ${extra}
3308
692
  id: props.id,
3309
693
  "aria-expanded": open,
3310
694
  onClick: () => setOpen(!open),
3311
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, { className: `lpc-chevron${open ? " lpc-chevronOpen" : ""}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
695
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: `lpc-chevron${open ? " lpc-chevronOpen" : ""}` }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3312
696
  className: "lpc-collapseTitle",
3313
697
  children: props.title
3314
698
  })]
@@ -4241,7 +1625,7 @@ ${extra}
4241
1625
  "aria-expanded": open,
4242
1626
  onClick: () => setOpen(!open),
4243
1627
  children: [
4244
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, { className: `lpc-chevron${open ? " lpc-chevronOpen" : ""}` }),
1628
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: `lpc-chevron${open ? " lpc-chevronOpen" : ""}` }),
4245
1629
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4246
1630
  className: "lpc-routeKey",
4247
1631
  children: route