svelte-streamdown 1.0.9 → 2.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 +144 -135
- package/dist/Block.svelte +14 -54
- package/dist/Elements/Alert.svelte +1 -2
- package/dist/Elements/Code.svelte +2 -4
- 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 +27 -19
- package/dist/marked/marked-alert.d.ts +2 -2
- package/dist/marked/marked-alert.js +6 -19
- package/dist/marked/marked-footnotes.js +1 -2
- 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 +59 -0
- package/dist/marked/marked-table.js +495 -0
- package/dist/theme.d.ts +24 -6
- package/dist/theme.js +22 -10
- 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,6 +20,27 @@ Perfect for AI-powered applications that need to stream and render markdown cont
|
|
|
20
20
|
|
|
21
21
|
## ✨ Main Features
|
|
22
22
|
|
|
23
|
+
|
|
24
|
+
### 🔄 Streaming-Optimized
|
|
25
|
+
|
|
26
|
+
- **Incomplete Markdown Parsing**: Handles unterminated blocks gracefully
|
|
27
|
+
- **Progressive Rendering**: Perfect for streaming AI responses
|
|
28
|
+
- **Real-time Updates**: Optimized for dynamic content
|
|
29
|
+
|
|
30
|
+
### 🔒 Security Hardening
|
|
31
|
+
|
|
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
|
+
|
|
23
44
|
### 🎨 Built-in Typography Styles
|
|
24
45
|
|
|
25
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.
|
|
@@ -28,7 +49,6 @@ Beautiful, responsive typography with **built-in Tailwind CSS classes** for head
|
|
|
28
49
|
|
|
29
50
|
Full support for GitHub Flavored Markdown including:
|
|
30
51
|
|
|
31
|
-
- Task lists
|
|
32
52
|
- Tables
|
|
33
53
|
- ~~Strikethrough~~
|
|
34
54
|
- Subscript (H~2~O)
|
|
@@ -99,43 +119,124 @@ pie title Project Time Allocation
|
|
|
99
119
|
"Meetings" : 15
|
|
100
120
|
```
|
|
101
121
|
|
|
102
|
-
###
|
|
122
|
+
### Complex table support
|
|
103
123
|
|
|
104
|
-
|
|
105
|
-
> Native support for Github style Alert
|
|
124
|
+
#### Colspan
|
|
106
125
|
|
|
107
|
-
|
|
126
|
+
| H1 | H2 | H3 |
|
|
127
|
+
| ------------------------- | --- | --- |
|
|
128
|
+
| This cell spans 3 columns |||
|
|
108
129
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
| Header 1 | Header 2 | Header 3 |
|
|
131
|
+
| ------------------------- | -------- | -------- |
|
|
132
|
+
| This cell spans 2 columns || Normal
|
|
133
|
+
| Normal | Normal | Normal |
|
|
112
134
|
|
|
113
|
-
|
|
135
|
+
#### Rowspan
|
|
136
|
+
| Header 1 | Header 2 |
|
|
137
|
+
| --------------- | -------- |
|
|
138
|
+
| This cell spans | Cell A |
|
|
139
|
+
| two rows ^ | Cell B |
|
|
140
|
+
#### Footer
|
|
141
|
+
| Header 1 | Header 2 |
|
|
142
|
+
| --------------- | -------- |
|
|
143
|
+
| Cell B | Cell A |
|
|
144
|
+
| --------------- | -------- |
|
|
145
|
+
| Footer ||
|
|
114
146
|
|
|
115
|
-
|
|
116
|
-
- **Link Safety**: Control link destinations
|
|
117
|
-
- **HTML Sanitization**: Prevent XSS attacks
|
|
147
|
+
#### Column alignment
|
|
118
148
|
|
|
119
|
-
|
|
149
|
+
| Left | Center | Right |
|
|
150
|
+
| :--- | :----: | ----: |
|
|
151
|
+
| A | B | C |
|
|
152
|
+
|
|
153
|
+
#### Multiple headers and very complex layout
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
| Product Category ||| Sales Data Q1-Q4 2024 ||||
|
|
157
|
+
| Product | Region || Q1 | Q2 | Q3 | Q4 |
|
|
158
|
+
| Name | Type | Area | Revenue | Revenue | Revenue | Revenue |
|
|
159
|
+
|-------------|---------|------------|---------|---------|---------|---------|
|
|
160
|
+
| Laptop Pro | Electronics | North America || $45,000 | $52,000 | $48,000 |
|
|
161
|
+
| Laptop Pro ^ | ^ | Europe | $32,000 | $38,000 | $41,000 | $44,000 |
|
|
162
|
+
| Laptop Pro ^ | ^ | Asia || $28,000 | $35,000 | $42,000 |
|
|
163
|
+
| Office Chair | Furniture | North America | $15,000 | $18,000 | $16,000 | $17,000 |
|
|
164
|
+
| Office Chair ^ | ^ | Europe | $12,000 | $14,000 | $15,000 | $16,000 |
|
|
165
|
+
| Wireless Mouse | Electronics | Global ||| $25,000 | $28,000 |
|
|
166
|
+
|-------------|---------|------------|---------|---------|---------|---------|
|
|
167
|
+
| **Total Revenue** ||| **$152,000** | **$185,000** | **$187,000** | **$205,000** |
|
|
168
|
+
|
|
169
|
+
### Complex list support
|
|
170
|
+
|
|
171
|
+
#### decimal
|
|
172
|
+
|
|
173
|
+
1. First item
|
|
174
|
+
2. Second item
|
|
175
|
+
3. Third item
|
|
176
|
+
|
|
177
|
+
#### lower-alpha
|
|
178
|
+
|
|
179
|
+
a. First item
|
|
180
|
+
b. Second item
|
|
181
|
+
c. Third item
|
|
182
|
+
|
|
183
|
+
#### upper-alpha
|
|
184
|
+
|
|
185
|
+
A. First item
|
|
186
|
+
B. Second item
|
|
187
|
+
C. Third item
|
|
188
|
+
|
|
189
|
+
#### lower-roman
|
|
190
|
+
|
|
191
|
+
i. First item
|
|
192
|
+
ii. Second item
|
|
193
|
+
iii. Third item
|
|
194
|
+
|
|
195
|
+
#### upper-roman
|
|
196
|
+
|
|
197
|
+
I. First item
|
|
198
|
+
II. Second item
|
|
199
|
+
III. Third item
|
|
200
|
+
|
|
201
|
+
#### Nested Lists
|
|
202
|
+
|
|
203
|
+
1. First level (numeric)
|
|
204
|
+
a. Second level (lowercase alpha)
|
|
205
|
+
i. Third level (lowercase roman)
|
|
206
|
+
- Fourth level (bullet)
|
|
207
|
+
I. Fifth level (uppercase roman)
|
|
208
|
+
A. Sixth level (uppercase alpha)
|
|
209
|
+
|
|
210
|
+
2. Back to the first level
|
|
211
|
+
|
|
212
|
+
#### Task List
|
|
213
|
+
|
|
214
|
+
- [ ] Uncompleted task
|
|
215
|
+
- [x] Completed task
|
|
216
|
+
- [ ] Another uncompleted task
|
|
217
|
+
- [ ] Nested uncompleted subtask
|
|
218
|
+
- [x] Nested completed subtask
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
### Alert Support
|
|
222
|
+
|
|
223
|
+
> [!IMPORTANT]
|
|
224
|
+
> Native support for Github style Alert
|
|
120
225
|
|
|
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
226
|
|
|
127
227
|
## 🔄 Differences from Original React Version
|
|
128
228
|
|
|
129
229
|
This Svelte port maintains feature parity with the original [Streamdown](https://streamdown.ai/) while adapting to Svelte's patterns:
|
|
130
230
|
|
|
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
|
|
231
|
+
| Aspect | Original (React) | Svelte Port |
|
|
232
|
+
| ----------------- | ------------------------ | ------------------------- |
|
|
233
|
+
| **Framework** | React | Svelte 5 |
|
|
234
|
+
| **Component API** | JSX Components | Svelte Snippets |
|
|
235
|
+
| **Styling** | Tailwind CSS | Tailwind CSS (compatible) |
|
|
236
|
+
| **Context** | React Context | Svelte Context |
|
|
237
|
+
| **Build System** | Vite/React | Vite/SvelteKit |
|
|
238
|
+
| **TypeScript** | Full TS support | Full TS support |
|
|
239
|
+
| **Engine** | Remark / Rehype + marked | marked only |
|
|
139
240
|
|
|
140
241
|
### Tailwind CSS Setup
|
|
141
242
|
|
|
@@ -184,13 +285,13 @@ This heading will use a custom component!`;
|
|
|
184
285
|
// Custom heading component
|
|
185
286
|
</script>
|
|
186
287
|
|
|
187
|
-
{#snippet
|
|
188
|
-
<h1 class="mb-4 text-4xl font-bold text-blue-600" {...props}>
|
|
189
|
-
{children}
|
|
288
|
+
{#snippet customHeading({ children, token })}
|
|
289
|
+
<h1 class="mb-4 text-4xl font-bold text-blue-600" {...token.props}>
|
|
290
|
+
{@render children()}
|
|
190
291
|
</h1>
|
|
191
292
|
{/snippet}
|
|
192
293
|
|
|
193
|
-
<Streamdown {content}
|
|
294
|
+
<Streamdown {content} heading={customHeading} />
|
|
194
295
|
```
|
|
195
296
|
|
|
196
297
|
### Security Configuration
|
|
@@ -220,9 +321,6 @@ This heading will use a custom component!`;
|
|
|
220
321
|
| `defaultOrigin` | `string` | - | Default origin for relative URLs |
|
|
221
322
|
| `allowedLinkPrefixes` | `string[]` | `['*']` | Allowed URL prefixes for links |
|
|
222
323
|
| `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
324
|
| `skipHtml` | `boolean` | - | Skip HTML parsing entirely |
|
|
227
325
|
| `unwrapDisallowed` | `boolean` | - | Unwrap instead of removing disallowed elements |
|
|
228
326
|
| `urlTransform` | `UrlTransform \| null` | - | Custom URL transformation function |
|
|
@@ -232,14 +330,13 @@ This heading will use a custom component!`;
|
|
|
232
330
|
| `shikiTheme` | `BundledTheme` | `'github-light'` | Code highlighting theme |
|
|
233
331
|
| `mermaidConfig` | `MermaidConfig` | - | Mermaid diagram configuration |
|
|
234
332
|
| `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
333
|
|
|
240
334
|
### Custom Component Props
|
|
241
335
|
|
|
242
|
-
**Every single markdown element** can be customized with Svelte snippets, giving you
|
|
336
|
+
**Every single markdown element** can be customized with Svelte snippets, giving you
|
|
337
|
+
complete control over styling and behavior:
|
|
338
|
+
|
|
339
|
+
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
340
|
|
|
244
341
|
```svelte
|
|
245
342
|
<script>
|
|
@@ -250,36 +347,36 @@ This heading will use a custom component!`;
|
|
|
250
347
|
This heading uses a custom component with your design system!`;
|
|
251
348
|
</script>
|
|
252
349
|
|
|
253
|
-
{#snippet
|
|
350
|
+
{#snippet customHeading({ children, token })}
|
|
254
351
|
<h1
|
|
255
352
|
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}
|
|
353
|
+
{...token.props}
|
|
257
354
|
>
|
|
258
355
|
{@render children()}
|
|
259
356
|
</h1>
|
|
260
357
|
{/snippet}
|
|
261
358
|
|
|
262
|
-
{#snippet customCode({ children,
|
|
263
|
-
<code class="rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800" {...props}>
|
|
359
|
+
{#snippet customCode({ children, token })}
|
|
360
|
+
<code class="rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800" {...token.props}>
|
|
264
361
|
{@render children()}
|
|
265
362
|
</code>
|
|
266
363
|
{/snippet}
|
|
267
364
|
|
|
268
|
-
{#snippet customBlockquote({ children,
|
|
365
|
+
{#snippet customBlockquote({ children, token })}
|
|
269
366
|
<blockquote
|
|
270
367
|
class="border-l-4 border-blue-500 pl-4 text-gray-600 italic dark:text-gray-300"
|
|
271
|
-
{...props}
|
|
368
|
+
{...token.props}
|
|
272
369
|
>
|
|
273
370
|
{@render children()}
|
|
274
371
|
</blockquote>
|
|
275
372
|
{/snippet}
|
|
276
373
|
|
|
277
|
-
<Streamdown {content}
|
|
374
|
+
<Streamdown {content} heading={customHeading} code={customCode} blockquote={customBlockquote} />
|
|
278
375
|
```
|
|
279
376
|
|
|
280
377
|
#### All Available Customizable Elements:
|
|
281
378
|
|
|
282
|
-
**Text Elements**: `
|
|
379
|
+
**Text Elements**: `heading`, `p`, `strong`, `em`, `del`
|
|
283
380
|
|
|
284
381
|
**Links & Media**: `a`, `img`
|
|
285
382
|
|
|
@@ -291,95 +388,7 @@ This heading uses a custom component with your design system!`;
|
|
|
291
388
|
|
|
292
389
|
**Special Content**: `blockquote`, `hr`, `alert`, `mermaid`, `math`, `inlineMath`
|
|
293
390
|
|
|
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`.
|
|
391
|
+
**Note**: The above elements are **supported by Streamdown** and should be customized using individual props or the theme system.
|
|
383
392
|
|
|
384
393
|
## 🎨 Advanced Theming System
|
|
385
394
|
|
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
|
|
@@ -15,50 +15,8 @@
|
|
|
15
15
|
const tokens = $derived(lex(parseIncompleteMarkdown(block.trim())));
|
|
16
16
|
|
|
17
17
|
const getChildren = (token: StreamdownToken) => {
|
|
18
|
-
if (token
|
|
19
|
-
return
|
|
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;
|
|
18
|
+
if (!token) {
|
|
19
|
+
return [];
|
|
62
20
|
}
|
|
63
21
|
const children =
|
|
64
22
|
'tokens' in token ? ((token.tokens || []) as StreamdownToken[]) : ([] as StreamdownToken[]);
|
|
@@ -79,15 +37,17 @@
|
|
|
79
37
|
|
|
80
38
|
{#snippet renderChildren(tokens: StreamdownToken[])}
|
|
81
39
|
{#each tokens as token}
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
{
|
|
87
|
-
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
40
|
+
{#if token}
|
|
41
|
+
{@const children = getChildren(token)}
|
|
42
|
+
{@const isTextOnlyNode = children.length === 0}
|
|
43
|
+
<Element {token}>
|
|
44
|
+
{#if isTextOnlyNode}
|
|
45
|
+
{'text' in token ? token.text : ''}
|
|
46
|
+
{:else}
|
|
47
|
+
{@render renderChildren(children)}
|
|
48
|
+
{/if}
|
|
49
|
+
</Element>
|
|
50
|
+
{/if}
|
|
91
51
|
{/each}
|
|
92
52
|
{/snippet}
|
|
93
53
|
|
|
@@ -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();
|
|
@@ -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>;
|
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
import Link from './Link.svelte';
|
|
4
4
|
import Code from './Code.svelte';
|
|
5
5
|
import Image from './Image.svelte';
|
|
6
|
-
import Table from './Table.svelte';
|
|
7
6
|
import Mermaid from './Mermaid.svelte';
|
|
8
7
|
import Math from './Math.svelte';
|
|
9
8
|
import Alert from './Alert.svelte';
|
|
10
9
|
import type { StreamdownToken } from '../marked/index.js';
|
|
11
10
|
import Slot from './Slot.svelte';
|
|
12
|
-
import { useStreamdown } from '../Streamdown.
|
|
11
|
+
import { useStreamdown } from '../Streamdown.js';
|
|
13
12
|
import FootnoteRef from './FootnoteRef.svelte';
|
|
14
13
|
let { token, children }: { token: StreamdownToken; children: Snippet } = $props();
|
|
15
14
|
const id = $props.id();
|
|
@@ -43,9 +42,13 @@
|
|
|
43
42
|
</blockquote>
|
|
44
43
|
</Slot>
|
|
45
44
|
{:else if token.type === 'code' && token.lang === 'mermaid'}
|
|
46
|
-
<
|
|
45
|
+
<Slot props={{ children, token }} render={streamdown.snippets.code}>
|
|
46
|
+
<Mermaid {token} />
|
|
47
|
+
</Slot>
|
|
47
48
|
{:else if token.type === 'code'}
|
|
48
|
-
<
|
|
49
|
+
<Slot props={{ children, token }} render={streamdown.snippets.code}>
|
|
50
|
+
<Code {token} />
|
|
51
|
+
</Slot>
|
|
49
52
|
{:else if token.type === 'codespan'}
|
|
50
53
|
<Slot props={{ children, token }} render={streamdown.snippets.codespan}>
|
|
51
54
|
<code class={streamdown.theme.codespan.base}>
|
|
@@ -54,8 +57,6 @@
|
|
|
54
57
|
</Slot>
|
|
55
58
|
{:else if token.type === 'list'}
|
|
56
59
|
{#if token.ordered}
|
|
57
|
-
{@const firstItemValue = token.items[0]?.value || 1}
|
|
58
|
-
{@const counterName = `counter-${id}`}
|
|
59
60
|
<Slot props={{ children, token }} render={streamdown.snippets.ol}>
|
|
60
61
|
<ol style:list-style-type={token.listType} class={streamdown.theme.ol.base}>
|
|
61
62
|
{@render children()}
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
<Slot props={{ children, token }} render={streamdown.snippets.li}>
|
|
73
74
|
<li
|
|
74
75
|
style:list-style-type={token.task ? 'none' : undefined}
|
|
75
|
-
value
|
|
76
|
+
{...token.value && !token.task ? { value: token.value } : {}}
|
|
76
77
|
class={streamdown.theme.li.base}
|
|
77
78
|
>
|
|
78
79
|
{#if token.task}
|
|
@@ -87,31 +88,67 @@
|
|
|
87
88
|
</li>
|
|
88
89
|
</Slot>
|
|
89
90
|
{:else if token.type === 'table'}
|
|
90
|
-
<
|
|
91
|
-
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
</
|
|
91
|
+
<Slot props={{ token, children }} render={streamdown.snippets.table}>
|
|
92
|
+
<div class={streamdown.theme.table.base}>
|
|
93
|
+
<table class={streamdown.theme.table.table}>
|
|
94
|
+
{@render children()}
|
|
95
|
+
</table>
|
|
96
|
+
</div>
|
|
96
97
|
</Slot>
|
|
97
|
-
{:else if token.type === '
|
|
98
|
-
<Slot props={{ token, children }} render={streamdown.snippets.
|
|
99
|
-
<thead class={streamdown.theme.
|
|
98
|
+
{:else if token.type === 'thead'}
|
|
99
|
+
<Slot props={{ token, children }} render={streamdown.snippets.thead}>
|
|
100
|
+
<thead class={streamdown.theme.thead.base}>
|
|
100
101
|
{@render children?.()}
|
|
101
102
|
</thead>
|
|
102
103
|
</Slot>
|
|
103
|
-
{:else if token.type === '
|
|
104
|
-
<Slot props={{
|
|
105
|
-
<
|
|
104
|
+
{:else if token.type === 'tbody'}
|
|
105
|
+
<Slot props={{ token, children }} render={streamdown.snippets.tbody}>
|
|
106
|
+
<tbody class={streamdown.theme.tbody.base}>
|
|
106
107
|
{@render children?.()}
|
|
107
|
-
</
|
|
108
|
+
</tbody>
|
|
108
109
|
</Slot>
|
|
109
|
-
{:else if token.type === '
|
|
110
|
-
<Slot props={{
|
|
111
|
-
<
|
|
110
|
+
{:else if token.type === 'tfoot'}
|
|
111
|
+
<Slot props={{ token, children }} render={streamdown.snippets.tfoot}>
|
|
112
|
+
<tfoot class={streamdown.theme.tfoot.base}>
|
|
113
|
+
{@render children?.()}
|
|
114
|
+
</tfoot>
|
|
115
|
+
</Slot>
|
|
116
|
+
{:else if token.type === 'tr'}
|
|
117
|
+
<Slot props={{ token, children }} render={streamdown.snippets.tr}>
|
|
118
|
+
<tr class={streamdown.theme.tr.base}>
|
|
112
119
|
{@render children?.()}
|
|
113
|
-
</
|
|
120
|
+
</tr>
|
|
114
121
|
</Slot>
|
|
122
|
+
{:else if token.type === 'td'}
|
|
123
|
+
{#if token.rowspan > 0}
|
|
124
|
+
<Slot props={{ children, token }} render={streamdown.snippets.td}>
|
|
125
|
+
<td
|
|
126
|
+
class={streamdown.theme.td.base}
|
|
127
|
+
{...token.colspan > 1 ? { colspan: token.colspan } : {}}
|
|
128
|
+
{...token.rowspan > 1 ? { rowspan: token.rowspan } : {}}
|
|
129
|
+
{...token.align && ['left', 'center', 'right', 'justify', 'char'].includes(token.align)
|
|
130
|
+
? { align: token.align as 'left' | 'center' | 'right' | 'justify' | 'char' }
|
|
131
|
+
: { align: 'left' }}
|
|
132
|
+
>
|
|
133
|
+
{@render children?.()}
|
|
134
|
+
</td>
|
|
135
|
+
</Slot>
|
|
136
|
+
{/if}
|
|
137
|
+
{:else if token.type === 'th'}
|
|
138
|
+
{#if token.rowspan > 0}
|
|
139
|
+
<Slot props={{ children, token }} render={streamdown.snippets.th}>
|
|
140
|
+
<th
|
|
141
|
+
class={streamdown.theme.th.base}
|
|
142
|
+
{...token.colspan > 1 ? { colspan: token.colspan } : {}}
|
|
143
|
+
{...token.rowspan > 1 ? { rowspan: token.rowspan } : {}}
|
|
144
|
+
{...token.align && ['left', 'center', 'right', 'justify', 'char'].includes(token.align)
|
|
145
|
+
? { align: token.align as 'left' | 'center' | 'right' | 'justify' | 'char' }
|
|
146
|
+
: { align: 'left' }}
|
|
147
|
+
>
|
|
148
|
+
{@render children?.()}
|
|
149
|
+
</th>
|
|
150
|
+
</Slot>
|
|
151
|
+
{/if}
|
|
115
152
|
{:else if token.type === 'image'}
|
|
116
153
|
<Image {token} {children} />
|
|
117
154
|
{:else if token.type === 'link'}
|
|
@@ -153,7 +190,15 @@
|
|
|
153
190
|
{:else if token.type === 'br'}
|
|
154
191
|
<br />
|
|
155
192
|
{:else if token.type === 'math'}
|
|
156
|
-
<
|
|
193
|
+
<Slot
|
|
194
|
+
props={{
|
|
195
|
+
children,
|
|
196
|
+
token
|
|
197
|
+
}}
|
|
198
|
+
render={streamdown.snippets.math}
|
|
199
|
+
>
|
|
200
|
+
<Math {token} />
|
|
201
|
+
</Slot>
|
|
157
202
|
{:else if token.type === 'alert'}
|
|
158
203
|
<Alert {token} {children} />
|
|
159
204
|
{:else if token.type === 'footnoteRef'}
|