svelte-streamdown 2.4.3 → 2.5.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 +98 -4
- package/dist/Block.svelte +4 -4
- package/dist/Block.svelte.d.ts +0 -1
- package/dist/Elements/Citation.svelte +285 -0
- package/dist/Elements/Citation.svelte.d.ts +7 -0
- package/dist/Elements/Code.svelte +2 -4
- package/dist/Elements/Element.svelte +9 -2
- package/dist/Elements/FootnoteRef.svelte +39 -89
- package/dist/Elements/Mermaid.svelte +55 -90
- package/dist/Elements/popover.svelte.d.ts +8 -0
- package/dist/Elements/popover.svelte.js +40 -0
- package/dist/Elements/stepperState.svelte.d.ts +35 -0
- package/dist/Elements/stepperState.svelte.js +98 -0
- package/dist/Streamdown.svelte +10 -3
- package/dist/Streamdown.svelte.d.ts +23 -2
- package/dist/context.svelte.d.ts +25 -11
- package/dist/context.svelte.js +14 -6
- package/dist/marked/index.d.ts +4 -2
- package/dist/marked/index.js +4 -2
- package/dist/marked/marked-align.d.ts +10 -0
- package/dist/marked/marked-align.js +36 -0
- package/dist/marked/marked-citations.d.ts +8 -0
- package/dist/marked/marked-citations.js +49 -0
- package/dist/marked/marked-table.js +36 -13
- package/dist/theme.d.ts +66 -21
- package/dist/theme.js +47 -17
- package/dist/utils/get.d.ts +1 -0
- package/dist/utils/get.js +25 -0
- package/dist/utils/panzoom.svelte.d.ts +1 -2
- package/dist/utils/panzoom.svelte.js +74 -43
- package/dist/utils/parse-incomplete-markdown.js +89 -1
- package/package.json +1 -1
|
@@ -32,7 +32,7 @@ export const usePanzoom = (opts = {}) => {
|
|
|
32
32
|
if (e.key === 'Escape' || e.keyCode === 27) {
|
|
33
33
|
if (isExpanded && !animating) {
|
|
34
34
|
// fire and forget
|
|
35
|
-
void
|
|
35
|
+
void expand(false);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -345,59 +345,91 @@ export const usePanzoom = (opts = {}) => {
|
|
|
345
345
|
};
|
|
346
346
|
});
|
|
347
347
|
}
|
|
348
|
-
const
|
|
348
|
+
const expand = (expand) => {
|
|
349
349
|
if (!eventTarget)
|
|
350
350
|
return;
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
351
|
+
// Capture first state
|
|
352
|
+
const first = eventTarget.getBoundingClientRect();
|
|
353
|
+
if (expand) {
|
|
354
|
+
// Expanding: immediately apply expanded state
|
|
355
|
+
eventTarget.parentElement.style.height = eventTarget.clientHeight + 'px';
|
|
356
|
+
eventTarget.dataset.expanded = 'true';
|
|
357
|
+
isExpanded = true;
|
|
358
358
|
zoomToFit();
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
359
|
+
// Force layout to get the final position
|
|
360
|
+
const last = eventTarget.getBoundingClientRect();
|
|
361
|
+
// Calculate the inverse transform
|
|
362
|
+
const deltaX = first.left - last.left;
|
|
363
|
+
const deltaY = first.top - last.top;
|
|
364
|
+
const deltaW = first.width / last.width;
|
|
365
|
+
const deltaH = first.height / last.height;
|
|
366
|
+
// Animate from original position to expanded
|
|
367
|
+
const animation = eventTarget.animate([
|
|
368
|
+
{
|
|
369
|
+
transformOrigin: '0 0',
|
|
370
|
+
transform: `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
transformOrigin: '0 0',
|
|
374
|
+
transform: 'translate(0px, 0px) scale(1, 1)'
|
|
375
|
+
}
|
|
376
|
+
], {
|
|
377
|
+
duration: 350,
|
|
378
|
+
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
|
|
379
|
+
fill: 'both'
|
|
380
|
+
});
|
|
381
|
+
animation.finished.then(() => {
|
|
382
|
+
animation.cancel();
|
|
364
383
|
});
|
|
365
384
|
}
|
|
366
385
|
else {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const run = () => {
|
|
379
|
-
if (!eventTarget)
|
|
380
|
-
return;
|
|
386
|
+
// Collapsing: keep expanded state during animation, then remove
|
|
387
|
+
const last = {
|
|
388
|
+
left: first.left,
|
|
389
|
+
top: first.top,
|
|
390
|
+
width: first.width,
|
|
391
|
+
height: first.height
|
|
392
|
+
};
|
|
393
|
+
// Calculate where it will be after collapsing
|
|
394
|
+
eventTarget.dataset.expanded = 'false';
|
|
395
|
+
const final = eventTarget.getBoundingClientRect();
|
|
396
|
+
// Restore expanded state for animation
|
|
381
397
|
eventTarget.dataset.expanded = 'true';
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
398
|
+
const deltaX = final.left - last.left;
|
|
399
|
+
const deltaY = final.top - last.top;
|
|
400
|
+
const deltaW = final.width / last.width;
|
|
401
|
+
const deltaH = final.height / last.height;
|
|
402
|
+
// Animate from expanded to collapsed
|
|
403
|
+
const animation = eventTarget.animate([
|
|
404
|
+
{
|
|
405
|
+
transformOrigin: '0 0',
|
|
406
|
+
transform: 'translate(0px, 0px) scale(1, 1)'
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
transformOrigin: '0 0',
|
|
410
|
+
transform: `translate(${deltaX}px, ${deltaY}px) scale(${deltaW}, ${deltaH})`
|
|
411
|
+
}
|
|
412
|
+
], {
|
|
413
|
+
duration: 350,
|
|
414
|
+
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
|
|
415
|
+
fill: 'both'
|
|
416
|
+
});
|
|
417
|
+
animation.finished.then(() => {
|
|
418
|
+
animation.cancel();
|
|
419
|
+
// Only remove expanded state after animation completes
|
|
420
|
+
if (!eventTarget)
|
|
421
|
+
return;
|
|
422
|
+
eventTarget.dataset.expanded = 'false';
|
|
423
|
+
eventTarget.parentElement.style.height = 'fit-content';
|
|
424
|
+
isExpanded = false;
|
|
425
|
+
zoomToFit();
|
|
388
426
|
});
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
run();
|
|
392
|
-
if (eventTarget)
|
|
393
|
-
eventTarget.style.viewTransitionName = '';
|
|
394
427
|
}
|
|
395
428
|
};
|
|
396
429
|
async function toggleExpand() {
|
|
397
|
-
zoomToFit();
|
|
398
430
|
if (isExpanded)
|
|
399
|
-
return
|
|
400
|
-
return expand();
|
|
431
|
+
return expand(false);
|
|
432
|
+
return expand(true);
|
|
401
433
|
}
|
|
402
434
|
function zoomToFit(padding = 0.05) {
|
|
403
435
|
if (!node)
|
|
@@ -467,7 +499,6 @@ export const usePanzoom = (opts = {}) => {
|
|
|
467
499
|
moveBy,
|
|
468
500
|
setTransform,
|
|
469
501
|
expand,
|
|
470
|
-
collapse,
|
|
471
502
|
toggleExpand,
|
|
472
503
|
get transform() {
|
|
473
504
|
return { x, y, scale };
|
|
@@ -106,6 +106,8 @@ class IncompleteMarkdownParser {
|
|
|
106
106
|
const lines = text.split('\n');
|
|
107
107
|
let inCodeBlock = false;
|
|
108
108
|
let inMathBlock = false;
|
|
109
|
+
let inCenterBlock = false;
|
|
110
|
+
let inRightBlock = false;
|
|
109
111
|
// Track which lines are in which contexts for state management
|
|
110
112
|
const lineContexts = [];
|
|
111
113
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -117,7 +119,24 @@ class IncompleteMarkdownParser {
|
|
|
117
119
|
if (line.trim().startsWith('$$') && !line.trim().includes('$$', 2)) {
|
|
118
120
|
inMathBlock = !inMathBlock;
|
|
119
121
|
}
|
|
120
|
-
|
|
122
|
+
if (line.trim() === '[center]') {
|
|
123
|
+
inCenterBlock = true;
|
|
124
|
+
}
|
|
125
|
+
if (line.trim() === '[/center]') {
|
|
126
|
+
inCenterBlock = false;
|
|
127
|
+
}
|
|
128
|
+
if (line.trim() === '[right]') {
|
|
129
|
+
inRightBlock = true;
|
|
130
|
+
}
|
|
131
|
+
if (line.trim() === '[/right]') {
|
|
132
|
+
inRightBlock = false;
|
|
133
|
+
}
|
|
134
|
+
lineContexts[i] = {
|
|
135
|
+
code: inCodeBlock,
|
|
136
|
+
math: inMathBlock,
|
|
137
|
+
center: inCenterBlock,
|
|
138
|
+
right: inRightBlock
|
|
139
|
+
};
|
|
121
140
|
}
|
|
122
141
|
// Set the final blocking contexts (for postprocessing)
|
|
123
142
|
const finalContexts = new Set();
|
|
@@ -125,6 +144,10 @@ class IncompleteMarkdownParser {
|
|
|
125
144
|
finalContexts.add('code');
|
|
126
145
|
if (inMathBlock)
|
|
127
146
|
finalContexts.add('math');
|
|
147
|
+
if (inCenterBlock)
|
|
148
|
+
finalContexts.add('center');
|
|
149
|
+
if (inRightBlock)
|
|
150
|
+
finalContexts.add('right');
|
|
128
151
|
// Return both the text and the updated state
|
|
129
152
|
return {
|
|
130
153
|
text: text, // Don't modify text in preprocess
|
|
@@ -142,6 +165,12 @@ class IncompleteMarkdownParser {
|
|
|
142
165
|
if (state.blockingContexts.has('math')) {
|
|
143
166
|
return text + '\n$$';
|
|
144
167
|
}
|
|
168
|
+
if (state.blockingContexts.has('center')) {
|
|
169
|
+
return text + '\n[/center]';
|
|
170
|
+
}
|
|
171
|
+
if (state.blockingContexts.has('right')) {
|
|
172
|
+
return text + '\n[/right]';
|
|
173
|
+
}
|
|
145
174
|
return text;
|
|
146
175
|
}
|
|
147
176
|
},
|
|
@@ -153,10 +182,14 @@ class IncompleteMarkdownParser {
|
|
|
153
182
|
if (line.trim() === '***') {
|
|
154
183
|
return line;
|
|
155
184
|
}
|
|
185
|
+
const isEndingWithTripleAsterisk = line.endsWith('***');
|
|
156
186
|
const tripleAsterisks = (line.match(/\*\*\*/g) || []).length;
|
|
157
187
|
if (tripleAsterisks % 2 === 1) {
|
|
158
188
|
const lastTripleAsteriskIndex = line.lastIndexOf('***');
|
|
159
189
|
const endOfCellOrLine = findEndOfCellOrLineContaining(line, lastTripleAsteriskIndex);
|
|
190
|
+
if (isEndingWithTripleAsterisk) {
|
|
191
|
+
return line.substring(0, lastTripleAsteriskIndex);
|
|
192
|
+
}
|
|
160
193
|
return line.substring(0, endOfCellOrLine) + '***' + line.substring(endOfCellOrLine);
|
|
161
194
|
}
|
|
162
195
|
return line;
|
|
@@ -172,8 +205,12 @@ class IncompleteMarkdownParser {
|
|
|
172
205
|
}
|
|
173
206
|
const doubleAsteriskMatches = (line.match(/\*\*/g) || []).length;
|
|
174
207
|
if (doubleAsteriskMatches % 2 === 1) {
|
|
208
|
+
const isEndingWithDoubleAsterisk = line.endsWith('**');
|
|
175
209
|
const lastDoubleAsteriskIndex = line.lastIndexOf('**');
|
|
176
210
|
const endOfCellOrLine = findEndOfCellOrLineContaining(line, lastDoubleAsteriskIndex);
|
|
211
|
+
if (isEndingWithDoubleAsterisk) {
|
|
212
|
+
return line.substring(0, lastDoubleAsteriskIndex);
|
|
213
|
+
}
|
|
177
214
|
return line.substring(0, endOfCellOrLine) + '**' + line.substring(endOfCellOrLine);
|
|
178
215
|
}
|
|
179
216
|
return line;
|
|
@@ -189,8 +226,12 @@ class IncompleteMarkdownParser {
|
|
|
189
226
|
}
|
|
190
227
|
const underscorePairs = (line.match(/__/g) || []).length;
|
|
191
228
|
if (underscorePairs % 2 === 1) {
|
|
229
|
+
const isEndingWithDoubleUnderscore = line.endsWith('__');
|
|
192
230
|
const lastDoubleUnderscoreIndex = line.lastIndexOf('__');
|
|
193
231
|
const endOfCellOrLine = findEndOfCellOrLineContaining(line, lastDoubleUnderscoreIndex);
|
|
232
|
+
if (isEndingWithDoubleUnderscore) {
|
|
233
|
+
return line.substring(0, lastDoubleUnderscoreIndex);
|
|
234
|
+
}
|
|
194
235
|
return line.substring(0, endOfCellOrLine) + '__' + line.substring(endOfCellOrLine);
|
|
195
236
|
}
|
|
196
237
|
return line;
|
|
@@ -203,11 +244,15 @@ class IncompleteMarkdownParser {
|
|
|
203
244
|
handler: ({ line }) => {
|
|
204
245
|
const tildePairs = (line.match(/~~/g) || []).length;
|
|
205
246
|
if (tildePairs % 2 === 1) {
|
|
247
|
+
const isEndingWithDoubleTilde = line.endsWith('~~');
|
|
206
248
|
const lastDoubleTildeIndex = line.lastIndexOf('~~');
|
|
207
249
|
const endOfCellOrLine = findEndOfCellOrLineContaining(line, lastDoubleTildeIndex);
|
|
208
250
|
// Only complete if there's content after the tildes
|
|
209
251
|
const contentAfterTildes = line.substring(lastDoubleTildeIndex + 2, endOfCellOrLine);
|
|
210
252
|
if (contentAfterTildes.trim().length > 0) {
|
|
253
|
+
if (isEndingWithDoubleTilde) {
|
|
254
|
+
return line.substring(0, lastDoubleTildeIndex);
|
|
255
|
+
}
|
|
211
256
|
return line.substring(0, endOfCellOrLine) + '~~' + line.substring(endOfCellOrLine);
|
|
212
257
|
}
|
|
213
258
|
}
|
|
@@ -390,6 +435,31 @@ class IncompleteMarkdownParser {
|
|
|
390
435
|
return line;
|
|
391
436
|
}
|
|
392
437
|
},
|
|
438
|
+
{
|
|
439
|
+
name: 'inlineCitation',
|
|
440
|
+
pattern: /\[/,
|
|
441
|
+
skipInBlockTypes: ['code', 'math'],
|
|
442
|
+
handler: ({ line }) => {
|
|
443
|
+
// Count unescaped opening brackets without matching closing brackets
|
|
444
|
+
let unclosedBrackets = 0;
|
|
445
|
+
for (let i = 0; i < line.length; i++) {
|
|
446
|
+
if (line[i] === '[' && (i === 0 || line[i - 1] !== '\\')) {
|
|
447
|
+
// Check if this bracket has a matching closing bracket later in the line
|
|
448
|
+
const restOfLine = line.substring(i + 1);
|
|
449
|
+
const closingIndex = restOfLine.indexOf(']');
|
|
450
|
+
if (closingIndex === -1) {
|
|
451
|
+
unclosedBrackets++;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// If there's an odd number of unclosed brackets, add closing bracket
|
|
456
|
+
if (unclosedBrackets % 2 === 1) {
|
|
457
|
+
const endOfCellOrLine = findEndOfCellOrLineContaining(line, line.length - 1);
|
|
458
|
+
return line.substring(0, endOfCellOrLine) + ']' + line.substring(endOfCellOrLine);
|
|
459
|
+
}
|
|
460
|
+
return line;
|
|
461
|
+
}
|
|
462
|
+
},
|
|
393
463
|
{
|
|
394
464
|
name: 'footnoteRef',
|
|
395
465
|
pattern: /\[\^[^\]\s,]*/,
|
|
@@ -482,6 +552,9 @@ class IncompleteMarkdownParser {
|
|
|
482
552
|
pattern: /\$\$/,
|
|
483
553
|
skipInBlockTypes: ['code', 'math'],
|
|
484
554
|
handler: ({ line }) => {
|
|
555
|
+
// Don't process block boundaries (lines that are just $$)
|
|
556
|
+
if (line.trim() === '$$')
|
|
557
|
+
return line;
|
|
485
558
|
const dollarPairs = (line.match(/\$\$/g) || []).length;
|
|
486
559
|
if (dollarPairs % 2 === 0)
|
|
487
560
|
return line;
|
|
@@ -582,6 +655,21 @@ class IncompleteMarkdownParser {
|
|
|
582
655
|
}
|
|
583
656
|
return line;
|
|
584
657
|
}
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
name: 'alignmentBlocks',
|
|
661
|
+
pattern: /^(\s*\[(center|right)\])$/,
|
|
662
|
+
skipInBlockTypes: ['code', 'math'],
|
|
663
|
+
handler: ({ line, state }) => {
|
|
664
|
+
// Check if this is an opening alignment tag without content or closing tag
|
|
665
|
+
const alignMatch = line.match(/^(\s*\[(center|right)\])$/);
|
|
666
|
+
if (alignMatch) {
|
|
667
|
+
const indent = alignMatch[1].length - alignMatch[1].trim().length;
|
|
668
|
+
const alignType = alignMatch[2];
|
|
669
|
+
return line + '\n' + ' '.repeat(indent) + '[/' + alignType + ']';
|
|
670
|
+
}
|
|
671
|
+
return line;
|
|
672
|
+
}
|
|
585
673
|
}
|
|
586
674
|
];
|
|
587
675
|
}
|