svelte-streamdown 2.5.0 → 2.5.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 CHANGED
@@ -69,6 +69,10 @@ Full support for
69
69
  with _rich_ **content** support
70
70
  and multiline
71
71
 
72
+ > [!NOTE]
73
+ > 🧠 **AI Prompting Tip:** For best results, use our [comprehensive prompt](/prompting) covering all supported markdown features.
74
+
75
+
72
76
  ### 💻 Interactive Code Blocks
73
77
 
74
78
  - Syntax highlighting powered by Shiki
@@ -22,8 +22,10 @@
22
22
 
23
23
  const {
24
24
  children,
25
- token
25
+ token,
26
+ id
26
27
  }: {
28
+ id: string;
27
29
  children: Snippet;
28
30
  token: AlertToken;
29
31
  } = $props();
@@ -37,6 +39,7 @@
37
39
  render={streamdown.snippets.alert}
38
40
  >
39
41
  <div
42
+ data-streamdown-alert={id}
40
43
  style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
41
44
  class={`${streamdown.theme.alert.base} ${streamdown.theme.alert[token.variant]}`}
42
45
  >
@@ -1,6 +1,7 @@
1
1
  import type { AlertToken } from '../marked/index.js';
2
2
  import type { Snippet } from 'svelte';
3
3
  type $$ComponentProps = {
4
+ id: string;
4
5
  children: Snippet;
5
6
  token: AlertToken;
6
7
  };
@@ -9,6 +9,7 @@
9
9
  import { StepperState } from './stepperState.svelte.js';
10
10
  import { Popover } from './popover.svelte.js';
11
11
  import { get } from '../utils/get.js';
12
+ import { chevronLeft, chevronRight } from './icons.js';
12
13
 
