svelte-streamdown 2.3.3 → 2.4.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/dist/Block.svelte +1 -8
- package/dist/Elements/Alert.svelte +4 -1
- package/dist/Elements/Code.svelte +8 -6
- package/dist/Elements/Element.svelte +24 -21
- package/dist/Elements/FootnoteRef.svelte +3 -1
- package/dist/Elements/Image.svelte +4 -1
- package/dist/Elements/Math.svelte +1 -1
- package/dist/Elements/Mermaid.svelte +1 -0
- package/dist/theme.d.ts +0 -9
- package/dist/theme.js +2 -8
- package/dist/utils/hightlighter.svelte.d.ts +1 -1
- package/dist/utils/hightlighter.svelte.js +10 -18
- package/dist/utils/parse-incomplete-markdown.js +63 -0
- package/package.json +1 -1
- package/dist/AnimatedBlock.svelte +0 -16
- package/dist/AnimatedBlock.svelte.d.ts +0 -7
package/dist/Block.svelte
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { lex, type StreamdownToken } from './marked/index.js';
|
|
5
5
|
import AnimatedText from './AnimatedText.svelte';
|
|
6
6
|
import { useStreamdown } from './context.svelte.js';
|
|
7
|
-
import AnimatedBlock from './AnimatedBlock.svelte';
|
|
8
7
|
|
|
9
8
|
let {
|
|
10
9
|
block,
|
|
@@ -38,10 +37,4 @@
|
|
|
38
37
|
{/each}
|
|
39
38
|
{/snippet}
|
|
40
39
|
|
|
41
|
-
{
|
|
42
|
-
<AnimatedBlock>
|
|
43
|
-
{@render renderChildren(tokens)}
|
|
44
|
-
</AnimatedBlock>
|
|
45
|
-
{:else}
|
|
46
|
-
{@render renderChildren(tokens)}
|
|
47
|
-
{/if}
|
|
40
|
+
{@render renderChildren(tokens)}
|
|
@@ -36,7 +36,10 @@
|
|
|
36
36
|
}}
|
|
37
37
|
render={streamdown.snippets.alert}
|
|
38
38
|
>
|
|
39
|
-
<div
|
|
39
|
+
<div
|
|
40
|
+
style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
41
|
+
class={`${streamdown.theme.alert.base} ${streamdown.theme.alert[token.variant]}`}
|
|
42
|
+
>
|
|
40
43
|
<div data-alert-title class={streamdown.theme.alert.title}>
|
|
41
44
|
{@render (streamdown.icons?.[token.variant] || icon)()}
|
|
42
45
|
{streamdown.translations?.alert?.[token.variant] || token.variant}
|
|
@@ -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,11 +45,12 @@
|
|
|
44
45
|
void highlighter.load(theme, lang);
|
|
45
46
|
});
|
|
46
47
|
});
|
|
47
|
-
|
|
48
|
-
const isMounted = streamdown.isMounted;
|
|
49
48
|
</script>
|
|
50
49
|
|
|
51
|
-
<div
|
|
50
|
+
<div
|
|
51
|
+
style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
52
|
+
class={streamdown.theme.code.base}
|
|
53
|
+
>
|
|
52
54
|
<div class={streamdown.theme.code.header}>
|
|
53
55
|
<span class={streamdown.theme.code.language}>{token.lang}</span>
|
|
54
56
|
{#if streamdown.controls.code}
|
|
@@ -84,7 +86,7 @@
|
|
|
84
86
|
<span class={streamdown.theme.code.line}>
|
|
85
87
|
{#each line as token}
|
|
86
88
|
<span
|
|
87
|
-
style={
|
|
89
|
+
style={streamdown.animationTextStyle}
|
|
88
90
|
style:color={token.color}
|
|
89
91
|
style:background-color={token.bgColor}
|
|
90
92
|
>
|
|
@@ -97,7 +99,7 @@
|
|
|
97
99
|
|
|
98
100
|
{#snippet Skeleton(lines: string[])}
|
|
99
101
|
{#each lines as line}
|
|
100
|
-
<span class={streamdown.theme.code.skeleton}>
|
|
102
|
+
<span style={streamdown.animationTextStyle} class={streamdown.theme.code.skeleton}>
|
|
101
103
|
{line.trim().length > 0 ? line : '\u200B'}
|
|
102
104
|
</span>
|
|
103
105
|
{/each}
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
import FootnoteRef from './FootnoteRef.svelte';
|
|
13
13
|
let { token, children }: { token: StreamdownToken; children: Snippet } = $props();
|
|
14
14
|
const streamdown = useStreamdown();
|
|
15
|
+
|
|
16
|
+
// Only apply animation on block level elements. Leaves text elements to be animated by their text children.
|
|
17
|
+
const style = $derived(streamdown.isMounted ? streamdown.animationBlockStyle : '');
|
|
15
18
|
</script>
|
|
16
19
|
|
|
17
20
|
{#if token.type === 'heading'}
|
|
@@ -23,40 +26,40 @@
|
|
|
23
26
|
render={streamdown.snippets.heading}
|
|
24
27
|
>
|
|
25
28
|
{#if token.depth === 1}
|
|
26
|
-
<h1 class={streamdown.theme[`h${token.depth}`].base}>
|
|
29
|
+
<h1 {style} class={streamdown.theme[`h${token.depth}`].base}>
|
|
27
30
|
{@render children()}
|
|
28
31
|
</h1>
|
|
29
32
|
{:else if token.depth === 2}
|
|
30
|
-
<h2 class={streamdown.theme[`h${token.depth}`].base}>
|
|
33
|
+
<h2 {style} class={streamdown.theme[`h${token.depth}`].base}>
|
|
31
34
|
{@render children()}
|
|
32
35
|
</h2>
|
|
33
36
|
{:else if token.depth === 3}
|
|
34
|
-
<h3 class={streamdown.theme[`h${token.depth}`].base}>
|
|
37
|
+
<h3 {style} class={streamdown.theme[`h${token.depth}`].base}>
|
|
35
38
|
{@render children()}
|
|
36
39
|
</h3>
|
|
37
40
|
{:else if token.depth === 4}
|
|
38
|
-
<h4 class={streamdown.theme[`h${token.depth}`].base}>
|
|
41
|
+
<h4 {style} class={streamdown.theme[`h${token.depth}`].base}>
|
|
39
42
|
{@render children()}
|
|
40
43
|
</h4>
|
|
41
44
|
{:else if token.depth === 5}
|
|
42
|
-
<h5 class={streamdown.theme[`h${token.depth}`].base}>
|
|
45
|
+
<h5 {style} class={streamdown.theme[`h${token.depth}`].base}>
|
|
43
46
|
{@render children()}
|
|
44
47
|
</h5>
|
|
45
48
|
{:else if token.depth === 6}
|
|
46
|
-
<h6 class={streamdown.theme[`h${token.depth}`].base}>
|
|
49
|
+
<h6 {style} class={streamdown.theme[`h${token.depth}`].base}>
|
|
47
50
|
{@render children()}
|
|
48
51
|
</h6>
|
|
49
52
|
{/if}
|
|
50
53
|
</Slot>
|
|
51
54
|
{:else if token.type === 'paragraph'}
|
|
52
55
|
<Slot props={{ children, token }} render={streamdown.snippets.paragraph}>
|
|
53
|
-
<p class={streamdown.theme.paragraph.base}>
|
|
56
|
+
<p {style} class={streamdown.theme.paragraph.base}>
|
|
54
57
|
{@render children()}
|
|
55
58
|
</p>
|
|
56
59
|
</Slot>
|
|
57
60
|
{:else if token.type === 'blockquote'}
|
|
58
61
|
<Slot props={{ children, token }} render={streamdown.snippets.blockquote}>
|
|
59
|
-
<blockquote class={streamdown.theme.blockquote.base}>
|
|
62
|
+
<blockquote {style} class={streamdown.theme.blockquote.base}>
|
|
60
63
|
{@render children()}
|
|
61
64
|
</blockquote>
|
|
62
65
|
</Slot>
|
|
@@ -91,7 +94,7 @@
|
|
|
91
94
|
{:else if token.type === 'list_item'}
|
|
92
95
|
<Slot props={{ children, token }} render={streamdown.snippets.li}>
|
|
93
96
|
<li
|
|
94
|
-
style
|
|
97
|
+
{style}
|
|
95
98
|
style:list-style-type={token.task ? 'none' : undefined}
|
|
96
99
|
{...token.value && !token.task ? { value: token.value } : {}}
|
|
97
100
|
class={streamdown.theme.li.base}
|
|
@@ -109,7 +112,7 @@
|
|
|
109
112
|
</Slot>
|
|
110
113
|
{:else if token.type === 'table'}
|
|
111
114
|
<Slot props={{ token, children }} render={streamdown.snippets.table}>
|
|
112
|
-
<div class={streamdown.theme.table.base}>
|
|
115
|
+
<div {style} class={streamdown.theme.table.base}>
|
|
113
116
|
<table class={streamdown.theme.table.table}>
|
|
114
117
|
{@render children()}
|
|
115
118
|
</table>
|
|
@@ -117,25 +120,25 @@
|
|
|
117
120
|
</Slot>
|
|
118
121
|
{:else if token.type === 'thead'}
|
|
119
122
|
<Slot props={{ token, children }} render={streamdown.snippets.thead}>
|
|
120
|
-
<thead class={streamdown.theme.thead.base}>
|
|
123
|
+
<thead {style} class={streamdown.theme.thead.base}>
|
|
121
124
|
{@render children?.()}
|
|
122
125
|
</thead>
|
|
123
126
|
</Slot>
|
|
124
127
|
{:else if token.type === 'tbody'}
|
|
125
128
|
<Slot props={{ token, children }} render={streamdown.snippets.tbody}>
|
|
126
|
-
<tbody class={streamdown.theme.tbody.base}>
|
|
129
|
+
<tbody {style} class={streamdown.theme.tbody.base}>
|
|
127
130
|
{@render children?.()}
|
|
128
131
|
</tbody>
|
|
129
132
|
</Slot>
|
|
130
133
|
{:else if token.type === 'tfoot'}
|
|
131
134
|
<Slot props={{ token, children }} render={streamdown.snippets.tfoot}>
|
|
132
|
-
<tfoot class={streamdown.theme.tfoot.base}>
|
|
135
|
+
<tfoot {style} class={streamdown.theme.tfoot.base}>
|
|
133
136
|
{@render children?.()}
|
|
134
137
|
</tfoot>
|
|
135
138
|
</Slot>
|
|
136
139
|
{:else if token.type === 'tr'}
|
|
137
140
|
<Slot props={{ token, children }} render={streamdown.snippets.tr}>
|
|
138
|
-
<tr class={streamdown.theme.tr.base}>
|
|
141
|
+
<tr {style} class={streamdown.theme.tr.base}>
|
|
139
142
|
{@render children?.()}
|
|
140
143
|
</tr>
|
|
141
144
|
</Slot>
|
|
@@ -143,6 +146,7 @@
|
|
|
143
146
|
{#if token.rowspan > 0}
|
|
144
147
|
<Slot props={{ children, token }} render={streamdown.snippets.td}>
|
|
145
148
|
<td
|
|
149
|
+
{style}
|
|
146
150
|
class={streamdown.theme.td.base}
|
|
147
151
|
{...token.colspan > 1 ? { colspan: token.colspan } : {}}
|
|
148
152
|
{...token.rowspan > 1 ? { rowspan: token.rowspan } : {}}
|
|
@@ -158,6 +162,7 @@
|
|
|
158
162
|
{#if token.rowspan > 0}
|
|
159
163
|
<Slot props={{ children, token }} render={streamdown.snippets.th}>
|
|
160
164
|
<th
|
|
165
|
+
{style}
|
|
161
166
|
class={streamdown.theme.th.base}
|
|
162
167
|
{...token.colspan > 1 ? { colspan: token.colspan } : {}}
|
|
163
168
|
{...token.rowspan > 1 ? { rowspan: token.rowspan } : {}}
|
|
@@ -205,7 +210,7 @@
|
|
|
205
210
|
</Slot>
|
|
206
211
|
{:else if token.type === 'hr'}
|
|
207
212
|
<Slot props={{ children, token }} render={streamdown.snippets.hr}>
|
|
208
|
-
<hr class={streamdown.theme.hr.base} />
|
|
213
|
+
<hr {style} class={streamdown.theme.hr.base} />
|
|
209
214
|
</Slot>
|
|
210
215
|
{:else if token.type === 'br'}
|
|
211
216
|
<br />
|
|
@@ -227,25 +232,23 @@
|
|
|
227
232
|
<!-- TODO Footnotes are rendered inside the FootnoteRef popover -->
|
|
228
233
|
{:else if token.type === 'descriptionList'}
|
|
229
234
|
<Slot props={{ children, token }} render={streamdown.snippets.descriptionList}>
|
|
230
|
-
<dl class={streamdown.theme.descriptionList.base}>
|
|
235
|
+
<dl style={streamdown.animationBlockStyle} class={streamdown.theme.descriptionList.base}>
|
|
231
236
|
{@render children()}
|
|
232
237
|
</dl>
|
|
233
238
|
</Slot>
|
|
234
239
|
{:else if token.type === 'description'}
|
|
235
240
|
<Slot props={{ children, token }} render={streamdown.snippets.description}>
|
|
236
|
-
|
|
237
|
-
{@render children()}
|
|
238
|
-
</div>
|
|
241
|
+
{@render children()}
|
|
239
242
|
</Slot>
|
|
240
243
|
{:else if token.type === 'descriptionTerm'}
|
|
241
244
|
<Slot props={{ children, token }} render={streamdown.snippets.descriptionTerm}>
|
|
242
|
-
<dt class={streamdown.theme.descriptionTerm.base}>
|
|
245
|
+
<dt {style} class={streamdown.theme.descriptionTerm.base}>
|
|
243
246
|
{@render children()}
|
|
244
247
|
</dt>
|
|
245
248
|
</Slot>
|
|
246
249
|
{:else if token.type === 'descriptionDetail'}
|
|
247
250
|
<Slot props={{ children, token }} render={streamdown.snippets.descriptionDetail}>
|
|
248
|
-
<dd class={streamdown.theme.descriptionDetail.base}>
|
|
251
|
+
<dd {style} class={streamdown.theme.descriptionDetail.base}>
|
|
249
252
|
{@render children()}
|
|
250
253
|
</dd>
|
|
251
254
|
</Slot>
|
|
@@ -90,12 +90,14 @@
|
|
|
90
90
|
render={streamdown.snippets.footnotePopover}
|
|
91
91
|
>
|
|
92
92
|
<dialog
|
|
93
|
+
style="z-index: 1000; position: fixed;"
|
|
93
94
|
id={'footnote-popover-' + id}
|
|
94
95
|
aria-modal="false"
|
|
95
96
|
transition:scale|global={{ start: 0.95, duration: 100 }}
|
|
96
97
|
{@attach clickOutside.attachment}
|
|
97
98
|
{@attach popoverAttachment}
|
|
98
99
|
open
|
|
100
|
+
popover="auto"
|
|
99
101
|
class={`${streamdown.theme.footnotePopover.base}`}
|
|
100
102
|
>
|
|
101
103
|
{#each token.content.lines as line}
|
|
@@ -112,7 +114,7 @@
|
|
|
112
114
|
render={streamdown.snippets.footnoteRef}
|
|
113
115
|
>
|
|
114
116
|
<button
|
|
115
|
-
style={streamdown.
|
|
117
|
+
style={streamdown.animationBlockStyle}
|
|
116
118
|
bind:this={reference}
|
|
117
119
|
class={streamdown.theme.footnoteRef.base}
|
|
118
120
|
onclick={() => (isOpen = !isOpen)}
|
|
@@ -31,7 +31,10 @@
|
|
|
31
31
|
}}
|
|
32
32
|
render={streamdown.snippets.image}
|
|
33
33
|
>
|
|
34
|
-
<span
|
|
34
|
+
<span
|
|
35
|
+
style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
36
|
+
class={streamdown.theme.image.base}
|
|
37
|
+
>
|
|
35
38
|
<img class={streamdown.theme.image.image} src={transformedUrl} alt={token.text} />
|
|
36
39
|
</span>
|
|
37
40
|
</Slot>
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
{@html html}
|
|
54
54
|
</span>
|
|
55
55
|
{:else}
|
|
56
|
-
<div class="h-fit w-full">
|
|
56
|
+
<div style={streamdown.isMounted ? streamdown.animationBlockStyle : ''} class="h-fit w-full">
|
|
57
57
|
<div class="overflow-x-auto">
|
|
58
58
|
<div bind:this={inner} class={streamdown.theme.math.block}>
|
|
59
59
|
{@html html}
|
package/dist/theme.d.ts
CHANGED
|
@@ -131,9 +131,6 @@ export declare const theme: {
|
|
|
131
131
|
descriptionList: {
|
|
132
132
|
base: string;
|
|
133
133
|
};
|
|
134
|
-
description: {
|
|
135
|
-
base: string;
|
|
136
|
-
};
|
|
137
134
|
descriptionTerm: {
|
|
138
135
|
base: string;
|
|
139
136
|
};
|
|
@@ -272,9 +269,6 @@ export declare const shadcnTheme: {
|
|
|
272
269
|
descriptionList: {
|
|
273
270
|
base: string;
|
|
274
271
|
};
|
|
275
|
-
description: {
|
|
276
|
-
base: string;
|
|
277
|
-
};
|
|
278
272
|
descriptionTerm: {
|
|
279
273
|
base: string;
|
|
280
274
|
};
|
|
@@ -418,9 +412,6 @@ export declare const mergeTheme: (customTheme?: DeepPartialTheme, baseTheme?: "t
|
|
|
418
412
|
descriptionList: {
|
|
419
413
|
base: string;
|
|
420
414
|
};
|
|
421
|
-
description: {
|
|
422
|
-
base: string;
|
|
423
|
-
};
|
|
424
415
|
descriptionTerm: {
|
|
425
416
|
base: string;
|
|
426
417
|
};
|
package/dist/theme.js
CHANGED
|
@@ -132,11 +132,8 @@ export const theme = {
|
|
|
132
132
|
descriptionList: {
|
|
133
133
|
base: 'my-4 space-y-2'
|
|
134
134
|
},
|
|
135
|
-
description: {
|
|
136
|
-
base: 'border-l-2 border-gray-200 pl-4'
|
|
137
|
-
},
|
|
138
135
|
descriptionTerm: {
|
|
139
|
-
base: 'font-semibold text-gray-900'
|
|
136
|
+
base: 'font-semibold text-gray-900 border-l-2 border-gray-200 pl-4'
|
|
140
137
|
},
|
|
141
138
|
descriptionDetail: {
|
|
142
139
|
base: 'text-gray-700 ml-4 leading-relaxed'
|
|
@@ -273,11 +270,8 @@ export const shadcnTheme = {
|
|
|
273
270
|
descriptionList: {
|
|
274
271
|
base: 'my-4 space-y-2'
|
|
275
272
|
},
|
|
276
|
-
description: {
|
|
277
|
-
base: 'border-l-2 border-border pl-4'
|
|
278
|
-
},
|
|
279
273
|
descriptionTerm: {
|
|
280
|
-
base: 'font-semibold text-foreground'
|
|
274
|
+
base: 'font-semibold text-foreground border-l-2 border-border pl-4'
|
|
281
275
|
},
|
|
282
276
|
descriptionDetail: {
|
|
283
277
|
base: 'text-muted-foreground ml-4 leading-relaxed'
|
|
@@ -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);
|
|
@@ -69,27 +72,17 @@ class HighlighterManager {
|
|
|
69
72
|
return Object.hasOwn(bundledLanguages, language);
|
|
70
73
|
};
|
|
71
74
|
isReady(theme, language = 'bash') {
|
|
72
|
-
return (
|
|
73
|
-
|
|
74
|
-
this.loadedThemes.get(theme) === true
|
|
75
|
+
return (!!this.highlighter &&
|
|
76
|
+
!(this.highlighter instanceof Promise) &&
|
|
77
|
+
this.loadedThemes.get(theme) === true &&
|
|
78
|
+
this.loadedLanguages.get(this.isLanguageSupported(language) ? language : 'bash') === true);
|
|
75
79
|
}
|
|
76
80
|
/**
|
|
77
81
|
* Preloads themes by creating minimal highlighter instances.
|
|
78
82
|
* This reduces flickering when switching themes.
|
|
79
83
|
*/
|
|
80
84
|
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
|
-
}
|
|
85
|
+
await Promise.all(Array.from(this.preloadedThemes).map((theme) => this.loadTheme(theme, highlighter)));
|
|
93
86
|
}
|
|
94
87
|
/**
|
|
95
88
|
* Ensures the highlighter is ready for the given theme and language.
|
|
@@ -101,7 +94,6 @@ class HighlighterManager {
|
|
|
101
94
|
!(this.highlighter instanceof Promise) &&
|
|
102
95
|
themeLoader === true &&
|
|
103
96
|
languageLoader === true) {
|
|
104
|
-
// already loaded
|
|
105
97
|
return;
|
|
106
98
|
}
|
|
107
99
|
const highlighter = await this.loadHighlighter();
|
|
@@ -131,7 +123,7 @@ class HighlighterManager {
|
|
|
131
123
|
return [];
|
|
132
124
|
}
|
|
133
125
|
}
|
|
134
|
-
static create(preloadedThemes) {
|
|
126
|
+
static create(preloadedThemes = []) {
|
|
135
127
|
if (typeof window !== 'undefined' && 'STREAMDOWN_HIGHLIGHTER' in window) {
|
|
136
128
|
const previousHighlighter = window.STREAMDOWN_HIGHLIGHTER;
|
|
137
129
|
preloadedThemes.forEach((theme) => previousHighlighter.preloadedThemes.add(theme));
|
|
@@ -807,6 +807,67 @@ const handleIncompleteFootnotes = (text) => {
|
|
|
807
807
|
}
|
|
808
808
|
return result;
|
|
809
809
|
};
|
|
810
|
+
// Completes incomplete description list items (:term -> :term:)
|
|
811
|
+
// Also processes incomplete inline formatting within the description content
|
|
812
|
+
const handleIncompleteDescriptionList = (text) => {
|
|
813
|
+
const lines = text.split('\n');
|
|
814
|
+
let inFence = false;
|
|
815
|
+
let fenceChar = '';
|
|
816
|
+
let fenceIndent = '';
|
|
817
|
+
for (let i = 0; i < lines.length; i++) {
|
|
818
|
+
const line = lines[i];
|
|
819
|
+
// Check for fence open/close
|
|
820
|
+
const fenceMatch = line.match(/^(\s*)(`{3,}|~{3,})(.*)$/);
|
|
821
|
+
if (fenceMatch) {
|
|
822
|
+
const indent = fenceMatch[1];
|
|
823
|
+
const fence = fenceMatch[2];
|
|
824
|
+
const lang = fenceMatch[3];
|
|
825
|
+
if (!inFence) {
|
|
826
|
+
// Opening fence
|
|
827
|
+
inFence = true;
|
|
828
|
+
fenceChar = fence.substring(0, 1); // Store just the first character
|
|
829
|
+
fenceIndent = indent;
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
else if (fence.startsWith(fenceChar) && indent === fenceIndent) {
|
|
833
|
+
// Closing fence - match the fence character type and indentation
|
|
834
|
+
inFence = false;
|
|
835
|
+
fenceChar = '';
|
|
836
|
+
fenceIndent = '';
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
// Skip processing if inside a fence
|
|
841
|
+
if (inFence) {
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
// Detect description-list lines with /^\s*:[^:]+$/
|
|
845
|
+
const descriptionMatch = line.match(/^(\s*:[^:]+)$/);
|
|
846
|
+
if (descriptionMatch) {
|
|
847
|
+
const fullMatch = descriptionMatch[1];
|
|
848
|
+
const colonIndex = fullMatch.indexOf(':');
|
|
849
|
+
const beforeColon = fullMatch.substring(0, colonIndex + 1);
|
|
850
|
+
const content = fullMatch.substring(colonIndex + 1);
|
|
851
|
+
// Apply formatters in this order
|
|
852
|
+
let processedContent = content;
|
|
853
|
+
processedContent = handleIncompleteBoldItalic(processedContent); // ***
|
|
854
|
+
processedContent = handleIncompleteBold(processedContent); // **
|
|
855
|
+
processedContent = handleIncompleteDoubleUnderscoreItalic(processedContent); // __
|
|
856
|
+
processedContent = handleIncompleteStrikethrough(processedContent); // ~~
|
|
857
|
+
processedContent = handleIncompleteInlineCode(processedContent); // `
|
|
858
|
+
processedContent = handleIncompleteSingleAsteriskItalic(processedContent); // *
|
|
859
|
+
processedContent = handleIncompleteSingleUnderscoreItalic(processedContent); // _
|
|
860
|
+
processedContent = handleIncompleteSub(processedContent); // ~
|
|
861
|
+
processedContent = handleIncompleteSup(processedContent); // ^
|
|
862
|
+
processedContent = handleIncompleteBlockKatex(processedContent);
|
|
863
|
+
processedContent = handleIncompleteInlineMath(processedContent);
|
|
864
|
+
processedContent = handleIncompleteLinksAndImages(processedContent);
|
|
865
|
+
// Set the line to beforeColon + processedContent + ':'
|
|
866
|
+
lines[i] = beforeColon + processedContent + ':';
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return lines.join('\n');
|
|
870
|
+
};
|
|
810
871
|
// Parses markdown text and removes incomplete tokens to prevent partial rendering
|
|
811
872
|
export const parseIncompleteMarkdown = (text) => {
|
|
812
873
|
if (!text || typeof text !== 'string') {
|
|
@@ -818,6 +879,8 @@ export const parseIncompleteMarkdown = (text) => {
|
|
|
818
879
|
result = handleIncompleteCodeBlock(result);
|
|
819
880
|
// Handle incomplete footnotes FIRST (before any other processing to avoid conflicts)
|
|
820
881
|
result = handleIncompleteFootnotes(result);
|
|
882
|
+
// Handle incomplete description lists (block-level elements)
|
|
883
|
+
result = handleIncompleteDescriptionList(result);
|
|
821
884
|
// Handle various formatting completions ONLY if not inside a code block
|
|
822
885
|
// Handle double patterns before single patterns for proper priority
|
|
823
886
|
result = handleIncompleteBoldItalic(result); // ***
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
import { useStreamdown } from './context.svelte.js';
|
|
4
|
-
|
|
5
|
-
let { children }: { children: Snippet } = $props();
|
|
6
|
-
|
|
7
|
-
const streamdown = useStreamdown();
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
{#if streamdown.isMounted}
|
|
11
|
-
<div style={streamdown.animationBlockStyle}>
|
|
12
|
-
{@render children()}
|
|
13
|
-
</div>
|
|
14
|
-
{:else}
|
|
15
|
-
{@render children()}
|
|
16
|
-
{/if}
|