svelte-streamdown 4.0.0 → 4.1.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 +211 -38
- package/dist/Block.svelte +10 -4
- package/dist/Block.svelte.d.ts +2 -0
- package/dist/Elements/Alert.svelte +2 -1
- package/dist/Elements/Citation.svelte +9 -2
- package/dist/Elements/Code.svelte +65 -24
- package/dist/Elements/Code.svelte.d.ts +4 -2
- package/dist/Elements/Element.svelte +36 -11
- package/dist/Elements/Element.svelte.d.ts +1 -0
- package/dist/Elements/FootnoteRef.svelte +1 -0
- package/dist/Elements/Image.svelte +3 -2
- package/dist/Elements/Link.svelte +3 -2
- package/dist/Elements/Mermaid.svelte +69 -14
- package/dist/Elements/Mermaid.svelte.d.ts +4 -2
- package/dist/Elements/MermaidDownload.svelte +30 -9
- package/dist/Elements/MermaidDownload.svelte.d.ts +2 -0
- package/dist/Elements/TableDownload.svelte +60 -78
- package/dist/Elements/fallbacks/CodeFallback.svelte +28 -3
- package/dist/Elements/fallbacks/CodeFallback.svelte.d.ts +2 -0
- package/dist/Elements/fallbacks/MermaidFallback.svelte +12 -3
- package/dist/Elements/fallbacks/MermaidFallback.svelte.d.ts +2 -0
- package/dist/Elements/icons.js +10 -1
- package/dist/Elements/srOnly.d.ts +1 -0
- package/dist/Elements/srOnly.js +3 -0
- package/dist/Streamdown.svelte +68 -13
- package/dist/context.svelte.d.ts +98 -22
- package/dist/context.svelte.js +38 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/marked/index.d.ts +8 -1
- package/dist/marked/index.js +65 -14
- package/dist/marked/marked-footnotes.js +6 -2
- package/dist/marked/marked-math.js +40 -1
- package/dist/marked/marked-subsup.js +16 -3
- package/dist/utils/fence.d.ts +16 -0
- package/dist/utils/fence.js +39 -0
- package/dist/utils/parse-incomplete-markdown.d.ts +5 -1
- package/dist/utils/parse-incomplete-markdown.js +347 -122
- package/dist/utils/save.js +4 -1
- package/dist/utils/table-export.d.ts +14 -0
- package/dist/utils/table-export.js +82 -0
- package/dist/utils/url.js +6 -2
- package/dist/utils/usePinnedScroll.svelte.d.ts +22 -0
- package/dist/utils/usePinnedScroll.svelte.js +36 -0
- package/package.json +4 -2
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { useStreamdown } from '../../context.svelte.js';
|
|
3
|
+
import { usePinnedScroll } from '../../utils/usePinnedScroll.svelte.js';
|
|
3
4
|
import type { Tokens } from 'marked';
|
|
4
5
|
|
|
5
6
|
const {
|
|
6
7
|
token,
|
|
7
|
-
id
|
|
8
|
+
id,
|
|
9
|
+
incomplete = false
|
|
8
10
|
}: {
|
|
9
11
|
token: Tokens.Code;
|
|
10
12
|
id: string;
|
|
13
|
+
/** The fence is still being streamed; nothing below it is final yet. */
|
|
14
|
+
incomplete?: boolean;
|
|
11
15
|
} = $props();
|
|
12
16
|
|
|
13
17
|
const streamdown = useStreamdown();
|
|
18
|
+
|
|
19
|
+
// Same trim as Code.svelte/Mermaid.svelte: marked keeps the fence's trailing
|
|
20
|
+
// blank lines in `text`, so they render as empty lines and flicker in and out
|
|
21
|
+
// on nearly every streamed chunk. This is the default renderer, so it needs it
|
|
22
|
+
// too.
|
|
23
|
+
const code = $derived(token.text.replace(/\n+$/, ''));
|
|
24
|
+
|
|
25
|
+
// Same scroll container as Code.svelte: `pre` already scrolls horizontally.
|
|
26
|
+
const pinnedScroll = usePinnedScroll({
|
|
27
|
+
get maxHeight() {
|
|
28
|
+
return streamdown.codeBlockMaxHeight;
|
|
29
|
+
},
|
|
30
|
+
get content() {
|
|
31
|
+
return code;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
14
34
|
</script>
|
|
15
35
|
|
|
16
36
|
<div
|
|
17
37
|
data-streamdown-code={id}
|
|
38
|
+
data-incomplete={incomplete || undefined}
|
|
18
39
|
style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
19
40
|
class={streamdown.theme.code.base}
|
|
20
41
|
>
|
|
@@ -22,8 +43,12 @@
|
|
|
22
43
|
<span class={streamdown.theme.code.language}>{token.lang}</span>
|
|
23
44
|
</div>
|
|
24
45
|
<div style="height: fit-content; width: 100%;" class={streamdown.theme.code.container}>
|
|
25
|
-
<pre
|
|
26
|
-
|
|
46
|
+
<pre
|
|
47
|
+
class={streamdown.theme.code.pre}
|
|
48
|
+
style:max-height={streamdown.codeBlockMaxHeight}
|
|
49
|
+
style:overflow-y={streamdown.codeBlockMaxHeight ? 'auto' : undefined}
|
|
50
|
+
{@attach pinnedScroll}><code
|
|
51
|
+
>{#each code.split('\n') as line}<span class={streamdown.theme.code.line}
|
|
27
52
|
><span style={streamdown.isMounted ? streamdown.animationTextStyle : ''}
|
|
28
53
|
>{line.trim().length > 0 ? line : '\u200B'}</span
|
|
29
54
|
></span
|
|
@@ -2,6 +2,8 @@ import type { Tokens } from 'marked';
|
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
token: Tokens.Code;
|
|
4
4
|
id: string;
|
|
5
|
+
/** The fence is still being streamed; nothing below it is final yet. */
|
|
6
|
+
incomplete?: boolean;
|
|
5
7
|
};
|
|
6
8
|
declare const CodeFallback: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
9
|
type CodeFallback = ReturnType<typeof CodeFallback>;
|
|
@@ -4,16 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
token,
|
|
7
|
-
id
|
|
7
|
+
id,
|
|
8
|
+
incomplete = false
|
|
8
9
|
}: {
|
|
9
10
|
token: Tokens.Code;
|
|
10
11
|
id: string;
|
|
12
|
+
/** The fence is still being streamed; nothing below it is final yet. */
|
|
13
|
+
incomplete?: boolean;
|
|
11
14
|
} = $props();
|
|
12
15
|
|
|
13
16
|
const streamdown = useStreamdown();
|
|
17
|
+
|
|
18
|
+
// Same trim as Code.svelte/Mermaid.svelte: marked keeps the fence's trailing
|
|
19
|
+
// blank lines in `text`, so they render as empty lines and flicker in and out
|
|
20
|
+
// on nearly every streamed chunk. This is the default renderer, so it needs it
|
|
21
|
+
// too.
|
|
22
|
+
const chart = $derived(token.text.replace(/\n+$/, ''));
|
|
14
23
|
</script>
|
|
15
24
|
|
|
16
|
-
<div data-streamdown-mermaid={id}>
|
|
25
|
+
<div data-streamdown-mermaid={id} data-incomplete={incomplete || undefined}>
|
|
17
26
|
<div
|
|
18
27
|
style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
|
|
19
28
|
class={streamdown.theme.code.base}
|
|
@@ -23,7 +32,7 @@
|
|
|
23
32
|
</div>
|
|
24
33
|
<div style="height: fit-content; width: 100%;" class={streamdown.theme.code.container}>
|
|
25
34
|
<pre class={streamdown.theme.code.pre}><code
|
|
26
|
-
>{#each
|
|
35
|
+
>{#each chart.split('\n') as line}<span class={streamdown.theme.code.line}
|
|
27
36
|
><span style={streamdown.isMounted ? streamdown.animationTextStyle : ''}
|
|
28
37
|
>{line.trim().length > 0 ? line : '\u200B'}</span
|
|
29
38
|
></span
|
|
@@ -2,6 +2,8 @@ import type { Tokens } from 'marked';
|
|
|
2
2
|
type $$ComponentProps = {
|
|
3
3
|
token: Tokens.Code;
|
|
4
4
|
id: string;
|
|
5
|
+
/** The fence is still being streamed; nothing below it is final yet. */
|
|
6
|
+
incomplete?: boolean;
|
|
5
7
|
};
|
|
6
8
|
declare const MermaidFallback: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
9
|
type MermaidFallback = ReturnType<typeof MermaidFallback>;
|
package/dist/Elements/icons.js
CHANGED
|
@@ -4,6 +4,7 @@ export const copyIcon = createRawSnippet(() => {
|
|
|
4
4
|
render: () => {
|
|
5
5
|
return `
|
|
6
6
|
<svg
|
|
7
|
+
aria-hidden="true"
|
|
7
8
|
xmlns="http://www.w3.org/2000/svg"
|
|
8
9
|
width="100%"
|
|
9
10
|
height="100%"
|
|
@@ -24,6 +25,7 @@ export const downloadIcon = createRawSnippet(() => {
|
|
|
24
25
|
return {
|
|
25
26
|
render: () => {
|
|
26
27
|
return `<svg
|
|
28
|
+
aria-hidden="true"
|
|
27
29
|
xmlns="http://www.w3.org/2000/svg"
|
|
28
30
|
width="100%"
|
|
29
31
|
height="100%"
|
|
@@ -44,7 +46,8 @@ export const checkIcon = createRawSnippet(() => {
|
|
|
44
46
|
return {
|
|
45
47
|
render: () => {
|
|
46
48
|
return `
|
|
47
|
-
<svg
|
|
49
|
+
<svg
|
|
50
|
+
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="M20 6 9 17l-5-5"/></svg>
|
|
48
51
|
`;
|
|
49
52
|
}
|
|
50
53
|
};
|
|
@@ -53,6 +56,7 @@ export const zoomInIcon = createRawSnippet(() => {
|
|
|
53
56
|
return {
|
|
54
57
|
render: () => `
|
|
55
58
|
<svg
|
|
59
|
+
aria-hidden="true"
|
|
56
60
|
width="100%"
|
|
57
61
|
height="100%"
|
|
58
62
|
viewBox="0 0 24 24"
|
|
@@ -75,6 +79,7 @@ export const zoomOutIcon = createRawSnippet(() => {
|
|
|
75
79
|
return {
|
|
76
80
|
render: () => `
|
|
77
81
|
<svg
|
|
82
|
+
aria-hidden="true"
|
|
78
83
|
width="100%"
|
|
79
84
|
height="100%"
|
|
80
85
|
viewBox="0 0 24 24"
|
|
@@ -96,6 +101,7 @@ export const fitViewIcon = createRawSnippet(() => {
|
|
|
96
101
|
return {
|
|
97
102
|
render: () => `
|
|
98
103
|
<svg
|
|
104
|
+
aria-hidden="true"
|
|
99
105
|
width="100%"
|
|
100
106
|
height="100%"
|
|
101
107
|
viewBox="0 0 24 24"
|
|
@@ -119,6 +125,7 @@ export const fullscreenIcon = createRawSnippet(() => {
|
|
|
119
125
|
return {
|
|
120
126
|
render: () => `
|
|
121
127
|
<svg
|
|
128
|
+
aria-hidden="true"
|
|
122
129
|
width="100%"
|
|
123
130
|
height="100%"
|
|
124
131
|
viewBox="0 0 24 24"
|
|
@@ -139,6 +146,7 @@ export const chevronRight = createRawSnippet(() => {
|
|
|
139
146
|
return {
|
|
140
147
|
render: () => `
|
|
141
148
|
<svg
|
|
149
|
+
aria-hidden="true"
|
|
142
150
|
xmlns="http://www.w3.org/2000/svg"
|
|
143
151
|
width="100%"
|
|
144
152
|
height="100%"
|
|
@@ -158,6 +166,7 @@ export const chevronLeft = createRawSnippet(() => {
|
|
|
158
166
|
return {
|
|
159
167
|
render: () => `
|
|
160
168
|
<svg
|
|
169
|
+
aria-hidden="true"
|
|
161
170
|
xmlns="http://www.w3.org/2000/svg"
|
|
162
171
|
width="100%"
|
|
163
172
|
height="100%"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const srOnly = "position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// Visually hidden but announced. Inline rather than a theme class: a live region
|
|
2
|
+
// must keep working in apps that do not ship Tailwind's `sr-only`.
|
|
3
|
+
export const srOnly = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;';
|
package/dist/Streamdown.svelte
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
<script lang="ts" generics="Source extends Record<string, any> = Record<string, any>">
|
|
2
2
|
import Block from './Block.svelte';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
StreamdownContext,
|
|
5
|
+
defaultTranslations,
|
|
6
|
+
type StreamdownProps
|
|
7
|
+
} from './context.svelte.js';
|
|
4
8
|
import { mergeTheme, shadcnTheme } from './theme.js';
|
|
5
9
|
import { parseBlocks, createParseBlocksCache } from './marked/index.js';
|
|
10
|
+
import { hasUnclosedFence } from './utils/fence.js';
|
|
6
11
|
|
|
7
12
|
let {
|
|
8
13
|
content = '',
|
|
@@ -23,6 +28,8 @@
|
|
|
23
28
|
streamdown = $bindable(),
|
|
24
29
|
renderHtml,
|
|
25
30
|
controls,
|
|
31
|
+
codeBlockMaxHeight,
|
|
32
|
+
tableMaxHeight,
|
|
26
33
|
animation,
|
|
27
34
|
element = $bindable(),
|
|
28
35
|
icons,
|
|
@@ -94,7 +101,11 @@
|
|
|
94
101
|
return renderHtml;
|
|
95
102
|
},
|
|
96
103
|
get translations() {
|
|
97
|
-
|
|
104
|
+
// Resolved once, like the theme, so components never fall back themselves.
|
|
105
|
+
return {
|
|
106
|
+
alert: { ...defaultTranslations.alert, ...translations?.alert },
|
|
107
|
+
controls: { ...defaultTranslations.controls, ...translations?.controls }
|
|
108
|
+
};
|
|
98
109
|
},
|
|
99
110
|
get highlightLanguages() {
|
|
100
111
|
return highlightLanguages;
|
|
@@ -123,19 +134,53 @@
|
|
|
123
134
|
};
|
|
124
135
|
},
|
|
125
136
|
get controls() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
137
|
+
// `controls` is sugar: `false` turns every control off, `true`/undefined
|
|
138
|
+
// turns them all on, and each section is a boolean or an object of the
|
|
139
|
+
// same shape. Flatten it once here so components stay boolean lookups.
|
|
140
|
+
const sections = typeof controls === 'object' && controls !== null ? controls : {};
|
|
141
|
+
const resolve = (section: boolean | Record<string, any> | undefined) => {
|
|
142
|
+
const config = typeof section === 'object' && section !== null ? section : {};
|
|
143
|
+
const enabled = controls === false || section === false ? false : (config.enabled ?? true);
|
|
144
|
+
const download = config.download;
|
|
145
|
+
return {
|
|
146
|
+
enabled,
|
|
147
|
+
copy: enabled && config.copy !== false,
|
|
148
|
+
download: enabled && download !== false,
|
|
149
|
+
filename:
|
|
150
|
+
typeof download === 'object' && download !== null ? download.filename : undefined
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
const code = resolve(sections.code);
|
|
154
|
+
const table = resolve(sections.table);
|
|
155
|
+
const mermaid = resolve(sections.mermaid);
|
|
156
|
+
const tableSection =
|
|
157
|
+
typeof sections.table === 'object' && sections.table ? sections.table : {};
|
|
158
|
+
const mermaidSection =
|
|
159
|
+
typeof sections.mermaid === 'object' && sections.mermaid ? sections.mermaid : {};
|
|
132
160
|
return {
|
|
133
|
-
code:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
code: code.enabled,
|
|
162
|
+
codeCopy: code.copy,
|
|
163
|
+
codeDownload: code.download,
|
|
164
|
+
codeDownloadFilename: code.filename ?? 'file',
|
|
165
|
+
table: table.enabled,
|
|
166
|
+
tableCopy: table.copy,
|
|
167
|
+
tableDownload: table.download,
|
|
168
|
+
tableDownloadFilename: table.filename ?? 'table',
|
|
169
|
+
tableCsvSeparator: tableSection.csvSeparator ?? ',',
|
|
170
|
+
mermaid: mermaid.enabled,
|
|
171
|
+
mermaidDownload: mermaid.download,
|
|
172
|
+
mermaidDownloadFilename: mermaid.filename ?? 'diagram',
|
|
173
|
+
// Wheel zoom is a gesture, not a button: it stays on unless it is
|
|
174
|
+
// turned off explicitly or every control is.
|
|
175
|
+
mermaidMouseWheelZoom: controls !== false && mermaidSection.mouseWheelZoom !== false
|
|
137
176
|
};
|
|
138
177
|
},
|
|
178
|
+
get codeBlockMaxHeight() {
|
|
179
|
+
return codeBlockMaxHeight;
|
|
180
|
+
},
|
|
181
|
+
get tableMaxHeight() {
|
|
182
|
+
return tableMaxHeight;
|
|
183
|
+
},
|
|
139
184
|
get children() {
|
|
140
185
|
return children;
|
|
141
186
|
},
|
|
@@ -161,6 +206,12 @@
|
|
|
161
206
|
const blocks = $derived(
|
|
162
207
|
isStatic ? content : parseBlocks(content, streamdown.extensions, blocksCache)
|
|
163
208
|
);
|
|
209
|
+
|
|
210
|
+
// Only the tail of a live stream can be mid-fence — a static render is finished
|
|
211
|
+
// by definition. Computed once here, not per block or per token.
|
|
212
|
+
const lastBlockIncomplete = $derived(
|
|
213
|
+
!isStatic && blocks.length > 0 && hasUnclosedFence(blocks[blocks.length - 1])
|
|
214
|
+
);
|
|
164
215
|
</script>
|
|
165
216
|
|
|
166
217
|
<div bind:this={element} class={className}>
|
|
@@ -168,7 +219,11 @@
|
|
|
168
219
|
<Block static={isStatic} block={content} />
|
|
169
220
|
{:else}
|
|
170
221
|
{#each blocks as block, index (`${id}-block-${index}`)}
|
|
171
|
-
<Block
|
|
222
|
+
<Block
|
|
223
|
+
static={isStatic}
|
|
224
|
+
{block}
|
|
225
|
+
incomplete={lastBlockIncomplete && index === blocks.length - 1}
|
|
226
|
+
/>
|
|
172
227
|
{/each}
|
|
173
228
|
{/if}
|
|
174
229
|
</div>
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -4,16 +4,55 @@ import type { MermaidConfig } from 'mermaid';
|
|
|
4
4
|
import type { KatexOptions } from 'katex';
|
|
5
5
|
import type { HighlightTheme } from '@tanstack/highlight/theme';
|
|
6
6
|
import type { LanguageDefinition } from '@tanstack/highlight';
|
|
7
|
+
/**
|
|
8
|
+
* Every user-visible string the components render themselves. Nested by area,
|
|
9
|
+
* like the theme; `Streamdown` merges the `translations` prop over
|
|
10
|
+
* `defaultTranslations` once so components always read a complete object.
|
|
11
|
+
*/
|
|
12
|
+
export type Translations = {
|
|
13
|
+
alert: {
|
|
14
|
+
note: string;
|
|
15
|
+
tip: string;
|
|
16
|
+
warning: string;
|
|
17
|
+
caution: string;
|
|
18
|
+
important: string;
|
|
19
|
+
};
|
|
20
|
+
controls: {
|
|
21
|
+
copyCode: string;
|
|
22
|
+
copiedCode: string;
|
|
23
|
+
downloadCode: string;
|
|
24
|
+
copyTable: string;
|
|
25
|
+
copiedTable: string;
|
|
26
|
+
downloadTable: string;
|
|
27
|
+
tableFormatMarkdown: string;
|
|
28
|
+
tableFormatHtml: string;
|
|
29
|
+
tableFormatCsv: string;
|
|
30
|
+
tableFormatTsv: string;
|
|
31
|
+
downloadDiagram: string;
|
|
32
|
+
downloadDiagramPng: string;
|
|
33
|
+
downloadDiagramSvg: string;
|
|
34
|
+
downloadDiagramMmd: string;
|
|
35
|
+
zoomIn: string;
|
|
36
|
+
zoomOut: string;
|
|
37
|
+
resetView: string;
|
|
38
|
+
fullscreen: string;
|
|
39
|
+
exitFullscreen: string;
|
|
40
|
+
diagram: string;
|
|
41
|
+
previousCitation: string;
|
|
42
|
+
nextCitation: string;
|
|
43
|
+
blockedUrl: string;
|
|
44
|
+
imageBlocked: string;
|
|
45
|
+
imageNoDescription: string;
|
|
46
|
+
linkBlocked: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export declare const defaultTranslations: Translations;
|
|
7
50
|
export interface StreamdownContext extends Omit<StreamdownProps, keyof Snippets | 'class' | 'theme' | 'highlightTheme' | 'inlineCitationsMode'> {
|
|
8
51
|
snippets: Snippets;
|
|
9
52
|
highlightTheme: HighlightTheme;
|
|
10
53
|
theme: Theme;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
mermaid: boolean;
|
|
14
|
-
mermaidMouseWheelZoom: boolean;
|
|
15
|
-
table: boolean;
|
|
16
|
-
};
|
|
54
|
+
translations: Translations;
|
|
55
|
+
controls: ResolvedControls;
|
|
17
56
|
inlineCitationsMode: 'list' | 'carousel';
|
|
18
57
|
animation: {
|
|
19
58
|
enabled: boolean;
|
|
@@ -33,8 +72,9 @@ export declare class StreamdownContext<Source extends Record<string, any> = Reco
|
|
|
33
72
|
});
|
|
34
73
|
}
|
|
35
74
|
export declare const useStreamdown: () => StreamdownContext<Record<string, any>>;
|
|
36
|
-
import type { AlertToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoot, THeadRow, TRow, TD, TH, Extension, GenericToken, CitationToken, MdxToken } from './marked/index.js';
|
|
75
|
+
import type { AlertToken, CodeToken, MathToken, SubSupToken, TableToken, THead, TBody, TFoot, THeadRow, TRow, TD, TH, Extension, GenericToken, CitationToken, MdxToken } from './marked/index.js';
|
|
37
76
|
import type { Tokens } from 'marked';
|
|
77
|
+
import type { CsvSeparator } from './utils/table-export.js';
|
|
38
78
|
import type { ListItemToken, ListToken } from './marked/marked-list.js';
|
|
39
79
|
import type { Footnote, FootnoteRef, FootnoteToken } from './marked/marked-footnotes.js';
|
|
40
80
|
import type { DescriptionDetailToken, DescriptionListToken, DescriptionTermToken, DescriptionToken } from './marked/marked-dl.js';
|
|
@@ -89,9 +129,53 @@ export type Snippets<Source extends Record<string, any> = Record<string, any>> =
|
|
|
89
129
|
key: string;
|
|
90
130
|
} : K extends 'mdx' ? {
|
|
91
131
|
props: Record<string, number | string | boolean | null | undefined>;
|
|
132
|
+
} : K extends 'code' | 'mermaid' ? {
|
|
133
|
+
/** The fence is still streaming: defer expensive work. */
|
|
134
|
+
incomplete: boolean;
|
|
92
135
|
} : {})
|
|
93
136
|
]>;
|
|
94
137
|
};
|
|
138
|
+
/** `filename` is the base name; the extension comes from the format. */
|
|
139
|
+
type DownloadControl<Token> = boolean | {
|
|
140
|
+
filename?: string | ((token: Token) => string);
|
|
141
|
+
};
|
|
142
|
+
export type CodeControls = boolean | {
|
|
143
|
+
enabled?: boolean;
|
|
144
|
+
copy?: boolean;
|
|
145
|
+
download?: DownloadControl<CodeToken>;
|
|
146
|
+
};
|
|
147
|
+
export type TableControls = boolean | {
|
|
148
|
+
enabled?: boolean;
|
|
149
|
+
copy?: boolean;
|
|
150
|
+
download?: DownloadControl<TableToken>;
|
|
151
|
+
csvSeparator?: CsvSeparator;
|
|
152
|
+
};
|
|
153
|
+
export type MermaidControls = boolean | {
|
|
154
|
+
enabled?: boolean;
|
|
155
|
+
download?: DownloadControl<CodeToken>;
|
|
156
|
+
mouseWheelZoom?: boolean;
|
|
157
|
+
};
|
|
158
|
+
export type Controls = boolean | {
|
|
159
|
+
code?: CodeControls;
|
|
160
|
+
table?: TableControls;
|
|
161
|
+
mermaid?: MermaidControls;
|
|
162
|
+
};
|
|
163
|
+
/** What `Streamdown.svelte` flattens `controls` into, so components stay dumb. */
|
|
164
|
+
export type ResolvedControls = {
|
|
165
|
+
code: boolean;
|
|
166
|
+
codeCopy: boolean;
|
|
167
|
+
codeDownload: boolean;
|
|
168
|
+
codeDownloadFilename: string | ((token: CodeToken) => string);
|
|
169
|
+
table: boolean;
|
|
170
|
+
tableCopy: boolean;
|
|
171
|
+
tableDownload: boolean;
|
|
172
|
+
tableDownloadFilename: string | ((token: TableToken) => string);
|
|
173
|
+
tableCsvSeparator: CsvSeparator;
|
|
174
|
+
mermaid: boolean;
|
|
175
|
+
mermaidDownload: boolean;
|
|
176
|
+
mermaidDownloadFilename: string | ((token: CodeToken) => string);
|
|
177
|
+
mermaidMouseWheelZoom: boolean;
|
|
178
|
+
};
|
|
95
179
|
export type StreamdownProps<Source extends Record<string, any> = Record<string, any>> = {
|
|
96
180
|
streamdown?: StreamdownContext;
|
|
97
181
|
static?: boolean;
|
|
@@ -114,23 +198,13 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
114
198
|
highlightThemes?: Record<string, HighlightTheme>;
|
|
115
199
|
mermaidConfig?: MermaidConfig;
|
|
116
200
|
katexConfig?: KatexOptions | ((inline: boolean) => KatexOptions);
|
|
201
|
+
/** Partial overrides; unset keys fall back to `defaultTranslations`. */
|
|
117
202
|
translations?: {
|
|
118
|
-
|
|
119
|
-
note?: string;
|
|
120
|
-
tip?: string;
|
|
121
|
-
warning?: string;
|
|
122
|
-
caution?: string;
|
|
123
|
-
important?: string;
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
controls?: {
|
|
127
|
-
code?: boolean;
|
|
128
|
-
mermaid?: boolean | {
|
|
129
|
-
enabled?: boolean;
|
|
130
|
-
mouseWheelZoom?: boolean;
|
|
131
|
-
};
|
|
132
|
-
table?: boolean;
|
|
203
|
+
[K in keyof Translations]?: Partial<Translations[K]>;
|
|
133
204
|
};
|
|
205
|
+
controls?: Controls;
|
|
206
|
+
codeBlockMaxHeight?: string;
|
|
207
|
+
tableMaxHeight?: string;
|
|
134
208
|
renderHtml?: boolean | ((token: Tokens.HTML | Tokens.Tag) => string);
|
|
135
209
|
animation?: {
|
|
136
210
|
animateOnMount?: boolean;
|
|
@@ -171,10 +245,12 @@ export type StreamdownProps<Source extends Record<string, any> = Record<string,
|
|
|
171
245
|
code?: Component<{
|
|
172
246
|
token: Tokens.Code;
|
|
173
247
|
id: string;
|
|
248
|
+
incomplete: boolean;
|
|
174
249
|
}, any, any>;
|
|
175
250
|
mermaid?: Component<{
|
|
176
251
|
token: Tokens.Code;
|
|
177
252
|
id: string;
|
|
253
|
+
incomplete: boolean;
|
|
178
254
|
}, any, any>;
|
|
179
255
|
math?: Component<{
|
|
180
256
|
token: MathToken;
|
package/dist/context.svelte.js
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
import { getContext, onMount, setContext } from 'svelte';
|
|
2
|
+
// Alert titles stay lowercase: the theme capitalizes them with CSS.
|
|
3
|
+
export const defaultTranslations = {
|
|
4
|
+
alert: {
|
|
5
|
+
note: 'note',
|
|
6
|
+
tip: 'tip',
|
|
7
|
+
warning: 'warning',
|
|
8
|
+
caution: 'caution',
|
|
9
|
+
important: 'important'
|
|
10
|
+
},
|
|
11
|
+
controls: {
|
|
12
|
+
copyCode: 'Copy code',
|
|
13
|
+
copiedCode: 'Code copied',
|
|
14
|
+
downloadCode: 'Download code',
|
|
15
|
+
copyTable: 'Copy table',
|
|
16
|
+
copiedTable: 'Table copied',
|
|
17
|
+
downloadTable: 'Download table',
|
|
18
|
+
tableFormatMarkdown: 'Markdown',
|
|
19
|
+
tableFormatHtml: 'HTML',
|
|
20
|
+
tableFormatCsv: 'CSV',
|
|
21
|
+
tableFormatTsv: 'TSV',
|
|
22
|
+
downloadDiagram: 'Download diagram',
|
|
23
|
+
downloadDiagramPng: 'PNG',
|
|
24
|
+
downloadDiagramSvg: 'SVG',
|
|
25
|
+
downloadDiagramMmd: 'MMD',
|
|
26
|
+
zoomIn: 'Zoom in',
|
|
27
|
+
zoomOut: 'Zoom out',
|
|
28
|
+
resetView: 'Zoom to fit',
|
|
29
|
+
fullscreen: 'Expand diagram',
|
|
30
|
+
exitFullscreen: 'Collapse diagram',
|
|
31
|
+
diagram: 'Diagram',
|
|
32
|
+
previousCitation: 'Previous citation',
|
|
33
|
+
nextCitation: 'Next citation',
|
|
34
|
+
blockedUrl: 'Blocked URL',
|
|
35
|
+
imageBlocked: 'Image blocked',
|
|
36
|
+
imageNoDescription: 'No description',
|
|
37
|
+
linkBlocked: 'blocked'
|
|
38
|
+
}
|
|
39
|
+
};
|
|
2
40
|
export class StreamdownContext {
|
|
3
41
|
footnotes = {
|
|
4
42
|
refs: new Map(),
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { default as Streamdown } from './Streamdown.svelte';
|
|
2
|
-
export { useStreamdown, type StreamdownProps } from './context.svelte.js';
|
|
2
|
+
export { useStreamdown, defaultTranslations, type StreamdownProps, type Translations, type Controls, type CodeControls, type TableControls, type MermaidControls, type ResolvedControls } from './context.svelte.js';
|
|
3
|
+
export { extractTableData, tableDataToCSV, tableDataToTSV, tableDataToMarkdown, tableDataToHTML, type TableData, type CsvSeparator } from './utils/table-export.js';
|
|
3
4
|
export { theme, shadcnTheme, mergeTheme, type Theme } from './theme.js';
|
|
4
|
-
export { type Extension, type StreamdownToken, lex, parseBlocks } from './marked/index.js';
|
|
5
|
+
export { type CodeToken, type Extension, type StreamdownToken, lex, parseBlocks } from './marked/index.js';
|
|
5
6
|
export { parseIncompleteMarkdown, type Plugin, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
|
|
6
7
|
export { defineLanguage, type LanguageDefinition } from '@tanstack/highlight';
|
|
7
8
|
export type { HighlightTheme } from '@tanstack/highlight/theme';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Streamdown } from './Streamdown.svelte';
|
|
2
|
-
export { useStreamdown } from './context.svelte.js';
|
|
2
|
+
export { useStreamdown, defaultTranslations } from './context.svelte.js';
|
|
3
|
+
export { extractTableData, tableDataToCSV, tableDataToTSV, tableDataToMarkdown, tableDataToHTML } from './utils/table-export.js';
|
|
3
4
|
export { theme, shadcnTheme, mergeTheme } from './theme.js';
|
|
4
5
|
export { lex, parseBlocks } from './marked/index.js';
|
|
5
6
|
export { parseIncompleteMarkdown, IncompleteMarkdownParser } from './utils/parse-incomplete-markdown.js';
|
package/dist/marked/index.d.ts
CHANGED
|
@@ -23,7 +23,14 @@ export type Extension = {
|
|
|
23
23
|
start?: TokenizerStartFunction;
|
|
24
24
|
applyInBlockParsing?: boolean;
|
|
25
25
|
};
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* A fenced code block. `lang` is the first word of the info string; everything
|
|
28
|
+
* after it is `meta` (`undefined` when there is none).
|
|
29
|
+
*/
|
|
30
|
+
export type CodeToken = Tokens.Code & {
|
|
31
|
+
meta?: string;
|
|
32
|
+
};
|
|
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;
|
|
27
34
|
export type { TableToken, THead, TBody, TFoot, THeadRow, TRow, TH, TD } from './marked-table.js';
|
|
28
35
|
export declare const lex: (markdown: string, extensions?: Extension[]) => StreamdownToken[];
|
|
29
36
|
/**
|