svelte-streamdown 2.2.7 → 2.3.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 +47 -47
- package/dist/AnimatedText.svelte +6 -8
- package/dist/Block.svelte +2 -2
- package/dist/Elements/Alert.svelte +19 -18
- package/dist/Elements/Code.svelte +20 -17
- package/dist/Elements/Image.svelte +22 -20
- package/dist/Elements/Link.svelte +1 -1
- package/dist/Elements/Math.svelte +4 -7
- package/dist/Elements/Mermaid.svelte +76 -68
- package/dist/Streamdown.svelte +4 -0
- package/dist/context.svelte.d.ts +16 -3
- package/dist/context.svelte.js +1 -3
- package/dist/marked/index.d.ts +4 -2
- package/dist/marked/index.js +10 -6
- package/dist/marked/marked-alert.d.ts +1 -1
- package/dist/marked/marked-alert.js +14 -5
- package/dist/marked/marked-br.d.ts +12 -0
- package/dist/marked/marked-br.js +21 -0
- package/dist/marked/marked-footnotes.d.ts +1 -1
- package/dist/marked/marked-footnotes.js +1 -1
- package/dist/marked/marked-hr.d.ts +12 -0
- package/dist/marked/marked-hr.js +24 -0
- package/dist/marked/marked-list.d.ts +2 -1
- package/dist/marked/marked-list.js +9 -3
- package/dist/marked/marked-math.d.ts +7 -21
- package/dist/marked/marked-math.js +38 -17
- package/dist/marked/marked-subsup.d.ts +1 -1
- package/dist/marked/marked-subsup.js +1 -1
- package/dist/marked/marked-table.d.ts +1 -0
- package/dist/marked/marked-table.js +11 -4
- package/dist/theme.d.ts +9 -1
- package/dist/theme.js +2 -0
- package/dist/utils/hightlighter.svelte.d.ts +15 -13
- package/dist/utils/hightlighter.svelte.js +101 -57
- package/dist/utils/parse-incomplete-markdown.js +352 -157
- package/package.json +4 -2
- package/dist/Elements/Table.svelte +0 -30
- package/dist/Elements/Table.svelte.d.ts +0 -9
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
token: Tokens.Code;
|
|
15
15
|
} = $props();
|
|
16
16
|
|
|
17
|
-
const code = $derived(token.text);
|
|
18
17
|
let mermaid = $state<any>(null);
|
|
19
18
|
onMount(async () => {
|
|
20
19
|
mermaid = (await import('mermaid')).default;
|
|
@@ -204,7 +203,7 @@
|
|
|
204
203
|
{#if mermaid}
|
|
205
204
|
<div
|
|
206
205
|
class={streamdown.theme.mermaid.base}
|
|
207
|
-
{@attach (node) => renderMermaid(
|
|
206
|
+
{@attach (node) => renderMermaid(token.text, node)}
|
|
208
207
|
{@attach insider.attach}
|
|
209
208
|
data-expanded={'false'}
|
|
210
209
|
>
|
|
@@ -216,25 +215,7 @@
|
|
|
216
215
|
onclick={() => panzoom.zoomToFit()}
|
|
217
216
|
data-panzoom-ignore
|
|
218
217
|
>
|
|
219
|
-
|
|
220
|
-
class={streamdown.theme.mermaid.icon}
|
|
221
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
222
|
-
viewBox="0 0 24 24"
|
|
223
|
-
fill="none"
|
|
224
|
-
stroke="currentColor"
|
|
225
|
-
stroke-width="2"
|
|
226
|
-
stroke-linecap="round"
|
|
227
|
-
stroke-linejoin="round"
|
|
228
|
-
><path d="M3 7V5a2 2 0 0 1 2-2h2" /><path d="M17 3h2a2 2 0 0 1 2 2v2" /><path
|
|
229
|
-
d="M21 17v2a2 2 0 0 1-2 2h-2"
|
|
230
|
-
/><path d="M7 21H5a2 2 0 0 1-2-2v-2" /><rect
|
|
231
|
-
width="10"
|
|
232
|
-
height="8"
|
|
233
|
-
x="7"
|
|
234
|
-
y="8"
|
|
235
|
-
rx="1"
|
|
236
|
-
/></svg
|
|
237
|
-
>
|
|
218
|
+
{@render (streamdown.icons?.fitView || fitViewIcon)()}
|
|
238
219
|
</button>
|
|
239
220
|
<button
|
|
240
221
|
class={streamdown.theme.mermaid.button}
|
|
@@ -242,66 +223,23 @@
|
|
|
242
223
|
onclick={() => panzoom.zoomIn()}
|
|
243
224
|
data-panzoom-ignore
|
|
244
225
|
>
|
|
245
|
-
|
|
246
|
-
class={streamdown.theme.mermaid.icon}
|
|
247
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
248
|
-
viewBox="0 0 24 24"
|
|
249
|
-
fill="none"
|
|
250
|
-
stroke="currentColor"
|
|
251
|
-
stroke-width="2"
|
|
252
|
-
stroke-linecap="round"
|
|
253
|
-
stroke-linejoin="round"
|
|
254
|
-
><circle cx="11" cy="11" r="8" /><line x1="21" x2="16.65" y1="21" y2="16.65" /><line
|
|
255
|
-
x1="11"
|
|
256
|
-
x2="11"
|
|
257
|
-
y1="8"
|
|
258
|
-
y2="14"
|
|
259
|
-
/><line x1="8" x2="14" y1="11" y2="11" /></svg
|
|
260
|
-
>
|
|
226
|
+
{@render (streamdown.icons?.zoomIn || zoomInIcon)()}
|
|
261
227
|
</button>
|
|
262
228
|
<button
|
|
263
229
|
class={streamdown.theme.mermaid.button}
|
|
264
230
|
aria-label="Zoom out"
|
|
265
231
|
onclick={() => panzoom.zoomOut()}
|
|
266
232
|
data-panzoom-ignore
|
|
267
|
-
><svg
|
|
268
|
-
class={streamdown.theme.mermaid.icon}
|
|
269
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
270
|
-
viewBox="0 0 24 24"
|
|
271
|
-
fill="none"
|
|
272
|
-
stroke="currentColor"
|
|
273
|
-
stroke-width="2"
|
|
274
|
-
stroke-linecap="round"
|
|
275
|
-
stroke-linejoin="round"
|
|
276
|
-
><circle cx="11" cy="11" r="8" /><line x1="21" x2="16.65" y1="21" y2="16.65" /><line
|
|
277
|
-
x1="8"
|
|
278
|
-
x2="14"
|
|
279
|
-
y1="11"
|
|
280
|
-
y2="11"
|
|
281
|
-
/></svg
|
|
282
|
-
></button
|
|
283
233
|
>
|
|
234
|
+
{@render (streamdown.icons?.zoomOut || zoomOutIcon)()}
|
|
235
|
+
</button>
|
|
284
236
|
<button
|
|
285
237
|
class={streamdown.theme.mermaid.button}
|
|
286
238
|
aria-label="Toggle expand"
|
|
287
239
|
onclick={() => panzoom.toggleExpand()}
|
|
288
240
|
data-panzoom-ignore
|
|
289
241
|
>
|
|
290
|
-
|
|
291
|
-
class={streamdown.theme.mermaid.icon}
|
|
292
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
293
|
-
viewBox="0 0 24 24"
|
|
294
|
-
fill="none"
|
|
295
|
-
stroke="currentColor"
|
|
296
|
-
stroke-width="2"
|
|
297
|
-
stroke-linecap="round"
|
|
298
|
-
stroke-linejoin="round"
|
|
299
|
-
><path d="m15 15 6 6" /><path d="m15 9 6-6" /><path d="M21 16v5h-5" /><path
|
|
300
|
-
d="M21 8V3h-5"
|
|
301
|
-
/><path d="M3 16v5h5" /><path d="m3 21 6-6" /><path d="M3 8V3h5" /><path
|
|
302
|
-
d="M9 9 3 3"
|
|
303
|
-
/></svg
|
|
304
|
-
>
|
|
242
|
+
{@render (streamdown.icons?.fullscreen || fullscreenIcon)()}
|
|
305
243
|
</button>
|
|
306
244
|
</div>
|
|
307
245
|
{/if}
|
|
@@ -311,6 +249,76 @@
|
|
|
311
249
|
<div class={streamdown.theme.mermaid.base}></div>
|
|
312
250
|
{/if}
|
|
313
251
|
|
|
252
|
+
{#snippet zoomInIcon()}
|
|
253
|
+
<svg
|
|
254
|
+
class={streamdown.theme.mermaid.icon}
|
|
255
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
256
|
+
viewBox="0 0 24 24"
|
|
257
|
+
fill="none"
|
|
258
|
+
stroke="currentColor"
|
|
259
|
+
stroke-width="2"
|
|
260
|
+
stroke-linecap="round"
|
|
261
|
+
stroke-linejoin="round"
|
|
262
|
+
>
|
|
263
|
+
<circle cx="11" cy="11" r="8" /><line x1="21" x2="16.65" y1="21" y2="16.65" />
|
|
264
|
+
<line x1="11" x2="11" y1="8" y2="14" />
|
|
265
|
+
<line x1="8" x2="14" y1="11" y2="11" />
|
|
266
|
+
</svg>
|
|
267
|
+
{/snippet}
|
|
268
|
+
|
|
269
|
+
{#snippet zoomOutIcon()}
|
|
270
|
+
<svg
|
|
271
|
+
class={streamdown.theme.mermaid.icon}
|
|
272
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
273
|
+
viewBox="0 0 24 24"
|
|
274
|
+
fill="none"
|
|
275
|
+
stroke="currentColor"
|
|
276
|
+
stroke-width="2"
|
|
277
|
+
stroke-linecap="round"
|
|
278
|
+
stroke-linejoin="round"
|
|
279
|
+
>
|
|
280
|
+
<circle cx="11" cy="11" r="8" /><line x1="21" x2="16.65" y1="21" y2="16.65" /><line
|
|
281
|
+
x1="8"
|
|
282
|
+
x2="14"
|
|
283
|
+
y1="11"
|
|
284
|
+
y2="11"
|
|
285
|
+
/>
|
|
286
|
+
</svg>
|
|
287
|
+
{/snippet}
|
|
288
|
+
|
|
289
|
+
{#snippet fitViewIcon()}
|
|
290
|
+
<svg
|
|
291
|
+
class={streamdown.theme.mermaid.icon}
|
|
292
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
293
|
+
viewBox="0 0 24 24"
|
|
294
|
+
fill="none"
|
|
295
|
+
stroke="currentColor"
|
|
296
|
+
stroke-width="2"
|
|
297
|
+
stroke-linecap="round"
|
|
298
|
+
stroke-linejoin="round"
|
|
299
|
+
>
|
|
300
|
+
<path d="M3 7V5a2 2 0 0 1 2-2h2" />
|
|
301
|
+
<path d="M17 3h2a2 2 0 0 1 2 2v2" /><path d="M21 17v2a2 2 0 0 1-2 2h-2" />
|
|
302
|
+
<path d="M7 21H5a2 2 0 0 1-2-2v-2" /><rect width="10" height="8" x="7" y="8" rx="1" />
|
|
303
|
+
</svg>
|
|
304
|
+
{/snippet}
|
|
305
|
+
|
|
306
|
+
{#snippet fullscreenIcon()}
|
|
307
|
+
<svg
|
|
308
|
+
class={streamdown.theme.mermaid.icon}
|
|
309
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
310
|
+
viewBox="0 0 24 24"
|
|
311
|
+
fill="none"
|
|
312
|
+
stroke="currentColor"
|
|
313
|
+
stroke-width="2"
|
|
314
|
+
stroke-linecap="round"
|
|
315
|
+
stroke-linejoin="round"
|
|
316
|
+
>
|
|
317
|
+
<path d="m15 15 6 6" /><path d="m15 9 6-6" /><path d="M21 16v5h-5" /><path d="M21 8V3h-5" />
|
|
318
|
+
<path d="M3 16v5h5" /><path d="m3 21 6-6" /><path d="M3 8V3h5" /><path d="M9 9 3 3" />
|
|
319
|
+
</svg>
|
|
320
|
+
{/snippet}
|
|
321
|
+
|
|
314
322
|
<style>
|
|
315
323
|
/* View Transition styles for zoom effect */
|
|
316
324
|
::view-transition-old(panzoom-element),
|
package/dist/Streamdown.svelte
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
controls,
|
|
25
25
|
animation,
|
|
26
26
|
element = $bindable(),
|
|
27
|
+
icons,
|
|
27
28
|
...snippets
|
|
28
29
|
}: StreamdownProps = $props();
|
|
29
30
|
|
|
@@ -97,6 +98,9 @@
|
|
|
97
98
|
code: codeControls,
|
|
98
99
|
mermaid: mermaidControls
|
|
99
100
|
};
|
|
101
|
+
},
|
|
102
|
+
get icons() {
|
|
103
|
+
return icons;
|
|
100
104
|
}
|
|
101
105
|
});
|
|
102
106
|
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { BundledTheme } from 'shiki';
|
|
2
1
|
import type { Snippet } from 'svelte';
|
|
3
|
-
import type { Theme } from './theme.js';
|
|
2
|
+
import type { DeepPartialTheme, Theme } from './theme.js';
|
|
4
3
|
import type { MermaidConfig } from 'mermaid';
|
|
5
4
|
import type { KatexOptions } from 'katex';
|
|
5
|
+
import type { BundledTheme } from 'shiki';
|
|
6
6
|
export interface StreamdownContext extends Omit<StreamdownProps, keyof Snippets | 'class' | 'theme' | 'shikiTheme'> {
|
|
7
7
|
snippets: Snippets;
|
|
8
8
|
shikiTheme: BundledTheme;
|
|
@@ -81,7 +81,7 @@ export type StreamdownProps = {
|
|
|
81
81
|
defaultOrigin?: string;
|
|
82
82
|
allowedLinkPrefixes?: string[];
|
|
83
83
|
allowedImagePrefixes?: string[];
|
|
84
|
-
theme?:
|
|
84
|
+
theme?: DeepPartialTheme;
|
|
85
85
|
baseTheme?: 'tailwind' | 'shadcn';
|
|
86
86
|
mergeTheme?: boolean;
|
|
87
87
|
shikiTheme?: BundledTheme;
|
|
@@ -110,5 +110,18 @@ export type StreamdownProps = {
|
|
|
110
110
|
timingFunction?: 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear';
|
|
111
111
|
tokenize?: 'word' | 'char';
|
|
112
112
|
};
|
|
113
|
+
icons?: {
|
|
114
|
+
copy?: Snippet;
|
|
115
|
+
download?: Snippet;
|
|
116
|
+
fullscreen?: Snippet;
|
|
117
|
+
zoomIn?: Snippet;
|
|
118
|
+
zoomOut?: Snippet;
|
|
119
|
+
fitView?: Snippet;
|
|
120
|
+
note?: Snippet;
|
|
121
|
+
tip?: Snippet;
|
|
122
|
+
warning?: Snippet;
|
|
123
|
+
caution?: Snippet;
|
|
124
|
+
important?: Snippet;
|
|
125
|
+
};
|
|
113
126
|
} & Partial<Snippets>;
|
|
114
127
|
export {};
|
package/dist/context.svelte.js
CHANGED
|
@@ -10,9 +10,7 @@ export class StreamdownContext {
|
|
|
10
10
|
animation-duration: ${this.animation.duration}ms;
|
|
11
11
|
animation-timing-function: ${this.animation.timingFunction};
|
|
12
12
|
animation-iteration-count: 1;
|
|
13
|
-
animation-fill-mode: forwards
|
|
14
|
-
white-space: pre-wrap;
|
|
15
|
-
display: inline-block;`
|
|
13
|
+
animation-fill-mode: forwards;`
|
|
16
14
|
: undefined);
|
|
17
15
|
animationBlockStyle = $derived(this.animation.enabled
|
|
18
16
|
? `animation-name: sd-${this.animation.type};
|
package/dist/marked/index.d.ts
CHANGED
|
@@ -4,9 +4,11 @@ import { type FootnoteToken } from './marked-footnotes.js';
|
|
|
4
4
|
import { type MathToken } from './marked-math.js';
|
|
5
5
|
import { type SubSupToken } from './marked-subsup.js';
|
|
6
6
|
import { type ListItemToken, type ListToken } from './marked-list.js';
|
|
7
|
+
import { type BrToken } from './marked-br.js';
|
|
8
|
+
import { type HrToken } from './marked-hr.js';
|
|
7
9
|
import { type TableToken, type THead, type TBody, type TFoot, type THeadRow, type TRow, type TH, type TD } from './marked-table.js';
|
|
8
|
-
export type StreamdownToken = Exclude<MarkedToken, Tokens.List | Tokens.ListItem> | ListToken | ListItemToken | MathToken | AlertToken | FootnoteToken | SubSupToken | TableToken | THead | TBody | TFoot | THeadRow | TRow | TH | TD;
|
|
10
|
+
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;
|
|
9
11
|
export type { TableToken, THead, TBody, TFoot, THeadRow, TRow, TH, TD } from './marked-table.js';
|
|
10
12
|
export declare const lex: (markdown: string) => StreamdownToken[];
|
|
11
13
|
export declare const parseBlocks: (markdown: string) => string[];
|
|
12
|
-
export type { MathToken, AlertToken, FootnoteToken, SubSupToken };
|
|
14
|
+
export type { MathToken, AlertToken, FootnoteToken, SubSupToken, BrToken, HrToken };
|
package/dist/marked/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Lexer } from 'marked';
|
|
2
|
-
import markedAlert
|
|
3
|
-
import markedFootnote
|
|
2
|
+
import { markedAlert } from './marked-alert.js';
|
|
3
|
+
import { markedFootnote } from './marked-footnotes.js';
|
|
4
4
|
import { markedMath } from './marked-math.js';
|
|
5
|
-
import markedSubSup
|
|
5
|
+
import { markedSubSup } from './marked-subsup.js';
|
|
6
6
|
import { markedList } from './marked-list.js';
|
|
7
|
+
import { markedBr } from './marked-br.js';
|
|
8
|
+
import { markedHr } from './marked-hr.js';
|
|
7
9
|
import { markedTable } from './marked-table.js';
|
|
8
10
|
const extensions = [
|
|
9
11
|
markedTable(),
|
|
@@ -11,7 +13,9 @@ const extensions = [
|
|
|
11
13
|
markedAlert(),
|
|
12
14
|
markedMath(),
|
|
13
15
|
markedSubSup(),
|
|
14
|
-
markedList()
|
|
16
|
+
markedList(),
|
|
17
|
+
markedBr(),
|
|
18
|
+
markedHr()
|
|
15
19
|
];
|
|
16
20
|
const parseExtensions = (...ext) => {
|
|
17
21
|
const options = {
|
|
@@ -47,9 +51,9 @@ const parseExtensions = (...ext) => {
|
|
|
47
51
|
});
|
|
48
52
|
return options;
|
|
49
53
|
};
|
|
50
|
-
const blockLexer = new Lexer(parseExtensions(markedFootnote(), markedTable()));
|
|
54
|
+
const blockLexer = new Lexer(parseExtensions(markedHr(), markedFootnote(), markedTable()));
|
|
51
55
|
export const lex = (markdown) => {
|
|
52
|
-
return new Lexer(parseExtensions(markedTable(), markedFootnote(), markedAlert(), markedMath(), markedSubSup(), markedList()))
|
|
56
|
+
return new Lexer(parseExtensions(markedHr(), markedTable(), markedFootnote(), markedAlert(), markedMath(), markedSubSup(), markedList(), markedBr()))
|
|
53
57
|
.lex(markdown)
|
|
54
58
|
.filter((token) => token.type !== 'space' && token.type !== 'footnote');
|
|
55
59
|
};
|
|
@@ -2,7 +2,7 @@ import type { Tokenizer, Tokens } from 'marked';
|
|
|
2
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
|
|
5
|
+
export declare function markedAlert(): {
|
|
6
6
|
extensions: Array<{
|
|
7
7
|
name: string;
|
|
8
8
|
level: 'block' | 'inline';
|
|
@@ -3,7 +3,7 @@ const variants = ['note', 'tip', 'important', 'warning', 'caution'];
|
|
|
3
3
|
export function createSyntaxPattern(type) {
|
|
4
4
|
return `^\\s*[\\*_]*\\[!${type.toUpperCase()}\\][\\*_]*\\s*`;
|
|
5
5
|
}
|
|
6
|
-
export
|
|
6
|
+
export function markedAlert() {
|
|
7
7
|
const defaultLexer = new Lexer({ gfm: true });
|
|
8
8
|
const defaultTokenizer = defaultLexer.options.tokenizer;
|
|
9
9
|
return {
|
|
@@ -24,14 +24,22 @@ export default function markedAlert() {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
export function processAlertToken(token, tokenizer) {
|
|
27
|
-
const matchedVariant = variants.find((type) => new RegExp(createSyntaxPattern(type)).test(('text' in token && token.text) || ''));
|
|
28
|
-
if (!matchedVariant)
|
|
27
|
+
const matchedVariant = variants.find((type) => new RegExp(createSyntaxPattern(type), 'i').test(('text' in token && token.text) || ''));
|
|
28
|
+
if (!matchedVariant) {
|
|
29
|
+
Object.assign(token, {
|
|
30
|
+
tokens: token.tokens
|
|
31
|
+
.map((token) => {
|
|
32
|
+
return tokenizer.lexer.blockTokens(token.raw, [])[0];
|
|
33
|
+
})
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
});
|
|
29
36
|
return;
|
|
37
|
+
}
|
|
38
|
+
const alertPattern = new RegExp(`[\\*_]*\\[!${matchedVariant.toUpperCase()}\\][\\*_]*`, 'g');
|
|
30
39
|
const tokens = token.tokens
|
|
31
40
|
.map((token) => {
|
|
32
41
|
let cleanedRaw = token.raw;
|
|
33
42
|
// Remove alert markers with any markdown formatting (asterisks/underscores)
|
|
34
|
-
const alertPattern = new RegExp(`[\\*_]*\\[!${matchedVariant.toUpperCase()}\\][\\*_]*`, 'g');
|
|
35
43
|
cleanedRaw = cleanedRaw.replaceAll(alertPattern, '').trim();
|
|
36
44
|
return tokenizer.lexer.blockTokens(cleanedRaw, [])[0];
|
|
37
45
|
})
|
|
@@ -39,6 +47,7 @@ export function processAlertToken(token, tokenizer) {
|
|
|
39
47
|
Object.assign(token, {
|
|
40
48
|
type: 'alert',
|
|
41
49
|
variant: matchedVariant,
|
|
42
|
-
tokens
|
|
50
|
+
tokens,
|
|
51
|
+
text: token.text.replace(alertPattern, '').trim()
|
|
43
52
|
});
|
|
44
53
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TokenizerExtensionFunction } from 'marked';
|
|
2
|
+
export interface BrToken {
|
|
3
|
+
type: 'br';
|
|
4
|
+
raw: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function markedBr(): {
|
|
7
|
+
extensions: Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
level: 'inline';
|
|
10
|
+
tokenizer: TokenizerExtensionFunction;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function markedBr() {
|
|
2
|
+
return {
|
|
3
|
+
extensions: [
|
|
4
|
+
{
|
|
5
|
+
name: 'br',
|
|
6
|
+
level: 'inline',
|
|
7
|
+
tokenizer(src) {
|
|
8
|
+
// Match HTML <br> tags (with or without closing slash, case insensitive)
|
|
9
|
+
const match = src.match(/^<br\s*\/?>/i);
|
|
10
|
+
if (match) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'br',
|
|
13
|
+
raw: match[0]
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TokenizerExtensionFunction } from 'marked';
|
|
2
|
+
export interface HrToken {
|
|
3
|
+
type: 'hr';
|
|
4
|
+
raw: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function markedHr(): {
|
|
7
|
+
extensions: Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
level: 'block';
|
|
10
|
+
tokenizer: TokenizerExtensionFunction;
|
|
11
|
+
}>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function markedHr() {
|
|
2
|
+
return {
|
|
3
|
+
extensions: [
|
|
4
|
+
{
|
|
5
|
+
name: 'hr',
|
|
6
|
+
level: 'block',
|
|
7
|
+
tokenizer(src) {
|
|
8
|
+
// Match horizontal rules according to CommonMark spec:
|
|
9
|
+
// 3 or more matching -, _, or * characters, with optional spaces between
|
|
10
|
+
// Must be at start of string and match entire line
|
|
11
|
+
const match = src.match(/^[ \t]*(-[ \t]*-[ \t]*-+|_[ \t]*_[ \t]*_+|\*[ \t]*\*[ \t]*\*+)[ \t]*(?:\n|$)/);
|
|
12
|
+
if (match) {
|
|
13
|
+
const raw = match[0].replace(/\n$/, ''); // Remove trailing newline from raw
|
|
14
|
+
return {
|
|
15
|
+
type: 'hr',
|
|
16
|
+
raw: raw
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -4,7 +4,7 @@ export declare function romanToInt(roman: string): number;
|
|
|
4
4
|
export declare const romanUpper = "(?:C|XC|L?X{0,3}(?:IX|IV|V?I{0,3}))";
|
|
5
5
|
export declare const romanLower = "(?:c|xc|l?x{0,3}(?:ix|iv|v?i{0,3}))";
|
|
6
6
|
export declare const bulletPattern = "(?:[*+-]|(?:\\d{1,9}|[a-zA-Z]|(?:C|XC|L?X{0,3}(?:IX|IV|V?I{0,3}))|(?:c|xc|l?x{0,3}(?:ix|iv|v?i{0,3})))[.)])";
|
|
7
|
-
export declare const rule = "^( {0,3}(?:[*+-]|(?:\\d{1,9}|[a-zA-Z]|(?:C|XC|L?X{0,3}(?:IX|IV|V?I{0,3}))|(?:c|xc|l?x{0,3}(?:ix|iv|v?i{0,3})))[.)]))([ \\t][^\\n]
|
|
7
|
+
export declare const rule = "^( {0,3}(?:[*+-]|(?:\\d{1,9}|[a-zA-Z]|(?:C|XC|L?X{0,3}(?:IX|IV|V?I{0,3}))|(?:c|xc|l?x{0,3}(?:ix|iv|v?i{0,3})))[.)]))([ \\t][^\\n]*|[ \\t])?(?:\\n|$)";
|
|
8
8
|
export declare function markedList(): {
|
|
9
9
|
extensions: Array<{
|
|
10
10
|
name: string;
|
|
@@ -18,6 +18,7 @@ export interface ListToken {
|
|
|
18
18
|
ordered: boolean;
|
|
19
19
|
listType: 'decimal' | 'lower-alpha' | 'upper-alpha' | 'lower-roman' | 'upper-roman' | null;
|
|
20
20
|
loose: boolean;
|
|
21
|
+
start?: number;
|
|
21
22
|
tokens: ListItemToken[];
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
@@ -25,7 +25,7 @@ export const romanUpper = '(?:C|XC|L?X{0,3}(?:IX|IV|V?I{0,3}))';
|
|
|
25
25
|
export const romanLower = '(?:c|xc|l?x{0,3}(?:ix|iv|v?i{0,3}))';
|
|
26
26
|
// Fixed regex pattern - carefully balanced parentheses
|
|
27
27
|
export const bulletPattern = `(?:[*+-]|(?:\\d{1,9}|[a-zA-Z]|${romanUpper}|${romanLower})[.)])`;
|
|
28
|
-
export const rule = `^( {0,3}${bulletPattern})([ \\t][^\\n]
|
|
28
|
+
export const rule = `^( {0,3}${bulletPattern})([ \\t][^\\n]*|[ \\t])?(?:\\n|$)`;
|
|
29
29
|
function finalizeList(list, lexer) {
|
|
30
30
|
if (list.tokens.length === 0)
|
|
31
31
|
return;
|
|
@@ -97,10 +97,12 @@ export function markedList() {
|
|
|
97
97
|
ordered: isOrdered,
|
|
98
98
|
listType: isOrdered ? type : null,
|
|
99
99
|
loose: false,
|
|
100
|
+
start: undefined, // Will be set when first item is processed
|
|
100
101
|
tokens: []
|
|
101
102
|
};
|
|
102
103
|
// Get next list item
|
|
103
|
-
|
|
104
|
+
// Updated regex to properly handle empty list items (space after bullet, then newline)
|
|
105
|
+
const itemRegex = new RegExp(`^( {0,3}${bull})([\t ][^\\n]*|[\t ])?(?:\\n|$)`);
|
|
104
106
|
let endsWithBlankLine = false;
|
|
105
107
|
// Check if current bullet point can start a new List Item
|
|
106
108
|
while (src) {
|
|
@@ -112,7 +114,9 @@ export function markedList() {
|
|
|
112
114
|
raw = cap[0];
|
|
113
115
|
const bullet = cap[1].trim();
|
|
114
116
|
src = src.substring(raw.length);
|
|
115
|
-
const line = cap[2]
|
|
117
|
+
const line = cap[2]
|
|
118
|
+
? cap[2].split('\n', 1)[0].replace(/^\t+/, (t) => ' '.repeat(3 * t.length))
|
|
119
|
+
: '';
|
|
116
120
|
const nextLine = src.split('\n', 1)[0];
|
|
117
121
|
const blankLine = !line.trim();
|
|
118
122
|
let indent = 0;
|
|
@@ -199,6 +203,8 @@ export function markedList() {
|
|
|
199
203
|
if (expectedValue === null) {
|
|
200
204
|
// First item: set expectedValue to this item's value (or 1 if parsing failed)
|
|
201
205
|
expectedValue = value ?? 1;
|
|
206
|
+
// Set the start property for ordered lists
|
|
207
|
+
list.start = expectedValue;
|
|
202
208
|
}
|
|
203
209
|
else {
|
|
204
210
|
// Subsequent items: check if value matches expected
|
|
@@ -1,30 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TokenizerExtensionFunction, TokenizerStartFunction } from 'marked';
|
|
2
2
|
export declare function markedMath(): {
|
|
3
|
-
extensions:
|
|
3
|
+
extensions: Array<{
|
|
4
4
|
name: string;
|
|
5
|
-
level:
|
|
6
|
-
tokenizer
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
raw: string;
|
|
10
|
-
text: string;
|
|
11
|
-
} | undefined;
|
|
12
|
-
start?: undefined;
|
|
13
|
-
} | {
|
|
14
|
-
name: string;
|
|
15
|
-
level: string;
|
|
16
|
-
start(src: string): number | undefined;
|
|
17
|
-
tokenizer(this: TokenizerThis, src: string): {
|
|
18
|
-
type: "math";
|
|
19
|
-
isInline: true;
|
|
20
|
-
raw: string;
|
|
21
|
-
text: string;
|
|
22
|
-
} | undefined;
|
|
23
|
-
})[];
|
|
5
|
+
level: 'block' | 'inline';
|
|
6
|
+
tokenizer: TokenizerExtensionFunction;
|
|
7
|
+
start?: TokenizerStartFunction;
|
|
8
|
+
}>;
|
|
24
9
|
};
|
|
25
10
|
export type MathToken = {
|
|
26
11
|
type: 'math';
|
|
27
12
|
raw: string;
|
|
28
13
|
text: string;
|
|
29
14
|
isInline: boolean;
|
|
15
|
+
displayMode: boolean;
|
|
30
16
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
const blockRule = /^(
|
|
1
|
+
// Math parsing rules
|
|
2
|
+
// Block math: supports both newline format ($$\nmath\n$$) and single-line format ($$math$$)
|
|
3
|
+
const blockRule = /^(\$\$)(?:\n((?:\\[\s\S]|[^\\])+?)\n\1(?:\n|$)|([^$\n]+?)\1(?=\s|$|$))/;
|
|
4
|
+
// Inline math: handles both single ($) and double ($$) dollar delimiters
|
|
5
|
+
// Avoids matching currency by checking context and requiring proper content
|
|
6
|
+
const inlineRule = /^(\${1,2})(?!\$)((?:[^$\n]|\\\$)*?)\1(?!\d)/;
|
|
4
7
|
export function markedMath() {
|
|
5
8
|
return {
|
|
6
9
|
extensions: [
|
|
@@ -10,11 +13,14 @@ export function markedMath() {
|
|
|
10
13
|
tokenizer(src) {
|
|
11
14
|
const match = src.match(blockRule);
|
|
12
15
|
if (match) {
|
|
16
|
+
// match[2] is multiline format, match[3] is single-line format
|
|
17
|
+
const content = (match[2] || match[3]).trim();
|
|
13
18
|
return {
|
|
14
19
|
type: 'math',
|
|
15
20
|
isInline: false,
|
|
21
|
+
displayMode: true,
|
|
16
22
|
raw: match[0],
|
|
17
|
-
text:
|
|
23
|
+
text: content
|
|
18
24
|
};
|
|
19
25
|
}
|
|
20
26
|
}
|
|
@@ -23,31 +29,46 @@ export function markedMath() {
|
|
|
23
29
|
name: 'math',
|
|
24
30
|
level: 'inline',
|
|
25
31
|
start(src) {
|
|
26
|
-
let index;
|
|
27
|
-
let
|
|
28
|
-
while (
|
|
29
|
-
|
|
30
|
-
if (
|
|
32
|
+
let index = 0;
|
|
33
|
+
let searchSrc = src;
|
|
34
|
+
while (searchSrc) {
|
|
35
|
+
const dollarIndex = searchSrc.indexOf('$');
|
|
36
|
+
if (dollarIndex === -1) {
|
|
31
37
|
return;
|
|
32
38
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
const currentIndex = index + dollarIndex;
|
|
40
|
+
const possibleMath = src.substring(currentIndex);
|
|
41
|
+
// Check if this could be math (not currency)
|
|
42
|
+
if (possibleMath.match(inlineRule)) {
|
|
43
|
+
// Additional check: avoid currency patterns like $5.00
|
|
44
|
+
const beforeChar = currentIndex > 0 ? src[currentIndex - 1] : '';
|
|
45
|
+
const afterDollar = possibleMath.substring(1, 6); // Check first few chars after $
|
|
46
|
+
// Skip if it looks like currency (digit immediately after $ or decimal pattern)
|
|
47
|
+
if (!/^\d+(\.\d{2})?\s/.test(afterDollar)) {
|
|
48
|
+
return currentIndex;
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
|
-
|
|
51
|
+
index += dollarIndex + 1;
|
|
52
|
+
searchSrc = src.substring(index);
|
|
41
53
|
}
|
|
42
54
|
},
|
|
43
55
|
tokenizer(src) {
|
|
44
56
|
const match = src.match(inlineRule);
|
|
45
57
|
if (match) {
|
|
58
|
+
// Additional validation: avoid currency patterns
|
|
59
|
+
const content = match[2];
|
|
60
|
+
if (/^\d+(\.\d{2})?$/.test(content.trim())) {
|
|
61
|
+
// This looks like currency, skip it
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Double dollars are display mode, single dollars are inline
|
|
65
|
+
const isDisplayMode = match[1] === '$$';
|
|
46
66
|
return {
|
|
47
67
|
type: 'math',
|
|
48
|
-
isInline:
|
|
68
|
+
isInline: !isDisplayMode,
|
|
69
|
+
displayMode: isDisplayMode,
|
|
49
70
|
raw: match[0],
|
|
50
|
-
text:
|
|
71
|
+
text: content.trim()
|
|
51
72
|
};
|
|
52
73
|
}
|
|
53
74
|
}
|