svelte-streamdown 1.0.9 → 2.0.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 (37) hide show
  1. package/README.md +160 -143
  2. package/dist/Block.svelte +12 -72
  3. package/dist/Elements/Alert.svelte +1 -2
  4. package/dist/Elements/Code.svelte +7 -7
  5. package/dist/Elements/Code.svelte.d.ts +0 -2
  6. package/dist/Elements/Element.svelte +70 -25
  7. package/dist/Elements/FootnoteRef.svelte +19 -6
  8. package/dist/Elements/Image.svelte +1 -1
  9. package/dist/Elements/Link.svelte +1 -1
  10. package/dist/Elements/Math.svelte +13 -25
  11. package/dist/Elements/Math.svelte.d.ts +0 -2
  12. package/dist/Elements/Mermaid.svelte +105 -115
  13. package/dist/Elements/Mermaid.svelte.d.ts +0 -2
  14. package/dist/Elements/Table.svelte +1 -1
  15. package/dist/Streamdown.d.ts +52 -10
  16. package/dist/Streamdown.js +19 -1
  17. package/dist/Streamdown.svelte +3 -41
  18. package/dist/Streamdown.svelte.d.ts +1 -20
  19. package/dist/marked/index.d.ts +3 -20
  20. package/dist/marked/index.js +24 -19
  21. package/dist/marked/marked-alert.d.ts +2 -2
  22. package/dist/marked/marked-alert.js +7 -21
  23. package/dist/marked/marked-footnotes.js +2 -3
  24. package/dist/marked/marked-list.d.ts +2 -2
  25. package/dist/marked/marked-list.js +28 -11
  26. package/dist/marked/marked-math.js +1 -1
  27. package/dist/marked/marked-subsup.d.ts +1 -1
  28. package/dist/marked/marked-subsup.js +2 -1
  29. package/dist/marked/marked-table.d.ts +66 -0
  30. package/dist/marked/marked-table.js +495 -0
  31. package/dist/theme.d.ts +24 -6
  32. package/dist/theme.js +26 -14
  33. package/dist/utils/panzoom.svelte.d.ts +17 -18
  34. package/dist/utils/panzoom.svelte.js +0 -2
  35. package/dist/utils/useClickOutside.svelte.js +0 -1
  36. package/dist/utils/useKeyDown.svelte.js +4 -1
  37. package/package.json +1 -2
package/README.md CHANGED
@@ -20,19 +20,46 @@ Perfect for AI-powered applications that need to stream and render markdown cont
20
20
 
21
21
  ## ✨ Main Features
22
22
 
23
- ### 🎨 Built-in Typography Styles
24
23
 
25
- Beautiful, responsive typography with **built-in Tailwind CSS classes** for headings, lists, code blocks, and more. Comes with a complete default theme that works out of the box.
24
+ ### 🔄 Streaming-Optimized
26
25
 
27
- ### 📝 GitHub Flavored Markdown
26
+ - **Incomplete Markdown Parsing**: Handles unterminated blocks gracefully
27
+ - **Progressive Rendering**: Perfect for streaming AI responses
28
+ - **Real-time Updates**: Optimized for dynamic content
28
29
 
29
- Full support for GitHub Flavored Markdown including:
30
+ ### 🔒 Security Hardening
30
31
 
