svelte-streamdown 3.1.2 → 4.0.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 +116 -37
- package/dist/Elements/Code.svelte +12 -50
- package/dist/Streamdown.svelte +12 -15
- package/dist/context.svelte.d.ts +9 -8
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/marked/index.d.ts +12 -0
- package/dist/marked/index.js +120 -35
- package/dist/marked/marked-br.js +6 -0
- package/dist/marked/marked-citations.js +6 -0
- package/dist/marked/marked-footnotes.js +12 -2
- package/dist/marked/marked-math.js +5 -0
- package/dist/marked/marked-subsup.js +8 -0
- package/dist/marked/marked-table.js +15 -12
- package/dist/theme.d.ts +0 -3
- package/dist/theme.js +0 -2
- package/dist/utils/highlightThemes.d.ts +4 -0
- package/dist/utils/highlightThemes.js +14 -0
- package/dist/utils/hightlighter.svelte.d.ts +6 -29
- package/dist/utils/hightlighter.svelte.js +27 -212
- package/package.json +3 -5
- package/dist/utils/bundledLanguages.d.ts +0 -8
- package/dist/utils/bundledLanguages.js +0 -143
|
@@ -1,219 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
'github-dark': githubDark,
|
|
11
|
-
'github-light': githubLight
|
|
12
|
-
};
|
|
13
|
-
class HighlighterManager {
|
|
14
|
-
loadedLanguages = new SvelteMap();
|
|
15
|
-
highlighter = $state(null);
|
|
16
|
-
customLanguages;
|
|
17
|
-
languageLoaders;
|
|
18
|
-
additionalThemes;
|
|
19
|
-
constructor(languages, additionalThemes, additionalLanguages) {
|
|
20
|
-
// Store additional themes (will be loaded with highlighter)
|
|
21
|
-
this.additionalThemes = additionalThemes || {};
|
|
22
|
-
// Merge languages: default + additional
|
|
23
|
-
const allLanguages = additionalLanguages ? [...languages, ...additionalLanguages] : languages;
|
|
24
|
-
this.languageLoaders = new Map();
|
|
25
|
-
allLanguages.forEach((l) => {
|
|
26
|
-
this.languageLoaders.set(l.id, l.import);
|
|
27
|
-
if (l.aliases) {
|
|
28
|
-
l.aliases.forEach((alias) => this.languageLoaders.set(alias, l.import));
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
// Build custom languages set for validation
|
|
32
|
-
if (additionalLanguages) {
|
|
33
|
-
const additionalSet = createLanguageSet(additionalLanguages);
|
|
34
|
-
this.customLanguages = new Set([...supportedLanguages, ...additionalSet]);
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
this.customLanguages = supportedLanguages;
|
|
38
|
-
}
|
|
39
|
-
if (typeof window !== 'undefined') {
|
|
40
|
-
Object.assign(window, {
|
|
41
|
-
STREAMDOWN_HIGHLIGHTER: this
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async loadHighlighter() {
|
|
46
|
-
if (this.highlighter instanceof Promise) {
|
|
47
|
-
return this.highlighter;
|
|
48
|
-
}
|
|
49
|
-
else if (!this.highlighter) {
|
|
50
|
-
this.highlighter = new Promise(async (resolve, reject) => {
|
|
51
|
-
try {
|
|
52
|
-
const engine = createJavaScriptRegexEngine({ forgiving: true });
|
|
53
|
-
// Load default themes + any additional themes immediately
|
|
54
|
-
const allThemes = [
|
|
55
|
-
...Object.values(DEFAULT_THEMES),
|
|
56
|
-
...Object.values(this.additionalThemes)
|
|
57
|
-
];
|
|
58
|
-
const highlighter = await createHighlighterCore({
|
|
59
|
-
themes: allThemes,
|
|
60
|
-
langs: [],
|
|
61
|
-
engine
|
|
62
|
-
});
|
|
63
|
-
this.highlighter = highlighter;
|
|
64
|
-
resolve(highlighter);
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
reject(error);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
return this.highlighter;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
return this.highlighter;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
isThemeAvailable(theme) {
|
|
77
|
-
return theme in DEFAULT_THEMES || theme in this.additionalThemes;
|
|
1
|
+
import { createHighlighter, defaultHighlighter, allLanguages } from '@tanstack/highlight';
|
|
2
|
+
const highlighters = new WeakMap();
|
|
3
|
+
const getHighlighter = (extra) => {
|
|
4
|
+
if (!extra?.length)
|
|
5
|
+
return defaultHighlighter;
|
|
6
|
+
let highlighter = highlighters.get(extra);
|
|
7
|
+
if (!highlighter) {
|
|
8
|
+
highlighter = createHighlighter({ languages: [...allLanguages, ...extra] });
|
|
9
|
+
highlighters.set(extra, highlighter);
|
|
78
10
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.then((langModule) => {
|
|
97
|
-
const langObj = langModule.default || langModule;
|
|
98
|
-
return highlighter.loadLanguage(langObj);
|
|
99
|
-
})
|
|
100
|
-
.then(() => {
|
|
101
|
-
this.loadedLanguages.set(language, true);
|
|
102
|
-
})
|
|
103
|
-
.catch((err) => {
|
|
104
|
-
this.loadedLanguages.set(language, false);
|
|
105
|
-
throw err;
|
|
106
|
-
});
|
|
107
|
-
this.loadedLanguages.set(language, languageLoaderPromise);
|
|
108
|
-
await languageLoaderPromise;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
isLanguageSupported = (language) => {
|
|
112
|
-
return this.customLanguages.has(language);
|
|
113
|
-
};
|
|
114
|
-
isReady(theme, language) {
|
|
115
|
-
// Check if theme is available
|
|
116
|
-
if (!this.isThemeAvailable(theme)) {
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
// For unsupported languages, we don't need to load anything (will use plaintext)
|
|
120
|
-
if (!language || !this.isLanguageSupported(language)) {
|
|
121
|
-
return !!this.highlighter && !(this.highlighter instanceof Promise);
|
|
122
|
-
}
|
|
123
|
-
return (!!this.highlighter &&
|
|
124
|
-
!(this.highlighter instanceof Promise) &&
|
|
125
|
-
this.loadedLanguages.get(language) === true);
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Ensures the highlighter is ready for the given theme and language.
|
|
129
|
-
*/
|
|
130
|
-
async load(theme, language) {
|
|
131
|
-
// For unsupported languages, only need to load highlighter (themes are pre-loaded)
|
|
132
|
-
if (!language || !this.isLanguageSupported(language)) {
|
|
133
|
-
if (this.highlighter !== null && !(this.highlighter instanceof Promise)) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
await this.loadHighlighter();
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
const languageLoader = this.loadedLanguages.get(language);
|
|
140
|
-
if (this.highlighter !== null &&
|
|
141
|
-
!(this.highlighter instanceof Promise) &&
|
|
142
|
-
languageLoader === true) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const highlighter = await this.loadHighlighter();
|
|
146
|
-
await this.loadLanguage(language, highlighter);
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Highlights code synchronously. Must call isReady() first.
|
|
150
|
-
* Returns plaintext tokens for unsupported languages.
|
|
151
|
-
*/
|
|
152
|
-
highlightCode(code, language, theme) {
|
|
153
|
-
try {
|
|
154
|
-
const highlighter = this.highlighter;
|
|
155
|
-
if (!highlighter || highlighter instanceof Promise) {
|
|
156
|
-
// Return plaintext tokens when highlighter is not ready
|
|
157
|
-
return code.split('\n').map((line) => [
|
|
158
|
-
{
|
|
159
|
-
content: line,
|
|
160
|
-
color: undefined,
|
|
161
|
-
bgColor: undefined
|
|
162
|
-
}
|
|
163
|
-
]);
|
|
164
|
-
}
|
|
165
|
-
// For unsupported languages, return plaintext tokens
|
|
166
|
-
if (!language || !this.isLanguageSupported(language)) {
|
|
167
|
-
return code.split('\n').map((line) => [
|
|
168
|
-
{
|
|
169
|
-
content: line,
|
|
170
|
-
color: undefined,
|
|
171
|
-
bgColor: undefined
|
|
172
|
-
}
|
|
173
|
-
]);
|
|
174
|
-
}
|
|
175
|
-
const tokens = highlighter.codeToTokensBase(code, {
|
|
176
|
-
lang: language,
|
|
177
|
-
theme
|
|
178
|
-
});
|
|
179
|
-
return tokens;
|
|
180
|
-
}
|
|
181
|
-
catch (error) {
|
|
182
|
-
// Return plaintext tokens on error
|
|
183
|
-
return code.split('\n').map((line) => [
|
|
184
|
-
{
|
|
185
|
-
content: line,
|
|
186
|
-
color: undefined,
|
|
187
|
-
bgColor: undefined
|
|
188
|
-
}
|
|
189
|
-
]);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
static create(languages = bundledLanguagesInfo, additionalThemes, additionalLanguages) {
|
|
193
|
-
if (typeof window !== 'undefined' && 'STREAMDOWN_HIGHLIGHTER' in window) {
|
|
194
|
-
const previousHighlighter = window.STREAMDOWN_HIGHLIGHTER;
|
|
195
|
-
// Merge additional themes
|
|
196
|
-
if (additionalThemes) {
|
|
197
|
-
Object.assign(previousHighlighter.additionalThemes, additionalThemes);
|
|
198
|
-
}
|
|
199
|
-
// Merge additional languages with existing set
|
|
200
|
-
if (additionalLanguages) {
|
|
201
|
-
additionalLanguages.forEach((lang) => {
|
|
202
|
-
previousHighlighter.languageLoaders.set(lang.id, lang.import);
|
|
203
|
-
if (lang.aliases) {
|
|
204
|
-
lang.aliases.forEach((alias) => previousHighlighter.languageLoaders.set(alias, lang.import));
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
const additionalSet = createLanguageSet(additionalLanguages);
|
|
208
|
-
additionalSet.forEach((lang) => previousHighlighter.customLanguages.add(lang));
|
|
209
|
-
}
|
|
210
|
-
return previousHighlighter;
|
|
11
|
+
return highlighter;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Tokenizes code into lines of tokens. Synchronous and isomorphic: works during SSR.
|
|
15
|
+
* Unknown languages fall back to plaintext.
|
|
16
|
+
*/
|
|
17
|
+
export function highlightLines(code, lang, extra) {
|
|
18
|
+
const tokens = getHighlighter(extra).tokenize(code, { lang }).tokens;
|
|
19
|
+
const lines = [[]];
|
|
20
|
+
for (const token of tokens) {
|
|
21
|
+
const parts = token.value.split('\n');
|
|
22
|
+
for (let i = 0; i < parts.length; i++) {
|
|
23
|
+
if (i > 0)
|
|
24
|
+
lines.push([]);
|
|
25
|
+
// ponytail: empty segments carry no text, so skip them instead of emitting empty spans
|
|
26
|
+
if (parts[i])
|
|
27
|
+
lines[lines.length - 1].push({ ...token, value: parts[i] });
|
|
211
28
|
}
|
|
212
|
-
return new HighlighterManager(languages, additionalThemes, additionalLanguages);
|
|
213
29
|
}
|
|
30
|
+
return lines;
|
|
214
31
|
}
|
|
215
|
-
// Export the class for those who want to create their own instances
|
|
216
|
-
export { HighlighterManager };
|
|
217
32
|
export const languageExtensionMap = {
|
|
218
33
|
'1c': '1c',
|
|
219
34
|
'1c-query': '1cq',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-streamdown",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"packageManager": "pnpm@10.32.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -94,13 +94,11 @@
|
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
96
|
"@floating-ui/dom": "^1.7.4",
|
|
97
|
-
"@
|
|
98
|
-
"@shikijs/themes": "^3.17.1",
|
|
97
|
+
"@tanstack/highlight": "^0.1.0",
|
|
99
98
|
"clsx": "^2.1.1",
|
|
100
99
|
"katex": "^0.16.22",
|
|
101
|
-
"marked": "
|
|
100
|
+
"marked": "18.0.12",
|
|
102
101
|
"mermaid": "^11.15.0",
|
|
103
|
-
"shiki": "^3.13.0",
|
|
104
102
|
"tailwind-merge": "^3.3.1"
|
|
105
103
|
}
|
|
106
104
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export type LanguageInfo = {
|
|
2
|
-
id: string;
|
|
3
|
-
aliases?: string[];
|
|
4
|
-
import: () => Promise<any>;
|
|
5
|
-
};
|
|
6
|
-
export declare const bundledLanguagesInfo: LanguageInfo[];
|
|
7
|
-
export declare function createLanguageSet(languages: LanguageInfo[]): Set<string>;
|
|
8
|
-
export declare const supportedLanguages: Set<string>;
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
export const bundledLanguagesInfo = [
|
|
2
|
-
// Web essentials
|
|
3
|
-
{
|
|
4
|
-
id: 'javascript',
|
|
5
|
-
aliases: ['js'],
|
|
6
|
-
import: () => import('@shikijs/langs/javascript')
|
|
7
|
-
},
|
|
8
|
-
{
|
|
9
|
-
id: 'typescript',
|
|
10
|
-
aliases: ['ts'],
|
|
11
|
-
import: () => import('@shikijs/langs/typescript')
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
id: 'html',
|
|
15
|
-
import: () => import('@shikijs/langs/html')
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
id: 'css',
|
|
19
|
-
import: () => import('@shikijs/langs/css')
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: 'json',
|
|
23
|
-
import: () => import('@shikijs/langs/json')
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
id: 'jsx',
|
|
27
|
-
import: () => import('@shikijs/langs/jsx')
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: 'tsx',
|
|
31
|
-
import: () => import('@shikijs/langs/tsx')
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
id: 'markdown',
|
|
35
|
-
aliases: ['md'],
|
|
36
|
-
import: () => import('@shikijs/langs/markdown')
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: 'yaml',
|
|
40
|
-
aliases: ['yml'],
|
|
41
|
-
import: () => import('@shikijs/langs/yaml')
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
id: 'xml',
|
|
45
|
-
import: () => import('@shikijs/langs/xml')
|
|
46
|
-
},
|
|
47
|
-
// Backend languages
|
|
48
|
-
{
|
|
49
|
-
id: 'python',
|
|
50
|
-
aliases: ['py'],
|
|
51
|
-
import: () => import('@shikijs/langs/python')
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: 'java',
|
|
55
|
-
import: () => import('@shikijs/langs/java')
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
id: 'go',
|
|
59
|
-
import: () => import('@shikijs/langs/go')
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: 'rust',
|
|
63
|
-
aliases: ['rs'],
|
|
64
|
-
import: () => import('@shikijs/langs/rust')
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
id: 'ruby',
|
|
68
|
-
aliases: ['rb'],
|
|
69
|
-
import: () => import('@shikijs/langs/ruby')
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: 'php',
|
|
73
|
-
import: () => import('@shikijs/langs/php')
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: 'c',
|
|
77
|
-
import: () => import('@shikijs/langs/c')
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: 'cpp',
|
|
81
|
-
aliases: ['c++'],
|
|
82
|
-
import: () => import('@shikijs/langs/cpp')
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
id: 'csharp',
|
|
86
|
-
aliases: ['c#', 'cs'],
|
|
87
|
-
import: () => import('@shikijs/langs/csharp')
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: 'sql',
|
|
91
|
-
import: () => import('@shikijs/langs/sql')
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
id: 'swift',
|
|
95
|
-
import: () => import('@shikijs/langs/swift')
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
id: 'kotlin',
|
|
99
|
-
aliases: ['kt', 'kts'],
|
|
100
|
-
import: () => import('@shikijs/langs/kotlin')
|
|
101
|
-
},
|
|
102
|
-
// Config/Shell
|
|
103
|
-
{
|
|
104
|
-
id: 'shellscript',
|
|
105
|
-
aliases: ['bash', 'sh', 'shell', 'zsh'],
|
|
106
|
-
import: () => import('@shikijs/langs/shellscript')
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
id: 'docker',
|
|
110
|
-
aliases: ['dockerfile'],
|
|
111
|
-
import: () => import('@shikijs/langs/docker')
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
id: 'toml',
|
|
115
|
-
import: () => import('@shikijs/langs/toml')
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
id: 'graphql',
|
|
119
|
-
aliases: ['gql'],
|
|
120
|
-
import: () => import('@shikijs/langs/graphql')
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
id: 'svelte',
|
|
124
|
-
import: () => import('@shikijs/langs/svelte')
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
id: 'vue',
|
|
128
|
-
import: () => import('@shikijs/langs/vue')
|
|
129
|
-
}
|
|
130
|
-
];
|
|
131
|
-
// Helper function to create a Set of all language IDs and aliases from language info
|
|
132
|
-
export function createLanguageSet(languages) {
|
|
133
|
-
const set = new Set();
|
|
134
|
-
languages.forEach((lang) => {
|
|
135
|
-
set.add(lang.id);
|
|
136
|
-
if (lang.aliases) {
|
|
137
|
-
lang.aliases.forEach((alias) => set.add(alias));
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
return set;
|
|
141
|
-
}
|
|
142
|
-
// Create a Set of all supported language IDs and aliases for fast lookup (default set)
|
|
143
|
-
export const supportedLanguages = createLanguageSet(bundledLanguagesInfo);
|