svelte-streamdown 2.2.6 → 2.3.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 +47 -47
- package/dist/AnimatedText.svelte +6 -8
- package/dist/Block.svelte +2 -2
- package/dist/Elements/Alert.svelte +19 -18
- package/dist/Elements/Code.svelte +20 -17
- package/dist/Elements/Image.svelte +22 -20
- package/dist/Elements/Link.svelte +1 -1
- package/dist/Elements/Math.svelte +4 -7
- package/dist/Elements/Mermaid.svelte +76 -68
- package/dist/Streamdown.svelte +4 -0
- package/dist/context.svelte.d.ts +16 -3
- package/dist/context.svelte.js +1 -3
- package/dist/marked/index.d.ts +4 -2
- package/dist/marked/index.js +10 -6
- package/dist/marked/marked-alert.d.ts +1 -1
- package/dist/marked/marked-alert.js +14 -5
- package/dist/marked/marked-br.d.ts +12 -0
- package/dist/marked/marked-br.js +21 -0
- package/dist/marked/marked-footnotes.d.ts +1 -1
- package/dist/marked/marked-footnotes.js +1 -1
- package/dist/marked/marked-hr.d.ts +12 -0
- package/dist/marked/marked-hr.js +24 -0
- package/dist/marked/marked-list.d.ts +2 -1
- package/dist/marked/marked-list.js +9 -3
- package/dist/marked/marked-math.d.ts +3 -2
- package/dist/marked/marked-math.js +41 -21
- package/dist/marked/marked-subsup.d.ts +1 -1
- package/dist/marked/marked-subsup.js +1 -1
- package/dist/marked/marked-table.d.ts +1 -0
- package/dist/marked/marked-table.js +11 -4
- package/dist/theme.d.ts +9 -1
- package/dist/theme.js +2 -0
- package/dist/utils/hightlighter.svelte.d.ts +15 -13
- package/dist/utils/hightlighter.svelte.js +101 -57
- package/dist/utils/parse-incomplete-markdown.js +352 -157
- package/package.json +4 -2
- package/dist/Elements/Table.svelte +0 -30
- package/dist/Elements/Table.svelte.d.ts +0 -9
|
@@ -114,8 +114,11 @@ const processSpans = (cells, count, prevRow = [], maxColspan = null) => {
|
|
|
114
114
|
for (i = 0; i < processedCells.length; i++) {
|
|
115
115
|
const cell = processedCells[i];
|
|
116
116
|
let cellText = cell.text;
|
|
117
|
-
// Handle Rowspan - cells ending with ^
|
|
118
|
-
if (
|
|
117
|
+
// Handle Rowspan - cells ending with ^ (but not superscript ^text^)
|
|
118
|
+
// Check if it's a rowspan indicator (single ^ at end) vs superscript (^text^)
|
|
119
|
+
const isRowspanIndicator = cellText.slice(-1) === '^' &&
|
|
120
|
+
!cellText.match(/\^[^^\n\r]+\^$/); // Not a superscript pattern ^text^
|
|
121
|
+
if (isRowspanIndicator && prevRow.length > 0) {
|
|
119
122
|
// Clean the ^ indicator from the cell text
|
|
120
123
|
cell.text = cellText.slice(0, -1).trim();
|
|
121
124
|
cellText = cell.text;
|
|
@@ -179,7 +182,10 @@ const processSpans = (cells, count, prevRow = [], maxColspan = null) => {
|
|
|
179
182
|
}
|
|
180
183
|
// If no target was found but it's a rowspan cell, clean the ^ indicator
|
|
181
184
|
if (!targetFound && cell.rowspan > 0) {
|
|
182
|
-
|
|
185
|
+
// Only clean if it was actually a rowspan indicator, not superscript
|
|
186
|
+
if (isRowspanIndicator) {
|
|
187
|
+
cell.text = cell.text.slice(0, -1);
|
|
188
|
+
}
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
}
|
|
@@ -485,7 +491,8 @@ export function markedTable(options = {}) {
|
|
|
485
491
|
const item = {
|
|
486
492
|
type: 'table',
|
|
487
493
|
tokens,
|
|
488
|
-
raw: cap[0]
|
|
494
|
+
raw: cap[0],
|
|
495
|
+
align: alignment
|
|
489
496
|
};
|
|
490
497
|
return item;
|
|
491
498
|
}
|
package/dist/theme.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export declare const theme: {
|
|
|
40
40
|
base: string;
|
|
41
41
|
container: string;
|
|
42
42
|
header: string;
|
|
43
|
+
buttons: string;
|
|
43
44
|
button: string;
|
|
44
45
|
language: string;
|
|
45
46
|
skeleton: string;
|
|
@@ -168,6 +169,7 @@ export declare const shadcnTheme: {
|
|
|
168
169
|
base: string;
|
|
169
170
|
container: string;
|
|
170
171
|
header: string;
|
|
172
|
+
buttons: string;
|
|
171
173
|
button: string;
|
|
172
174
|
language: string;
|
|
173
175
|
skeleton: string;
|
|
@@ -257,7 +259,11 @@ export declare const shadcnTheme: {
|
|
|
257
259
|
};
|
|
258
260
|
};
|
|
259
261
|
export type Theme = typeof theme;
|
|
260
|
-
|
|
262
|
+
type DeepPartial<T> = {
|
|
263
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
264
|
+
};
|
|
265
|
+
export type DeepPartialTheme = DeepPartial<Theme>;
|
|
266
|
+
export declare const mergeTheme: (customTheme?: DeepPartialTheme, baseTheme?: "tailwind" | "shadcn") => {
|
|
261
267
|
link: {
|
|
262
268
|
base: string;
|
|
263
269
|
blocked: string;
|
|
@@ -297,6 +303,7 @@ export declare const mergeTheme: (customTheme?: Partial<Theme>, baseTheme?: "tai
|
|
|
297
303
|
base: string;
|
|
298
304
|
container: string;
|
|
299
305
|
header: string;
|
|
306
|
+
buttons: string;
|
|
300
307
|
button: string;
|
|
301
308
|
language: string;
|
|
302
309
|
skeleton: string;
|
|
@@ -385,3 +392,4 @@ export declare const mergeTheme: (customTheme?: Partial<Theme>, baseTheme?: "tai
|
|
|
385
392
|
base: string;
|
|
386
393
|
};
|
|
387
394
|
};
|
|
395
|
+
export {};
|
package/dist/theme.js
CHANGED
|
@@ -41,6 +41,7 @@ export const theme = {
|
|
|
41
41
|
base: 'my-4 w-full overflow-hidden rounded-xl border border-gray-200 flex flex-col',
|
|
42
42
|
container: ' relative overflow-visible bg-gray-100 p-2 font-mono text-sm ',
|
|
43
43
|
header: 'flex items-center justify-between bg-gray-100/80 p-2 text-gray-600 text-xs',
|
|
44
|
+
buttons: 'flex items-center gap-2',
|
|
44
45
|
button: 'cursor-pointer size-6 p-1 text-gray-600 transition-all hover:text-gray-900 rounded hover:bg-gray-100',
|
|
45
46
|
language: 'ml-1 font-mono lowercase',
|
|
46
47
|
skeleton: 'block rounded-md font-mono text-transparent bg-gray-200 scale-y-90 animate-pulse whitespace-nowrap',
|
|
@@ -169,6 +170,7 @@ export const shadcnTheme = {
|
|
|
169
170
|
base: 'my-4 w-full overflow-hidden rounded-lg border border-border flex flex-col',
|
|
170
171
|
container: 'relative overflow-visible bg-muted p-2 font-mono text-sm',
|
|
171
172
|
header: 'flex items-center justify-between bg-muted/80 px-2 py-1 text-muted-foreground text-xs',
|
|
173
|
+
buttons: 'flex items-center gap-2',
|
|
172
174
|
button: 'cursor-pointer size-6 p-1 text-muted-foreground transition-all hover:text-foreground rounded hover:bg-muted',
|
|
173
175
|
language: 'ml-1 font-mono lowercase',
|
|
174
176
|
skeleton: 'block rounded-md font-mono text-transparent bg-border/80 scale-y-90 w-fit animate-pulse whitespace-nowrap',
|
|
@@ -1,29 +1,31 @@
|
|
|
1
|
-
import { type BundledLanguage, type BundledTheme, type ThemedToken } from 'shiki';
|
|
2
|
-
import { SvelteSet } from 'svelte/reactivity';
|
|
1
|
+
import { type BundledLanguage, type BundledTheme, type HighlighterGeneric, type ThemedToken } from 'shiki';
|
|
2
|
+
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
3
3
|
export declare const loadShiki: () => Promise<[any, import("shiki").CreateHighlighterFactory<BundledLanguage, BundledTheme>]>;
|
|
4
|
+
export type Highlighter = HighlighterGeneric<BundledLanguage, BundledTheme>;
|
|
4
5
|
declare class HighlighterManager {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
preloadedThemes: BundledTheme[];
|
|
10
|
-
loadedLanguages: SvelteSet<BundledLanguage>;
|
|
11
|
-
loadedThemes: SvelteSet<BundledTheme>;
|
|
6
|
+
loadedLanguages: SvelteMap<BundledLanguage, boolean | Promise<void>>;
|
|
7
|
+
loadedThemes: SvelteMap<BundledTheme, boolean | Promise<void>>;
|
|
8
|
+
preloadedThemes: SvelteSet<BundledTheme>;
|
|
9
|
+
highlighter: Highlighter | Promise<Highlighter> | null;
|
|
12
10
|
constructor(preloadedThemes: BundledTheme[]);
|
|
13
|
-
|
|
11
|
+
private loadHighlighter;
|
|
12
|
+
private loadTheme;
|
|
13
|
+
private loadLanguage;
|
|
14
|
+
private isLanguageSupported;
|
|
15
|
+
isReady(theme: BundledTheme, language?: string | undefined): boolean;
|
|
14
16
|
/**
|
|
15
17
|
* Preloads themes by creating minimal highlighter instances.
|
|
16
18
|
* This reduces flickering when switching themes.
|
|
17
19
|
*/
|
|
18
|
-
preloadThemes(
|
|
20
|
+
preloadThemes(highlighter: Highlighter): Promise<void>;
|
|
19
21
|
/**
|
|
20
22
|
* Ensures the highlighter is ready for the given theme and language.
|
|
21
23
|
*/
|
|
22
|
-
load(theme: BundledTheme, language
|
|
24
|
+
load(theme: BundledTheme, language?: string | undefined): Promise<void>;
|
|
23
25
|
/**
|
|
24
26
|
* Highlights code synchronously. Must call isReady() first.
|
|
25
27
|
*/
|
|
26
|
-
highlightCode(code: string, language:
|
|
28
|
+
highlightCode(code: string, language: string | undefined, theme: BundledTheme): ThemedToken[][];
|
|
27
29
|
static create(preloadedThemes: BundledTheme[]): HighlighterManager;
|
|
28
30
|
}
|
|
29
31
|
export { HighlighterManager };
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { bundledLanguages } from 'shiki';
|
|
2
|
-
import { untrack } from 'svelte';
|
|
3
2
|
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
4
|
-
const isLanguageSupported = (language) => {
|
|
5
|
-
return Object.hasOwn(bundledLanguages, language);
|
|
6
|
-
};
|
|
7
3
|
export const loadShiki = async () => {
|
|
8
4
|
return Promise.all([
|
|
9
5
|
import('shiki/engine/javascript').then((mod) => mod.createJavaScriptRegexEngine({ forgiving: true })),
|
|
@@ -11,87 +7,135 @@ export const loadShiki = async () => {
|
|
|
11
7
|
]);
|
|
12
8
|
};
|
|
13
9
|
class HighlighterManager {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
preloadedThemes;
|
|
19
|
-
loadedLanguages = new SvelteSet();
|
|
20
|
-
loadedThemes = new SvelteSet();
|
|
10
|
+
loadedLanguages = new SvelteMap();
|
|
11
|
+
loadedThemes = new SvelteMap();
|
|
12
|
+
preloadedThemes = new SvelteSet();
|
|
13
|
+
highlighter = $state(null);
|
|
21
14
|
constructor(preloadedThemes) {
|
|
22
|
-
|
|
15
|
+
preloadedThemes.forEach((theme) => this.preloadedThemes.add(theme));
|
|
23
16
|
if (typeof window !== 'undefined') {
|
|
24
17
|
Object.assign(window, {
|
|
25
18
|
STREAMDOWN_HIGHLIGHTER: this
|
|
26
19
|
});
|
|
27
20
|
}
|
|
28
21
|
}
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
async loadHighlighter() {
|
|
23
|
+
if (this.highlighter instanceof Promise) {
|
|
24
|
+
return this.highlighter;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
this.highlighter = new Promise((resolve, reject) => {
|
|
28
|
+
loadShiki().then(([engine, createHighlighter]) => {
|
|
29
|
+
return createHighlighter({
|
|
30
|
+
themes: [],
|
|
31
|
+
langs: [],
|
|
32
|
+
engine
|
|
33
|
+
}).then((highlighter) => {
|
|
34
|
+
this.highlighter = highlighter;
|
|
35
|
+
resolve(highlighter);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
return this.highlighter;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async loadTheme(theme, highlighter) {
|
|
43
|
+
const themeLoader = this.loadedThemes.get(theme);
|
|
44
|
+
if (themeLoader instanceof Promise) {
|
|
45
|
+
await themeLoader;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const themeLoaderPromise = highlighter.loadTheme(theme).then(() => {
|
|
49
|
+
this.loadedThemes.set(theme, true);
|
|
50
|
+
});
|
|
51
|
+
this.loadedThemes.set(theme, themeLoaderPromise);
|
|
52
|
+
await themeLoaderPromise;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async loadLanguage(language, highlighter) {
|
|
56
|
+
const languageLoader = this.loadedLanguages.get(language);
|
|
57
|
+
if (languageLoader instanceof Promise) {
|
|
58
|
+
await languageLoader;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const languageLoaderPromise = highlighter.loadLanguage(language).then(() => {
|
|
62
|
+
this.loadedLanguages.set(language, true);
|
|
63
|
+
});
|
|
64
|
+
this.loadedLanguages.set(language, languageLoaderPromise);
|
|
65
|
+
await languageLoaderPromise;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
isLanguageSupported = (language) => {
|
|
69
|
+
return Object.hasOwn(bundledLanguages, language);
|
|
70
|
+
};
|
|
71
|
+
isReady(theme, language = 'bash') {
|
|
72
|
+
return (!(this.highlighter instanceof Promise) &&
|
|
73
|
+
this.loadedLanguages.get(this.isLanguageSupported(language) ? language : 'bash') === true &&
|
|
74
|
+
this.loadedThemes.get(theme) === true);
|
|
31
75
|
}
|
|
32
76
|
/**
|
|
33
77
|
* Preloads themes by creating minimal highlighter instances.
|
|
34
78
|
* This reduces flickering when switching themes.
|
|
35
79
|
*/
|
|
36
|
-
async preloadThemes(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
await Promise.all(themes
|
|
41
|
-
.filter((theme) => !this.highlighters.has(`${theme}:${language}`))
|
|
42
|
-
.map(async (theme) => {
|
|
43
|
-
if (!this.createHighlighter || !this.engine) {
|
|
80
|
+
async preloadThemes(highlighter) {
|
|
81
|
+
for (const theme of this.preloadedThemes) {
|
|
82
|
+
const themeLoader = this.loadedThemes.get(theme);
|
|
83
|
+
if (themeLoader === true) {
|
|
44
84
|
return;
|
|
45
85
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
86
|
+
else if (themeLoader instanceof Promise) {
|
|
87
|
+
await themeLoader;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
await this.loadTheme(theme, highlighter);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
53
93
|
}
|
|
54
94
|
/**
|
|
55
95
|
* Ensures the highlighter is ready for the given theme and language.
|
|
56
96
|
*/
|
|
57
|
-
async load(theme, language) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
97
|
+
async load(theme, language = 'bash') {
|
|
98
|
+
const themeLoader = this.loadedThemes.get(theme);
|
|
99
|
+
const languageLoader = this.loadedLanguages.get(this.isLanguageSupported(language) ? language : 'bash');
|
|
100
|
+
if (this.highlighter !== null &&
|
|
101
|
+
!(this.highlighter instanceof Promise) &&
|
|
102
|
+
themeLoader === true &&
|
|
103
|
+
languageLoader === true) {
|
|
104
|
+
// already loaded
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const highlighter = await this.loadHighlighter();
|
|
108
|
+
await Promise.all([
|
|
109
|
+
this.loadTheme(theme, highlighter),
|
|
110
|
+
this.loadLanguage(this.isLanguageSupported(language) ? language : 'bash', highlighter)
|
|
111
|
+
]);
|
|
112
|
+
await this.preloadThemes(highlighter);
|
|
74
113
|
}
|
|
75
114
|
/**
|
|
76
115
|
* Highlights code synchronously. Must call isReady() first.
|
|
77
116
|
*/
|
|
78
|
-
highlightCode(code, language, theme) {
|
|
79
|
-
|
|
80
|
-
|
|
117
|
+
highlightCode(code, language = 'bash', theme) {
|
|
118
|
+
try {
|
|
119
|
+
// const highlighter = this.highlighters.get(`${theme}:${language}`);
|
|
120
|
+
const highlighter = this.highlighter;
|
|
121
|
+
if (!highlighter || highlighter instanceof Promise) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const tokens = highlighter.codeToTokensBase(code, {
|
|
125
|
+
lang: this.isLanguageSupported(language) ? language : 'bash',
|
|
126
|
+
theme
|
|
127
|
+
});
|
|
128
|
+
return tokens;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
81
131
|
return [];
|
|
82
132
|
}
|
|
83
|
-
const tokens = highlighter.codeToTokensBase(code, {
|
|
84
|
-
lang: isLanguageSupported(language) ? language : 'text',
|
|
85
|
-
theme: theme
|
|
86
|
-
});
|
|
87
|
-
return tokens;
|
|
88
133
|
}
|
|
89
134
|
static create(preloadedThemes) {
|
|
90
135
|
if (typeof window !== 'undefined' && 'STREAMDOWN_HIGHLIGHTER' in window) {
|
|
91
136
|
const previousHighlighter = window.STREAMDOWN_HIGHLIGHTER;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return window.STREAMDOWN_HIGHLIGHTER;
|
|
137
|
+
preloadedThemes.forEach((theme) => previousHighlighter.preloadedThemes.add(theme));
|
|
138
|
+
return previousHighlighter;
|
|
95
139
|
}
|
|
96
140
|
return new HighlighterManager(preloadedThemes);
|
|
97
141
|
}
|