svelte-streamdown 2.2.7 → 2.3.1

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.
Files changed (38) hide show
  1. package/README.md +47 -51
  2. package/dist/AnimatedText.svelte +6 -8
  3. package/dist/Block.svelte +2 -2
  4. package/dist/Elements/Alert.svelte +19 -18
  5. package/dist/Elements/Code.svelte +20 -17
  6. package/dist/Elements/Image.svelte +22 -20
  7. package/dist/Elements/Link.svelte +1 -1
  8. package/dist/Elements/Math.svelte +4 -7
  9. package/dist/Elements/Mermaid.svelte +76 -68
  10. package/dist/Streamdown.svelte +4 -10
  11. package/dist/context.svelte.d.ts +17 -4
  12. package/dist/context.svelte.js +2 -1
  13. package/dist/marked/index.d.ts +4 -2
  14. package/dist/marked/index.js +10 -6
  15. package/dist/marked/marked-alert.d.ts +1 -1
  16. package/dist/marked/marked-alert.js +14 -5
  17. package/dist/marked/marked-br.d.ts +12 -0
  18. package/dist/marked/marked-br.js +21 -0
  19. package/dist/marked/marked-footnotes.d.ts +1 -1
  20. package/dist/marked/marked-footnotes.js +1 -1
  21. package/dist/marked/marked-hr.d.ts +12 -0
  22. package/dist/marked/marked-hr.js +24 -0
  23. package/dist/marked/marked-list.d.ts +2 -1
  24. package/dist/marked/marked-list.js +9 -3
  25. package/dist/marked/marked-math.d.ts +7 -21
  26. package/dist/marked/marked-math.js +88 -17
  27. package/dist/marked/marked-subsup.d.ts +1 -1
  28. package/dist/marked/marked-subsup.js +1 -1
  29. package/dist/marked/marked-table.d.ts +1 -0
  30. package/dist/marked/marked-table.js +11 -4
  31. package/dist/theme.d.ts +9 -1
  32. package/dist/theme.js +4 -2
  33. package/dist/utils/hightlighter.svelte.d.ts +15 -13
  34. package/dist/utils/hightlighter.svelte.js +101 -57
  35. package/dist/utils/parse-incomplete-markdown.js +352 -157
  36. package/package.json +4 -2
  37. package/dist/Elements/Table.svelte +0 -30
  38. package/dist/Elements/Table.svelte.d.ts +0 -9
package/README.md CHANGED
@@ -20,7 +20,6 @@ Perfect for AI-powered applications that need to stream and render markdown cont
20
20
 
21
21
  ## ✨ Main Features
22
22
 
23
-
24
23
  ### 🔄 Streaming-Optimized
25
24
 
26
25
  - **Incomplete Markdown Parsing**: Handles unterminated blocks gracefully
@@ -28,12 +27,10 @@ Perfect for AI-powered applications that need to stream and render markdown cont
28
27
  - **Real-time Updates**: Optimized for dynamic content
29
28
  - **Smooth Animations**: Animate tokens and blocks as they are streamed.
30
29
 
31
-
32
30
  ### 🔒 Security Hardening
33
31
 
34
32
  - **Image Origin Control**: Whitelist allowed image sources
35
33
  - **Link Safety**: Control link destinations
36
- - **HTML Sanitization**: Prevent XSS attacks
37
34
 
38
35
  ### 🎯 Fully Customizable Components & Theming
39
36
 
@@ -49,8 +46,9 @@ Beautiful, responsive typography with **built-in Tailwind CSS classes** for head
49
46
 
50
47
  ### 📝 Extensive Markdown Features
51
48
 
52
- Full support for
53
- - Basic text marks: **bold**, *italic*, `code`, ~~Strikethrough~~
49
+ Full support for
50
+
51
+ - Basic text marks: **bold**, _italic_, `code`, ~~Strikethrough~~
54
52
  - ~Subscript~ and ^Superscript^