31
- - Task lists
32
- - Tables
33
- - ~~Strikethrough~~
34
- - Subscript (H~2~O)
35
- - Superscript (E = mc^2^)
32
+ - **Image Origin Control**: Whitelist allowed image sources
33
+ - **Link Safety**: Control link destinations
34
+ - **HTML Sanitization**: Prevent XSS attacks
35
+
36
+ ### 🎯 Fully Customizable Components & Theming
37
+
38
+ - **Every component customizable** with Svelte snippets
39
+ - **Granular theming system** - customize every part of every component
40
+ - Override default styling and behavior for any markdown element
41
+ - Full control over rendering with type-safe props
42
+ - Seamless integration with your design system
43
+
44
+ ### 🎨 Built-in Typography Styles
45
+
46
+ Beautiful, responsive typography with **built-in Tailwind CSS classes** for headings, lists, code blocks, and more. Comes with a complete default theme that works out of the box.
47
+
48
+ ### 📝 Extensive Markdown Features
49
+
50
+ Full support for
51
+ - Basic text marks: **bold**, *italic*, `code`, ~~Strikethrough~~
52
+ - ~Subscript~ and ^Superscript^
53
+ - [Links](https://svelte-streamdown.beynar.workers.dev/)
54
+ - Headings (H1–H6)
55
+ - Blockquotes
56
+ - Github alert
57
+ - Ordered & unordered lists (including roman, alpha, nested)
58
+ - Task lists ([ ] and [x])
59
+ - Code blocks
60
+ - Mermaid diagrams
61
+ - Math $expressions$
62
+ - Complex tables
36
63
  - Footnotes [^1]
37
64
 
38
65
  [^1]:
@@ -99,49 +126,131 @@ pie title Project Time Allocation
99
126
  "Meetings" : 15
100
127
  ```
101
128
 
102
- ### Alert Support
129
+ ### Complex table support
103
130
 
104
- > [!IMPORTANT]
105
- > Native support for Github style Alert
131
+ #### Colspan
106
132
 
107
- ### 🔄 Streaming-Optimized
133
+ | H1 | H2 | H3 |
134
+ | ------------------------- | --- | --- |
135
+ | This cell spans 3 columns |||
108
136
 
109
- - **Incomplete Markdown Parsing**: Handles unterminated blocks gracefully
110
- - **Progressive Rendering**: Perfect for streaming AI responses
111
- - **Real-time Updates**: Optimized for dynamic content
137
+ | Header 1 | Header 2 | Header 3 |
138
+ | ------------------------- | -------- | -------- |
139
+ | This cell spans 2 columns || Normal
140
+ | Normal | Normal | Normal |
112
141
 
113
- ### 🔒 Security Hardening
142
+ #### Rowspan
143
+ | Header 1 | Header 2 |
144
+ | --------------- | -------- |
145
+ | This cell spans | Cell A |
146
+ | two rows ^ | Cell B |
147
+ #### Footer
148
+ | Header 1 | Header 2 |
149
+ | --------------- | -------- |
150
+ | Cell B | Cell A |
151
+ | --------------- | -------- |
152
+ | Footer ||
114
153
 
115
- - **Image Origin Control**: Whitelist allowed image sources
116
- - **Link Safety**: Control link destinations
117
- - **HTML Sanitization**: Prevent XSS attacks
154
+ #### Column alignment
118
155
 
119
- ### 🎯 Fully Customizable Components & Theming
156
+ | Left | Center | Right |
157
+ | :--- | :----: | ----: |
158
+ | A | B | C |
159
+
160
+ #### Multiple headers and very complex layout
161
+
162
+
163
+ | Product Category ||| Sales Data Q1-Q4 2024 ||||
164
+ | Product | Region || Q1 | Q2 | Q3 | Q4 |
165
+ | Name | Type | Area | Revenue | Revenue | Revenue | Revenue |
166
+ |-------------|---------|------------|---------|---------|---------|---------|
167
+ | Laptop Pro | Electronics | North America || $45,000 | $52,000 | $48,000 |
168
+ | Laptop Pro ^ | ^ | Europe | $32,000 | $38,000 | $41,000 | $44,000 |
169
+ | Laptop Pro ^ | ^ | Asia || $28,000 | $35,000 | $42,000 |
170
+ | Office Chair | Furniture | North America | $15,000 | $18,000 | $16,000 | $17,000 |
171
+ | Office Chair ^ | ^ | Europe | $12,000 | $14,000 | $15,000 | $16,000 |
172
+ | Wireless Mouse | Electronics | Global ||| $25,000 | $28,000 |
173
+ |-------------|---------|------------|---------|---------|---------|---------|
174
+ | **Total Revenue** ||| **$152,000** | **$185,000** | **$187,000** | **$205,000** |
175
+
176
+ ### Complex list support
177
+
178
+ #### decimal
179
+
180
+ 1. First item
181
+ 2. Second item
182
+ 3. Third item
183
+
184
+ #### lower-alpha
185
+
186
+ a. First item
187
+ b. Second item
188
+ c. Third item
189
+
190
+ #### upper-alpha
191
+
192
+ A. First item
193
+ B. Second item
194
+ C. Third item
195
+
196
+ #### lower-roman
197
+
198
+ i. First item
199
+ ii. Second item
200
+ iii. Third item
201
+
202
+ #### upper-roman
203
+
204
+ I. First item
205
+ II. Second item
206
+ III. Third item
207
+
208
+ #### Nested Lists
209
+
210
+ 1. First level (numeric)
211
+ a. Second level (lowercase alpha)
212
+ i. Third level (lowercase roman)
213
+ - Fourth level (bullet)
214
+ I. Fifth level (uppercase roman)
215
+ A. Sixth level (uppercase alpha)
216
+
217
+ 2. Back to the first level
218
+
219
+ #### Task List
220
+
221
+ - [ ] Uncompleted task
222
+ - [x] Completed task
223
+ - [ ] Another uncompleted task
224
+ - [ ] Nested uncompleted subtask
225
+ - [x] Nested completed subtask
226
+
227
+
228
+ ### Alert Support
229
+
230
+ > [!IMPORTANT]
231
+ > Native support for Github style Alert
120
232
 
121
- - **Every component customizable** with Svelte snippets
122
- - **Granular theming system** - customize every part of every component
123
- - Override default styling and behavior for any markdown element
124
- - Full control over rendering with type-safe props
125
- - Seamless integration with your design system
126
233
 
127
234
  ## 🔄 Differences from Original React Version
128
235
 
129
236
  This Svelte port maintains feature parity with the original [Streamdown](https://streamdown.ai/) while adapting to Svelte's patterns:
130
237
 
131
- | Aspect | Original (React) | Svelte Port |
132
- | ----------------- | ---------------- | ------------------------- |
133
- | **Framework** | React | Svelte 5 |
134
- | **Component API** | JSX Components | Svelte Snippets |
135
- | **Styling** | Tailwind CSS | Tailwind CSS (compatible) |
136
- | **Context** | React Context | Svelte Context |
137
- | **Build System** | Vite/React | Vite/SvelteKit |
138
- | **TypeScript** | Full TS support | Full TS support |
238
+ | Aspect | Original (React) | Svelte Port |
239
+ | ----------------- | ------------------------ | ------------------------- |
240
+ | **Framework** | React | Svelte 5 |
241
+ | **Component API** | JSX Components | Svelte Snippets |
242
+ | **Styling** | Tailwind CSS | Tailwind CSS (compatible) |
243
+ | **Context** | React Context | Svelte Context |
244
+ | **Build System** | Vite/React | Vite/SvelteKit |
245
+ | **TypeScript** | Full TS support | Full TS support |
246
+ | **Engine** | Remark / Rehype + marked | marked only |
139
247
 
140
248
  ### Tailwind CSS Setup
141
249
 
142
250
  > [!NOTE]
143
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:
144
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.
253
+ >
145
254
  > This ensures that all Streamdown's default styling is included in your Tailwind build process.
146
255
 
147
256
  ```css
@@ -184,13 +293,13 @@ This heading will use a custom component!`;
184
293
  // Custom heading component
185
294
  </script>
186
295
 
187
- {#snippet customH1({ children, props })}
188
- <h1 class="mb-4 text-4xl font-bold text-blue-600" {...props}>
189
- {children}
296
+ {#snippet customHeading({ children, token })}
297
+ <h1 class="mb-4 text-4xl font-bold text-blue-600" {...token.props}>
298
+ {@render children()}
190
299
  </h1>
191
300
  {/snippet}
192
301
 
193
- <Streamdown {content} h1={customH1} />
302
+ <Streamdown {content} heading={customHeading} />
194
303
  ```
195
304
 
196
305
  ### Security Configuration
@@ -220,9 +329,6 @@ This heading will use a custom component!`;
220
329
  | `defaultOrigin` | `string` | - | Default origin for relative URLs |
221
330
  | `allowedLinkPrefixes` | `string[]` | `['*']` | Allowed URL prefixes for links |
222
331
  | `allowedImagePrefixes` | `string[]` | `['*']` | Allowed URL prefixes for images |
223
- | `allowElement` | `AllowElement \| null` | - | Custom element filtering function |
224
- | `allowedElements` | `readonly string[] \| null` | - | Whitelist of allowed HTML elements |
225
- | `disallowedElements` | `readonly string[] \| null` | - | Blacklist of disallowed HTML elements |
226
332
  | `skipHtml` | `boolean` | - | Skip HTML parsing entirely |
227
333
  | `unwrapDisallowed` | `boolean` | - | Unwrap instead of removing disallowed elements |
228
334
  | `urlTransform` | `UrlTransform \| null` | - | Custom URL transformation function |
@@ -232,14 +338,13 @@ This heading will use a custom component!`;
232
338
  | `shikiTheme` | `BundledTheme` | `'github-light'` | Code highlighting theme |
233
339
  | `mermaidConfig` | `MermaidConfig` | - | Mermaid diagram configuration |
234
340
  | `katexConfig` | `KatexOptions \| ((inline: boolean) => KatexOptions)` | - | KaTeX math rendering options |
235
- | `remarkPlugins` | `PluggableList` | - | Additional remark plugins |
236
- | `rehypePlugins` | `PluggableList` | - | Additional rehype plugins |
237
- | `remarkRehypeOptions` | `RemarkRehypeOptions` | - | Remark-rehype conversion options |
238
- | `customElements` | `Record<string, Snippet<[ElementProps]>>` | - | Custom snippets for not handled nodes |
239
341
 
240
342
  ### Custom Component Props
241
343
 
242
- **Every single markdown element** can be customized with Svelte snippets, giving you complete control over styling and behavior:
344
+ **Every single markdown element** can be customized with Svelte snippets, giving you
345
+ complete control over styling and behavior:
346
+
347
+ Each snippet receives `{ children, token }` where `token` is a typed token object containing the parsed markdown token with its properties and children is a snippet to be rendered.
243
348
 
244
349
  ```svelte
245
350
  <script>
@@ -250,36 +355,36 @@ This heading will use a custom component!`;
250
355
  This heading uses a custom component with your design system!`;
251
356
  </script>
252
357
 
253
- {#snippet customH1({ children, ...props })}
358
+ {#snippet customHeading({ children, token })}
254
359
  <h1
255
360
  class="text-gradient mb-6 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-4xl font-bold text-transparent"
256
- {...props}
361
+ {...token.props}
257
362
  >
258
363
  {@render children()}
259
364
  </h1>
260
365
  {/snippet}
261
366
 
262
- {#snippet customCode({ children, ...props })}
263
- <code class="rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800" {...props}>
367
+ {#snippet customCode({ children, token })}
368
+ <code class="rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800" {...token.props}>
264
369
  {@render children()}
265
370
  </code>
266
371
  {/snippet}
267
372
 
268
- {#snippet customBlockquote({ children, ...props })}
373
+ {#snippet customBlockquote({ children, token })}
269
374
  <blockquote
270
375
  class="border-l-4 border-blue-500 pl-4 text-gray-600 italic dark:text-gray-300"
271
- {...props}
376
+ {...token.props}
272
377
  >
273
378
  {@render children()}
274
379
  </blockquote>
275
380
  {/snippet}
276
381
 
277
- <Streamdown {content} h1={customH1} code={customCode} blockquote={customBlockquote} />
382
+ <Streamdown {content} heading={customHeading} code={customCode} blockquote={customBlockquote} />
278
383
  ```
279
384
 
280
385
  #### All Available Customizable Elements:
281
386
 
282
- **Text Elements**: `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `p`, `strong`, `em`, `del`
387
+ **Text Elements**: `heading`, `p`, `strong`, `em`, `del`
283
388
 
284
389
  **Links & Media**: `a`, `img`
285
390
 
@@ -291,95 +396,7 @@ This heading uses a custom component with your design system!`;
291
396
 
292
397
  **Special Content**: `blockquote`, `hr`, `alert`, `mermaid`, `math`, `inlineMath`
293
398
 
294
- **Note**: The above elements are **supported by Streamdown** and should be customized using individual props or the theme system. Use `customElements` only for HTML elements **not in this list** (like `div`, `span`, `section`, `article`, etc.).
295
-
296
- Each snippet receives `{ children, ...props }` where `props` contains all element attributes and classes.
297
-
298
- ### Using `customElements` Record
299
-
300
- The `customElements` prop is specifically for HTML elements that are **not handled by the library by default**. For elements already supported by Streamdown (like `h1`, `p`, `code`, etc.), use individual props or the theme system instead.
301
-
302
- ```svelte
303
- <script>
304
- import { Streamdown } from 'svelte-streamdown';
305
-
306
- let content = `# Custom Elements Example
307
-
308
- This content contains HTML elements not handled by Streamdown by default:
309
-
310
- <div class="special">This is a custom div element</div>
311
-
312
- <span class="highlight">This is a custom span element</span>
313
-
314
- <section class="wrapper">
315
- <article>This is a custom article inside a section</article>
316
- </section>`;
317
-
318
- // Define custom components for unsupported HTML elements
319
- </script>
320
-
321
- {#snippet customDiv({ children, props, className, node })}
322
- <div class="rounded-lg border-2 border-blue-500 p-4 {className}" {...props}>
323
- {@render children()}
324
- </div>
325
- {/snippet}
326
-
327
- {#snippet customSpan({ children, props, className, node })}
328
- <span class="rounded bg-yellow-200 px-2 py-1 {className}" {...props}>
329
- {@render children()}
330
- </span>
331
- {/snippet}
332
-
333
- {#snippet customSection({ children, props, className, node })}
334
- <section class="my-8 rounded-xl bg-gray-50 p-6 {className}" {...props}>
335
- {@render children()}
336
- </section>
337
- {/snippet}
338
-
339
- {#snippet customArticle({ children, props, className, node })}
340
- <article class="prose max-w-none {className}" {...props}>
341
- {@render children()}
342
- </article>
343
- {/snippet}
344
-
345
- <Streamdown
346
- {content}
347
- customElements={{
348
- div: customDiv,
349
- span: customSpan,
350
- section: customSection,
351
- article: customArticle
352
- }}
353
- />
354
- ```
355
-
356
- ### Benefits of `customElements`
357
-
358
- - **Handle Unsupported Elements**: Define components for HTML elements not built into Streamdown
359
- - **Semantic HTML Support**: Use elements like `<section>`, `<article>`, `<aside>`, `<nav>`, etc.
360
- - **Fallback Support**: Automatically handles unknown elements that would otherwise be ignored
361
- - **Type Safety**: Full TypeScript support with `ElementProps` interface
362
-
363
- ### `customElements` vs Individual Props
364
-
365
- Use the right approach for the right elements:
366
-
367
- **Individual Props** (for supported Streamdown elements):
368
-
369
- ```svelte
370
- <Streamdown {content} h1={customH1} p={customP} code={customCode} />
371
- ```
372
-
373
- **CustomElements Record** (for unsupported HTML elements):
374
-
375
- ```svelte
376
- <Streamdown
377
- {content}
378
- customElements={{ div: customDiv, span: customSpan, section: customSection }}
379
- />
380
- ```
381
-
382
- **Note**: For supported elements (h1, p, code, etc.), use individual props or the theme system. For unsupported elements (div, span, section, etc.), use `customElements`.
399
+ **Note**: The above elements are **supported by Streamdown** and should be customized using individual props or the theme system.
383
400
 
384
401
  ## 🎨 Advanced Theming System
385
402
 
package/dist/Block.svelte CHANGED
@@ -4,7 +4,7 @@
4
4
  <script lang="ts">
5
5
  import { parseIncompleteMarkdown } from './utils/parse-incomplete-markdown.js';
6
6
  import Element from './Elements/Element.svelte';
7
- import { lex, type StreamdownToken, type TableRow } from './marked/index.js';
7
+ import { lex, type StreamdownToken } from './marked/index.js';
8
8
 
9
9
  let {
10
10
  block
@@ -13,81 +13,21 @@
13
13
  } = $props();
14
14
 
15
15
  const tokens = $derived(lex(parseIncompleteMarkdown(block.trim())));
16
-
17
- const getChildren = (token: StreamdownToken) => {
18
- if (token.type === 'list') {
19
- return token.items as StreamdownToken[];
20
- }
21
-
22
- if (token.type === 'table') {
23
- const headerRow = token.header?.length
24
- ? ({
25
- type: 'tableRow',
26
- raw: '',
27
- tokens: token.header.map((cell) => ({
28
- type: 'th',
29
- raw: '',
30
- ...cell
31
- })),
32
- isHeader: true
33
- } satisfies TableRow)
34
- : null;
35
-
36
- const rows = token.rows.reduce(
37
- (acc, cells) => {
38
- const row = {
39
- type: 'tableRow',
40
- raw: '',
41
- tokens: cells.map((cell) => ({
42
- type: 'td',
43
- raw: '',
44
- ...cell
45
- })),
46
- isHeader: false
47
- } satisfies TableRow;
48
- acc.push(row);
49
- return acc;
50
- },
51
- headerRow
52
- ? ([
53
- {
54
- type: 'tableHead',
55
- tokens: [headerRow]
56
- }
57
- ] as StreamdownToken[])
58
- : []
59
- );
60
-
61
- return rows;
62
- }
63
- const children =
64
- 'tokens' in token ? ((token.tokens || []) as StreamdownToken[]) : ([] as StreamdownToken[]);
65
-
66
- if (!children.length && token.type !== 'text') {
67
- return [
68
- {
69
- type: 'text',
70
- text: 'text' in token ? token.text : '',
71
- raw: 'raw' in token ? token.raw : '',
72
- tokens: []
73
- }
74
- ] satisfies StreamdownToken[];
75
- }
76
- return children;
77
- };
78
16
  </script>
79
17
 
80
18
  {#snippet renderChildren(tokens: StreamdownToken[])}
81
19
  {#each tokens as token}
82
- {@const children = getChildren(token)}
83
- {@const isTextOnlyNode = children.length === 0}
84
- <Element {token}>
85
- {#if isTextOnlyNode}
86
- {'text' in token ? token.text : ''}
87
- {:else}
88
- {@render renderChildren(children)}
89
- {/if}
90
- </Element>
20
+ {#if token}
21
+ {@const children = (token as any)?.tokens || []}
22
+ {@const isTextOnlyNode = children.length === 0}
23
+ <Element {token}>
24
+ {#if isTextOnlyNode}
25
+ {'text' in token ? token.text : ''}
26
+ {:else}
27
+ {@render renderChildren(children)}
28
+ {/if}
29
+ </Element>
30
+ {/if}
91
31
  {/each}
92
32
  {/snippet}
93
33
 
@@ -13,7 +13,7 @@
13
13
  /><path d="M12 17h.01" />`
14
14
  };
15
15
 
16
- import { useStreamdown } from '../Streamdown.svelte';
16
+ import { useStreamdown } from '../Streamdown.js';
17
17
  import Slot from './Slot.svelte';
18
18
  import type { AlertToken } from '../marked/index.js';
19
19
  import type { Snippet } from 'svelte';
@@ -35,7 +35,6 @@
35
35
  <Slot
36
36
  props={{
37
37
  children,
38
- alertType,
39
38
  token
40
39
  }}
41
40
  render={streamdown.snippets.alert}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { useStreamdown } from '../Streamdown.svelte';
2
+ import { useStreamdown } from '../Streamdown.js';
3
3
  import { save } from '../utils/save.js';
4
4
  import { useCopy } from '../utils/copy.svelte.js';
5
5
  import { HighlighterManager, languageExtensionMap } from '../utils/hightlighter.svelte.js';
@@ -7,11 +7,9 @@
7
7
  import type { Snippet } from 'svelte';
8
8
 
9
9
  const {
10
- token,
11
- children
10
+ token
12
11
  }: {
13
12
  token: Tokens.Code;
14
- children: Snippet;
15
13
  } = $props();
16
14
 
17
15
  const streamdown = useStreamdown();
@@ -67,7 +65,10 @@
67
65
  </div>
68
66
  <div style="height: fit-content; width: 100%;" class={streamdown.theme.code.container}>
69
67
  <div>
68
+ <!-- {@render Skeleton()} -->
70
69
  {#if highlighter.isReady(theme, language as any)}
70
+ <!-- {@render Skeleton()} -->
71
+
71
72
  {@const code = highlighter.highlightCode(
72
73
  codeContent,
73
74
  language as any,
@@ -82,16 +83,15 @@
82
83
  </div>
83
84
  </div>
84
85
 
85
- <!-- Need to improve this -->
86
86
  {#snippet Skeleton()}
87
- {@const lines = codeContent.split('\n')}
87
+ {@const lines = token.text.split('\n')}
88
88
  <!-- -->
89
89
 
90
90
  <!-- --><code
91
91
  class={streamdown.theme.code.pre}
92
92
  style="height: fit-content; width: 100%; display: flex; flex-direction: column;"
93
93
  ><!-- -->{#each lines as line}<!-- --><span class={streamdown.theme.code.skeleton}
94
- >{line}</span
94
+ >{line.trim().length > 0 ? line : '\u200B'}</span
95
95
  ><!-- -->
96
96
  <!-- -->{/each}<!-- --></code
97
97
  ><!-- -->
@@ -1,8 +1,6 @@
1
1
  import type { Tokens } from 'marked';
2
- import type { Snippet } from 'svelte';
3
2
  type $$ComponentProps = {
4
3
  token: Tokens.Code;
5
- children: Snippet;
6
4
  };
7
5
  declare const Code: import("svelte").Component<$$ComponentProps, {}, "">;
8
6
  type Code = ReturnType<typeof Code>;