svelte-streamdown 1.0.4 → 1.0.5

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.
@@ -1,7 +1,4 @@
1
1
  <script lang="ts">
2
- import { setContext } from 'svelte';
3
- import type { Snippet } from 'svelte';
4
- import type { BundledLanguage, BundledTheme } from 'shiki';
5
2
  import { useStreamdown } from '../Streamdown.svelte';
6
3
  import { save } from '../utils/save.js';
7
4
  import { useCopy } from '../utils/copy.svelte.js';
@@ -11,8 +8,8 @@
11
8
 
12
9
  let { node, className, props }: ElementProps = $props();
13
10
 
14
- const highlighter = HighlighterManager.create();
15
11
  const streamdown = useStreamdown();
12
+ const highlighter = HighlighterManager.create(streamdown.shikiPreloadThemes || []);
16
13
  const theme = $derived(streamdown.shikiTheme);
17
14
  let codeContent = $derived((node.children[0] as any).value);
18
15
  const language = $derived(node.properties.language as string);
@@ -41,8 +38,6 @@
41
38
  $effect(() => {
42
39
  void highlighter.load(theme, language as any);
43
40
  });
44
-
45
- $inspect(highlighter.isReady(theme, language as any));
46
41
  </script>
47
42
 
48
43
  <div
@@ -127,6 +127,9 @@
127
127
  },
128
128
  get customElements() {
129
129
  return customElements;
130
+ },
131
+ get shikiPreloadThemes() {
132
+ return shikiPreloadThemes;
130
133
  }
131
134
  });
132
135
  const id = $props.id();