55
53
  - [Links](https://svelte-streamdown.beynar.workers.dev/)
56
54
  - Headings (H1–H6)
@@ -58,9 +56,10 @@ Full support for
58
56
  - Github alert
59
57
  - Ordered & unordered lists (including roman, alpha, nested)
60
58
  - Task lists ([ ] and [x])
61
- - Code blocks
59
+ - Code blocks
62
60
  - Mermaid diagrams
63
61
  - Math $expressions$
62
+ - Escaping currency symbols ($140)
64
63
  - Complex tables
65
64
  - Footnotes [^1]
66
65
 
@@ -134,24 +133,27 @@ pie title Project Time Allocation
134
133
 
135
134
  | H1 | H2 | H3 |
136
135
  | ------------------------- | --- | --- |
137
- | This cell spans 3 columns |||
136
+ | This cell spans 3 columns | | |
138
137
 
139
138
  | Header 1 | Header 2 | Header 3 |
140
139
  | ------------------------- | -------- | -------- |
141
- | This cell spans 2 columns || Normal
140
+ | This cell spans 2 columns | | Normal |
142
141
  | Normal | Normal | Normal |
143
142
 
144
143
  #### Rowspan
144
+
145
145
  | Header 1 | Header 2 |
146
146
  | --------------- | -------- |
147
147
  | This cell spans | Cell A |
148
148
  | two rows ^ | Cell B |
149
+
149
150
  #### Footer
151
+
150
152
  | Header 1 | Header 2 |
151
153
  | --------------- | -------- |
152
- | Cell B | Cell A |
154
+ | Cell B | Cell A |
153
155
  | --------------- | -------- |
154
- | Footer ||
156
+ | Footer | |
155
157
 
156
158
  #### Column alignment
157
159
 
@@ -161,7 +163,6 @@ pie title Project Time Allocation
161
163
 
162
164
  #### Multiple headers and very complex layout
163
165
 
164
-
165
166
  | Product Category ||| Sales Data Q1-Q4 2024 ||||
166
167
  | Product | Region || Q1 | Q2 | Q3 | Q4 |
167
168
  | Name | Type | Area | Revenue | Revenue | Revenue | Revenue |
@@ -179,8 +180,8 @@ pie title Project Time Allocation
179
180
 
180
181
  #### decimal
181
182
 
182
- 1. First item
183
- 2. Second item
183
+ 1. First item
184
+ 2. Second item
184
185
  3. Third item
185
186
 
186
187
  #### lower-alpha
@@ -211,10 +212,9 @@ III. Third item
211
212
 
212
213
  1. First level (numeric)
213
214
  a. Second level (lowercase alpha)
214
- i. Third level (lowercase roman)
215
- - Fourth level (bullet)
216
- I. Fifth level (uppercase roman)
217
- A. Sixth level (uppercase alpha)
215
+ i. Third level (lowercase roman) - Fourth level (bullet)
216
+ I. Fifth level (uppercase roman)
217
+ A. Sixth level (uppercase alpha)
218
218
 
219
219
  2. Back to the first level
220
220
 
@@ -226,13 +226,11 @@ III. Third item
226
226
  - [ ] Nested uncompleted subtask
227
227
  - [x] Nested completed subtask
228
228
 
229
-
230
229
  ### Alert Support
231
230
 
232
231
  > [!IMPORTANT]
233
232
  > Native support for Github style Alert
234
233
 
235
-
236
234
  ## 🔄 Differences from Original React Version
237
235
 
238
236
  This Svelte port maintains feature parity with the original [Streamdown](https://streamdown.ai/) while adapting to Svelte's patterns:
@@ -252,7 +250,7 @@ This Svelte port maintains feature parity with the original [Streamdown](https:/
252
250
  > [!NOTE]
253
251
  > Streamdown comes with **built-in Tailwind CSS classes** for beautiful default styling. To ensure all styles are included in your build, add the following to your `app.css` or main CSS file:
254
252
  > This setup is primarily necessary if you're using Tailwind CSS v4's new `@source` directive or if you have aggressive purging enabled in older versions. If you're using standard Tailwind CSS v3+ with default purging, Streamdown's styles should be automatically included when the component is imported and used in your application.
255
- >
253
+ >
256
254
  > This ensures that all Streamdown's default styling is included in your Tailwind build process.
257
255
 
258
256
  ```css
@@ -261,8 +259,6 @@ This Svelte port maintains feature parity with the original [Streamdown](https:/
261
259
  @source "../node_modules/svelte-streamdown/**/*";
262
260
  ```
263
261
 
264
-
265
-
266
262
  ## 🎭 Animation System
267
263
 
268
264
  Streamdown includes a sophisticated animation system designed specifically for streaming AI content, providing smooth and engaging visual feedback as text appears on screen.
@@ -277,21 +273,22 @@ The animation system works by:
277
273
 
278
274
  ### Animation Types
279
275
 
280
- Choose from five distinct animation styles:
276
+ Choose from 4 distinct animation styles:
281
277
 
282
278
  #### `fade`
279
+
283
280
  A clean fade-in effect where text smoothly appears from transparent to opaque.
284
281
 
285
282
  #### `blur`
286
- Text starts slightly blurred and comes into focus while fading in, creating a smooth reveal effect.
287
283
 
288
- #### `typewriter`
289
- A typewriter effect where text appears character by character, mimicking the look of someone typing.
284
+ Text starts slightly blurred and comes into focus while fading in, creating a smooth reveal effect.
290
285
 
291
286
  #### `slideUp`
287
+
292
288
  Text slides up from below while fading in, creating a dynamic upward motion.
293
289
 
294
290
  #### `slideDown`
291
+
295
292
  Text slides down from above while fading in, creating a dynamic downward motion.
296
293
 
297
294
  > [!TIP]
@@ -300,7 +297,6 @@ Text slides down from above while fading in, creating a dynamic downward motion.
300
297
  > [!WARNING]
301
298
  > Character-level tokenization (`tokenize: 'char'`) creates significantly more DOM elements than word-level tokenization. Use character tokenization sparingly and only when the typewriter effect is essential for your user experience.
302
299
 
303
-
304
300
  ## 🚀 Quick Start
305
301
 
306
302
  ### Basic Usage
@@ -363,30 +359,30 @@ This heading will use a custom component!`;
363
359
 
364
360
  ## 📋 Props API
365
361
 
366
- | Prop | Type | Default | Description |
367
- | ------------------------- | ----------------------------------------------------- | ---------------- | ---------------------------------------------- |
368
- | `content` | `string` | - | **Required.** The markdown content to render |
369
- | `class` | `string` | - | CSS class names for the wrapper element |
370
- | `parseIncompleteMarkdown` | `boolean` | `true` | Parse and fix incomplete markdown syntax |
371
- | `defaultOrigin` | `string` | - | Default origin for relative URLs |
372
- | `allowedLinkPrefixes` | `string[]` | `['*']` | Allowed URL prefixes for links |
373
- | `allowedImagePrefixes` | `string[]` | `['*']` | Allowed URL prefixes for images |
374
- | `skipHtml` | `boolean` | - | Skip HTML parsing entirely |
375
- | `unwrapDisallowed` | `boolean` | - | Unwrap instead of removing disallowed elements |
376
- | `urlTransform` | `UrlTransform \| null` | - | Custom URL transformation function |
377
- | `theme` | `Partial<Theme>` | - | Custom theme overrides |
378
- | `baseTheme` | `'tailwind' \| 'shadcn'` | `'tailwind'` | Base theme to use before applying overrides |
379
- | `mergeTheme` | `boolean` | `true` | Whether to merge theme with base theme |
380
- | `shikiTheme` | `BundledTheme` | `'github-light'` | Code highlighting theme |
381
- | `mermaidConfig` | `MermaidConfig` | - | Mermaid diagram configuration |
382
- | `katexConfig` | `KatexOptions \| ((inline: boolean) => KatexOptions)` | - | KaTeX math rendering options |
383
- | `animation` | `AnimationConfig` | - | Animation configuration for streaming content |
384
- | `animation.enabled` | `boolean` | `false` | Enable/disable animations |
385
- | `animation.type` | `'fade' \| 'blur' \| 'typewriter' \| 'slideUp' \| 'slideDown'` | `'blur'` | Animation style for text appearance |
386
- | `animation.duration` | `number` | `500` | Animation duration in milliseconds |
387
- | `animation.timingFunction`| `'ease' \| 'ease-in' \| 'ease-out' \| 'ease-in-out' \| 'linear'` | `'ease-in'` | CSS timing function for animations |
388
- | `animation.tokenize` | `'word' \| 'char'` | `'word'` | Tokenization method for text animations |
389
- | `animation.animateOnMount` | `boolean` | `false` | Run the token animation on mount or not, useful if you render the Streamdown component in the same time as the first token is receive from the LLM |
362
+ | Prop | Type | Default | Description |
363
+ | -------------------------- | ---------------------------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
364
+ | `content` | `string` | - | **Required.** The markdown content to render |
365
+ | `class` | `string` | - | CSS class names for the wrapper element |
366
+ | `parseIncompleteMarkdown` | `boolean` | `true` | Parse and fix incomplete markdown syntax |
367
+ | `defaultOrigin` | `string` | - | Default origin for relative URLs |
368
+ | `allowedLinkPrefixes` | `string[]` | `['*']` | Allowed URL prefixes for links |
369
+ | `allowedImagePrefixes` | `string[]` | `['*']` | Allowed URL prefixes for images |
370
+ | `skipHtml` | `boolean` | - | Skip HTML parsing entirely |
371
+ | `unwrapDisallowed` | `boolean` | - | Unwrap instead of removing disallowed elements |
372
+ | `urlTransform` | `UrlTransform \| null` | - | Custom URL transformation function |
373
+ | `theme` | `DeepPartial<Theme>` | - | Custom theme overrides |
374
+ | `baseTheme` | `'tailwind' \| 'shadcn'` | `'tailwind'` | Base theme to use before applying overrides |
375
+ | `mergeTheme` | `boolean` | `true` | Whether to merge theme with base theme |
376
+ | `shikiTheme` | `BundledTheme` | `'github-light'` | Code highlighting theme |
377
+ | `mermaidConfig` | `MermaidConfig` | - | Mermaid diagram configuration |
378
+ | `katexConfig` | `KatexOptions \| ((inline: boolean) => KatexOptions)` | - | KaTeX math rendering options |
379
+ | `animation` | `AnimationConfig` | - | Animation configuration for streaming content |
380
+ | `animation.enabled` | `boolean` | `false` | Enable/disable animations |
381
+ | `animation.type` | `'fade' \| 'blur' \| 'typewriter' \| 'slideUp' \| 'slideDown'` | `'blur'` | Animation style for text appearance |
382
+ | `animation.duration` | `number` | `500` | Animation duration in milliseconds |
383
+ | `animation.timingFunction` | `'ease' \| 'ease-in' \| 'ease-out' \| 'ease-in-out' \| 'linear'` | `'ease-in'` | CSS timing function for animations |
384
+ | `animation.tokenize` | `'word' \| 'char'` | `'word'` | Tokenization method for text animations |
385
+ | `animation.animateOnMount` | `boolean` | `false` | Run the token animation on mount or not, useful if you render the Streamdown component in the same time as the first token is receive from the LLM |
390
386
 
391
387
  ### Custom Component Props
392
388
 
@@ -16,18 +16,16 @@
16
16
 
17
17
  return text.split(splitRegex).filter((token) => token.length > 0);
18
18
  };
19
-
19
+
20
20
  const tokens = $derived(tokenizeNewContent(text));
21
21
  </script>
22
22
 
23
23
  {#if streamdown.isMounted}
24
- <span>
25
- {#each tokens as token}
26
- <span style={streamdown.animationTextStyle}>
27
- {token}
28
- </span>
29
- {/each}
30
- </span>
24
+ {#each tokens as token}
25
+ <span style={streamdown.animationTextStyle}>
26
+ {token}
27
+ </span>
28
+ {/each}
31
29
  {:else}
32
30
  {text}
33
31
  {/if}
package/dist/Block.svelte CHANGED
@@ -13,13 +13,13 @@
13
13
  block: string;
14
14
  insideFootnote?: boolean;
15
15
  } = $props();
16
- const id = $props.id();
16
+
17
17
  const tokens = $derived(lex(parseIncompleteMarkdown(block.trim())));
18
18
  const streamdown = useStreamdown();
19
19
  </script>
20
20
 
21
21
  {#snippet renderChildren(tokens: StreamdownToken[])}
22
- {#each tokens as token, i (`${id}-block-${i}`)}
22
+ {#each tokens as token}
23
23
  {#if token}
24
24
  {@const children = (token as any)?.tokens || []}
25
25
  {@const isTextOnlyNode = children.length === 0}
@@ -27,9 +27,6 @@
27
27
  children: Snippet;
28
28
  token: AlertToken;
29
29
  } = $props();
30
-
31
- const alertType = $derived(token.variant);
32
- const title = $derived(streamdown.translations?.alert?.[alertType] || alertType);
33
30
  </script>
34
31
 
35
32
  <Slot
@@ -39,23 +36,27 @@
39
36
  }}
40
37
  render={streamdown.snippets.alert}
41
38
  >
42
- <div class={`${streamdown.theme.alert.base} ${streamdown.theme.alert[alertType]}`}>
39
+ <div class={`${streamdown.theme.alert.base} ${streamdown.theme.alert[token.variant]}`}>
43
40
  <div data-alert-title class={streamdown.theme.alert.title}>
44
- <svg
45
- xmlns="http://www.w3.org/2000/svg"
46
- width="24"
47
- height="24"
48
- viewBox="0 0 24 24"
49
- fill="none"
50
- stroke-width="2"
51
- stroke-linecap="round"
52
- stroke-linejoin="round"
53
- class={streamdown.theme.alert.icon}
54
- >
55
- {@html icons[alertType]}
56
- </svg>
57
- {title}
41
+ {@render (streamdown.icons?.[token.variant] || icon)()}
42
+ {streamdown.translations?.alert?.[token.variant] || token.variant}
58
43
  </div>
59
44
  {@render children()}
60
45
  </div>
61
46
  </Slot>
47
+
48
+ {#snippet icon()}
49
+ <svg
50
+ xmlns="http://www.w3.org/2000/svg"
51
+ width="24"
52
+ height="24"
53
+ viewBox="0 0 24 24"
54
+ fill="none"
55
+ stroke-width="2"
56
+ stroke-linecap="round"
57
+ stroke-linejoin="round"
58
+ class={streamdown.theme.alert.icon}
59
+ >
60
+ {@html icons[token.variant]}
61
+ </svg>
62
+ {/snippet}
@@ -5,6 +5,7 @@
5
5
  import { HighlighterManager, languageExtensionMap } from '../utils/hightlighter.svelte.js';
6
6
  import type { Tokens } from 'marked';
7
7
  import { type ThemedToken } from 'shiki';
8
+ import { untrack } from 'svelte';
8
9
 
9
10
  const {
10
11
  token
@@ -14,12 +15,9 @@
14
15
 
15
16
  const streamdown = useStreamdown();
16
17
  const highlighter = HighlighterManager.create(streamdown.shikiPreloadThemes || []);
17
- const theme = $derived(streamdown.shikiTheme);
18
- let codeContent = $derived(token.text);
19
- const language = $derived(token.lang || '');
20
18
  const copy = useCopy({
21
19
  get content() {
22
- return codeContent;
20
+ return token.text;
23
21
  }
24
22
  });
25
23
 
@@ -28,48 +26,51 @@
28
26
  const downloadCode = () => {
29
27
  try {
30
28
  const extension =
31
- language && language in languageExtensionMap
32
- ? languageExtensionMap[language as keyof typeof languageExtensionMap]
29
+ token.lang && token.lang in languageExtensionMap
30
+ ? languageExtensionMap[token.lang as keyof typeof languageExtensionMap]
33
31
  : 'txt';
34
32
  const filename = `file.${extension}`;
35
33
  const mimeType = 'text/plain';
36
- save(filename, codeContent, mimeType);
34
+ save(filename, token.text, mimeType);
37
35
  } catch (error) {
38
36
  console.error('Failed to download file:', error);
39
37
  }
40
38
  };
41
39
 
42
40
  $effect(() => {
43
- void highlighter.load(theme, language as any);
41
+ const theme = streamdown.shikiTheme;
42
+ const lang = token.lang;
43
+ untrack(() => {
44
+ void highlighter.load(theme, lang);
45
+ });
44
46
  });
45
47
 
46
48
  const isMounted = streamdown.isMounted;
47
49
  </script>
48
50
 
49
- <div class={streamdown.theme.code.base} data-language={language}>
50
- <div class={streamdown.theme.code.header} data-code-block-header data-language={language}>
51
- <span class={streamdown.theme.code.language}>{language}</span>
51
+ <div class={streamdown.theme.code.base}>
52
+ <div class={streamdown.theme.code.header}>
53
+ <span class={streamdown.theme.code.language}>{token.lang}</span>
52
54
  {#if streamdown.controls.code}
53
- <div class="flex items-center gap-2">
54
- <!-- Download button snippet -->
55
+ <div class={streamdown.theme.code.buttons}>
55
56
  <button
56
57
  class={streamdown.theme.code.button}
57
58
  onclick={downloadCode}
58
59
  title="Download file"
59
60
  type="button"
60
61
  >
61
- {@render downloadIcon()}
62
+ {@render (streamdown.icons?.download || downloadIcon)()}
62
63
  </button>
63
64
 
64
65
  <button class={streamdown.theme.code.button} onclick={copy.copy} type="button">
65
- {@render copyIcon()}
66
+ {@render (streamdown.icons?.copy || copyIcon)()}
66
67
  </button>
67
68
  </div>
68
69
  {/if}
69
70
  </div>
70
71
  <div style="height: fit-content; width: 100%;" class={streamdown.theme.code.container}>
71
- {#if highlighter.isReady(theme, language as any)}
72
- {@const tokens = highlighter.highlightCode(codeContent, language as any, theme)}
72
+ {#if highlighter.isReady(streamdown.shikiTheme, token.lang)}
73
+ {@const tokens = highlighter.highlightCode(token.text, token.lang, streamdown.shikiTheme)}
73
74
  <pre class={streamdown.theme.code.pre}><code>{@render Tokens(tokens)}</code></pre>
74
75
  {:else}
75
76
  <pre class={streamdown.theme.code.pre}><code>{@render Skeleton(token.text.split('\n'))}</code
@@ -93,6 +94,7 @@
93
94
  </span>
94
95
  {/each}
95
96
  {/snippet}
97
+
96
98
  {#snippet Skeleton(lines: string[])}
97
99
  {#each lines as line}
98
100
  <span class={streamdown.theme.code.skeleton}>
@@ -100,6 +102,7 @@
100
102
  </span>
101
103
  {/each}
102
104
  {/snippet}
105
+
103
106
  {#snippet copyIcon()}
104
107
  <svg
105
108
  xmlns="http://www.w3.org/2000/svg"
@@ -20,25 +20,27 @@
20
20
  );
21
21
  </script>
22
22
 
23
- {#if transformedUrl}
24
- <Slot
25
- props={{
26
- src: transformedUrl,
27
- alt: token.text,
28
- children,
29
- token
30
- }}
31
- render={streamdown.snippets.image}
32
- >
33
- <span class={streamdown.theme.image.base}>
34
- <img class={streamdown.theme.image.image} src={transformedUrl} alt={token.text} />
23
+ {#if token.href !== 'streamdown:incomplete-image'}
24
+ {#if transformedUrl}
25
+ <Slot
26
+ props={{
27
+ src: transformedUrl,
28
+ alt: token.text,
29
+ children,
30
+ token
31
+ }}
32
+ render={streamdown.snippets.image}
33
+ >
34
+ <span class={streamdown.theme.image.base}>
35
+ <img class={streamdown.theme.image.image} src={transformedUrl} alt={token.text} />
36
+ </span>
37
+ </Slot>
38
+ {:else}
39
+ <span
40
+ class="inline-block rounded bg-gray-200 px-3 py-1 text-sm text-gray-600 dark:bg-gray-700 dark:text-gray-400"
41
+ title={`Blocked URL: ${token.href}`}
42
+ >
43
+ [Image blocked: {token.text || 'No description'}]
35
44
  </span>
36
- </Slot>
37
- {:else}
38
- <span
39
- class="inline-block rounded bg-gray-200 px-3 py-1 text-sm text-gray-600 dark:bg-gray-700 dark:text-gray-400"
40
- title={`Blocked URL: ${token.href}`}
41
- >
42
- [Image blocked: {token.text || 'No description'}]
43
- </span>
45
+ {/if}
44
46
  {/if}
@@ -20,7 +20,7 @@
20
20
  );
21
21
  </script>
22
22
 
23
- {#if transformedUrl}
23
+ {#if transformedUrl || token.href === 'streamdown:incomplete-link'}
24
24
  <Slot
25
25
  props={{
26
26
  href: transformedUrl,
@@ -5,16 +5,13 @@
5
5
  import type { KatexOptions } from 'katex';
6
6
  import 'katex/dist/katex.min.css';
7
7
 
8
- const streamdown = useStreamdown();
9
-
10
8
  const {
11
9
  token
12
10
  }: {
13
11
  token: MathToken;
14
12
  } = $props();
15
13
 
16
- const isInline = $derived(token.isInline);
17
-
14
+ const streamdown = useStreamdown();
18
15
  let katexInstance = $state<typeof import('katex') | null>(null);
19
16
 
20
17
  onMount(() => {
@@ -30,9 +27,9 @@
30
27
  }
31
28
  const config: KatexOptions = {
32
29
  output: 'html',
33
- displayMode: !isInline,
30
+ displayMode: !token.isInline,
34
31
  ...(typeof streamdown.katexConfig === 'function'
35
- ? streamdown.katexConfig(isInline)
32
+ ? streamdown.katexConfig(token.isInline)
36
33
  : streamdown.katexConfig || {})
37
34
  };
38
35
  const code = token.text;
@@ -47,7 +44,7 @@
47
44
  });
48
45
  </script>
49
46
 
50
- {#if isInline}
47
+ {#if token.isInline}
51
48
  <span
52
49
  style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
53
50
  bind:this={inner}