@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.
- package/lib/client.js +4 -2620
- package/package.json +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
|
|
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
|