svelte-streamdown 2.3.2 → 2.3.4
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 +7 -0
- package/dist/Elements/Code.svelte +7 -5
- package/dist/Elements/Element.svelte +24 -0
- package/dist/context.svelte.d.ts +5 -0
- package/dist/marked/index.d.ts +2 -1
- package/dist/marked/index.js +3 -2
- package/dist/marked/marked-dl.d.ts +24 -0
- package/dist/marked/marked-dl.js +45 -0
- package/dist/theme.d.ts +36 -0
- package/dist/theme.js +25 -1
- package/dist/utils/hightlighter.svelte.d.ts +2 -2
- package/dist/utils/hightlighter.svelte.js +10 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,6 +231,13 @@ III. Third item
|
|
|
231
231
|
> [!IMPORTANT]
|
|
232
232
|
> Native support for Github style Alert
|
|
233
233
|
|
|
234
|
+
### Description List
|
|
235
|
+
|
|
236
|
+
: Topic 1 : Description 1
|
|
237
|
+
: **Topic 2** : *Description 2*
|
|
238
|
+
: Topic 3 : Description 3
|
|
239
|
+
: Topic 3 : Description 3
|
|
240
|
+
|
|
234
241
|
## 🔄 Differences from Original React Version
|
|
235
242
|
|
|
236
243
|
This Svelte port maintains feature parity with the original [Streamdown](https://streamdown.ai/) while adapting to Svelte's patterns:
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
} = $props();
|
|
15
15
|
|
|
16
16
|
const streamdown = useStreamdown();
|
|
17
|
-
const highlighter = HighlighterManager.create(streamdown.shikiPreloadThemes
|
|
17
|
+
const highlighter = HighlighterManager.create(streamdown.shikiPreloadThemes);
|
|
18
|
+
|
|
18
19
|
const copy = useCopy({
|
|
19
20
|
get content() {
|
|
20
21
|
return token.text;
|
|
@@ -44,8 +45,6 @@
|
|
|
44
45
|
void highlighter.load(theme, lang);
|
|
45
46
|
});
|
|
46
47
|
});
|
|
47
|
-
|
|
48
|
-
const isMounted = streamdown.isMounted;
|
|
49
48
|
</script>
|
|
50
49
|
|
|
51
50
|
<div class={streamdown.theme.code.base}>
|
|
@@ -84,7 +83,7 @@
|
|
|
84
83
|
<span class={streamdown.theme.code.line}>
|
|
85
84
|
{#each line as token}
|
|
86
85
|
<span
|
|
87
|
-
style={isMounted ? streamdown.animationTextStyle : undefined}
|
|
86
|
+
style={streamdown.isMounted ? streamdown.animationTextStyle : undefined}
|
|
88
87
|
style:color={token.color}
|
|
89
88
|
style:background-color={token.bgColor}
|
|
90
89
|
>
|
|
@@ -97,7 +96,10 @@
|
|
|
97
96
|
|
|
98
97
|
{#snippet Skeleton(lines: string[])}
|
|
99
98
|
{#each lines as line}
|
|
100
|
-
<span
|
|
99
|
+
<span
|
|
100
|
+
style={streamdown.isMounted ? streamdown.animationTextStyle : undefined}
|
|
101
|
+
class={streamdown.theme.code.skeleton}
|
|
102
|
+
>
|
|
101
103
|
{line.trim().length > 0 ? line : '\u200B'}
|
|
102
104
|
</span>
|
|
103
105
|
{/each}
|
|
@@ -225,6 +225,30 @@
|
|
|
225
225
|
<FootnoteRef {token} />
|
|
226
226
|
{:else if token.type === 'footnote'}
|
|
227
227
|
<!-- TODO Footnotes are rendered inside the FootnoteRef popover -->
|
|
228
|
+
{:else if token.type === 'descriptionList'}
|
|
229
|
+
<Slot props={{ children, token }} render={streamdown.snippets.descriptionList}>
|
|
230
|
+
<dl class={streamdown.theme.descriptionList.base}>
|
|
231
|
+
{@render children()}
|
|
232
|
+
</dl>
|
|
233
|
+
</Slot>
|
|
234
|
+
{:else if token.type === 'description'}
|
|
235
|
+
<Slot props={{ children, token }} render={streamdown.snippets.description}>
|
|
236
|
+
<div class={streamdown.theme.description.base}>
|
|
237
|
+
{@render children()}
|
|
238
|
+
</div>
|
|
239
|
+
</Slot>
|
|
240
|
+
{:else if token.type === 'descriptionTerm'}
|
|
241
|
+
<Slot props={{ children, token }} render={streamdown.snippets.descriptionTerm}>
|
|
242
|
+
<dt class={streamdown.theme.descriptionTerm.base}>
|
|
243
|
+
{@render children()}
|
|
244
|
+
</dt>
|
|
245
|
+
</Slot>
|
|
246
|
+
{:else if token.type === 'descriptionDetail'}
|
|
247
|
+
<Slot props={{ children, token }} render={streamdown.snippets.descriptionDetail}>
|
|
248
|
+
<dd class={streamdown.theme.descriptionDetail.base}>
|
|
249
|
+
{@render children()}
|
|
250
|
+
</dd>
|
|
251
|
+
</Slot>
|
|
228
252
|
{:else if token.type === 'def'}
|
|
229
253
|
<!-- TODO This does not seems to be tokenized for now -->
|
|
230
254
|
{:else if token.type === 'escape'}
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ import type { AlertToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoo
|
|
|
32
32
|
import type { Tokens } from 'marked';
|
|
33
33
|
import type { ListItemToken, ListToken } from './marked/marked-list.js';
|
|
34
34
|
import type { Footnote, FootnoteRef, FootnoteToken } from './marked/marked-footnotes.js';
|
|
35
|
+
import type { DescriptionDetailToken, DescriptionListToken, DescriptionTermToken, DescriptionToken } from './marked/marked-dl.js';
|
|
35
36
|
type TokenSnippet = {
|
|
36
37
|
heading: Tokens.Heading;
|
|
37
38
|
paragraph: Tokens.Paragraph;
|
|
@@ -62,6 +63,10 @@ type TokenSnippet = {
|
|
|
62
63
|
footnotePopover: FootnoteToken;
|
|
63
64
|
sup: SubSupToken;
|
|
64
65
|
sub: SubSupToken;
|
|
66
|
+
descriptionList: DescriptionListToken;
|
|
67
|
+
description: DescriptionToken;
|
|
68
|
+
descriptionTerm: DescriptionTermToken;
|
|
69
|
+
descriptionDetail: DescriptionDetailToken;
|
|
65
70
|
};
|
|
66
71
|
type PredefinedElements = keyof TokenSnippet;
|
|
67
72
|
export type Snippets = {
|
package/dist/marked/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { type ListItemToken, type ListToken } from './marked-list.js';
|
|
|
7
7
|
import { type BrToken } from './marked-br.js';
|
|
8
8
|
import { type HrToken } from './marked-hr.js';
|
|
9
9
|
import { type TableToken, type THead, type TBody, type TFoot, type THeadRow, type TRow, type TH, type TD } from './marked-table.js';
|
|
10
|
+
import { type DescriptionDetailToken, type DescriptionListToken, type DescriptionTermToken, type DescriptionToken } from './marked-dl.js';
|
|
10
11
|
export type GenericToken = {
|
|
11
12
|
type: string;
|
|
12
13
|
raw: string;
|
|
@@ -19,7 +20,7 @@ export type Extension = {
|
|
|
19
20
|
start?: TokenizerStartFunction;
|
|
20
21
|
applyInBlockParsing?: boolean;
|
|
21
22
|
};
|
|
22
|
-
export type StreamdownToken = Exclude<MarkedToken, Tokens.List | Tokens.ListItem> | ListToken | ListItemToken | MathToken | AlertToken | FootnoteToken | SubSupToken | BrToken | HrToken | TableToken | THead | TBody | TFoot | THeadRow | TRow | TH | TD;
|
|
23
|
+
export type StreamdownToken = Exclude<MarkedToken, Tokens.List | Tokens.ListItem> | ListToken | ListItemToken | MathToken | AlertToken | FootnoteToken | SubSupToken | BrToken | HrToken | TableToken | THead | TBody | TFoot | THeadRow | TRow | TH | TD | DescriptionListToken | DescriptionToken | DescriptionDetailToken | DescriptionTermToken;
|
|
23
24
|
export type { TableToken, THead, TBody, TFoot, THeadRow, TRow, TH, TD } from './marked-table.js';
|
|
24
25
|
export declare const lex: (markdown: string, extensions?: Extension[]) => StreamdownToken[];
|
|
25
26
|
export declare const parseBlocks: (markdown: string, extensions?: Extension[]) => string[];
|
package/dist/marked/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { markedList } from './marked-list.js';
|
|
|
7
7
|
import { markedBr } from './marked-br.js';
|
|
8
8
|
import { markedHr } from './marked-hr.js';
|
|
9
9
|
import { markedTable } from './marked-table.js';
|
|
10
|
+
import { markedDl, markedDt } from './marked-dl.js';
|
|
10
11
|
const parseExtensions = (...extensions) => {
|
|
11
12
|
const options = {
|
|
12
13
|
gfm: true,
|
|
@@ -40,12 +41,12 @@ const parseExtensions = (...extensions) => {
|
|
|
40
41
|
return options;
|
|
41
42
|
};
|
|
42
43
|
export const lex = (markdown, extensions = []) => {
|
|
43
|
-
return new Lexer(parseExtensions(markedHr, markedTable, ...markedFootnote(), markedAlert, ...markedMath, markedSub, markedSup, markedList, markedBr, ...extensions))
|
|
44
|
+
return new Lexer(parseExtensions(markedHr, markedTable, ...markedFootnote(), markedAlert, ...markedMath, markedSub, markedSup, markedList, markedBr, markedDl, markedDt, ...extensions))
|
|
44
45
|
.lex(markdown)
|
|
45
46
|
.filter((token) => token.type !== 'space' && token.type !== 'footnote');
|
|
46
47
|
};
|
|
47
48
|
export const parseBlocks = (markdown, extensions = []) => {
|
|
48
|
-
const blockLexer = new Lexer(parseExtensions(markedHr, ...markedFootnote(), markedTable, ...extensions.filter(({ level, applyInBlockParsing }) => level === 'block' && applyInBlockParsing)));
|
|
49
|
+
const blockLexer = new Lexer(parseExtensions(markedHr, ...markedFootnote(), markedDl, markedTable, ...extensions.filter(({ level, applyInBlockParsing }) => level === 'block' && applyInBlockParsing)));
|
|
49
50
|
return blockLexer.blockTokens(markdown, []).reduce((acc, block) => {
|
|
50
51
|
if (block.type === 'space' || block.type === 'footnote') {
|
|
51
52
|
return acc;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Extension, GenericToken } from './index.js';
|
|
2
|
+
export declare const markedDl: Extension;
|
|
3
|
+
export declare const markedDt: Extension;
|
|
4
|
+
export type DescriptionListToken = {
|
|
5
|
+
type: 'descriptionList';
|
|
6
|
+
raw: string;
|
|
7
|
+
text: string;
|
|
8
|
+
tokens: DescriptionToken[];
|
|
9
|
+
};
|
|
10
|
+
export type DescriptionToken = {
|
|
11
|
+
type: 'description';
|
|
12
|
+
raw: string;
|
|
13
|
+
tokens: [DescriptionTermToken, DescriptionDetailToken];
|
|
14
|
+
};
|
|
15
|
+
export type DescriptionTermToken = {
|
|
16
|
+
type: 'descriptionTerm';
|
|
17
|
+
raw: string;
|
|
18
|
+
tokens: GenericToken[];
|
|
19
|
+
};
|
|
20
|
+
export type DescriptionDetailToken = {
|
|
21
|
+
type: 'descriptionDetail';
|
|
22
|
+
raw: string;
|
|
23
|
+
tokens: GenericToken[];
|
|
24
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const markedDl = {
|
|
2
|
+
name: 'descriptionList',
|
|
3
|
+
level: 'block', // Is this a block-level or inline-level tokenizer?
|
|
4
|
+
tokenizer(src) {
|
|
5
|
+
const rule = /^(?:[ \t]*:[^:\n]+:[ \t]?[^\n]*(?:\n|$))+/;
|
|
6
|
+
const match = rule.exec(src);
|
|
7
|
+
if (match) {
|
|
8
|
+
const token = {
|
|
9
|
+
type: 'descriptionList',
|
|
10
|
+
raw: match[0],
|
|
11
|
+
text: match[0].trim(),
|
|
12
|
+
tokens: this.lexer.inlineTokens(match[0].trim()).filter((t) => t.type === 'description')
|
|
13
|
+
};
|
|
14
|
+
return token;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export const markedDt = {
|
|
19
|
+
name: 'description',
|
|
20
|
+
level: 'inline',
|
|
21
|
+
tokenizer(src) {
|
|
22
|
+
const rule = /^\s*:([^:\n]+):([^:\n]*)(?:\n|$)/;
|
|
23
|
+
const match = rule.exec(src);
|
|
24
|
+
if (match) {
|
|
25
|
+
const term = match[1].trim();
|
|
26
|
+
const detail = match[2].trim();
|
|
27
|
+
return {
|
|
28
|
+
type: 'description',
|
|
29
|
+
raw: match[0],
|
|
30
|
+
tokens: [
|
|
31
|
+
{
|
|
32
|
+
type: 'descriptionTerm',
|
|
33
|
+
raw: term,
|
|
34
|
+
tokens: this.lexer.inlineTokens(term)
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'descriptionDetail',
|
|
38
|
+
raw: detail,
|
|
39
|
+
tokens: this.lexer.inlineTokens(detail)
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
package/dist/theme.d.ts
CHANGED
|
@@ -128,6 +128,18 @@ export declare const theme: {
|
|
|
128
128
|
footnotePopover: {
|
|
129
129
|
base: string;
|
|
130
130
|
};
|
|
131
|
+
descriptionList: {
|
|
132
|
+
base: string;
|
|
133
|
+
};
|
|
134
|
+
description: {
|
|
135
|
+
base: string;
|
|
136
|
+
};
|
|
137
|
+
descriptionTerm: {
|
|
138
|
+
base: string;
|
|
139
|
+
};
|
|
140
|
+
descriptionDetail: {
|
|
141
|
+
base: string;
|
|
142
|
+
};
|
|
131
143
|
};
|
|
132
144
|
export declare const shadcnTheme: {
|
|
133
145
|
link: {
|
|
@@ -257,6 +269,18 @@ export declare const shadcnTheme: {
|
|
|
257
269
|
footnotePopover: {
|
|
258
270
|
base: string;
|
|
259
271
|
};
|
|
272
|
+
descriptionList: {
|
|
273
|
+
base: string;
|
|
274
|
+
};
|
|
275
|
+
description: {
|
|
276
|
+
base: string;
|
|
277
|
+
};
|
|
278
|
+
descriptionTerm: {
|
|
279
|
+
base: string;
|
|
280
|
+
};
|
|
281
|
+
descriptionDetail: {
|
|
282
|
+
base: string;
|
|
283
|
+
};
|
|
260
284
|
};
|
|
261
285
|
export type Theme = typeof theme;
|
|
262
286
|
type DeepPartial<T> = {
|
|
@@ -391,5 +415,17 @@ export declare const mergeTheme: (customTheme?: DeepPartialTheme, baseTheme?: "t
|
|
|
391
415
|
footnotePopover: {
|
|
392
416
|
base: string;
|
|
393
417
|
};
|
|
418
|
+
descriptionList: {
|
|
419
|
+
base: string;
|
|
420
|
+
};
|
|
421
|
+
description: {
|
|
422
|
+
base: string;
|
|
423
|
+
};
|
|
424
|
+
descriptionTerm: {
|
|
425
|
+
base: string;
|
|
426
|
+
};
|
|
427
|
+
descriptionDetail: {
|
|
428
|
+
base: string;
|
|
429
|
+
};
|
|
394
430
|
};
|
|
395
431
|
export {};
|
package/dist/theme.js
CHANGED
|
@@ -128,6 +128,18 @@ export const theme = {
|
|
|
128
128
|
},
|
|
129
129
|
footnotePopover: {
|
|
130
130
|
base: 'fixed z-50 max-h-[30vh] max-w-3xl overflow-y-auto rounded-lg bg-background p-4 shadow'
|
|
131
|
+
},
|
|
132
|
+
descriptionList: {
|
|
133
|
+
base: 'my-4 space-y-2'
|
|
134
|
+
},
|
|
135
|
+
description: {
|
|
136
|
+
base: 'border-l-2 border-gray-200 pl-4'
|
|
137
|
+
},
|
|
138
|
+
descriptionTerm: {
|
|
139
|
+
base: 'font-semibold text-gray-900'
|
|
140
|
+
},
|
|
141
|
+
descriptionDetail: {
|
|
142
|
+
base: 'text-gray-700 ml-4 leading-relaxed'
|
|
131
143
|
}
|
|
132
144
|
};
|
|
133
145
|
export const shadcnTheme = {
|
|
@@ -247,7 +259,7 @@ export const shadcnTheme = {
|
|
|
247
259
|
base: ''
|
|
248
260
|
},
|
|
249
261
|
em: {
|
|
250
|
-
base: 'italic
|
|
262
|
+
base: 'italic'
|
|
251
263
|
},
|
|
252
264
|
del: {
|
|
253
265
|
base: 'text-muted-foreground'
|
|
@@ -257,6 +269,18 @@ export const shadcnTheme = {
|
|
|
257
269
|
},
|
|
258
270
|
footnotePopover: {
|
|
259
271
|
base: 'fixed z-50 max-h-[30vh] shadow max-w-3xl overflow-y-auto rounded-lg bg-background p-4'
|
|
272
|
+
},
|
|
273
|
+
descriptionList: {
|
|
274
|
+
base: 'my-4 space-y-2'
|
|
275
|
+
},
|
|
276
|
+
description: {
|
|
277
|
+
base: 'border-l-2 border-border pl-4'
|
|
278
|
+
},
|
|
279
|
+
descriptionTerm: {
|
|
280
|
+
base: 'font-semibold text-foreground'
|
|
281
|
+
},
|
|
282
|
+
descriptionDetail: {
|
|
283
|
+
base: 'text-muted-foreground ml-4 leading-relaxed'
|
|
260
284
|
}
|
|
261
285
|
};
|
|
262
286
|
export const mergeTheme = (customTheme, baseTheme) => {
|
|
@@ -17,7 +17,7 @@ declare class HighlighterManager {
|
|
|
17
17
|
* Preloads themes by creating minimal highlighter instances.
|
|
18
18
|
* This reduces flickering when switching themes.
|
|
19
19
|
*/
|
|
20
|
-
preloadThemes(highlighter: Highlighter): Promise<void>;
|
|
20
|
+
preloadThemes(highlighter: Highlighter): Promise<void[]>;
|
|
21
21
|
/**
|
|
22
22
|
* Ensures the highlighter is ready for the given theme and language.
|
|
23
23
|
*/
|
|
@@ -26,7 +26,7 @@ declare class HighlighterManager {
|
|
|
26
26
|
* Highlights code synchronously. Must call isReady() first.
|
|
27
27
|
*/
|
|
28
28
|
highlightCode(code: string, language: string | undefined, theme: BundledTheme): ThemedToken[][];
|
|
29
|
-
static create(preloadedThemes
|
|
29
|
+
static create(preloadedThemes?: BundledTheme[]): HighlighterManager;
|
|
30
30
|
}
|
|
31
31
|
export { HighlighterManager };
|
|
32
32
|
export declare const languageExtensionMap: Record<string, string>;
|
|
@@ -23,7 +23,7 @@ class HighlighterManager {
|
|
|
23
23
|
if (this.highlighter instanceof Promise) {
|
|
24
24
|
return this.highlighter;
|
|
25
25
|
}
|
|
26
|
-
else {
|
|
26
|
+
else if (!this.highlighter) {
|
|
27
27
|
this.highlighter = new Promise((resolve, reject) => {
|
|
28
28
|
loadShiki().then(([engine, createHighlighter]) => {
|
|
29
29
|
return createHighlighter({
|
|
@@ -38,6 +38,9 @@ class HighlighterManager {
|
|
|
38
38
|
});
|
|
39
39
|
return this.highlighter;
|
|
40
40
|
}
|
|
41
|
+
else {
|
|
42
|
+
return this.highlighter;
|
|
43
|
+
}
|
|
41
44
|
}
|
|
42
45
|
async loadTheme(theme, highlighter) {
|
|
43
46
|
const themeLoader = this.loadedThemes.get(theme);
|
|
@@ -50,6 +53,7 @@ class HighlighterManager {
|
|
|
50
53
|
});
|
|
51
54
|
this.loadedThemes.set(theme, themeLoaderPromise);
|
|
52
55
|
await themeLoaderPromise;
|
|
56
|
+
this.loadedThemes.set(theme, true);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
async loadLanguage(language, highlighter) {
|
|
@@ -63,6 +67,7 @@ class HighlighterManager {
|
|
|
63
67
|
});
|
|
64
68
|
this.loadedLanguages.set(language, languageLoaderPromise);
|
|
65
69
|
await languageLoaderPromise;
|
|
70
|
+
this.loadedLanguages.set(language, true);
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
isLanguageSupported = (language) => {
|
|
@@ -70,26 +75,15 @@ class HighlighterManager {
|
|
|
70
75
|
};
|
|
71
76
|
isReady(theme, language = 'bash') {
|
|
72
77
|
return (!(this.highlighter instanceof Promise) &&
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
78
|
+
this.loadedThemes.get(theme) === true &&
|
|
79
|
+
this.loadedLanguages.get(this.isLanguageSupported(language) ? language : 'bash') === true);
|
|
75
80
|
}
|
|
76
81
|
/**
|
|
77
82
|
* Preloads themes by creating minimal highlighter instances.
|
|
78
83
|
* This reduces flickering when switching themes.
|
|
79
84
|
*/
|
|
80
85
|
async preloadThemes(highlighter) {
|
|
81
|
-
|
|
82
|
-
const themeLoader = this.loadedThemes.get(theme);
|
|
83
|
-
if (themeLoader === true) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
else if (themeLoader instanceof Promise) {
|
|
87
|
-
await themeLoader;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
await this.loadTheme(theme, highlighter);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
86
|
+
return Promise.all(Array.from(this.preloadedThemes).map((theme) => this.loadTheme(theme, highlighter)));
|
|
93
87
|
}
|
|
94
88
|
/**
|
|
95
89
|
* Ensures the highlighter is ready for the given theme and language.
|
|
@@ -101,7 +95,6 @@ class HighlighterManager {
|
|
|
101
95
|
!(this.highlighter instanceof Promise) &&
|
|
102
96
|
themeLoader === true &&
|
|
103
97
|
languageLoader === true) {
|
|
104
|
-
// already loaded
|
|
105
98
|
return;
|
|
106
99
|
}
|
|
107
100
|
const highlighter = await this.loadHighlighter();
|
|
@@ -131,7 +124,7 @@ class HighlighterManager {
|
|
|
131
124
|
return [];
|
|
132
125
|
}
|
|
133
126
|
}
|
|
134
|
-
static create(preloadedThemes) {
|
|
127
|
+
static create(preloadedThemes = []) {
|
|
135
128
|
if (typeof window !== 'undefined' && 'STREAMDOWN_HIGHLIGHTER' in window) {
|
|
136
129
|
const previousHighlighter = window.STREAMDOWN_HIGHLIGHTER;
|
|
137
130
|
preloadedThemes.forEach((theme) => previousHighlighter.preloadedThemes.add(theme));
|