svelte-streamdown 4.1.0 → 4.2.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 +149 -47
- package/dist/Block.svelte +19 -8
- package/dist/Block.svelte.d.ts +2 -0
- package/dist/Elements/Code.svelte +15 -4
- package/dist/Elements/Code.svelte.d.ts +2 -0
- package/dist/Elements/Element.svelte +32 -11
- package/dist/Elements/Element.svelte.d.ts +1 -0
- package/dist/Elements/Mermaid.svelte +5 -11
- package/dist/Elements/Mermaid.svelte.d.ts +1 -0
- package/dist/Elements/TableDownload.svelte +44 -3
- package/dist/Elements/TableDownload.svelte.d.ts +3 -1
- package/dist/Elements/fallbacks/CodeFallback.svelte +16 -6
- package/dist/Elements/fallbacks/CodeFallback.svelte.d.ts +3 -2
- package/dist/Elements/icons.d.ts +1 -0
- package/dist/Elements/icons.js +8 -0
- package/dist/Streamdown.svelte +68 -6
- package/dist/context.svelte.d.ts +37 -1
- package/dist/context.svelte.js +3 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/marked/index.d.ts +11 -4
- package/dist/marked/index.js +54 -45
- package/dist/marked/marked-mdx.d.ts +27 -1
- package/dist/marked/marked-mdx.js +37 -8
- package/dist/theme.d.ts +6 -0
- package/dist/theme.js +12 -4
- package/dist/utils/expand.svelte.d.ts +21 -0
- package/dist/utils/expand.svelte.js +46 -0
- package/dist/utils/fence.d.ts +12 -1
- package/dist/utils/fence.js +34 -17
- package/dist/utils/line-numbers.d.ts +23 -0
- package/dist/utils/line-numbers.js +30 -0
- package/dist/utils/normalize-html-indentation.d.ts +10 -0
- package/dist/utils/normalize-html-indentation.js +52 -0
- package/dist/utils/parse-incomplete-markdown.d.ts +20 -6
- package/dist/utils/parse-incomplete-markdown.js +173 -69
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { useStreamdown } from '../context.svelte.js';
|
|
3
3
|
import { scale } from 'svelte/transition';
|
|
4
|
-
import { checkIcon, copyIcon, downloadIcon } from './icons.js';
|
|
4
|
+
import { checkIcon, closeIcon, copyIcon, downloadIcon, fullscreenIcon } from './icons.js';
|
|
5
5
|
import { Popover } from './popover.svelte.js';
|
|
6
6
|
import { useClickOutside } from '../utils/useClickOutside.svelte.js';
|
|
7
7
|
import { useKeyDown } from '../utils/useKeyDown.svelte.js';
|
|
8
|
+
import { useExpand } from '../utils/expand.svelte.js';
|
|
8
9
|
import type { TableToken } from '../marked/marked-table.js';
|
|
9
10
|
import { useCopy } from '../utils/copy.svelte.js';
|
|
10
11
|
import { save } from '../utils/save.js';
|
|
@@ -15,10 +16,13 @@
|
|
|
15
16
|
|
|
16
17
|
let {
|
|
17
18
|
token,
|
|
18
|
-
id
|
|
19
|
+
id,
|
|
20
|
+
expanded = $bindable(false)
|
|
19
21
|
}: {
|
|
20
22
|
token: TableToken;
|
|
21
23
|
id: string;
|
|
24
|
+
/** Owned by the caller: the wrapper it renders is what expands. */
|
|
25
|
+
expanded?: boolean;
|
|
22
26
|
} = $props();
|
|
23
27
|
const streamdown = useStreamdown();
|
|
24
28
|
const popover = new Popover();
|
|
@@ -76,7 +80,22 @@
|
|
|
76
80
|
}
|
|
77
81
|
};
|
|
78
82
|
|
|
79
|
-
const tableElement = () => document.querySelector(`[data-streamdown-table="${id}"]`);
|
|
83
|
+
const tableElement = () => document.querySelector<HTMLElement>(`[data-streamdown-table="${id}"]`);
|
|
84
|
+
|
|
85
|
+
const expand = useExpand({
|
|
86
|
+
get expanded() {
|
|
87
|
+
return expanded;
|
|
88
|
+
},
|
|
89
|
+
set expanded(value: boolean) {
|
|
90
|
+
expanded = value;
|
|
91
|
+
},
|
|
92
|
+
getTarget: tableElement
|
|
93
|
+
});
|
|
94
|
+
const expandLabel = $derived(
|
|
95
|
+
expanded
|
|
96
|
+
? streamdown.translations.controls.exitTableFullscreen
|
|
97
|
+
: streamdown.translations.controls.tableFullscreen
|
|
98
|
+
);
|
|
80
99
|
|
|
81
100
|
const copyOrDownload = (type: Format) => {
|
|
82
101
|
if (type === 'Markdown') {
|
|
@@ -145,9 +164,15 @@
|
|
|
145
164
|
</dialog>
|
|
146
165
|
{/if}
|
|
147
166
|
|
|
167
|
+
<!-- The wrapper this toolbar controls becomes `position: fixed` when expanded,
|
|
168
|
+
so the buttons have to leave the flow with it or they end up underneath. -->
|
|
148
169
|
<div
|
|
149
170
|
data-streamdown-table-download
|
|
150
171
|
class=" right-0 ml-auto flex items-center justify-end gap-2 p-1"
|
|
172
|
+
style:position={expanded ? 'fixed' : undefined}
|
|
173
|
+
style:top={expanded ? '24px' : undefined}
|
|
174
|
+
style:right={expanded ? '32px' : undefined}
|
|
175
|
+
style:z-index={expanded ? 2147483647 : undefined}
|
|
151
176
|
>
|
|
152
177
|
{#each modes as mode (mode)}
|
|
153
178
|
<button
|
|
@@ -186,6 +211,22 @@
|
|
|
186
211
|
{/if}
|
|
187
212
|
</button>
|
|
188
213
|
{/each}
|
|
214
|
+
{#if streamdown.controls.tableFullscreen}
|
|
215
|
+
<button
|
|
216
|
+
class={streamdown.theme.components.button}
|
|
217
|
+
onclick={(e) => expand.toggle(e.currentTarget)}
|
|
218
|
+
type="button"
|
|
219
|
+
title={expandLabel}
|
|
220
|
+
aria-label={expandLabel}
|
|
221
|
+
aria-pressed={expanded}
|
|
222
|
+
>
|
|
223
|
+
{#if expanded}
|
|
224
|
+
{@render (streamdown.icons?.close || closeIcon)()}
|
|
225
|
+
{:else}
|
|
226
|
+
{@render (streamdown.icons?.fullscreen || fullscreenIcon)()}
|
|
227
|
+
{/if}
|
|
228
|
+
</button>
|
|
229
|
+
{/if}
|
|
189
230
|
<span aria-live="polite" style={srOnly}
|
|
190
231
|
>{copy.isCopied ? streamdown.translations.controls.copiedTable : ''}</span
|
|
191
232
|
>
|
|
@@ -2,7 +2,9 @@ import type { TableToken } from '../marked/marked-table.js';
|
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
token: TableToken;
|
|
4
4
|
id: string;
|
|
5
|
+
/** Owned by the caller: the wrapper it renders is what expands. */
|
|
6
|
+
expanded?: boolean;
|
|
5
7
|
};
|
|
6
|
-
declare const TableDownload: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
declare const TableDownload: import("svelte").Component<$$ComponentProps, {}, "expanded">;
|
|
7
9
|
type TableDownload = ReturnType<typeof TableDownload>;
|
|
8
10
|
export default TableDownload;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { useStreamdown } from '../../context.svelte.js';
|
|
3
3
|
import { usePinnedScroll } from '../../utils/usePinnedScroll.svelte.js';
|
|
4
|
-
import
|
|
4
|
+
import { resolveLineNumbers } from '../../utils/line-numbers.js';
|
|
5
|
+
import type { CodeToken } from '../../marked/index.js';
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
token,
|
|
8
9
|
id,
|
|
9
|
-
incomplete = false
|
|
10
|
+
incomplete = false,
|
|
11
|
+
animate = true
|
|
10
12
|
}: {
|
|
11
|
-
token:
|
|
13
|
+
token: CodeToken;
|
|
12
14
|
id: string;
|
|
13
15
|
/** The fence is still being streamed; nothing below it is final yet. */
|
|
14
16
|
incomplete?: boolean;
|
|
17
|
+
animate?: boolean;
|
|
15
18
|
} = $props();
|
|
16
19
|
|
|
17
20
|
const streamdown = useStreamdown();
|
|
@@ -22,6 +25,8 @@
|
|
|
22
25
|
// too.
|
|
23
26
|
const code = $derived(token.text.replace(/\n+$/, ''));
|
|
24
27
|
|
|
28
|
+
const lineNumbers = $derived(resolveLineNumbers(token.meta, streamdown.lineNumbers ?? false));
|
|
29
|
+
|
|
25
30
|
// Same scroll container as Code.svelte: `pre` already scrolls horizontally.
|
|
26
31
|
const pinnedScroll = usePinnedScroll({
|
|
27
32
|
get maxHeight() {
|
|
@@ -36,7 +41,7 @@
|
|
|
36
41
|
<div
|
|
37
42
|
data-streamdown-code={id}
|
|
38
43
|
data-incomplete={incomplete || undefined}
|
|
39
|
-
style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
44
|
+
style={animate && streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
40
45
|
class={streamdown.theme.code.base}
|
|
41
46
|
>
|
|
42
47
|
<div class={streamdown.theme.code.header}>
|
|
@@ -45,11 +50,16 @@
|
|
|
45
50
|
<div style="height: fit-content; width: 100%;" class={streamdown.theme.code.container}>
|
|
46
51
|
<pre
|
|
47
52
|
class={streamdown.theme.code.pre}
|
|
53
|
+
data-line-numbers={lineNumbers.enabled || undefined}
|
|
54
|
+
style:counter-reset={lineNumbers.enabled ? `sd-line ${lineNumbers.start - 1}` : undefined}
|
|
48
55
|
style:max-height={streamdown.codeBlockMaxHeight}
|
|
49
56
|
style:overflow-y={streamdown.codeBlockMaxHeight ? 'auto' : undefined}
|
|
50
57
|
{@attach pinnedScroll}><code
|
|
51
|
-
>{#each code.split('\n') as line}<span
|
|
52
|
-
|
|
58
|
+
>{#each code.split('\n') as line}<span
|
|
59
|
+
class="{streamdown.theme.code.line}{lineNumbers.enabled
|
|
60
|
+
? ` ${streamdown.theme.code.lineNumber}`
|
|
61
|
+
: ''}"
|
|
62
|
+
><span style={animate && streamdown.isMounted ? streamdown.animationTextStyle : ''}
|
|
53
63
|
>{line.trim().length > 0 ? line : '\u200B'}</span
|
|
54
64
|
></span
|
|
55
65
|
>{/each}</code
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CodeToken } from '../../marked/index.js';
|
|
2
2
|
type $$ComponentProps = {
|
|
3
|
-
token:
|
|
3
|
+
token: CodeToken;
|
|
4
4
|
id: string;
|
|
5
5
|
/** The fence is still being streamed; nothing below it is final yet. */
|
|
6
6
|
incomplete?: boolean;
|
|
7
|
+
animate?: boolean;
|
|
7
8
|
};
|
|
8
9
|
declare const CodeFallback: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
10
|
type CodeFallback = ReturnType<typeof CodeFallback>;
|
package/dist/Elements/icons.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export declare const zoomInIcon: import("svelte").Snippet<[]>;
|
|
|
5
5
|
export declare const zoomOutIcon: import("svelte").Snippet<[]>;
|
|
6
6
|
export declare const fitViewIcon: import("svelte").Snippet<[]>;
|
|
7
7
|
export declare const fullscreenIcon: import("svelte").Snippet<[]>;
|
|
8
|
+
export declare const closeIcon: import("svelte").Snippet<[]>;
|
|
8
9
|
export declare const chevronRight: import("svelte").Snippet<[]>;
|
|
9
10
|
export declare const chevronLeft: import("svelte").Snippet<[]>;
|
package/dist/Elements/icons.js
CHANGED
|
@@ -142,6 +142,14 @@ export const fullscreenIcon = createRawSnippet(() => {
|
|
|
142
142
|
`
|
|
143
143
|
};
|
|
144
144
|
});
|
|
145
|
+
export const closeIcon = createRawSnippet(() => {
|
|
146
|
+
return {
|
|
147
|
+
render: () => `
|
|
148
|
+
<svg
|
|
149
|
+
aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
150
|
+
`
|
|
151
|
+
};
|
|
152
|
+
});
|
|
145
153
|
export const chevronRight = createRawSnippet(() => {
|
|
146
154
|
return {
|
|
147
155
|
render: () => `
|
package/dist/Streamdown.svelte
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
type StreamdownProps
|
|
7
7
|
} from './context.svelte.js';
|
|
8
8
|
import { mergeTheme, shadcnTheme } from './theme.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
parseBlocks,
|
|
11
|
+
createParseBlocksCache,
|
|
12
|
+
compileTags,
|
|
13
|
+
DEFAULT_TAGS
|
|
14
|
+
} from './marked/index.js';
|
|
15
|
+
import { normalizeHtmlIndentation as dedentHtml } from './utils/normalize-html-indentation.js';
|
|
10
16
|
import { hasUnclosedFence } from './utils/fence.js';
|
|
11
17
|
|
|
12
18
|
let {
|
|
@@ -30,6 +36,7 @@
|
|
|
30
36
|
controls,
|
|
31
37
|
codeBlockMaxHeight,
|
|
32
38
|
tableMaxHeight,
|
|
39
|
+
lineNumbers = false,
|
|
33
40
|
animation,
|
|
34
41
|
element = $bindable(),
|
|
35
42
|
icons,
|
|
@@ -38,6 +45,9 @@
|
|
|
38
45
|
sources,
|
|
39
46
|
inlineCitationsMode = 'carousel',
|
|
40
47
|
mdxComponents,
|
|
48
|
+
customTags,
|
|
49
|
+
literalTagContent,
|
|
50
|
+
normalizeHtmlIndentation = false,
|
|
41
51
|
components,
|
|
42
52
|
static: isStatic,
|
|
43
53
|
...snippets
|
|
@@ -55,6 +65,24 @@
|
|
|
55
65
|
mermaidConfig?.theme ? mermaidConfig.theme : darkMode.current ? 'dark' : 'default'
|
|
56
66
|
);
|
|
57
67
|
|
|
68
|
+
// Per-instance incremental state: append-only content updates re-lex only
|
|
69
|
+
// the last couple of blocks instead of the whole document.
|
|
70
|
+
const blocksCache = createParseBlocksCache();
|
|
71
|
+
|
|
72
|
+
// One compile per allowlist identity, shared by the lexer and the streaming
|
|
73
|
+
// completer so the two can never disagree about what counts as a tag.
|
|
74
|
+
const tags = $derived.by(() => {
|
|
75
|
+
const names = [...(customTags ?? []), ...Object.keys(mdxComponents ?? {})];
|
|
76
|
+
// With nothing to compile, share DEFAULT_TAGS: the lexer options cache is
|
|
77
|
+
// keyed on this object, so a private copy per instance would rebuild and
|
|
78
|
+
// retain its own ~20 tokenizer registrations for no reason.
|
|
79
|
+
return names.length === 0 && !literalTagContent?.length
|
|
80
|
+
? DEFAULT_TAGS
|
|
81
|
+
: compileTags(names, literalTagContent);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const source = $derived(normalizeHtmlIndentation ? dedentHtml(content) : content);
|
|
85
|
+
|
|
58
86
|
streamdown = new StreamdownContext({
|
|
59
87
|
get element() {
|
|
60
88
|
return element;
|
|
@@ -77,6 +105,12 @@
|
|
|
77
105
|
get highlightTheme() {
|
|
78
106
|
return resolvedHighlightTheme;
|
|
79
107
|
},
|
|
108
|
+
get animateUpdate() {
|
|
109
|
+
// A streamed chunk keeps animating; a replacement or a bulk append (the
|
|
110
|
+
// demo's "Show All" is an append of 95% of the document) renders plain.
|
|
111
|
+
// The first parse stays animatable so `animateOnMount` keeps its meaning.
|
|
112
|
+
return blocksCache.lastUpdate !== 'bulk';
|
|
113
|
+
},
|
|
80
114
|
get snippets() {
|
|
81
115
|
return snippets;
|
|
82
116
|
},
|
|
@@ -166,6 +200,7 @@
|
|
|
166
200
|
tableCopy: table.copy,
|
|
167
201
|
tableDownload: table.download,
|
|
168
202
|
tableDownloadFilename: table.filename ?? 'table',
|
|
203
|
+
tableFullscreen: table.enabled && tableSection.fullscreen !== false,
|
|
169
204
|
tableCsvSeparator: tableSection.csvSeparator ?? ',',
|
|
170
205
|
mermaid: mermaid.enabled,
|
|
171
206
|
mermaidDownload: mermaid.download,
|
|
@@ -178,6 +213,9 @@
|
|
|
178
213
|
get codeBlockMaxHeight() {
|
|
179
214
|
return codeBlockMaxHeight;
|
|
180
215
|
},
|
|
216
|
+
get lineNumbers() {
|
|
217
|
+
return lineNumbers;
|
|
218
|
+
},
|
|
181
219
|
get tableMaxHeight() {
|
|
182
220
|
return tableMaxHeight;
|
|
183
221
|
},
|
|
@@ -193,6 +231,9 @@
|
|
|
193
231
|
get mdxComponents() {
|
|
194
232
|
return mdxComponents;
|
|
195
233
|
},
|
|
234
|
+
get tags() {
|
|
235
|
+
return tags;
|
|
236
|
+
},
|
|
196
237
|
get components() {
|
|
197
238
|
return components;
|
|
198
239
|
}
|
|
@@ -200,11 +241,8 @@
|
|
|
200
241
|
|
|
201
242
|
const id = $props.id();
|
|
202
243
|
|
|
203
|
-
// Per-instance incremental state: append-only content updates re-lex only
|
|
204
|
-
// the last couple of blocks instead of the whole document.
|
|
205
|
-
const blocksCache = createParseBlocksCache();
|
|
206
244
|
const blocks = $derived(
|
|
207
|
-
isStatic ?
|
|
245
|
+
isStatic ? source : parseBlocks(source, streamdown.extensions, blocksCache, tags)
|
|
208
246
|
);
|
|
209
247
|
|
|
210
248
|
// Only the tail of a live stream can be mid-fence — a static render is finished
|
|
@@ -216,12 +254,13 @@
|
|
|
216
254
|
|
|
217
255
|
<div bind:this={element} class={className}>
|
|
218
256
|
{#if isStatic}
|
|
219
|
-
<Block static={isStatic} block={
|
|
257
|
+
<Block static={isStatic} block={source} />
|
|
220
258
|
{:else}
|
|
221
259
|
{#each blocks as block, index (`${id}-block-${index}`)}
|
|
222
260
|
<Block
|
|
223
261
|
static={isStatic}
|
|
224
262
|
{block}
|
|
263
|
+
live={index === blocks.length - 1}
|
|
225
264
|
incomplete={lastBlockIncomplete && index === blocks.length - 1}
|
|
226
265
|
/>
|
|
227
266
|
{/each}
|
|
@@ -230,6 +269,29 @@
|
|
|
230
269
|
|
|
231
270
|
<style global>
|
|
232
271
|
:global {
|
|
272
|
+
/* Line numbers: a counter on the line span that already exists (why it is
|
|
273
|
+
here and not in the theme: see resolveLineNumbers). CSS ships as a string
|
|
274
|
+
at runtime, so keep this short. */
|
|
275
|
+
[data-streamdown-code] pre[data-line-numbers] > code > span {
|
|
276
|
+
counter-increment: sd-line;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
[data-streamdown-code] pre[data-line-numbers] > code > span::before {
|
|
280
|
+
content: counter(sd-line);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Lives here, not in Mermaid.svelte: the table overlay needs it on pages
|
|
284
|
+
that never render a diagram. */
|
|
285
|
+
[data-expanded='true'] {
|
|
286
|
+
position: fixed;
|
|
287
|
+
top: 16px;
|
|
288
|
+
left: 16px;
|
|
289
|
+
width: calc(100vw - 32px);
|
|
290
|
+
height: calc(100vh - 32px);
|
|
291
|
+
z-index: 2147483647;
|
|
292
|
+
margin: 0px;
|
|
293
|
+
}
|
|
294
|
+
|
|
233
295
|
@keyframes sd-fade {
|
|
234
296
|
from {
|
|
235
297
|
opacity: 0;
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export type Translations = {
|
|
|
28
28
|
tableFormatHtml: string;
|
|
29
29
|
tableFormatCsv: string;
|
|
30
30
|
tableFormatTsv: string;
|
|
31
|
+
tableFullscreen: string;
|
|
32
|
+
exitTableFullscreen: string;
|
|
33
|
+
table: string;
|
|
31
34
|
downloadDiagram: string;
|
|
32
35
|
downloadDiagramPng: string;
|
|
33
36
|
downloadDiagramSvg: string;
|
|
@@ -48,8 +51,12 @@ export type Translations = {
|
|
|
48
51
|
};
|
|
49
52
|
export declare const defaultTranslations: Translations;
|
|
50
53
|
export interface StreamdownContext extends Omit<StreamdownProps, keyof Snippets | 'class' | 'theme' | 'highlightTheme' | 'inlineCitationsMode'> {
|
|
54
|
+
/** `customTags` + `mdxComponents` keys compiled once; lexer and completer share it. */
|
|
55
|
+
tags: TagMatchers;
|
|
51
56
|
snippets: Snippets;
|
|
52
57
|
highlightTheme: HighlightTheme;
|
|
58
|
+
/** False while rendering a bulk update (a replacement or a paste-sized append); Block captures it per update. */
|
|
59
|
+
animateUpdate: boolean;
|
|
53
60
|
theme: Theme;
|
|
54
61
|
translations: Translations;
|
|
55
62
|
controls: ResolvedControls;
|
|
@@ -69,10 +76,12 @@ export declare class StreamdownContext<Source extends Record<string, any> = Reco
|
|
|
69
76
|
constructor(props: Omit<StreamdownProps, keyof Snippets | 'class' | 'highlightTheme'> & {
|
|
70
77
|
snippets: Snippets<Source>;
|
|
71
78
|
highlightTheme: HighlightTheme;
|
|
79
|
+
animateUpdate: boolean;
|
|
80
|
+
tags: TagMatchers;
|
|
72
81
|
});
|
|
73
82
|
}
|
|
74
83
|
export declare const useStreamdown: () => StreamdownContext<Record<string, any>>;
|
|
75
|
-
import type { AlertToken, CodeToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoot, THeadRow, TRow, TD, TH, Extension, GenericToken, CitationToken, MdxToken } from './marked/index.js';
|
|
84
|
+
import type { AlertToken, CodeToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoot, THeadRow, TRow, TD, TH, Extension, GenericToken, CitationToken, MdxToken, TagMatchers } from './marked/index.js';
|
|
76
85
|
import type { Tokens } from 'marked';
|
|
77
86
|
import type { CsvSeparator } from './utils/table-export.js';
|
|
78
87
|
import type { ListItemToken, ListToken } from './marked/marked-list.js';
|
|
@@ -148,6 +157,7 @@ export type TableControls = boolean | {
|
|
|
148
157
|
enabled?: boolean;
|
|
149
158
|
copy?: boolean;
|
|
150
159
|
download?: DownloadControl<TableToken>;
|
|
160
|
+
fullscreen?: boolean;
|
|
151
161
|
csvSeparator?: CsvSeparator;
|
|
152
162
|
};
|
|
153
163
|
export type MermaidControls = boolean | {
|
|
@@ -170,6 +180,7 @@ export type ResolvedControls = {
|
|
|
170
180
|
tableCopy: boolean;
|
|
171
181
|
tableDownload: boolean;
|
|
172
182
|
tableDownloadFilename: string | ((token: TableToken) => string);
|
|
183
|
+
tableFullscreen: boolean;
|
|
173
184
|
tableCsvSeparator: CsvSeparator;
|
|
174
185
|
mermaid: boolean;
|
|
175
186
|
mermaidDownload: boolean;
|
|
@@ -203,6 +214,13 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
203
214
|
[K in keyof Translations]?: Partial<Translations[K]>;
|
|
204
215
|
};
|
|
205
216
|
controls?: Controls;
|
|
217
|
+
/**
|
|
218
|
+
* Number the lines of every code block. Default false — upstream defaults it on,
|
|
219
|
+
* but turning it on here would change every existing render. A fence can flip it
|
|
220
|
+
* either way with a `lineNumbers` / `noLineNumbers` meta word, and start at N with
|
|
221
|
+
* `startLine=N`.
|
|
222
|
+
*/
|
|
223
|
+
lineNumbers?: boolean;
|
|
206
224
|
codeBlockMaxHeight?: string;
|
|
207
225
|
tableMaxHeight?: string;
|
|
208
226
|
renderHtml?: boolean | ((token: Tokens.HTML | Tokens.Tag) => string);
|
|
@@ -218,6 +236,7 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
218
236
|
copy?: Snippet;
|
|
219
237
|
download?: Snippet;
|
|
220
238
|
fullscreen?: Snippet;
|
|
239
|
+
close?: Snippet;
|
|
221
240
|
zoomIn?: Snippet;
|
|
222
241
|
zoomOut?: Snippet;
|
|
223
242
|
fitView?: Snippet;
|
|
@@ -231,6 +250,21 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
231
250
|
check?: Snippet;
|
|
232
251
|
};
|
|
233
252
|
extensions?: Extension[];
|
|
253
|
+
/**
|
|
254
|
+
* Extra tag names the MDX tokenizer accepts on top of PascalCase, so
|
|
255
|
+
* `<ai-thinking>**bold**</ai-thinking>` becomes a component with parsed
|
|
256
|
+
* children instead of a literal html block. Keys of `mdxComponents` are
|
|
257
|
+
* allowed automatically; nothing else lowercase is, so real HTML is untouched.
|
|
258
|
+
*/
|
|
259
|
+
customTags?: string[];
|
|
260
|
+
/** Tags whose children render verbatim — no Markdown, `**` and `_` left alone. */
|
|
261
|
+
literalTagContent?: string[];
|
|
262
|
+
/**
|
|
263
|
+
* Dedent pretty-printed HTML before parsing, so a nested `<div>` indented four
|
|
264
|
+
* spaces after a blank line is not read as an indented code block. Off by
|
|
265
|
+
* default: dedenting is lossy, and `<pre>`/`<code>` bodies are never touched.
|
|
266
|
+
*/
|
|
267
|
+
normalizeHtmlIndentation?: boolean;
|
|
234
268
|
children?: Snippet<[{
|
|
235
269
|
streamdown: StreamdownContext;
|
|
236
270
|
token: GenericToken;
|
|
@@ -246,11 +280,13 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
246
280
|
token: Tokens.Code;
|
|
247
281
|
id: string;
|
|
248
282
|
incomplete: boolean;
|
|
283
|
+
animate: boolean;
|
|
249
284
|
}, any, any>;
|
|
250
285
|
mermaid?: Component<{
|
|
251
286
|
token: Tokens.Code;
|
|
252
287
|
id: string;
|
|
253
288
|
incomplete: boolean;
|
|
289
|
+
animate: boolean;
|
|
254
290
|
}, any, any>;
|
|
255
291
|
math?: Component<{
|
|
256
292
|
token: MathToken;
|
package/dist/context.svelte.js
CHANGED
|
@@ -19,6 +19,9 @@ export const defaultTranslations = {
|
|
|
19
19
|
tableFormatHtml: 'HTML',
|
|
20
20
|
tableFormatCsv: 'CSV',
|
|
21
21
|
tableFormatTsv: 'TSV',
|
|
22
|
+
tableFullscreen: 'Expand table',
|
|
23
|
+
exitTableFullscreen: 'Collapse table',
|
|
24
|
+
table: 'Table',
|
|
22
25
|
downloadDiagram: 'Download diagram',
|
|
23
26
|
downloadDiagramPng: 'PNG',
|
|
24
27
|
downloadDiagramSvg: 'SVG',
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { default as Streamdown } from './Streamdown.svelte';
|
|
|
2
2
|
export { useStreamdown, defaultTranslations, type StreamdownProps, type Translations, type Controls, type CodeControls, type TableControls, type MermaidControls, type ResolvedControls } from './context.svelte.js';
|
|
3
3
|
export { extractTableData, tableDataToCSV, tableDataToTSV, tableDataToMarkdown, tableDataToHTML, type TableData, type CsvSeparator } from './utils/table-export.js';
|
|
4
4
|
export { theme, shadcnTheme, mergeTheme, type Theme } from './theme.js';
|
|
5
|
-
export { type CodeToken, type Extension, type StreamdownToken, lex, parseBlocks } from './marked/index.js';
|
|
6
|
-
export {
|
|
5
|
+
export { type CodeToken, type Extension, type StreamdownToken, type TagMatchers, compileTags, lex, parseBlocks } from './marked/index.js';
|
|
6
|
+
export { normalizeHtmlIndentation } from './utils/normalize-html-indentation.js';
|
|
7
|
+
export { parseIncompleteMarkdown, type Plugin, type CompleterOptions, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
|
|
7
8
|
export { defineLanguage, type LanguageDefinition } from '@tanstack/highlight';
|
|
8
9
|
export type { HighlightTheme } from '@tanstack/highlight/theme';
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export { default as Streamdown } from './Streamdown.svelte';
|
|
|
2
2
|
export { useStreamdown, defaultTranslations } from './context.svelte.js';
|
|
3
3
|
export { extractTableData, tableDataToCSV, tableDataToTSV, tableDataToMarkdown, tableDataToHTML } from './utils/table-export.js';
|
|
4
4
|
export { theme, shadcnTheme, mergeTheme } from './theme.js';
|
|
5
|
-
export { lex, parseBlocks } from './marked/index.js';
|
|
5
|
+
export { compileTags, lex, parseBlocks } from './marked/index.js';
|
|
6
|
+
export { normalizeHtmlIndentation } from './utils/normalize-html-indentation.js';
|
|
6
7
|
export { parseIncompleteMarkdown, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
|
|
7
8
|
export { defineLanguage } from '@tanstack/highlight';
|
package/dist/marked/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { type TableToken, type THead, type TBody, type TFoot, type THeadRow, typ
|
|
|
10
10
|
import { type DescriptionDetailToken, type DescriptionListToken, type DescriptionTermToken, type DescriptionToken } from './marked-dl.js';
|
|
11
11
|
import { type AlignToken } from './marked-align.js';
|
|
12
12
|
import { type CitationToken } from './marked-citations.js';
|
|
13
|
-
import { type MdxToken } from './marked-mdx.js';
|
|
13
|
+
import { type MdxToken, type TagMatchers } from './marked-mdx.js';
|
|
14
14
|
export type GenericToken = {
|
|
15
15
|
type: string;
|
|
16
16
|
raw: string;
|
|
@@ -32,7 +32,7 @@ export type CodeToken = Tokens.Code & {
|
|
|
32
32
|
};
|
|
33
33
|
export type StreamdownToken = Exclude<MarkedToken, Tokens.List | Tokens.ListItem | Tokens.Table | Tokens.Code> | CodeToken | ListToken | ListItemToken | MathToken | AlertToken | FootnoteToken | SubSupToken | BrToken | HrToken | TableToken | THead | TBody | TFoot | THeadRow | TRow | TH | TD | DescriptionListToken | DescriptionToken | DescriptionDetailToken | DescriptionTermToken | AlignToken | CitationToken | MdxToken;
|
|
34
34
|
export type { TableToken, THead, TBody, TFoot, THeadRow, TRow, TH, TD } from './marked-table.js';
|
|
35
|
-
export declare const lex: (markdown: string, extensions?: Extension[]) => StreamdownToken[];
|
|
35
|
+
export declare const lex: (markdown: string, extensions?: Extension[], tags?: TagMatchers) => StreamdownToken[];
|
|
36
36
|
/**
|
|
37
37
|
* Opaque incremental state for `parseBlocks`. Create one per Streamdown
|
|
38
38
|
* instance (or per simulated stream) and pass it on every call: append-only
|
|
@@ -57,7 +57,14 @@ export type ParseBlocksCache = {
|
|
|
57
57
|
keptBefore: number[];
|
|
58
58
|
/** persistent list of kept raws; only its tail is rewritten on append */
|
|
59
59
|
blocks: string[];
|
|
60
|
+
/**
|
|
61
|
+
* What kind of update the last call was. Rendering skips the streaming
|
|
62
|
+
* animation on a `bulk` one — a replacement, or an append far larger than any
|
|
63
|
+
* streamed chunk — where ~10k spans would otherwise all animate in one frame.
|
|
64
|
+
*/
|
|
65
|
+
lastUpdate: 'first' | 'stream' | 'bulk';
|
|
60
66
|
};
|
|
61
67
|
export declare const createParseBlocksCache: () => ParseBlocksCache;
|
|
62
|
-
export declare const parseBlocks: (markdown: string, extensions?: Extension[], cache?: ParseBlocksCache) => string[];
|
|
63
|
-
export
|
|
68
|
+
export declare const parseBlocks: (markdown: string, extensions?: Extension[], cache?: ParseBlocksCache, tags?: TagMatchers) => string[];
|
|
69
|
+
export { compileTags, DEFAULT_TAGS } from './marked-mdx.js';
|
|
70
|
+
export type { TagMatchers, MathToken, AlertToken, FootnoteToken, SubSupToken, BrToken, HrToken, AlignToken, CitationToken, MdxToken };
|