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.
- package/README.md +160 -143
- package/dist/Block.svelte +12 -72
- package/dist/Elements/Alert.svelte +1 -2
- package/dist/Elements/Code.svelte +7 -7
- package/dist/Elements/Code.svelte.d.ts +0 -2
- package/dist/Elements/Element.svelte +70 -25
- package/dist/Elements/FootnoteRef.svelte +19 -6
- package/dist/Elements/Image.svelte +1 -1
- package/dist/Elements/Link.svelte +1 -1
- package/dist/Elements/Math.svelte +13 -25
- package/dist/Elements/Math.svelte.d.ts +0 -2
- package/dist/Elements/Mermaid.svelte +105 -115
- package/dist/Elements/Mermaid.svelte.d.ts +0 -2
- package/dist/Elements/Table.svelte +1 -1
- package/dist/Streamdown.d.ts +52 -10
- package/dist/Streamdown.js +19 -1
- package/dist/Streamdown.svelte +3 -41
- package/dist/Streamdown.svelte.d.ts +1 -20
- package/dist/marked/index.d.ts +3 -20
- package/dist/marked/index.js +24 -19
- package/dist/marked/marked-alert.d.ts +2 -2
- package/dist/marked/marked-alert.js +7 -21
- package/dist/marked/marked-footnotes.js +2 -3
- package/dist/marked/marked-list.d.ts +2 -2
- package/dist/marked/marked-list.js +28 -11
- package/dist/marked/marked-math.js +1 -1
- package/dist/marked/marked-subsup.d.ts +1 -1
- package/dist/marked/marked-subsup.js +2 -1
- package/dist/marked/marked-table.d.ts +66 -0
- package/dist/marked/marked-table.js +495 -0
- package/dist/theme.d.ts +24 -6
- package/dist/theme.js +26 -14
- package/dist/utils/panzoom.svelte.d.ts +17 -18
- package/dist/utils/panzoom.svelte.js +0 -2
- package/dist/utils/useClickOutside.svelte.js +0 -1
- package/dist/utils/useKeyDown.svelte.js +4 -1
- 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
|
-
|
|
24
|
+
### 🔄 Streaming-Optimized
|
|
26
25
|
|
|
27
|
-
|
|
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
|
-
|
|
30
|
+
### 🔒 Security Hardening
|
|
30
31
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
###
|
|
129
|
+
### Complex table support
|
|
103
130
|
|
|
104
|
-
|
|
105
|
-
> Native support for Github style Alert
|
|
131
|
+
#### Colspan
|
|
106
132
|
|
|
107
|
-
|
|
133
|
+
| H1 | H2 | H3 |
|
|
134
|
+
| ------------------------- | --- | --- |
|
|
135
|
+
| This cell spans 3 columns |||
|
|
108
136
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
| Header 1 | Header 2 | Header 3 |
|
|
138
|
+
| ------------------------- | -------- | -------- |
|
|
139
|
+
| This cell spans 2 columns || Normal
|
|
140
|
+
| Normal | Normal | Normal |
|
|
112
141
|
|
|
113
|
-
|
|
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
|
-
|
|
116
|
-
- **Link Safety**: Control link destinations
|
|
117
|
-
- **HTML Sanitization**: Prevent XSS attacks
|
|
154
|
+
#### Column alignment
|
|
118
155
|
|
|
119
|
-
|
|
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)
|
|
132
|
-
| ----------------- |
|
|
133
|
-
| **Framework** | React
|
|
134
|
-
| **Component API** | JSX Components
|
|
135
|
-
| **Styling** | Tailwind CSS
|
|
136
|
-
| **Context** | React Context
|
|
137
|
-
| **Build System** | Vite/React
|
|
138
|
-
| **TypeScript** | 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
|
|
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}
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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}
|
|
382
|
+
<Streamdown {content} heading={customHeading} code={customCode} blockquote={customBlockquote} />
|
|
278
383
|
```
|
|
279
384
|
|
|
280
385
|
#### All Available Customizable Elements:
|
|
281
386
|
|
|
282
|
-
**Text Elements**: `
|
|
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.
|
|
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
|
|
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
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
{
|
|
87
|
-
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
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.
|
|
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.
|
|
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 =
|
|
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>;
|