streaming-markdown-react 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -20
- package/README.zh-CN.md +227 -0
- package/dist/index.d.mts +245 -2
- package/dist/index.d.ts +245 -2
- package/dist/index.js +671 -103
- package/dist/index.mjs +629 -97
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -30,15 +30,51 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
Block: () => Block,
|
|
33
34
|
CodeBlock: () => CodeBlock,
|
|
35
|
+
MemoBlockquote: () => MemoBlockquote,
|
|
36
|
+
MemoCode: () => MemoCode,
|
|
37
|
+
MemoDelete: () => MemoDelete,
|
|
38
|
+
MemoEmphasis: () => MemoEmphasis,
|
|
39
|
+
MemoH1: () => MemoH1,
|
|
40
|
+
MemoH2: () => MemoH2,
|
|
41
|
+
MemoH3: () => MemoH3,
|
|
42
|
+
MemoH4: () => MemoH4,
|
|
43
|
+
MemoH5: () => MemoH5,
|
|
44
|
+
MemoH6: () => MemoH6,
|
|
45
|
+
MemoHr: () => MemoHr,
|
|
46
|
+
MemoImage: () => MemoImage,
|
|
47
|
+
MemoLink: () => MemoLink,
|
|
48
|
+
MemoList: () => MemoList,
|
|
49
|
+
MemoListItem: () => MemoListItem,
|
|
50
|
+
MemoParagraph: () => MemoParagraph,
|
|
51
|
+
MemoPreformatted: () => MemoPreformatted,
|
|
52
|
+
MemoStrong: () => MemoStrong,
|
|
53
|
+
MemoTable: () => MemoTable,
|
|
54
|
+
MemoTableBody: () => MemoTableBody,
|
|
55
|
+
MemoTableCell: () => MemoTableCell,
|
|
56
|
+
MemoTableHead: () => MemoTableHead,
|
|
57
|
+
MemoTableRow: () => MemoTableRow,
|
|
34
58
|
MessageBlockRenderer: () => MessageBlockRenderer,
|
|
35
59
|
MessageBlockStatus: () => MessageBlockStatus,
|
|
36
60
|
MessageBlockStore: () => MessageBlockStore,
|
|
37
61
|
MessageBlockType: () => MessageBlockType,
|
|
38
62
|
MessageItem: () => MessageItem,
|
|
39
63
|
MessageStatus: () => MessageStatus,
|
|
64
|
+
ShikiHighlighterManager: () => ShikiHighlighterManager,
|
|
40
65
|
StreamingMarkdown: () => StreamingMarkdown,
|
|
66
|
+
getLanguageImport: () => getLanguageImport,
|
|
67
|
+
getSupportedLanguages: () => getSupportedLanguages,
|
|
68
|
+
getSupportedThemes: () => getSupportedThemes,
|
|
69
|
+
getThemeImport: () => getThemeImport,
|
|
70
|
+
highlighterManager: () => highlighterManager,
|
|
71
|
+
isLanguageSupported: () => isLanguageSupported,
|
|
72
|
+
isThemeSupported: () => isThemeSupported,
|
|
41
73
|
messageBlockStore: () => messageBlockStore,
|
|
74
|
+
parseMarkdownIntoBlocks: () => parseMarkdownIntoBlocks,
|
|
75
|
+
sameClassAndNode: () => sameClassAndNode,
|
|
76
|
+
sameNodePosition: () => sameNodePosition,
|
|
77
|
+
splitMarkdownIntoBlocks: () => splitMarkdownIntoBlocks,
|
|
42
78
|
useShikiHighlight: () => useShikiHighlight,
|
|
43
79
|
useSmoothStream: () => useSmoothStream
|
|
44
80
|
});
|
|
@@ -110,8 +146,7 @@ var MessageBlockStore = class {
|
|
|
110
146
|
var messageBlockStore = new MessageBlockStore();
|
|
111
147
|
|
|
112
148
|
// src/components/Markdown/StreamingMarkdown.tsx
|
|
113
|
-
var
|
|
114
|
-
var import_react_markdown = __toESM(require("react-markdown"));
|
|
149
|
+
var import_react5 = require("react");
|
|
115
150
|
var import_remark_gfm = __toESM(require("remark-gfm"));
|
|
116
151
|
|
|
117
152
|
// src/hooks/useSmoothStream.ts
|
|
@@ -225,8 +260,126 @@ function useSmoothStream({
|
|
|
225
260
|
return { addChunk, reset };
|
|
226
261
|
}
|
|
227
262
|
|
|
228
|
-
// src/
|
|
263
|
+
// src/utils/markdown/parseMarkdownIntoBlocks.ts
|
|
264
|
+
var import_marked = require("marked");
|
|
265
|
+
var blockIdCounter = 0;
|
|
266
|
+
function generateBlockId() {
|
|
267
|
+
blockIdCounter += 1;
|
|
268
|
+
return `block-${Date.now()}-${blockIdCounter}`;
|
|
269
|
+
}
|
|
270
|
+
function parseMarkdownIntoBlocks(markdown) {
|
|
271
|
+
if (!markdown.trim()) {
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
const hasFootnoteReference = /\[\^[^\]\s]{1,200}\](?!:)/.test(markdown);
|
|
275
|
+
const hasFootnoteDefinition = /\[\^[^\]\s]{1,200}\]:/.test(markdown);
|
|
276
|
+
if (hasFootnoteReference || hasFootnoteDefinition) {
|
|
277
|
+
return [markdown];
|
|
278
|
+
}
|
|
279
|
+
const tokens = import_marked.Lexer.lex(markdown, { gfm: true });
|
|
280
|
+
const mergedBlocks = [];
|
|
281
|
+
const htmlStack = [];
|
|
282
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
283
|
+
const token = tokens[i];
|
|
284
|
+
const currentBlock = token.raw;
|
|
285
|
+
if (htmlStack.length > 0) {
|
|
286
|
+
mergedBlocks[mergedBlocks.length - 1] += currentBlock;
|
|
287
|
+
if (token.type === "html") {
|
|
288
|
+
const closingTagMatch = currentBlock.match(/<\/(\w+)>/);
|
|
289
|
+
if (closingTagMatch) {
|
|
290
|
+
const closingTag = closingTagMatch[1];
|
|
291
|
+
if (htmlStack[htmlStack.length - 1] === closingTag) {
|
|
292
|
+
htmlStack.pop();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (token.type === "html" && token.block) {
|
|
299
|
+
const openingTagMatch = currentBlock.match(/<(\w+)[\s>]/);
|
|
300
|
+
if (openingTagMatch) {
|
|
301
|
+
const tagName = openingTagMatch[1];
|
|
302
|
+
const hasClosingTag = currentBlock.includes(`</${tagName}>`);
|
|
303
|
+
if (!hasClosingTag) {
|
|
304
|
+
htmlStack.push(tagName);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (currentBlock.trim() === "$" && mergedBlocks.length > 0) {
|
|
309
|
+
const previousBlock = mergedBlocks.at(-1);
|
|
310
|
+
if (!previousBlock) {
|
|
311
|
+
mergedBlocks.push(currentBlock);
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const prevStartsWith$ = previousBlock.trimStart().startsWith("$");
|
|
315
|
+
const prevDollarCount = (previousBlock.match(/\$\$/g) || []).length;
|
|
316
|
+
if (prevStartsWith$ && prevDollarCount % 2 === 1) {
|
|
317
|
+
mergedBlocks[mergedBlocks.length - 1] = previousBlock + currentBlock;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (mergedBlocks.length > 0 && currentBlock.trimEnd().endsWith("$")) {
|
|
322
|
+
const previousBlock = mergedBlocks.at(-1);
|
|
323
|
+
if (!previousBlock) {
|
|
324
|
+
mergedBlocks.push(currentBlock);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const prevStartsWith$ = previousBlock.trimStart().startsWith("$");
|
|
328
|
+
const prevDollarCount = (previousBlock.match(/\$\$/g) || []).length;
|
|
329
|
+
const currDollarCount = (currentBlock.match(/\$\$/g) || []).length;
|
|
330
|
+
if (prevStartsWith$ && prevDollarCount % 2 === 1 && !currentBlock.trimStart().startsWith("$") && currDollarCount === 1) {
|
|
331
|
+
mergedBlocks[mergedBlocks.length - 1] = previousBlock + currentBlock;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
mergedBlocks.push(currentBlock);
|
|
336
|
+
}
|
|
337
|
+
return mergedBlocks;
|
|
338
|
+
}
|
|
339
|
+
function splitMarkdownIntoBlocks({
|
|
340
|
+
messageId,
|
|
341
|
+
markdown,
|
|
342
|
+
status = "idle" /* IDLE */
|
|
343
|
+
}) {
|
|
344
|
+
const blocks = parseMarkdownIntoBlocks(markdown);
|
|
345
|
+
return blocks.map((content) => {
|
|
346
|
+
const block = {
|
|
347
|
+
id: generateBlockId(),
|
|
348
|
+
messageId,
|
|
349
|
+
type: "main_text" /* MAIN_TEXT */,
|
|
350
|
+
status,
|
|
351
|
+
content,
|
|
352
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
353
|
+
};
|
|
354
|
+
return block;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/components/Markdown/Block.tsx
|
|
229
359
|
var import_react2 = require("react");
|
|
360
|
+
var import_react_markdown = __toESM(require("react-markdown"));
|
|
361
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
362
|
+
var Block = (0, import_react2.memo)(
|
|
363
|
+
({ content, components, remarkPlugins, rehypePlugins, className }) => {
|
|
364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
365
|
+
import_react_markdown.default,
|
|
366
|
+
{
|
|
367
|
+
className,
|
|
368
|
+
components,
|
|
369
|
+
remarkPlugins,
|
|
370
|
+
rehypePlugins,
|
|
371
|
+
children: content
|
|
372
|
+
}
|
|
373
|
+
);
|
|
374
|
+
},
|
|
375
|
+
(prevProps, nextProps) => {
|
|
376
|
+
return prevProps.content === nextProps.content && prevProps.className === nextProps.className;
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
Block.displayName = "Block";
|
|
380
|
+
|
|
381
|
+
// src/hooks/useShikiHighlight.ts
|
|
382
|
+
var import_react3 = require("react");
|
|
230
383
|
var import_core = require("shiki/core");
|
|
231
384
|
var import_javascript = require("shiki/engine/javascript");
|
|
232
385
|
var highlighterPromise;
|
|
@@ -274,10 +427,10 @@ function useShikiHighlight({
|
|
|
274
427
|
language = "text",
|
|
275
428
|
theme = "light"
|
|
276
429
|
}) {
|
|
277
|
-
const [html, setHtml] = (0,
|
|
278
|
-
const [isLoading, setIsLoading] = (0,
|
|
279
|
-
const [error, setError] = (0,
|
|
280
|
-
(0,
|
|
430
|
+
const [html, setHtml] = (0, import_react3.useState)("");
|
|
431
|
+
const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
|
|
432
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
433
|
+
(0, import_react3.useEffect)(() => {
|
|
281
434
|
let cancelled = false;
|
|
282
435
|
async function highlight() {
|
|
283
436
|
try {
|
|
@@ -316,7 +469,7 @@ function escapeHtml(unsafe) {
|
|
|
316
469
|
}
|
|
317
470
|
|
|
318
471
|
// src/components/Markdown/CodeBlock.tsx
|
|
319
|
-
var
|
|
472
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
320
473
|
function CodeBlock({
|
|
321
474
|
code,
|
|
322
475
|
language = "text",
|
|
@@ -325,9 +478,9 @@ function CodeBlock({
|
|
|
325
478
|
}) {
|
|
326
479
|
const { html, isLoading } = useShikiHighlight({ code, language, theme });
|
|
327
480
|
if (isLoading) {
|
|
328
|
-
return /* @__PURE__ */ (0,
|
|
481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { "data-language": language, children: code }) });
|
|
329
482
|
}
|
|
330
|
-
return /* @__PURE__ */ (0,
|
|
483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
331
484
|
"div",
|
|
332
485
|
{
|
|
333
486
|
dangerouslySetInnerHTML: { __html: html },
|
|
@@ -340,89 +493,286 @@ function CodeBlock({
|
|
|
340
493
|
);
|
|
341
494
|
}
|
|
342
495
|
|
|
343
|
-
// src/components/Markdown/
|
|
344
|
-
var
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
496
|
+
// src/components/Markdown/MemoizedComponents.tsx
|
|
497
|
+
var import_react4 = require("react");
|
|
498
|
+
|
|
499
|
+
// src/utils/markdown/sameNodePosition.ts
|
|
500
|
+
function sameNodePosition(prev, next) {
|
|
501
|
+
if (!(prev?.position || next?.position)) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
if (!(prev?.position && next?.position)) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
const prevStart = prev.position.start;
|
|
508
|
+
const nextStart = next.position.start;
|
|
509
|
+
const prevEnd = prev.position.end;
|
|
510
|
+
const nextEnd = next.position.end;
|
|
511
|
+
return prevStart?.line === nextStart?.line && prevStart?.column === nextStart?.column && prevEnd?.line === nextEnd?.line && prevEnd?.column === nextEnd?.column;
|
|
512
|
+
}
|
|
513
|
+
function sameClassAndNode(prev, next) {
|
|
514
|
+
return prev.className === next.className && sameNodePosition(prev.node, next.node);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/components/Markdown/MemoizedComponents.tsx
|
|
518
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
519
|
+
var MemoH1 = (0, import_react4.memo)(
|
|
520
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className, ...props, children }),
|
|
521
|
+
(p, n) => sameClassAndNode(p, n)
|
|
522
|
+
);
|
|
523
|
+
MemoH1.displayName = "MemoH1";
|
|
524
|
+
var MemoH2 = (0, import_react4.memo)(
|
|
525
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className, ...props, children }),
|
|
526
|
+
(p, n) => sameClassAndNode(p, n)
|
|
527
|
+
);
|
|
528
|
+
MemoH2.displayName = "MemoH2";
|
|
529
|
+
var MemoH3 = (0, import_react4.memo)(
|
|
530
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className, ...props, children }),
|
|
531
|
+
(p, n) => sameClassAndNode(p, n)
|
|
532
|
+
);
|
|
533
|
+
MemoH3.displayName = "MemoH3";
|
|
534
|
+
var MemoH4 = (0, import_react4.memo)(
|
|
535
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h4", { className, ...props, children }),
|
|
536
|
+
(p, n) => sameClassAndNode(p, n)
|
|
537
|
+
);
|
|
538
|
+
MemoH4.displayName = "MemoH4";
|
|
539
|
+
var MemoH5 = (0, import_react4.memo)(
|
|
540
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h5", { className, ...props, children }),
|
|
541
|
+
(p, n) => sameClassAndNode(p, n)
|
|
542
|
+
);
|
|
543
|
+
MemoH5.displayName = "MemoH5";
|
|
544
|
+
var MemoH6 = (0, import_react4.memo)(
|
|
545
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h6", { className, ...props, children }),
|
|
546
|
+
(p, n) => sameClassAndNode(p, n)
|
|
547
|
+
);
|
|
548
|
+
MemoH6.displayName = "MemoH6";
|
|
549
|
+
var MemoParagraph = (0, import_react4.memo)(
|
|
550
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className, ...props, children }),
|
|
551
|
+
(p, n) => sameClassAndNode(p, n)
|
|
552
|
+
);
|
|
553
|
+
MemoParagraph.displayName = "MemoParagraph";
|
|
554
|
+
var MemoLink = (0, import_react4.memo)(
|
|
555
|
+
({ children, className, href, title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
556
|
+
"a",
|
|
557
|
+
{
|
|
558
|
+
className,
|
|
559
|
+
href,
|
|
560
|
+
title,
|
|
561
|
+
target: "_blank",
|
|
562
|
+
rel: "noopener noreferrer",
|
|
563
|
+
...props,
|
|
564
|
+
children
|
|
375
565
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
pre: (props) => {
|
|
390
|
-
const { children: children2, ...rest } = props;
|
|
391
|
-
if (children2?.type === CodeBlock) {
|
|
392
|
-
return children2;
|
|
393
|
-
}
|
|
394
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { style: { overflow: "visible" }, ...rest, children: children2 });
|
|
395
|
-
},
|
|
396
|
-
p: (props) => {
|
|
397
|
-
const hasCodeBlock = props?.node?.children?.some(
|
|
398
|
-
(child) => child.tagName === "pre" || child.tagName === "code"
|
|
399
|
-
);
|
|
400
|
-
if (hasCodeBlock) {
|
|
401
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: props.children });
|
|
402
|
-
}
|
|
403
|
-
const hasImage = props?.node?.children?.some((child) => child.tagName === "img");
|
|
404
|
-
if (hasImage) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...props });
|
|
405
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { ...props });
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
if (/<style\b[^>]*>/i.test(markdown)) {
|
|
409
|
-
baseComponents.style = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ...props });
|
|
566
|
+
),
|
|
567
|
+
(p, n) => sameClassAndNode(p, n) && p.href === n.href && p.title === n.title
|
|
568
|
+
);
|
|
569
|
+
MemoLink.displayName = "MemoLink";
|
|
570
|
+
var MemoBlockquote = (0, import_react4.memo)(
|
|
571
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("blockquote", { className, ...props, children }),
|
|
572
|
+
(p, n) => sameClassAndNode(p, n)
|
|
573
|
+
);
|
|
574
|
+
MemoBlockquote.displayName = "MemoBlockquote";
|
|
575
|
+
var MemoList = (0, import_react4.memo)(
|
|
576
|
+
({ children, className, ordered, start, ...props }) => {
|
|
577
|
+
if (ordered) {
|
|
578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ol", { className, start, ...props, children });
|
|
410
579
|
}
|
|
411
|
-
return {
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
|
|
580
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className, ...props, children });
|
|
581
|
+
},
|
|
582
|
+
(p, n) => sameClassAndNode(p, n) && p.ordered === n.ordered && p.start === n.start
|
|
583
|
+
);
|
|
584
|
+
MemoList.displayName = "MemoList";
|
|
585
|
+
var MemoListItem = (0, import_react4.memo)(
|
|
586
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { className, ...props, children }),
|
|
587
|
+
(p, n) => sameClassAndNode(p, n)
|
|
588
|
+
);
|
|
589
|
+
MemoListItem.displayName = "MemoListItem";
|
|
590
|
+
var MemoTable = (0, import_react4.memo)(
|
|
591
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("table", { className, ...props, children }),
|
|
592
|
+
(p, n) => sameClassAndNode(p, n)
|
|
593
|
+
);
|
|
594
|
+
MemoTable.displayName = "MemoTable";
|
|
595
|
+
var MemoTableHead = (0, import_react4.memo)(
|
|
596
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { className, ...props, children }),
|
|
597
|
+
(p, n) => sameClassAndNode(p, n)
|
|
598
|
+
);
|
|
599
|
+
MemoTableHead.displayName = "MemoTableHead";
|
|
600
|
+
var MemoTableBody = (0, import_react4.memo)(
|
|
601
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tbody", { className, ...props, children }),
|
|
602
|
+
(p, n) => sameClassAndNode(p, n)
|
|
603
|
+
);
|
|
604
|
+
MemoTableBody.displayName = "MemoTableBody";
|
|
605
|
+
var MemoTableRow = (0, import_react4.memo)(
|
|
606
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("tr", { className, ...props, children }),
|
|
607
|
+
(p, n) => sameClassAndNode(p, n)
|
|
608
|
+
);
|
|
609
|
+
MemoTableRow.displayName = "MemoTableRow";
|
|
610
|
+
var MemoTableCell = (0, import_react4.memo)(
|
|
611
|
+
({ children, className, isHeader, align, ...props }) => {
|
|
612
|
+
const style = align ? { textAlign: align } : void 0;
|
|
613
|
+
if (isHeader) {
|
|
614
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("th", { className, style, ...props, children });
|
|
615
|
+
}
|
|
616
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("td", { className, style, ...props, children });
|
|
617
|
+
},
|
|
618
|
+
(p, n) => sameClassAndNode(p, n) && p.isHeader === n.isHeader && p.align === n.align
|
|
619
|
+
);
|
|
620
|
+
MemoTableCell.displayName = "MemoTableCell";
|
|
621
|
+
var MemoCode = (0, import_react4.memo)(
|
|
622
|
+
({ children, className, inline, ...props }) => {
|
|
623
|
+
if (inline) {
|
|
624
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className, ...props, children });
|
|
625
|
+
}
|
|
626
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className, ...props, children });
|
|
627
|
+
},
|
|
628
|
+
(p, n) => sameClassAndNode(p, n) && p.inline === n.inline
|
|
629
|
+
);
|
|
630
|
+
MemoCode.displayName = "MemoCode";
|
|
631
|
+
var MemoPreformatted = (0, import_react4.memo)(
|
|
632
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { className, ...props, children }),
|
|
633
|
+
(p, n) => sameClassAndNode(p, n)
|
|
634
|
+
);
|
|
635
|
+
MemoPreformatted.displayName = "MemoPreformatted";
|
|
636
|
+
var MemoStrong = (0, import_react4.memo)(
|
|
637
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { className, ...props, children }),
|
|
638
|
+
(p, n) => sameClassAndNode(p, n)
|
|
639
|
+
);
|
|
640
|
+
MemoStrong.displayName = "MemoStrong";
|
|
641
|
+
var MemoEmphasis = (0, import_react4.memo)(
|
|
642
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("em", { className, ...props, children }),
|
|
643
|
+
(p, n) => sameClassAndNode(p, n)
|
|
644
|
+
);
|
|
645
|
+
MemoEmphasis.displayName = "MemoEmphasis";
|
|
646
|
+
var MemoHr = (0, import_react4.memo)(
|
|
647
|
+
({ className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("hr", { className, ...props }),
|
|
648
|
+
(p, n) => sameClassAndNode(p, n)
|
|
649
|
+
);
|
|
650
|
+
MemoHr.displayName = "MemoHr";
|
|
651
|
+
var MemoImage = (0, import_react4.memo)(
|
|
652
|
+
({ className, href, title, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
653
|
+
"img",
|
|
415
654
|
{
|
|
416
655
|
className,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
656
|
+
src: href,
|
|
657
|
+
alt: title ?? "",
|
|
658
|
+
title,
|
|
659
|
+
...props
|
|
420
660
|
}
|
|
421
|
-
)
|
|
422
|
-
|
|
661
|
+
),
|
|
662
|
+
(p, n) => sameClassAndNode(p, n) && p.href === n.href && p.title === n.title
|
|
663
|
+
);
|
|
664
|
+
MemoImage.displayName = "MemoImage";
|
|
665
|
+
var MemoDelete = (0, import_react4.memo)(
|
|
666
|
+
({ children, className, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("del", { className, ...props, children }),
|
|
667
|
+
(p, n) => sameClassAndNode(p, n)
|
|
668
|
+
);
|
|
669
|
+
MemoDelete.displayName = "MemoDelete";
|
|
670
|
+
|
|
671
|
+
// src/components/Markdown/StreamingMarkdown.tsx
|
|
672
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
673
|
+
var StreamingMarkdown = (0, import_react5.memo)(
|
|
674
|
+
function StreamingMarkdown2({
|
|
675
|
+
children,
|
|
676
|
+
className,
|
|
677
|
+
components: customComponents,
|
|
678
|
+
status = "idle",
|
|
679
|
+
onComplete,
|
|
680
|
+
minDelay = 10
|
|
681
|
+
}) {
|
|
682
|
+
const markdown = typeof children === "string" ? children : String(children || "");
|
|
683
|
+
const [displayedText, setDisplayedText] = (0, import_react5.useState)(status !== "streaming" ? markdown : "");
|
|
684
|
+
const previousChildrenRef = (0, import_react5.useRef)(status !== "streaming" ? markdown : "");
|
|
685
|
+
const generatedId = (0, import_react5.useId)();
|
|
686
|
+
const { addChunk, reset } = useSmoothStream({
|
|
687
|
+
onUpdate: setDisplayedText,
|
|
688
|
+
streamDone: status !== "streaming",
|
|
689
|
+
minDelay,
|
|
690
|
+
initialText: status !== "streaming" ? markdown : "",
|
|
691
|
+
onComplete
|
|
692
|
+
});
|
|
693
|
+
(0, import_react5.useEffect)(() => {
|
|
694
|
+
const currentContent = markdown;
|
|
695
|
+
const previousContent = previousChildrenRef.current;
|
|
696
|
+
if (currentContent !== previousContent) {
|
|
697
|
+
if (currentContent.startsWith(previousContent)) {
|
|
698
|
+
const delta = currentContent.slice(previousContent.length);
|
|
699
|
+
addChunk(delta);
|
|
700
|
+
} else {
|
|
701
|
+
reset(currentContent);
|
|
702
|
+
}
|
|
703
|
+
previousChildrenRef.current = currentContent;
|
|
704
|
+
}
|
|
705
|
+
}, [markdown, addChunk, reset]);
|
|
706
|
+
const blocks = (0, import_react5.useMemo)(
|
|
707
|
+
() => parseMarkdownIntoBlocks(displayedText),
|
|
708
|
+
[displayedText]
|
|
709
|
+
);
|
|
710
|
+
const components = (0, import_react5.useMemo)(() => {
|
|
711
|
+
const baseComponents = {
|
|
712
|
+
h1: MemoH1,
|
|
713
|
+
h2: MemoH2,
|
|
714
|
+
h3: MemoH3,
|
|
715
|
+
h4: MemoH4,
|
|
716
|
+
h5: MemoH5,
|
|
717
|
+
h6: MemoH6,
|
|
718
|
+
p: MemoParagraph,
|
|
719
|
+
a: MemoLink,
|
|
720
|
+
blockquote: MemoBlockquote,
|
|
721
|
+
ul: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MemoList, { ordered: false, ...props }),
|
|
722
|
+
ol: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MemoList, { ordered: true, ...props }),
|
|
723
|
+
li: MemoListItem,
|
|
724
|
+
table: MemoTable,
|
|
725
|
+
thead: MemoTableHead,
|
|
726
|
+
tbody: MemoTableBody,
|
|
727
|
+
tr: MemoTableRow,
|
|
728
|
+
th: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MemoTableCell, { isHeader: true, ...props }),
|
|
729
|
+
td: MemoTableCell,
|
|
730
|
+
strong: MemoStrong,
|
|
731
|
+
em: MemoEmphasis,
|
|
732
|
+
hr: MemoHr,
|
|
733
|
+
img: (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MemoImage, { href: props.src, title: props.alt, ...props }),
|
|
734
|
+
del: MemoDelete,
|
|
735
|
+
code: (props) => {
|
|
736
|
+
const { node, inline, className: className2, children: children2, ...rest } = props;
|
|
737
|
+
if (inline) {
|
|
738
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MemoCode, { inline: true, className: className2, node, ...rest, children: children2 });
|
|
739
|
+
}
|
|
740
|
+
const match = /language-(\w+)/.exec(className2 || "");
|
|
741
|
+
const language = match ? match[1] : void 0;
|
|
742
|
+
const code = String(children2).replace(/\n$/, "");
|
|
743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CodeBlock, { code, language, className: className2 });
|
|
744
|
+
},
|
|
745
|
+
pre: (props) => {
|
|
746
|
+
const { children: children2, ...rest } = props;
|
|
747
|
+
if (children2?.type === CodeBlock) {
|
|
748
|
+
return children2;
|
|
749
|
+
}
|
|
750
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MemoPreformatted, { ...rest, children: children2 });
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
if (/<style\b[^>]*>/i.test(markdown)) {
|
|
754
|
+
baseComponents.style = (props) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ...props });
|
|
755
|
+
}
|
|
756
|
+
return { ...baseComponents, ...customComponents };
|
|
757
|
+
}, [markdown, customComponents]);
|
|
758
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, children: blocks.map((block, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
759
|
+
Block,
|
|
760
|
+
{
|
|
761
|
+
content: block,
|
|
762
|
+
components,
|
|
763
|
+
remarkPlugins: [import_remark_gfm.default]
|
|
764
|
+
},
|
|
765
|
+
`${generatedId}-block-${index}`
|
|
766
|
+
)) });
|
|
767
|
+
},
|
|
768
|
+
(prevProps, nextProps) => {
|
|
769
|
+
return prevProps.children === nextProps.children && prevProps.status === nextProps.status && prevProps.className === nextProps.className;
|
|
770
|
+
}
|
|
771
|
+
);
|
|
772
|
+
StreamingMarkdown.displayName = "StreamingMarkdown";
|
|
423
773
|
|
|
424
774
|
// src/components/Message/MessageBlockRenderer.tsx
|
|
425
|
-
var
|
|
775
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
426
776
|
function MessageBlockRenderer({ block, className }) {
|
|
427
777
|
switch (block.type) {
|
|
428
778
|
case "main_text" /* MAIN_TEXT */:
|
|
@@ -431,7 +781,7 @@ function MessageBlockRenderer({ block, className }) {
|
|
|
431
781
|
case "error" /* ERROR */:
|
|
432
782
|
case "unknown" /* UNKNOWN */: {
|
|
433
783
|
const textBlock = block;
|
|
434
|
-
return /* @__PURE__ */ (0,
|
|
784
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
435
785
|
StreamingMarkdown,
|
|
436
786
|
{
|
|
437
787
|
className,
|
|
@@ -442,18 +792,18 @@ function MessageBlockRenderer({ block, className }) {
|
|
|
442
792
|
}
|
|
443
793
|
case "code" /* CODE */: {
|
|
444
794
|
const codeBlock = block;
|
|
445
|
-
return /* @__PURE__ */ (0,
|
|
795
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: codeBlock.content }) }) });
|
|
446
796
|
}
|
|
447
797
|
case "image" /* IMAGE */:
|
|
448
798
|
case "video" /* VIDEO */:
|
|
449
799
|
case "file" /* FILE */: {
|
|
450
800
|
const mediaBlock = block;
|
|
451
|
-
return /* @__PURE__ */ (0,
|
|
801
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: mediaBlock.url, target: "_blank", rel: "noopener noreferrer", children: mediaBlock.name ?? "Media File" }) });
|
|
452
802
|
}
|
|
453
803
|
case "tool" /* TOOL */:
|
|
454
804
|
case "citation" /* CITATION */: {
|
|
455
805
|
const toolBlock = block;
|
|
456
|
-
return /* @__PURE__ */ (0,
|
|
806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { children: JSON.stringify(toolBlock.payload ?? {}, null, 2) }) });
|
|
457
807
|
}
|
|
458
808
|
default:
|
|
459
809
|
return null;
|
|
@@ -461,17 +811,17 @@ function MessageBlockRenderer({ block, className }) {
|
|
|
461
811
|
}
|
|
462
812
|
|
|
463
813
|
// src/components/Message/MessageItem.tsx
|
|
464
|
-
var
|
|
814
|
+
var import_react6 = require("react");
|
|
465
815
|
var import_react_markdown2 = __toESM(require("react-markdown"));
|
|
466
816
|
var import_remark_gfm2 = __toESM(require("remark-gfm"));
|
|
467
817
|
|
|
468
818
|
// src/utils/markdown/splitMarkdownIntoBlocks.ts
|
|
469
|
-
var
|
|
470
|
-
function
|
|
471
|
-
|
|
472
|
-
return `block-${Date.now()}-${
|
|
819
|
+
var blockIdCounter2 = 0;
|
|
820
|
+
function generateBlockId2() {
|
|
821
|
+
blockIdCounter2 += 1;
|
|
822
|
+
return `block-${Date.now()}-${blockIdCounter2}`;
|
|
473
823
|
}
|
|
474
|
-
function
|
|
824
|
+
function splitMarkdownIntoBlocks2({
|
|
475
825
|
messageId,
|
|
476
826
|
markdown,
|
|
477
827
|
status = "idle" /* IDLE */
|
|
@@ -480,7 +830,7 @@ function splitMarkdownIntoBlocks({
|
|
|
480
830
|
return [];
|
|
481
831
|
}
|
|
482
832
|
const block = {
|
|
483
|
-
id:
|
|
833
|
+
id: generateBlockId2(),
|
|
484
834
|
messageId,
|
|
485
835
|
type: "main_text" /* MAIN_TEXT */,
|
|
486
836
|
status,
|
|
@@ -491,7 +841,7 @@ function splitMarkdownIntoBlocks({
|
|
|
491
841
|
}
|
|
492
842
|
|
|
493
843
|
// src/components/Message/MessageItem.tsx
|
|
494
|
-
var
|
|
844
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
495
845
|
function MessageItem({
|
|
496
846
|
children,
|
|
497
847
|
role = "assistant",
|
|
@@ -501,17 +851,17 @@ function MessageItem({
|
|
|
501
851
|
}) {
|
|
502
852
|
const markdown = typeof children === "string" ? children : String(children ?? "");
|
|
503
853
|
if (role === "user") {
|
|
504
|
-
return /* @__PURE__ */ (0,
|
|
854
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className, "data-role": "user", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_markdown2.default, { remarkPlugins: [import_remark_gfm2.default], children: markdown }) });
|
|
505
855
|
}
|
|
506
|
-
const generatedId = (0,
|
|
856
|
+
const generatedId = (0, import_react6.useId)();
|
|
507
857
|
const messageIdRef = messageId ?? generatedId;
|
|
508
|
-
const blocks = (0,
|
|
858
|
+
const blocks = (0, import_react6.useMemo)(() => {
|
|
509
859
|
const allBlocks = messageBlockStore.selectAll();
|
|
510
860
|
const oldBlockIds = allBlocks.filter((b) => b.messageId === messageIdRef).map((b) => b.id);
|
|
511
861
|
if (oldBlockIds.length > 0) {
|
|
512
862
|
messageBlockStore.remove(oldBlockIds);
|
|
513
863
|
}
|
|
514
|
-
const newBlocks =
|
|
864
|
+
const newBlocks = splitMarkdownIntoBlocks2({
|
|
515
865
|
messageId: messageIdRef,
|
|
516
866
|
markdown,
|
|
517
867
|
status: "idle" /* IDLE */
|
|
@@ -519,19 +869,237 @@ function MessageItem({
|
|
|
519
869
|
messageBlockStore.upsert(newBlocks);
|
|
520
870
|
return newBlocks;
|
|
521
871
|
}, [markdown, messageIdRef]);
|
|
522
|
-
return /* @__PURE__ */ (0,
|
|
872
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className, "data-message-id": messageIdRef, "data-role": role, children: blocks.map((block) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBlockRenderer, { block, className: blockClassName }, block.id)) });
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/utils/shiki/ShikiHighlighterManager.ts
|
|
876
|
+
var import_core2 = require("shiki/core");
|
|
877
|
+
var import_javascript2 = require("shiki/engine/javascript");
|
|
878
|
+
|
|
879
|
+
// src/utils/shiki/languageRegistry.ts
|
|
880
|
+
var LANGUAGE_REGISTRY = {
|
|
881
|
+
javascript: () => import("shiki/langs/javascript.mjs"),
|
|
882
|
+
js: () => import("shiki/langs/javascript.mjs"),
|
|
883
|
+
typescript: () => import("shiki/langs/typescript.mjs"),
|
|
884
|
+
ts: () => import("shiki/langs/typescript.mjs"),
|
|
885
|
+
tsx: () => import("shiki/langs/tsx.mjs"),
|
|
886
|
+
jsx: () => import("shiki/langs/jsx.mjs"),
|
|
887
|
+
python: () => import("shiki/langs/python.mjs"),
|
|
888
|
+
py: () => import("shiki/langs/python.mjs"),
|
|
889
|
+
java: () => import("shiki/langs/java.mjs"),
|
|
890
|
+
json: () => import("shiki/langs/json.mjs"),
|
|
891
|
+
html: () => import("shiki/langs/html.mjs"),
|
|
892
|
+
css: () => import("shiki/langs/css.mjs"),
|
|
893
|
+
bash: () => import("shiki/langs/bash.mjs"),
|
|
894
|
+
sh: () => import("shiki/langs/bash.mjs"),
|
|
895
|
+
shell: () => import("shiki/langs/shell.mjs"),
|
|
896
|
+
sql: () => import("shiki/langs/sql.mjs"),
|
|
897
|
+
markdown: () => import("shiki/langs/markdown.mjs"),
|
|
898
|
+
md: () => import("shiki/langs/markdown.mjs"),
|
|
899
|
+
go: () => import("shiki/langs/go.mjs"),
|
|
900
|
+
rust: () => import("shiki/langs/rust.mjs"),
|
|
901
|
+
rs: () => import("shiki/langs/rust.mjs"),
|
|
902
|
+
c: () => import("shiki/langs/c.mjs"),
|
|
903
|
+
cpp: () => import("shiki/langs/cpp.mjs"),
|
|
904
|
+
"c++": () => import("shiki/langs/cpp.mjs"),
|
|
905
|
+
csharp: () => import("shiki/langs/csharp.mjs"),
|
|
906
|
+
"c#": () => import("shiki/langs/csharp.mjs"),
|
|
907
|
+
cs: () => import("shiki/langs/csharp.mjs"),
|
|
908
|
+
php: () => import("shiki/langs/php.mjs"),
|
|
909
|
+
ruby: () => import("shiki/langs/ruby.mjs"),
|
|
910
|
+
rb: () => import("shiki/langs/ruby.mjs"),
|
|
911
|
+
swift: () => import("shiki/langs/swift.mjs"),
|
|
912
|
+
kotlin: () => import("shiki/langs/kotlin.mjs"),
|
|
913
|
+
kt: () => import("shiki/langs/kotlin.mjs"),
|
|
914
|
+
yaml: () => import("shiki/langs/yaml.mjs"),
|
|
915
|
+
yml: () => import("shiki/langs/yaml.mjs"),
|
|
916
|
+
xml: () => import("shiki/langs/xml.mjs"),
|
|
917
|
+
dockerfile: () => import("shiki/langs/dockerfile.mjs"),
|
|
918
|
+
graphql: () => import("shiki/langs/graphql.mjs"),
|
|
919
|
+
gql: () => import("shiki/langs/graphql.mjs"),
|
|
920
|
+
terraform: () => import("shiki/langs/terraform.mjs"),
|
|
921
|
+
tf: () => import("shiki/langs/terraform.mjs"),
|
|
922
|
+
lua: () => import("shiki/langs/lua.mjs"),
|
|
923
|
+
r: () => import("shiki/langs/r.mjs"),
|
|
924
|
+
scala: () => import("shiki/langs/scala.mjs"),
|
|
925
|
+
elixir: () => import("shiki/langs/elixir.mjs"),
|
|
926
|
+
ex: () => import("shiki/langs/elixir.mjs"),
|
|
927
|
+
dart: () => import("shiki/langs/dart.mjs"),
|
|
928
|
+
vue: () => import("shiki/langs/vue.mjs"),
|
|
929
|
+
svelte: () => import("shiki/langs/svelte.mjs"),
|
|
930
|
+
astro: () => import("shiki/langs/astro.mjs")
|
|
931
|
+
};
|
|
932
|
+
var THEME_REGISTRY = {
|
|
933
|
+
"github-light": () => import("shiki/themes/github-light.mjs"),
|
|
934
|
+
"github-dark": () => import("shiki/themes/github-dark.mjs"),
|
|
935
|
+
"github-dark-dimmed": () => import("shiki/themes/github-dark-dimmed.mjs"),
|
|
936
|
+
"dracula": () => import("shiki/themes/dracula.mjs"),
|
|
937
|
+
"monokai": () => import("shiki/themes/monokai.mjs"),
|
|
938
|
+
"nord": () => import("shiki/themes/nord.mjs"),
|
|
939
|
+
"one-dark-pro": () => import("shiki/themes/one-dark-pro.mjs"),
|
|
940
|
+
"solarized-light": () => import("shiki/themes/solarized-light.mjs"),
|
|
941
|
+
"solarized-dark": () => import("shiki/themes/solarized-dark.mjs"),
|
|
942
|
+
"vitesse-light": () => import("shiki/themes/vitesse-light.mjs"),
|
|
943
|
+
"vitesse-dark": () => import("shiki/themes/vitesse-dark.mjs")
|
|
944
|
+
};
|
|
945
|
+
function getLanguageImport(lang) {
|
|
946
|
+
const normalizedLang = lang.toLowerCase();
|
|
947
|
+
const importFn = LANGUAGE_REGISTRY[normalizedLang];
|
|
948
|
+
if (!importFn) {
|
|
949
|
+
console.warn(`[Shiki] Language "${lang}" not found in registry, falling back to plain text`);
|
|
950
|
+
return import("shiki/langs/javascript.mjs");
|
|
951
|
+
}
|
|
952
|
+
return importFn();
|
|
953
|
+
}
|
|
954
|
+
function getThemeImport(theme) {
|
|
955
|
+
const normalizedTheme = theme.toLowerCase();
|
|
956
|
+
const importFn = THEME_REGISTRY[normalizedTheme];
|
|
957
|
+
if (!importFn) {
|
|
958
|
+
console.warn(`[Shiki] Theme "${theme}" not found in registry, falling back to github-light`);
|
|
959
|
+
return import("shiki/themes/github-light.mjs");
|
|
960
|
+
}
|
|
961
|
+
return importFn();
|
|
962
|
+
}
|
|
963
|
+
function isLanguageSupported(lang) {
|
|
964
|
+
return lang.toLowerCase() in LANGUAGE_REGISTRY;
|
|
965
|
+
}
|
|
966
|
+
function isThemeSupported(theme) {
|
|
967
|
+
return theme.toLowerCase() in THEME_REGISTRY;
|
|
523
968
|
}
|
|
969
|
+
function getSupportedLanguages() {
|
|
970
|
+
return Object.keys(LANGUAGE_REGISTRY);
|
|
971
|
+
}
|
|
972
|
+
function getSupportedThemes() {
|
|
973
|
+
return Object.keys(THEME_REGISTRY);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// src/utils/shiki/ShikiHighlighterManager.ts
|
|
977
|
+
var ShikiHighlighterManager = class {
|
|
978
|
+
constructor() {
|
|
979
|
+
this.lightHighlighter = null;
|
|
980
|
+
this.darkHighlighter = null;
|
|
981
|
+
this.lightTheme = null;
|
|
982
|
+
this.darkTheme = null;
|
|
983
|
+
this.loadedLanguages = /* @__PURE__ */ new Set();
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* 高亮代码(返回 light 和 dark 两个主题的 HTML)
|
|
987
|
+
*
|
|
988
|
+
* @param code - 代码字符串
|
|
989
|
+
* @param language - 语言标识符
|
|
990
|
+
* @param themes - [light theme, dark theme] 元组
|
|
991
|
+
* @returns [lightHtml, darkHtml] - 两个主题的 HTML 元组
|
|
992
|
+
*/
|
|
993
|
+
async highlightCode(code, language, themes) {
|
|
994
|
+
const [lightTheme, darkTheme] = themes;
|
|
995
|
+
const needsLightRecreation = !this.lightHighlighter || this.lightTheme !== lightTheme;
|
|
996
|
+
const needsDarkRecreation = !this.darkHighlighter || this.darkTheme !== darkTheme;
|
|
997
|
+
if (needsLightRecreation || needsDarkRecreation) {
|
|
998
|
+
this.loadedLanguages.clear();
|
|
999
|
+
}
|
|
1000
|
+
const languageSupported = isLanguageSupported(language);
|
|
1001
|
+
const needsLanguageLoad = !this.loadedLanguages.has(language) && languageSupported;
|
|
1002
|
+
if (needsLightRecreation) {
|
|
1003
|
+
this.lightHighlighter = await (0, import_core2.createHighlighterCore)({
|
|
1004
|
+
themes: [getThemeImport(lightTheme)],
|
|
1005
|
+
langs: languageSupported ? [getLanguageImport(language)] : [],
|
|
1006
|
+
engine: (0, import_javascript2.createJavaScriptRegexEngine)({ forgiving: true })
|
|
1007
|
+
});
|
|
1008
|
+
this.lightTheme = lightTheme;
|
|
1009
|
+
if (languageSupported) {
|
|
1010
|
+
this.loadedLanguages.add(language);
|
|
1011
|
+
}
|
|
1012
|
+
} else if (needsLanguageLoad && this.lightHighlighter) {
|
|
1013
|
+
await this.lightHighlighter.loadLanguage(getLanguageImport(language));
|
|
1014
|
+
this.loadedLanguages.add(language);
|
|
1015
|
+
}
|
|
1016
|
+
if (needsDarkRecreation) {
|
|
1017
|
+
this.darkHighlighter = await (0, import_core2.createHighlighterCore)({
|
|
1018
|
+
themes: [getThemeImport(darkTheme)],
|
|
1019
|
+
langs: languageSupported ? [getLanguageImport(language)] : [],
|
|
1020
|
+
engine: (0, import_javascript2.createJavaScriptRegexEngine)({ forgiving: true })
|
|
1021
|
+
});
|
|
1022
|
+
this.darkTheme = darkTheme;
|
|
1023
|
+
} else if (needsLanguageLoad && this.darkHighlighter) {
|
|
1024
|
+
await this.darkHighlighter.loadLanguage(getLanguageImport(language));
|
|
1025
|
+
}
|
|
1026
|
+
const lang = languageSupported ? language : "text";
|
|
1027
|
+
const light = this.lightHighlighter?.codeToHtml(code, {
|
|
1028
|
+
lang,
|
|
1029
|
+
theme: lightTheme
|
|
1030
|
+
}) ?? "";
|
|
1031
|
+
const dark = this.darkHighlighter?.codeToHtml(code, {
|
|
1032
|
+
lang,
|
|
1033
|
+
theme: darkTheme
|
|
1034
|
+
}) ?? "";
|
|
1035
|
+
return [light, dark];
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* 高亮代码(单一主题版本)
|
|
1039
|
+
*/
|
|
1040
|
+
async highlightCodeSingle(code, language, theme) {
|
|
1041
|
+
const [light] = await this.highlightCode(code, language, [theme, theme]);
|
|
1042
|
+
return light;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* 清除缓存(用于测试或内存清理)
|
|
1046
|
+
*/
|
|
1047
|
+
clear() {
|
|
1048
|
+
this.lightHighlighter = null;
|
|
1049
|
+
this.darkHighlighter = null;
|
|
1050
|
+
this.lightTheme = null;
|
|
1051
|
+
this.darkTheme = null;
|
|
1052
|
+
this.loadedLanguages.clear();
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
var highlighterManager = new ShikiHighlighterManager();
|
|
524
1056
|
// Annotate the CommonJS export names for ESM import in node:
|
|
525
1057
|
0 && (module.exports = {
|
|
1058
|
+
Block,
|
|
526
1059
|
CodeBlock,
|
|
1060
|
+
MemoBlockquote,
|
|
1061
|
+
MemoCode,
|
|
1062
|
+
MemoDelete,
|
|
1063
|
+
MemoEmphasis,
|
|
1064
|
+
MemoH1,
|
|
1065
|
+
MemoH2,
|
|
1066
|
+
MemoH3,
|
|
1067
|
+
MemoH4,
|
|
1068
|
+
MemoH5,
|
|
1069
|
+
MemoH6,
|
|
1070
|
+
MemoHr,
|
|
1071
|
+
MemoImage,
|
|
1072
|
+
MemoLink,
|
|
1073
|
+
MemoList,
|
|
1074
|
+
MemoListItem,
|
|
1075
|
+
MemoParagraph,
|
|
1076
|
+
MemoPreformatted,
|
|
1077
|
+
MemoStrong,
|
|
1078
|
+
MemoTable,
|
|
1079
|
+
MemoTableBody,
|
|
1080
|
+
MemoTableCell,
|
|
1081
|
+
MemoTableHead,
|
|
1082
|
+
MemoTableRow,
|
|
527
1083
|
MessageBlockRenderer,
|
|
528
1084
|
MessageBlockStatus,
|
|
529
1085
|
MessageBlockStore,
|
|
530
1086
|
MessageBlockType,
|
|
531
1087
|
MessageItem,
|
|
532
1088
|
MessageStatus,
|
|
1089
|
+
ShikiHighlighterManager,
|
|
533
1090
|
StreamingMarkdown,
|
|
1091
|
+
getLanguageImport,
|
|
1092
|
+
getSupportedLanguages,
|
|
1093
|
+
getSupportedThemes,
|
|
1094
|
+
getThemeImport,
|
|
1095
|
+
highlighterManager,
|
|
1096
|
+
isLanguageSupported,
|
|
1097
|
+
isThemeSupported,
|
|
534
1098
|
messageBlockStore,
|
|
1099
|
+
parseMarkdownIntoBlocks,
|
|
1100
|
+
sameClassAndNode,
|
|
1101
|
+
sameNodePosition,
|
|
1102
|
+
splitMarkdownIntoBlocks,
|
|
535
1103
|
useShikiHighlight,
|
|
536
1104
|
useSmoothStream
|
|
537
1105
|
});
|