svelte-streamdown 2.3.1 → 2.3.2
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 +101 -82
- package/dist/Block.svelte +1 -1
- package/dist/Elements/Element.svelte +12 -2
- package/dist/Elements/index.d.ts +0 -1
- package/dist/Elements/index.js +0 -1
- package/dist/Streamdown.svelte +9 -1
- package/dist/context.svelte.d.ts +7 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/marked/index.d.ts +15 -3
- package/dist/marked/index.js +21 -33
- package/dist/marked/marked-alert.d.ts +2 -8
- package/dist/marked/marked-alert.js +14 -20
- package/dist/marked/marked-br.d.ts +2 -8
- package/dist/marked/marked-br.js +15 -21
- package/dist/marked/marked-footnotes.d.ts +2 -9
- package/dist/marked/marked-footnotes.js +55 -58
- package/dist/marked/marked-hr.d.ts +2 -8
- package/dist/marked/marked-hr.js +18 -24
- package/dist/marked/marked-list.d.ts +3 -9
- package/dist/marked/marked-list.js +176 -182
- package/dist/marked/marked-math.d.ts +2 -9
- package/dist/marked/marked-math.js +65 -69
- package/dist/marked/marked-subsup.d.ts +3 -9
- package/dist/marked/marked-subsup.js +38 -43
- package/dist/marked/marked-table.d.ts +2 -9
- package/dist/marked/marked-table.js +114 -123
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -261,14 +261,14 @@ This Svelte port maintains feature parity with the original [Streamdown](https:/
|
|
|
261
261
|
|
|
262
262
|
## 🎭 Animation System
|
|
263
263
|
|
|
264
|
-
Streamdown includes
|
|
264
|
+
Streamdown includes an animation system designed specifically for streaming AI content, providing smooth and engaging visual feedback as text appears on screen.
|
|
265
265
|
|
|
266
266
|
### How It Works
|
|
267
267
|
|
|
268
268
|
The animation system works by:
|
|
269
269
|
|
|
270
270
|
1. **Tokenization**: Text is broken down into tokens (words or characters) based on your configuration
|
|
271
|
-
2. **Sequential Animation**: Each token animates
|
|
271
|
+
2. **Sequential Animation**: Each token animates as it is received
|
|
272
272
|
3. **Block-level Animation**: Entire blocks (paragraphs, headings, code blocks) animate as units
|
|
273
273
|
|
|
274
274
|
### Animation Types
|
|
@@ -293,6 +293,8 @@ Text slides down from above while fading in, creating a dynamic downward motion.
|
|
|
293
293
|
|
|
294
294
|
> [!TIP]
|
|
295
295
|
> For production applications where the LLM is not streaming (static content), disable animations entirely by setting `animation.enabled = false` to minimize DOM elements and improve performance.
|
|
296
|
+
>
|
|
297
|
+
> If using AI SDK mind to smooth stream the content to using word-level tokenization to avoid partial words not being animated.
|
|
296
298
|
|
|
297
299
|
> [!WARNING]
|
|
298
300
|
> 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.
|
|
@@ -327,17 +329,15 @@ console.log('Hello from Streamdown!');
|
|
|
327
329
|
let content = `# Custom Components Example
|
|
328
330
|
|
|
329
331
|
This heading will use a custom component!`;
|
|
330
|
-
|
|
331
|
-
// Custom heading component
|
|
332
332
|
</script>
|
|
333
333
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
334
|
+
<Streamdown {content}>
|
|
335
|
+
{#snippet heading({ children })}
|
|
336
|
+
<h1 class="mb-4 text-4xl font-bold text-blue-600">
|
|
337
|
+
{@render children()}
|
|
338
|
+
</h1>
|
|
339
|
+
{/snippet}
|
|
340
|
+
</Streamdown>
|
|
341
341
|
```
|
|
342
342
|
|
|
343
343
|
### Security Configuration
|
|
@@ -359,73 +359,32 @@ This heading will use a custom component!`;
|
|
|
359
359
|
|
|
360
360
|
## 📋 Props API
|
|
361
361
|
|
|
362
|
-
| Prop | Type
|
|
363
|
-
| -------------------------- |
|
|
364
|
-
| `content` | `string`
|
|
365
|
-
| `class` | `string`
|
|
366
|
-
| `parseIncompleteMarkdown` | `boolean`
|
|
367
|
-
| `defaultOrigin` | `string`
|
|
368
|
-
| `allowedLinkPrefixes` | `string[]`
|
|
369
|
-
| `allowedImagePrefixes` | `string[]`
|
|
370
|
-
| `skipHtml` | `boolean`
|
|
371
|
-
| `unwrapDisallowed` | `boolean`
|
|
372
|
-
| `urlTransform` | `UrlTransform \| null`
|
|
373
|
-
| `theme` | `DeepPartial<Theme>`
|
|
374
|
-
| `baseTheme` | `'tailwind' \| 'shadcn'`
|
|
375
|
-
| `mergeTheme` | `boolean`
|
|
376
|
-
| `shikiTheme` | `BundledTheme`
|
|
377
|
-
| `mermaidConfig` | `MermaidConfig`
|
|
378
|
-
| `katexConfig` | `KatexOptions \| ((inline: boolean) => KatexOptions)`
|
|
379
|
-
| `animation` | `AnimationConfig`
|
|
380
|
-
| `animation.enabled` | `boolean`
|
|
381
|
-
| `animation.type` | `'fade' \| 'blur' \| 'typewriter' \| 'slideUp' \| 'slideDown'`
|
|
382
|
-
| `animation.duration` | `number`
|
|
383
|
-
| `animation.timingFunction` | `'ease' \| 'ease-in' \| 'ease-out' \| 'ease-in-out' \| 'linear'`
|
|
384
|
-
| `animation.tokenize` | `'word' \| 'char'`
|
|
385
|
-
| `animation.animateOnMount` | `boolean`
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
**Every single markdown element** can be customized with Svelte snippets, giving you
|
|
390
|
-
complete control over styling and behavior:
|
|
391
|
-
|
|
392
|
-
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.
|
|
393
|
-
|
|
394
|
-
```svelte
|
|
395
|
-
<script>
|
|
396
|
-
import { Streamdown } from 'svelte-streamdown';
|
|
397
|
-
|
|
398
|
-
let content = `# Fully Customizable
|
|
399
|
-
|
|
400
|
-
This heading uses a custom component with your design system!`;
|
|
401
|
-
</script>
|
|
402
|
-
|
|
403
|
-
{#snippet customHeading({ children, token })}
|
|
404
|
-
<h1
|
|
405
|
-
class="text-gradient mb-6 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-4xl font-bold text-transparent"
|
|
406
|
-
{...token.props}
|
|
407
|
-
>
|
|
408
|
-
{@render children()}
|
|
409
|
-
</h1>
|
|
410
|
-
{/snippet}
|
|
411
|
-
|
|
412
|
-
{#snippet customCode({ children, token })}
|
|
413
|
-
<code class="rounded bg-gray-100 px-2 py-1 font-mono text-sm dark:bg-gray-800" {...token.props}>
|
|
414
|
-
{@render children()}
|
|
415
|
-
</code>
|
|
416
|
-
{/snippet}
|
|
417
|
-
|
|
418
|
-
{#snippet customBlockquote({ children, token })}
|
|
419
|
-
<blockquote
|
|
420
|
-
class="border-l-4 border-blue-500 pl-4 text-gray-600 italic dark:text-gray-300"
|
|
421
|
-
{...token.props}
|
|
422
|
-
>
|
|
423
|
-
{@render children()}
|
|
424
|
-
</blockquote>
|
|
425
|
-
{/snippet}
|
|
426
|
-
|
|
427
|
-
<Streamdown {content} heading={customHeading} code={customCode} blockquote={customBlockquote} />
|
|
428
|
-
```
|
|
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 |
|
|
386
|
+
| `extensions` | `Array<Extension>` | `[]` | Custom marked tokenizers to render special markdown blocks or inline tokens |
|
|
387
|
+
| `children` | `Snippet<[{token:GenericToken, streamdown: StreamdownContext, children: Snippet` | `undefined` | Snippet used to render element that are not supported by Streamdown and tokenized by your custom extensions |
|
|
429
388
|
|
|
430
389
|
#### All Available Customizable Elements:
|
|
431
390
|
|
|
@@ -435,15 +394,15 @@ This heading uses a custom component with your design system!`;
|
|
|
435
394
|
|
|
436
395
|
**Lists**: `ul`, `ol`, `li`
|
|
437
396
|
|
|
438
|
-
**Code**: `code`, `
|
|
397
|
+
**Code**: `code`, `codeSpan`
|
|
439
398
|
|
|
440
|
-
**Tables**: `table`, `thead`, `tbody`, `tr`, `th`, `td`
|
|
399
|
+
**Tables**: `table`, `thead`, `tbody`, `tr`, `th`, `td`, `tfoot`
|
|
441
400
|
|
|
442
|
-
**Special Content**: `blockquote`, `hr`, `alert`, `mermaid`, `math`, `
|
|
401
|
+
**Special Content**: `blockquote`, `hr`, `alert`, `mermaid`, `math`, `footnoteRef`
|
|
443
402
|
|
|
444
403
|
**Note**: The above elements are **supported by Streamdown** and should be customized using individual props or the theme system.
|
|
445
404
|
|
|
446
|
-
## 🎨
|
|
405
|
+
## 🎨 Theming System
|
|
447
406
|
|
|
448
407
|
### Built-in Themes
|
|
449
408
|
|
|
@@ -548,6 +507,66 @@ Each component supports multiple themeable parts:
|
|
|
548
507
|
|
|
549
508
|
Themes are intelligently merged using Tailwind's class merging utility, so you only need to override the specific parts you want to customize while keeping the default styling for everything else.
|
|
550
509
|
|
|
510
|
+
## 💉 Extensibility
|
|
511
|
+
|
|
512
|
+
Streamdown is extensible through the use of custom extensions.
|
|
513
|
+
|
|
514
|
+
An extension is an object that has a `name`, a `level` and a `tokenizer` function.
|
|
515
|
+
|
|
516
|
+
- `name`: The name of the extension
|
|
517
|
+
- `level`: The level of the extension, can be `block` or `inline`
|
|
518
|
+
- `tokenizer`: The tokenizer function, see [marked](https://github.com/markedjs/marked) for more information
|
|
519
|
+
|
|
520
|
+
To render the extension custom tokens, you can then simply use the `children` snippet.
|
|
521
|
+
|
|
522
|
+
### Example
|
|
523
|
+
|
|
524
|
+
```svelte
|
|
525
|
+
<script lang="ts">
|
|
526
|
+
import { Streamdown, type Extension } from 'svelte-streamdown';
|
|
527
|
+
const markedCollapsible: Extension = {
|
|
528
|
+
name: 'collapsible',
|
|
529
|
+
level: 'block',
|
|
530
|
+
tokenizer(this, src) {
|
|
531
|
+
// Match [detail]...[detail] blocks (case insensitive)
|
|
532
|
+
const detailMatch = src.match(/^\[detail\](.*?)\[detail\]/is);
|
|
533
|
+
|
|
534
|
+
if (detailMatch) {
|
|
535
|
+
const content = detailMatch[1] || '';
|
|
536
|
+
const tokens = this.lexer.blockTokens(content);
|
|
537
|
+
|
|
538
|
+
return {
|
|
539
|
+
type: 'detail',
|
|
540
|
+
raw: detailMatch[0], // The entire matched string including tags
|
|
541
|
+
tokens
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
</script>
|
|
549
|
+
|
|
550
|
+
<Streamdown
|
|
551
|
+
extensions={[markedCollapsible]}
|
|
552
|
+
content={`
|
|
553
|
+
[detail]
|
|
554
|
+
This is a collapsible **section**
|
|
555
|
+
[detail]`}
|
|
556
|
+
>
|
|
557
|
+
{#snippet children({ token, streamdown, children })}
|
|
558
|
+
{#if token.type === 'detail'}
|
|
559
|
+
<details>
|
|
560
|
+
<summary> Detail </summary>
|
|
561
|
+
<div>
|
|
562
|
+
{@render children()}
|
|
563
|
+
</div>
|
|
564
|
+
</details>
|
|
565
|
+
{/if}
|
|
566
|
+
{/snippet}
|
|
567
|
+
</Streamdown>
|
|
568
|
+
```
|
|
569
|
+
|
|
551
570
|
## 🛠️ Development
|
|
552
571
|
|
|
553
572
|
### Setup
|
package/dist/Block.svelte
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
insideFootnote?: boolean;
|
|
15
15
|
} = $props();
|
|
16
16
|
|
|
17
|
-
const tokens = $derived(lex(parseIncompleteMarkdown(block.trim())));
|
|
18
17
|
const streamdown = useStreamdown();
|
|
18
|
+
const tokens = $derived(lex(parseIncompleteMarkdown(block.trim()), streamdown.extensions));
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
{#snippet renderChildren(tokens: StreamdownToken[])}
|
|
@@ -223,6 +223,16 @@
|
|
|
223
223
|
<Alert {token} {children} />
|
|
224
224
|
{:else if token.type === 'footnoteRef'}
|
|
225
225
|
<FootnoteRef {token} />
|
|
226
|
+
{:else if token.type === 'footnote'}
|
|
227
|
+
<!-- TODO Footnotes are rendered inside the FootnoteRef popover -->
|
|
228
|
+
{:else if token.type === 'def'}
|
|
229
|
+
<!-- TODO This does not seems to be tokenized for now -->
|
|
230
|
+
{:else if token.type === 'escape'}
|
|
231
|
+
<!-- TODO This does not seems to be tokenized for now -->
|
|
232
|
+
{:else if token.type === 'space'}
|
|
233
|
+
<!-- TODO This does not seems to be tokenized for now -->
|
|
234
|
+
{:else if token.type === 'text'}
|
|
235
|
+
{@render children()}
|
|
226
236
|
{:else if token.type === 'html'}
|
|
227
237
|
{#if streamdown.renderHtml}
|
|
228
238
|
{@const content =
|
|
@@ -230,6 +240,6 @@
|
|
|
230
240
|
{@html content}
|
|
231
241
|
{/if}
|
|
232
242
|
{:else}
|
|
233
|
-
<!-- For tokens we don't handle specifically,
|
|
234
|
-
{@render children?.()}
|
|
243
|
+
<!-- For tokens we don't handle specifically, it may certainely be a custom extension to to the children props to handle -->
|
|
244
|
+
{@render streamdown.children?.({ token, children, streamdown })}
|
|
235
245
|
{/if}
|
package/dist/Elements/index.d.ts
CHANGED
package/dist/Elements/index.js
CHANGED
package/dist/Streamdown.svelte
CHANGED
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
animation,
|
|
26
26
|
element = $bindable(),
|
|
27
27
|
icons,
|
|
28
|
+
children,
|
|
29
|
+
extensions,
|
|
28
30
|
...snippets
|
|
29
31
|
}: StreamdownProps = $props();
|
|
30
32
|
|
|
@@ -99,6 +101,12 @@
|
|
|
99
101
|
mermaid: mermaidControls
|
|
100
102
|
};
|
|
101
103
|
},
|
|
104
|
+
get children() {
|
|
105
|
+
return children;
|
|
106
|
+
},
|
|
107
|
+
get extensions() {
|
|
108
|
+
return extensions;
|
|
109
|
+
},
|
|
102
110
|
get icons() {
|
|
103
111
|
return icons;
|
|
104
112
|
}
|
|
@@ -106,7 +114,7 @@
|
|
|
106
114
|
|
|
107
115
|
const id = $props.id();
|
|
108
116
|
|
|
109
|
-
const blocks = $derived(parseBlocks(content));
|
|
117
|
+
const blocks = $derived(parseBlocks(content, streamdown.extensions));
|
|
110
118
|
</script>
|
|
111
119
|
|
|
112
120
|
<div bind:this={element} class={className}>
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare class StreamdownContext {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
export declare const useStreamdown: () => StreamdownContext;
|
|
31
|
-
import type { AlertToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoot, THeadRow, TRow, TD, TH } from './marked/index.js';
|
|
31
|
+
import type { AlertToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoot, THeadRow, TRow, TD, TH, Extension, GenericToken } from './marked/index.js';
|
|
32
32
|
import type { Tokens } from 'marked';
|
|
33
33
|
import type { ListItemToken, ListToken } from './marked/marked-list.js';
|
|
34
34
|
import type { Footnote, FootnoteRef, FootnoteToken } from './marked/marked-footnotes.js';
|
|
@@ -123,5 +123,11 @@ export type StreamdownProps = {
|
|
|
123
123
|
caution?: Snippet;
|
|
124
124
|
important?: Snippet;
|
|
125
125
|
};
|
|
126
|
+
extensions?: Extension[];
|
|
127
|
+
children?: Snippet<[{
|
|
128
|
+
streamdown: StreamdownContext;
|
|
129
|
+
token: GenericToken;
|
|
130
|
+
children: Snippet;
|
|
131
|
+
}]>;
|
|
126
132
|
} & Partial<Snippets>;
|
|
127
133
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as Streamdown } from './Streamdown.svelte';
|
|
2
2
|
export { useStreamdown, type StreamdownProps } from './context.svelte.js';
|
|
3
3
|
export { theme, shadcnTheme, mergeTheme, type Theme } from './theme.js';
|
|
4
|
-
export {
|
|
4
|
+
export { type Extension, type StreamdownToken } from './marked/index.js';
|
|
5
|
+
export { lex, parseBlocks } from './marked/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as Streamdown } from './Streamdown.svelte';
|
|
2
2
|
export { useStreamdown } from './context.svelte.js';
|
|
3
3
|
export { theme, shadcnTheme, mergeTheme } from './theme.js';
|
|
4
|
+
export {} from './marked/index.js';
|
|
4
5
|
export { lex, parseBlocks } from './marked/index.js';
|
package/dist/marked/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type MarkedToken, type Tokens } from 'marked';
|
|
1
|
+
import { type MarkedToken, type Token, type TokenizerStartFunction, type TokenizerThis, type Tokens, type TokensList } from 'marked';
|
|
2
2
|
import { type AlertToken } from './marked-alert.js';
|
|
3
3
|
import { type FootnoteToken } from './marked-footnotes.js';
|
|
4
4
|
import { type MathToken } from './marked-math.js';
|
|
@@ -7,8 +7,20 @@ import { type ListItemToken, type ListToken } from './marked-list.js';
|
|
|
7
7
|
import { type BrToken } from './marked-br.js';
|
|
8
8
|
import { type HrToken } from './marked-hr.js';
|
|
9
9
|
import { type TableToken, type THead, type TBody, type TFoot, type THeadRow, type TRow, type TH, type TD } from './marked-table.js';
|
|
10
|
+
export type GenericToken = {
|
|
11
|
+
type: string;
|
|
12
|
+
raw: string;
|
|
13
|
+
tokens?: Token[];
|
|
14
|
+
} & Record<string, any>;
|
|
15
|
+
export type Extension = {
|
|
16
|
+
name: string;
|
|
17
|
+
level: 'block' | 'inline';
|
|
18
|
+
tokenizer: (this: TokenizerThis, src: string, tokens: Token[] | TokensList) => GenericToken | undefined;
|
|
19
|
+
start?: TokenizerStartFunction;
|
|
20
|
+
applyInBlockParsing?: boolean;
|
|
21
|
+
};
|
|
10
22
|
export type StreamdownToken = Exclude<MarkedToken, Tokens.List | Tokens.ListItem> | ListToken | ListItemToken | MathToken | AlertToken | FootnoteToken | SubSupToken | BrToken | HrToken | TableToken | THead | TBody | TFoot | THeadRow | TRow | TH | TD;
|
|
11
23
|
export type { TableToken, THead, TBody, TFoot, THeadRow, TRow, TH, TD } from './marked-table.js';
|
|
12
|
-
export declare const lex: (markdown: string) => StreamdownToken[];
|
|
13
|
-
export declare const parseBlocks: (markdown: string) => string[];
|
|
24
|
+
export declare const lex: (markdown: string, extensions?: Extension[]) => StreamdownToken[];
|
|
25
|
+
export declare const parseBlocks: (markdown: string, extensions?: Extension[]) => string[];
|
|
14
26
|
export type { MathToken, AlertToken, FootnoteToken, SubSupToken, BrToken, HrToken };
|
package/dist/marked/index.js
CHANGED
|
@@ -2,22 +2,12 @@ import { Lexer } from 'marked';
|
|
|
2
2
|
import { markedAlert } from './marked-alert.js';
|
|
3
3
|
import { markedFootnote } from './marked-footnotes.js';
|
|
4
4
|
import { markedMath } from './marked-math.js';
|
|
5
|
-
import {
|
|
5
|
+
import { markedSub, markedSup } from './marked-subsup.js';
|
|
6
6
|
import { markedList } from './marked-list.js';
|
|
7
7
|
import { markedBr } from './marked-br.js';
|
|
8
8
|
import { markedHr } from './marked-hr.js';
|
|
9
9
|
import { markedTable } from './marked-table.js';
|
|
10
|
-
const
|
|
11
|
-
markedTable(),
|
|
12
|
-
markedFootnote(),
|
|
13
|
-
markedAlert(),
|
|
14
|
-
markedMath(),
|
|
15
|
-
markedSubSup(),
|
|
16
|
-
markedList(),
|
|
17
|
-
markedBr(),
|
|
18
|
-
markedHr()
|
|
19
|
-
];
|
|
20
|
-
const parseExtensions = (...ext) => {
|
|
10
|
+
const parseExtensions = (...extensions) => {
|
|
21
11
|
const options = {
|
|
22
12
|
gfm: true,
|
|
23
13
|
extensions: {
|
|
@@ -29,35 +19,33 @@ const parseExtensions = (...ext) => {
|
|
|
29
19
|
startInline: []
|
|
30
20
|
}
|
|
31
21
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if ('
|
|
35
|
-
|
|
36
|
-
options.extensions.startBlock.push(rest.start);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
options.extensions.startInline.push(rest.start);
|
|
40
|
-
}
|
|
22
|
+
extensions.forEach(({ level, name, tokenizer, ...rest }) => {
|
|
23
|
+
if ('start' in rest && rest.start) {
|
|
24
|
+
if (level === 'block') {
|
|
25
|
+
options.extensions.startBlock.push(rest.start);
|
|
41
26
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
options.extensions.block.push(tokenizer);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
options.extensions.inline.push(tokenizer);
|
|
48
|
-
}
|
|
27
|
+
else {
|
|
28
|
+
options.extensions.startInline.push(rest.start);
|
|
49
29
|
}
|
|
50
|
-
}
|
|
30
|
+
}
|
|
31
|
+
if (tokenizer) {
|
|
32
|
+
if (level === 'block') {
|
|
33
|
+
options.extensions.block.push(tokenizer);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
options.extensions.inline.push(tokenizer);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
51
39
|
});
|
|
52
40
|
return options;
|
|
53
41
|
};
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
return new Lexer(parseExtensions(markedHr(), markedTable(), markedFootnote(), markedAlert(), markedMath(), markedSubSup(), markedList(), markedBr()))
|
|
42
|
+
export const lex = (markdown, extensions = []) => {
|
|
43
|
+
return new Lexer(parseExtensions(markedHr, markedTable, ...markedFootnote(), markedAlert, ...markedMath, markedSub, markedSup, markedList, markedBr, ...extensions))
|
|
57
44
|
.lex(markdown)
|
|
58
45
|
.filter((token) => token.type !== 'space' && token.type !== 'footnote');
|
|
59
46
|
};
|
|
60
|
-
export const parseBlocks = (markdown) => {
|
|
47
|
+
export const parseBlocks = (markdown, extensions = []) => {
|
|
48
|
+
const blockLexer = new Lexer(parseExtensions(markedHr, ...markedFootnote(), markedTable, ...extensions.filter(({ level, applyInBlockParsing }) => level === 'block' && applyInBlockParsing)));
|
|
61
49
|
return blockLexer.blockTokens(markdown, []).reduce((acc, block) => {
|
|
62
50
|
if (block.type === 'space' || block.type === 'footnote') {
|
|
63
51
|
return acc;
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
+
import type { Extension } from './index.js';
|
|
1
2
|
import type { Tokenizer, Tokens } from 'marked';
|
|
2
|
-
import type { TokenizerExtensionFunction } from 'marked';
|
|
3
3
|
type variantType = 'note' | 'tip' | 'important' | 'warning' | 'caution';
|
|
4
4
|
export declare function createSyntaxPattern(type: variantType): string;
|
|
5
|
-
export declare
|
|
6
|
-
extensions: Array<{
|
|
7
|
-
name: string;
|
|
8
|
-
level: 'block' | 'inline';
|
|
9
|
-
tokenizer: TokenizerExtensionFunction;
|
|
10
|
-
}>;
|
|
11
|
-
};
|
|
5
|
+
export declare const markedAlert: Extension;
|
|
12
6
|
export declare function processAlertToken(token: Tokens.Blockquote, tokenizer: Tokenizer): void;
|
|
13
7
|
export type AlertToken = {
|
|
14
8
|
type: 'alert';
|
|
@@ -3,26 +3,20 @@ const variants = ['note', 'tip', 'important', 'warning', 'caution'];
|
|
|
3
3
|
export function createSyntaxPattern(type) {
|
|
4
4
|
return `^\\s*[\\*_]*\\[!${type.toUpperCase()}\\][\\*_]*\\s*`;
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
]
|
|
24
|
-
};
|
|
25
|
-
}
|
|
6
|
+
const defaultLexer = new Lexer({ gfm: true });
|
|
7
|
+
const defaultTokenizer = defaultLexer.options.tokenizer;
|
|
8
|
+
export const markedAlert = {
|
|
9
|
+
name: 'alert',
|
|
10
|
+
level: 'block',
|
|
11
|
+
tokenizer(src) {
|
|
12
|
+
const cap = defaultTokenizer.rules.block.blockquote.exec(src);
|
|
13
|
+
if (cap) {
|
|
14
|
+
const blockquoteToken = defaultTokenizer.blockquote(src);
|
|
15
|
+
blockquoteToken && processAlertToken(blockquoteToken, this.lexer.options.tokenizer);
|
|
16
|
+
return blockquoteToken;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
26
20
|
export function processAlertToken(token, tokenizer) {
|
|
27
21
|
const matchedVariant = variants.find((type) => new RegExp(createSyntaxPattern(type), 'i').test(('text' in token && token.text) || ''));
|
|
28
22
|
if (!matchedVariant) {
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Extension } from './index.js';
|
|
2
2
|
export interface BrToken {
|
|
3
3
|
type: 'br';
|
|
4
4
|
raw: string;
|
|
5
5
|
}
|
|
6
|
-
export declare
|
|
7
|
-
extensions: Array<{
|
|
8
|
-
name: string;
|
|
9
|
-
level: 'inline';
|
|
10
|
-
tokenizer: TokenizerExtensionFunction;
|
|
11
|
-
}>;
|
|
12
|
-
};
|
|
6
|
+
export declare const markedBr: Extension;
|
package/dist/marked/marked-br.js
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return undefined;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
};
|
|
21
|
-
}
|
|
1
|
+
export const markedBr = {
|
|
2
|
+
name: 'br',
|
|
3
|
+
level: 'inline',
|
|
4
|
+
tokenizer(src) {
|
|
5
|
+
// Match HTML <br> tags (with or without closing slash, case insensitive)
|
|
6
|
+
const match = src.match(/^<br\s*\/?>/i);
|
|
7
|
+
if (match) {
|
|
8
|
+
return {
|
|
9
|
+
type: 'br',
|
|
10
|
+
raw: match[0]
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
export declare function markedFootnote(): {
|
|
4
|
-
extensions: {
|
|
5
|
-
name: string;
|
|
6
|
-
level: 'block' | 'inline';
|
|
7
|
-
tokenizer: TokenizerExtensionFunction;
|
|
8
|
-
}[];
|
|
9
|
-
};
|
|
1
|
+
import { type StreamdownToken, type Extension } from './index.js';
|
|
2
|
+
export declare function markedFootnote(): Extension[];
|
|
10
3
|
/**
|
|
11
4
|
* Represents a single footnote.
|
|
12
5
|
*/
|