@@ -7,15 +7,16 @@ declare class HighlighterManager {
7
7
  private highlighters;
8
8
  private createHighlighter;
9
9
  private engine;
10
+ preloadedThemes: BundledTheme[];
10
11
  loadedLanguages: SvelteSet<BundledLanguage>;
11
12
  loadedThemes: SvelteSet<BundledTheme>;
12
- constructor();
13
+ constructor(preloadedThemes: BundledTheme[]);
13
14
  isReady(theme: BundledTheme, language: BundledLanguage): boolean;
14
15
  /**
15
16
  * Preloads themes by creating minimal highlighter instances.
16
17
  * This reduces flickering when switching themes.
17
18
  */
18
- preloadThemes(themes: BundledTheme[]): Promise<void>;
19
+ preloadThemes(themes: BundledTheme[], language: BundledLanguage): Promise<void>;
19
20
  /**
20
21
  * Ensures the highlighter is ready for the given theme and language.
21
22
  */
@@ -24,7 +25,7 @@ declare class HighlighterManager {
24
25
  * Highlights code synchronously. Must call isReady() first.
25
26
  */
26
27
  highlightCode(code: string, language: BundledLanguage, theme: BundledTheme, preClassName?: string): string;
27
- static create(): HighlighterManager;
28
+ static create(preloadedThemes: BundledTheme[]): HighlighterManager;
28
29
  }
29
30
  export { HighlighterManager };
30
31
  export declare const languageExtensionMap: Record<string, string>;
@@ -1,6 +1,7 @@
1
1
  import {} from 'shiki';
2
2
  import { untrack } from 'svelte';
3
3
  import { SvelteMap, SvelteSet } from 'svelte/reactivity';
4
+ import { StreamdownContext, useStreamdown } from './Streamdown.svelte';
4
5
  // Remove background styles from <pre> tags (inline style)
5
6
  const removePreBackground = (html) => {
6
7
  return html.replace(/<pre[^>]*style="[^"]*background[^";]*;?[^"]*"[^>]*>/g, (match) => match.replace(/style="[^"]*background[^";]*;?[^"]*"/, ''));
@@ -17,9 +18,11 @@ class HighlighterManager {
17
18
  highlighters = new SvelteMap();
18
19
  createHighlighter = null;
19
20
  engine = null;
21
+ preloadedThemes;
20
22
  loadedLanguages = new SvelteSet();
21
23
  loadedThemes = new SvelteSet();
22
- constructor() {
24
+ constructor(preloadedThemes) {
25
+ this.preloadedThemes = preloadedThemes;
23
26
  if (typeof window !== 'undefined') {
24
27
  Object.assign(window, {
25
28
  STREAMDOWN_HIGHLIGHTER: this
@@ -33,29 +36,29 @@ class HighlighterManager {
33
36
  * Preloads themes by creating minimal highlighter instances.
34
37
  * This reduces flickering when switching themes.
35
38
  */
36
- async preloadThemes(themes) {
37
- if (!this.highlighter) {
39
+ async preloadThemes(themes, language) {
40
+ if (!themes.length) {
38
41
  return;
39
42
  }
40
- const promises = themes
41
- .filter((theme) => !this.loadedThemes.has(theme))
43
+ await Promise.all(themes
44
+ .filter((theme) => !this.highlighters.has(`${theme}:${language}`))
42
45
  .map(async (theme) => {
43
- try {
44
- this.highlighter.loadTheme(theme);
45
- this.loadedThemes.add(theme);
46
- }
47
- catch (error) {
48
- console.warn(`Failed to preload theme ${theme}:`, error);
46
+ if (!this.createHighlighter || !this.engine) {
47
+ return;
49
48
  }
50
- });
51
- await Promise.all(promises);
49
+ const highlighter = await this.createHighlighter?.({
50
+ themes: [theme],
51
+ langs: [language],
52
+ engine: this.engine
53
+ });
54
+ this.highlighters.set(`${theme}:${language}`, highlighter);
55
+ }));
52
56
  }
53
57
  /**
54
58
  * Ensures the highlighter is ready for the given theme and language.
55
59
  */
56
60
  async load(theme, language) {
57
61
  return untrack(async () => {
58
- console.log('load', theme, language);
59
62
  if (!this.createHighlighter || !this.engine) {
60
63
  const [engine, createHighlighter] = await loadShiki();
61
64
  this.createHighlighter = createHighlighter;
@@ -69,41 +72,16 @@ class HighlighterManager {
69
72
  });
70
73
  this.highlighters.set(`${theme}:${language}`, highlighter);
71
74
  }
72
- // if (!highlighter) {
73
- // highlighter = await this.createHighlighter({
74
- // themes: [theme],
75
- // langs: [language],
76
- // engine: this.engine
77
- // });
78
- // this.highlighter = highlighter;
79
- // this.loadedThemes.add(theme);
80
- // this.loadedLanguages.add(language);
81
- // }
82
- // if (!this.loadedThemes.has(theme)) {
83
- // console.log('loading theme', theme);
84
- // await this.highlighter.loadTheme(theme);
85
- // }
86
- // if (!this.loadedLanguages.has(language)) {
87
- // console.log('loading language', language);
88
- // await this.highlighter.loadLanguage(language);
89
- // }
90
- // this.loadedThemes.add(theme);
91
- // this.loadedLanguages.add(language);
75
+ void this.preloadThemes(this.preloadedThemes || [], language);
92
76
  });
93
77
  }
94
78
  /**
95
79
  * Highlights code synchronously. Must call isReady() first.
96
80
  */
97
81
  highlightCode(code, language, theme, preClassName) {
98
- console.log('highlightCode', theme, language, this.isReady(theme, language));
99
- if (!this.isReady(theme, language)) {
100
- throw new Error(`Highlighter not ready for theme "${theme}" and language "${language}". ` +
101
- `Call await highlighter.isReady(theme, language) first.`);
102
- }
103
82
  const highlighter = this.highlighters.get(`${theme}:${language}`);
104
83
  if (!highlighter) {
105
- throw new Error(`Highlighter not loaded for theme "${theme}" and language "${language}". ` +
106
- `Call await highlighter.load(theme, language) first.`);
84
+ return '';
107
85
  }
108
86
  let html = highlighter.codeToHtml(code, {
109
87
  lang: language,
@@ -116,12 +94,14 @@ class HighlighterManager {
116
94
  }
117
95
  return html;
118
96
  }
119
- static create() {
97
+ static create(preloadedThemes) {
120
98
  if (typeof window !== 'undefined' && 'STREAMDOWN_HIGHLIGHTER' in window) {
99
+ const previousHighlighter = window.STREAMDOWN_HIGHLIGHTER;
100
+ previousHighlighter.preloadedThemes = Array.from(new Set([...previousHighlighter.preloadedThemes, ...preloadedThemes]));
121
101
  // @ts-ignore
122
102
  return window.STREAMDOWN_HIGHLIGHTER;
123
103
  }
124
- return new HighlighterManager();
104
+ return new HighlighterManager(preloadedThemes);
125
105
  }
126
106
  }
127
107
  // Export the class for those who want to create their own instances
@@ -1,26 +1,14 @@
1
1
  import { urlAttributes } from 'html-url-attributes';
2
2
  import remarkParse from 'remark-parse';
3
- import remarkRehype from 'remark-rehype';
3
+ import remarkRehype, {} from 'remark-rehype';
4
4
  import { unified } from 'unified';
5
5
  import { visit } from 'unist-util-visit';
6
- import { VFile } from 'vfile';
7
6
  import remarkGfm from 'remark-gfm';
8
7
  import remarkMath from 'remark-math';
9
8
  import { rehypeGithubAlerts } from './alerts.js';
10
- import { squeezeParagraphs } from 'mdast-squeeze-paragraphs';
11
9
  const emptyPlugins = [];
12
10
  const emptyRemarkRehypeOptions = { allowDangerousHtml: true };
13
11
  const safeProtocol = /^(https?|ircs?|mailto|xmpp)$/i;
14
- function createFile(content) {
15
- const file = new VFile();
16
- if (typeof content === 'string') {
17
- file.value = content;
18
- }
19
- else {
20
- throw new Error('Unexpected value `' + content + '` for content, expected `string`');
21
- }
22
- return file;
23
- }
24
12
  export function parseMarkdown(context, content) {
25
13
  const rehypePlugins = context.rehypePlugins || emptyPlugins;
26
14
  const remarkPlugins = context.remarkPlugins || emptyPlugins;
@@ -35,9 +23,8 @@ export function parseMarkdown(context, content) {
35
23
  .use(remarkRehype, remarkRehypeOptions)
36
24
  .use(rehypePlugins)
37
25
  .use([rehypeGithubAlerts]);
38
- const file = createFile(content);
39
- const tree = processor.parse(file);
40
- return post(processor.runSync(tree, file), {
26
+ const tree = processor.runSync(processor.parse(content));
27
+ return post(tree, {
41
28
  allowElement: context.allowElement,
42
29
  allowedElements: context.allowedElements,
43
30
  disallowedElements: context.disallowedElements,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-streamdown",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -78,6 +78,7 @@
78
78
  "mdast": "^3.0.0",
79
79
  "mdast-squeeze-paragraphs": "^6.0.0",
80
80
  "mermaid": "^11.11.0",
81
+ "micromark": "^4.0.2",
81
82
  "rehype-github-alerts": "^4.1.1",
82
83
  "rehype-katex": "^7.0.1",
83
84
  "remark-gfm": "^4.0.1",
@@ -87,7 +88,6 @@
87
88
  "shiki": "^3.12.2",
88
89
  "tailwind-merge": "^3.3.1",
89
90
  "unified": "^11.0.5",
90
- "unist-util-visit": "^5.0.0",
91
- "vfile": "^6.0.3"
91
+ "unist-util-visit": "^5.0.0"
92
92
  }
93
93
  }