13
14
  const {
14
15
  token
@@ -82,6 +83,7 @@
82
83
 
83
84
  {#if popover.isOpen}
84
85
  <dialog
86
+ data-streamdown-citation-popover={id}
85
87
  id={'citation-popover-' + id}
86
88
  aria-modal="false"
87
89
  transition:scale|global={{ start: 0.95, duration: 200 }}
@@ -231,36 +233,9 @@
231
233
  </div>
232
234
  {/snippet}
233
235
 
234
- {#snippet chevronRight()}
235
- <svg
236
- xmlns="http://www.w3.org/2000/svg"
237
- width="100%"
238
- height="100%"
239
- viewBox="0 0 24 24"
240
- fill="none"
241
- stroke="currentColor"
242
- stroke-width="2"
243
- stroke-linecap="round"
244
- stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg
245
- >
246
- {/snippet}
247
-
248
- {#snippet chevronLeft()}
249
- <svg
250
- xmlns="http://www.w3.org/2000/svg"
251
- width="100%"
252
- height="100%"
253
- viewBox="0 0 24 24"
254
- fill="none"
255
- stroke="currentColor"
256
- stroke-width="2"
257
- stroke-linecap="round"
258
- stroke-linejoin="round"><path d="m15 18-6-6 6-6" /></svg
259
- >
260
- {/snippet}
261
-
262
236
  {#if citationWithSources.length > 0}
263
237
  <button
238
+ data-streamdown-citation-preview={id}
264
239
  style={streamdown.animationBlockStyle}
265
240
  bind:this={popover.reference}
266
241
  class={streamdown.theme.inlineCitation.preview}
@@ -6,11 +6,14 @@
6
6
  import type { Tokens } from 'marked';
7
7
  import { type ThemedToken } from 'shiki';
8
8
  import { untrack } from 'svelte';
9
+ import { checkIcon, copyIcon, downloadIcon } from './icons.js';
9
10
 
10
11
  const {
11
- token
12
+ token,
13
+ id
12
14
  }: {
13
15
  token: Tokens.Code;
16
+ id: string;
14
17
  } = $props();
15
18
 
16
19
  const streamdown = useStreamdown();
@@ -48,6 +51,7 @@
48
51
  </script>
49
52
 
50
53
  <div
54
+ data-streamdown-code={id}
51
55
  style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
52
56
  class={streamdown.theme.code.base}
53
57
  >
@@ -58,14 +62,18 @@
58
62
  <button
59
63
  class={streamdown.theme.components.button}
60
64
  onclick={downloadCode}
61
- title="Download file"
65
+ title="Download code"
62
66
  type="button"
63
67
  >
64
68
  {@render (streamdown.icons?.download || downloadIcon)()}
65
69
  </button>
66
70
 
67
71
  <button class={streamdown.theme.components.button} onclick={copy.copy} type="button">
68
- {@render (streamdown.icons?.copy || copyIcon)()}
72
+ {#if copy.isCopied}
73
+ {@render (streamdown.icons?.check || checkIcon)()}
74
+ {:else}
75
+ {@render (streamdown.icons?.copy || copyIcon)()}
76
+ {/if}
69
77
  </button>
70
78
  </div>
71
79
  {/if}
@@ -107,37 +115,3 @@
107
115
  </span>
108
116
  {/each}
109
117
  {/snippet}
110
-
111
- {#snippet copyIcon()}
112
- <svg
113
- xmlns="http://www.w3.org/2000/svg"
114
- width="100%"
115
- height="100%"
116
- viewBox="0 0 24 24"
117
- fill="none"
118
- stroke="currentColor"
119
- stroke-width="2"
120
- stroke-linecap="round"
121
- stroke-linejoin="round"
122
- ><rect width="8" height="4" x="8" y="2" rx="1" ry="1" /><path
123
- d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"
124
- /></svg
125
- >
126
- {/snippet}
127
-
128
- {#snippet downloadIcon()}
129
- <svg
130
- xmlns="http://www.w3.org/2000/svg"
131
- width="100%"
132
- height="100%"
133
- viewBox="0 0 24 24"
134
- fill="none"
135
- stroke="currentColor"
136
- stroke-width="2"
137
- stroke-linecap="round"
138
- stroke-linejoin="round"
139
- ><path d="M12 15V3" /><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><path
140
- d="m7 10 5 5 5-5"
141
- /></svg
142
- >
143
- {/snippet}
@@ -1,6 +1,7 @@
1
1
  import type { Tokens } from 'marked';
2
2
  type $$ComponentProps = {
3
3
  token: Tokens.Code;
4
+ id: string;
4
5
  };
5
6
  declare const Code: import("svelte").Component<$$ComponentProps, {}, "">;
6
7
  type Code = ReturnType<typeof Code>;
@@ -11,11 +11,13 @@
11
11
  import { useStreamdown } from '../context.svelte.js';
12
12
  import FootnoteRef from './FootnoteRef.svelte';
13
13
  import Citation from './Citation.svelte';
14
+ import TableDownload from './TableDownload.svelte';
14
15
  let { token, children }: { token: StreamdownToken; children: Snippet } = $props();
15
16
  const streamdown = useStreamdown();
16
17
 
17
18
  // Only apply animation on block level elements. Leaves text elements to be animated by their text children.
18
19
  const style = $derived(streamdown.isMounted ? streamdown.animationBlockStyle : '');
20
+ const id = $props.id();
19
21
  </script>
20
22
 
21
23
  {#if token.type === 'heading'}
@@ -27,67 +29,71 @@
27
29
  render={streamdown.snippets.heading}
28
30
  >
29
31
  {#if token.depth === 1}
30
- <h1 {style} class={streamdown.theme[`h${token.depth}`].base}>
32
+ <h1 data-streamdown-heading-1={id} {style} class={streamdown.theme[`h${token.depth}`].base}>
31
33
  {@render children()}
32
34
  </h1>
33
35
  {:else if token.depth === 2}
34
- <h2 {style} class={streamdown.theme[`h${token.depth}`].base}>
36
+ <h2 data-streamdown-heading-2={id} {style} class={streamdown.theme[`h${token.depth}`].base}>
35
37
  {@render children()}
36
38
  </h2>
37
39
  {:else if token.depth === 3}
38
- <h3 {style} class={streamdown.theme[`h${token.depth}`].base}>
40
+ <h3 data-streamdown-heading-3={id} {style} class={streamdown.theme[`h${token.depth}`].base}>
39
41
  {@render children()}
40
42
  </h3>
41
43
  {:else if token.depth === 4}
42
- <h4 {style} class={streamdown.theme[`h${token.depth}`].base}>
44
+ <h4 data-streamdown-heading-4={id} {style} class={streamdown.theme[`h${token.depth}`].base}>
43
45
  {@render children()}
44
46
  </h4>
45
47
  {:else if token.depth === 5}
46
- <h5 {style} class={streamdown.theme[`h${token.depth}`].base}>
48
+ <h5 data-streamdown-heading-5={id} {style} class={streamdown.theme[`h${token.depth}`].base}>
47
49
  {@render children()}
48
50
  </h5>
49
51
  {:else if token.depth === 6}
50
- <h6 {style} class={streamdown.theme[`h${token.depth}`].base}>
52
+ <h6 data-streamdown-heading-6={id} {style} class={streamdown.theme[`h${token.depth}`].base}>
51
53
  {@render children()}
52
54
  </h6>
53
55
  {/if}
54
56
  </Slot>
55
57
  {:else if token.type === 'paragraph'}
56
58
  <Slot props={{ children, token }} render={streamdown.snippets.paragraph}>
57
- <p {style} class={streamdown.theme.paragraph.base}>
59
+ <p data-streamdown-paragraph={id} {style} class={streamdown.theme.paragraph.base}>
58
60
  {@render children()}
59
61
  </p>
60
62
  </Slot>
61
63
  {:else if token.type === 'blockquote'}
62
64
  <Slot props={{ children, token }} render={streamdown.snippets.blockquote}>
63
- <blockquote {style} class={streamdown.theme.blockquote.base}>
65
+ <blockquote data-streamdown-blockquote={id} {style} class={streamdown.theme.blockquote.base}>
64
66
  {@render children()}
65
67
  </blockquote>
66
68
  </Slot>
67
69
  {:else if token.type === 'code' && token.lang === 'mermaid'}
68
70
  <Slot props={{ children, token }} render={streamdown.snippets.code}>
69
- <Mermaid {token} />
71
+ <Mermaid {id} {token} />
70
72
  </Slot>
71
73
  {:else if token.type === 'code'}
72
74
  <Slot props={{ children, token }} render={streamdown.snippets.code}>
73
- <Code {token} />
75
+ <Code {id} {token} />
74
76
  </Slot>
75
77
  {:else if token.type === 'codespan'}
76
78
  <Slot props={{ children, token }} render={streamdown.snippets.codespan}>
77
- <code class={streamdown.theme.codespan.base}>
79
+ <code data-streamdown-codespan={id} class={streamdown.theme.codespan.base}>
78
80
  {@render children()}
79
81
  </code>
80
82
  </Slot>
81
83
  {:else if token.type === 'list'}
82
84
  {#if token.ordered}
83
85
  <Slot props={{ children, token }} render={streamdown.snippets.ol}>
84
- <ol style:list-style-type={token.listType} class={streamdown.theme.ol.base}>
86
+ <ol
87
+ data-streamdown-ol={id}
88
+ style:list-style-type={token.listType}
89
+ class={streamdown.theme.ol.base}
90
+ >
85
91
  {@render children()}
86
92
  </ol>
87
93
  </Slot>
88
94
  {:else}
89
95
  <Slot props={{ children, token }} render={streamdown.snippets.ul}>
90
- <ul class={streamdown.theme.ul.base}>
96
+ <ul data-streamdown-ul={id} class={streamdown.theme.ul.base}>
91
97
  {@render children()}
92
98
  </ul>
93
99
  </Slot>
@@ -95,6 +101,7 @@
95
101
  {:else if token.type === 'list_item'}
96
102
  <Slot props={{ children, token }} render={streamdown.snippets.li}>
97
103
  <li
104
+ data-streamdown-li={id}
98
105
  {style}
99
106
  style:list-style-type={token.task ? 'none' : undefined}
100
107
  {...token.value && !token.task ? { value: token.value } : {}}
@@ -113,7 +120,15 @@
113
120
  </Slot>
114
121
  {:else if token.type === 'table'}
115
122
  <Slot props={{ token, children }} render={streamdown.snippets.table}>
116
- <div {style} class={streamdown.theme.table.base} style:overscroll-behavior-x="none">
123
+ {#if streamdown.controls.table}
124
+ <TableDownload {id} {token} />
125
+ {/if}
126
+ <div
127
+ data-streamdown-table={id}
128
+ {style}
129
+ class={`${streamdown.theme.table.base} group`}
130
+ style:overscroll-behavior-x="none"
131
+ >
117
132
  <table class={streamdown.theme.table.table}>
118
133
  {@render children()}
119
134
  </table>
@@ -121,25 +136,25 @@
121
136
  </Slot>
122
137
  {:else if token.type === 'thead'}
123
138
  <Slot props={{ token, children }} render={streamdown.snippets.thead}>
124
- <thead {style} class={streamdown.theme.thead.base}>
139
+ <thead data-streamdown-thead={id} {style} class={streamdown.theme.thead.base}>
125
140
  {@render children?.()}
126
141
  </thead>
127
142
  </Slot>
128
143
  {:else if token.type === 'tbody'}
129
144
  <Slot props={{ token, children }} render={streamdown.snippets.tbody}>
130
- <tbody {style} class={streamdown.theme.tbody.base}>
145
+ <tbody data-streamdown-tbody={id} {style} class={streamdown.theme.tbody.base}>
131
146
  {@render children?.()}
132
147
  </tbody>
133
148
  </Slot>
134
149
  {:else if token.type === 'tfoot'}
135
150
  <Slot props={{ token, children }} render={streamdown.snippets.tfoot}>
136
- <tfoot {style} class={streamdown.theme.tfoot.base}>
151
+ <tfoot data-streamdown-tfoot={id} {style} class={streamdown.theme.tfoot.base}>
137
152
  {@render children?.()}
138
153
  </tfoot>
139
154
  </Slot>
140
155
  {:else if token.type === 'tr'}
141
156
  <Slot props={{ token, children }} render={streamdown.snippets.tr}>
142
- <tr {style} class={streamdown.theme.tr.base}>
157
+ <tr data-streamdown-tr={id} {style} class={streamdown.theme.tr.base}>
143
158
  {@render children?.()}
144
159
  </tr>
145
160
  </Slot>
@@ -148,6 +163,7 @@
148
163
  <Slot props={{ children, token }} render={streamdown.snippets.td}>
149
164
  <td
150
165
  {style}
166
+ data-streamdown-td={id}
151
167
  class={streamdown.theme.td.base}
152
168
  {...token.colspan > 1 ? { colspan: token.colspan } : {}}
153
169
  {...token.rowspan > 1 ? { rowspan: token.rowspan } : {}}
@@ -164,6 +180,7 @@
164
180
  <Slot props={{ children, token }} render={streamdown.snippets.th}>
165
181
  <th
166
182
  {style}
183
+ data-streamdown-th={id}
167
184
  class={streamdown.theme.th.base}
168
185
  {...token.colspan > 1 ? { colspan: token.colspan } : {}}
169
186
  {...token.rowspan > 1 ? { rowspan: token.rowspan } : {}}
@@ -176,45 +193,45 @@
176
193
  </Slot>
177
194
  {/if}
178
195
  {:else if token.type === 'image'}
179
- <Image {token} {children} />
196
+ <Image {id} {token} {children} />
180
197
  {:else if token.type === 'link'}
181
- <Link {token} {children} />
198
+ <Link {id} {token} {children} />
182
199
  {:else if token.type === 'sub'}
183
200
  <Slot props={{ children, token }} render={streamdown.snippets.sub}>
184
- <sub class={streamdown.theme.sub.base}>
201
+ <sub data-streamdown-sub={id} class={streamdown.theme.sub.base}>
185
202
  {@render children()}
186
203
  </sub>
187
204
  </Slot>
188
205
  {:else if token.type === 'sup'}
189
206
  <Slot props={{ children, token }} render={streamdown.snippets.sup}>
190
- <sup class={streamdown.theme.sup.base}>
207
+ <sup data-streamdown-sup={id} class={streamdown.theme.sup.base}>
191
208
  {@render children()}
192
209
  </sup>
193
210
  </Slot>
194
211
  {:else if token.type === 'strong'}
195
212
  <Slot props={{ children, token }} render={streamdown.snippets.strong}>
196
- <strong class={streamdown.theme.strong.base}>
213
+ <strong data-streamdown-strong={id} class={streamdown.theme.strong.base}>
197
214
  {@render children()}
198
215
  </strong>
199
216
  </Slot>
200
217
  {:else if token.type === 'em'}
201
218
  <Slot props={{ children, token }} render={streamdown.snippets.em}>
202
- <em class={streamdown.theme.em.base}>
219
+ <em data-streamdown-em={id} class={streamdown.theme.em.base}>
203
220
  {@render children()}
204
221
  </em>
205
222
  </Slot>
206
223
  {:else if token.type === 'del'}
207
224
  <Slot props={{ children, token }} render={streamdown.snippets.del}>
208
- <del class={streamdown.theme.del.base}>
225
+ <del data-streamdown-del={id} class={streamdown.theme.del.base}>
209
226
  {@render children()}
210
227
  </del>
211
228
  </Slot>
212
229
  {:else if token.type === 'hr'}
213
230
  <Slot props={{ children, token }} render={streamdown.snippets.hr}>
214
- <hr {style} class={streamdown.theme.hr.base} />
231
+ <hr data-streamdown-hr={id} {style} class={streamdown.theme.hr.base} />
215
232
  </Slot>
216
233
  {:else if token.type === 'br'}
217
- <br />
234
+ <br data-streamdown-br={id} />
218
235
  {:else if token.type === 'math'}
219
236
  <Slot
220
237
  props={{
@@ -223,10 +240,10 @@
223
240
  }}
224
241
  render={streamdown.snippets.math}
225
242
  >
226
- <Math {token} />
243
+ <Math {id} {token} />
227
244
  </Slot>
228
245
  {:else if token.type === 'alert'}
229
- <Alert {token} {children} />
246
+ <Alert {id} {token} {children} />
230
247
  {:else if token.type === 'footnoteRef'}
231
248
  <Slot props={{ token }} render={streamdown.snippets.footnoteRef}>
232
249
  <FootnoteRef {token} />
@@ -239,7 +256,11 @@
239
256
  <!-- TODO Footnotes are rendered inside the FootnoteRef popover -->
240
257
  {:else if token.type === 'descriptionList'}
241
258
  <Slot props={{ children, token }} render={streamdown.snippets.descriptionList}>
242
- <dl style={streamdown.animationBlockStyle} class={streamdown.theme.descriptionList.base}>
259
+ <dl
260
+ data-streamdown-description-list={id}
261
+ style={streamdown.animationBlockStyle}
262
+ class={streamdown.theme.descriptionList.base}
263
+ >
243
264
  {@render children()}
244
265
  </dl>
245
266
  </Slot>
@@ -249,13 +270,17 @@
249
270
  </Slot>
250
271
  {:else if token.type === 'descriptionTerm'}
251
272
  <Slot props={{ children, token }} render={streamdown.snippets.descriptionTerm}>
252
- <dt {style} class={streamdown.theme.descriptionTerm.base}>
273
+ <dt data-streamdown-description-term={id} {style} class={streamdown.theme.descriptionTerm.base}>
253
274
  {@render children()}
254
275
  </dt>
255
276
  </Slot>
256
277
  {:else if token.type === 'descriptionDetail'}
257
278
  <Slot props={{ children, token }} render={streamdown.snippets.descriptionDetail}>
258
- <dd {style} class={streamdown.theme.descriptionDetail.base}>
279
+ <dd
280
+ data-streamdown-description-detail={id}
281
+ {style}
282
+ class={streamdown.theme.descriptionDetail.base}
283
+ >
259
284
  {@render children()}
260
285
  </dd>
261
286
  </Slot>
@@ -41,6 +41,7 @@
41
41
 
42
42
  {#if popover.isOpen}
43
43
  <dialog
44
+ data-streamdown-footnote-popover={id}
44
45
  id={'footnote-popover-' + id}
45
46
  aria-modal="false"
46
47
  transition:scale|global={{ start: 0.95, duration: 100 }}
@@ -65,6 +66,7 @@
65
66
 
66
67
  {#if token.label !== 'streamdown:footnote'}
67
68
  <button
69
+ data-streamdown-footnote-ref={id}
68
70
  style={streamdown.animationBlockStyle}
69
71
  bind:this={popover.reference}
70
72
  class={streamdown.theme.footnoteRef.base}
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { useStreamdown } from '../context.svelte.js';
3
- import { transformUrl } from '../utils/url.js';
3
+ import { isPathRelativeUrl, transformUrl } from '../utils/url.js';
4
4
  import Slot from './Slot.svelte';
5
5
  import type { Tokens } from 'marked';
6
6
  import type { Snippet } from 'svelte';
@@ -9,22 +9,26 @@
9
9
 
10
10
  const {
11
11
  children,
12
- token
12
+ token,
13
+ id
13
14
  }: {
14
15
  children: Snippet;
15
16
  token: Tokens.Image;
17
+ id: string;
16
18
  } = $props();
17
19
 
20
+ const isRelativeUrl = $derived(isPathRelativeUrl(token.href));
21
+
18
22
  const transformedUrl = $derived(
19
23
  transformUrl(token.href, streamdown.allowedImagePrefixes ?? [], streamdown.defaultOrigin)
20
24
  );
21
25
  </script>
22
26
 
23
27
  {#if token.href !== 'streamdown:incomplete-image'}
24
- {#if transformedUrl}
28
+ {#if transformedUrl || isRelativeUrl}
25
29
  <Slot
26
30
  props={{
27
- src: transformedUrl,
31
+ src: isRelativeUrl ? token.href : transformedUrl,
28
32
  alt: token.text,
29
33
  children,
30
34
  token
@@ -32,14 +36,20 @@
32
36
  render={streamdown.snippets.image}
33
37
  >
34
38
  <span
39
+ data-streamdown-image={id}
35
40
  style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
36
41
  class={streamdown.theme.image.base}
37
42
  >
38
- <img class={streamdown.theme.image.image} src={transformedUrl} alt={token.text} />
43
+ <img
44
+ class={streamdown.theme.image.image}
45
+ src={isRelativeUrl ? token.href : transformedUrl}
46
+ alt={token.text}
47
+ />
39
48
  </span>
40
49
  </Slot>
41
50
  {:else}
42
51
  <span
52
+ data-streamdown-image-blocked={id}
43
53
  class="inline-block rounded bg-gray-200 px-3 py-1 text-sm text-gray-600 dark:bg-gray-700 dark:text-gray-400"
44
54
  title={`Blocked URL: ${token.href}`}
45
55
  >
@@ -3,6 +3,7 @@ import type { Snippet } from 'svelte';
3
3
  type $$ComponentProps = {
4
4
  children: Snippet;
5
5
  token: Tokens.Image;
6
+ id: string;
6
7
  };
7
8
  declare const Image: import("svelte").Component<$$ComponentProps, {}, "">;
8
9
  type Image = ReturnType<typeof Image>;
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { useStreamdown } from '../context.svelte.js';
3
- import { transformUrl } from '../utils/url.js';
3
+ import { isPathRelativeUrl, transformUrl } from '../utils/url.js';
4
4
  import Slot from './Slot.svelte';
5
5
  import type { Tokens } from 'marked';
6
6
  import type { Snippet } from 'svelte';
@@ -9,18 +9,22 @@
9
9
 
10
10
  const {
11
11
  children,
12
- token
12
+ token,
13
+ id
13
14
  }: {
14
15
  children: Snippet;
15
16
  token: Tokens.Link;
17
+ id: string;
16
18
  } = $props();
17
19
 
20
+ const isRelativeUrl = $derived(isPathRelativeUrl(token.href));
21
+
18
22
  const transformedUrl = $derived(
19
23
  transformUrl(token.href, streamdown.allowedLinkPrefixes ?? [], streamdown.defaultOrigin)
20
24
  );
21
25
  </script>
22
26
 
23
- {#if transformedUrl || token.href === 'streamdown:incomplete-link'}
27
+ {#if transformedUrl || token.href === 'streamdown:incomplete-link' || isRelativeUrl}
24
28
  <Slot
25
29
  props={{
26
30
  href: transformedUrl,
@@ -33,16 +37,18 @@
33
37
  render={streamdown.snippets.link}
34
38
  >
35
39
  <a
40
+ data-streamdown-link={id}
36
41
  class={streamdown.theme.link.base}
37
- href={transformedUrl}
38
- target="_blank"
39
- rel="noopener noreferrer"
42
+ {...isRelativeUrl
43
+ ? { href: token.href }
44
+ : { href: transformedUrl, target: '_blank', rel: 'noopener noreferrer' }}
40
45
  >
41
46
  {@render children()}
42
47
  </a>
43
48
  </Slot>
44
49
  {:else}
45
50
  <span
51
+ data-streamdown-link-blocked={id}
46
52
  class={streamdown.theme.link.blocked}
47
53
  title={token.title ? `Blocked URL: ${token.href}` : undefined}
48
54
  >
@@ -3,6 +3,7 @@ import type { Snippet } from 'svelte';
3
3
  type $$ComponentProps = {
4
4
  children: Snippet;
5
5
  token: Tokens.Link;
6
+ id: string;
6
7
  };
7
8
  declare const Link: import("svelte").Component<$$ComponentProps, {}, "">;
8
9
  type Link = ReturnType<typeof Link>;
@@ -6,9 +6,11 @@
6
6
  import 'katex/dist/katex.min.css';
7
7
 
8
8
  const {
9
- token
9
+ token,
10
+ id
10
11
  }: {
11
12
  token: MathToken;
13
+ id: string;
12
14
  } = $props();
13
15
 
14
16
  const streamdown = useStreamdown();
@@ -46,6 +48,7 @@
46
48
 
47
49
  {#if token.isInline}
48
50
  <span
51
+ data-streamdown-inline-math={id}
49
52
  style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
50
53
  bind:this={inner}
51
54
  class={streamdown.theme.math.inline}
@@ -53,7 +56,12 @@
53
56
  {@html html}
54
57
  </span>
55
58
  {:else}
56
- <div style={streamdown.isMounted ? streamdown.animationBlockStyle : ''} class="h-fit w-full">
59
+ <div
60
+ data-streamdown-block-math={id}
61
+ style={streamdown.isMounted ? streamdown.animationBlockStyle : ''}
62
+ style:height="fit-content"
63
+ style:width="100%"
64
+ >
57
65
  <div class="overflow-x-auto">
58
66
  <div bind:this={inner} class={streamdown.theme.math.block}>
59
67
  {@html html}
@@ -2,6 +2,7 @@ import type { MathToken } from '../marked/index.js';
2
2
  import 'katex/dist/katex.min.css';
3
3
  type $$ComponentProps = {
4
4
  token: MathToken;
5
+ id: string;
5
6
  };
6
7
  declare const Math: import("svelte").Component<$$ComponentProps, {}, "">;
7
8
  type Math = ReturnType<typeof Math>;