svelte-streamdown 2.3.1 → 2.3.3

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.
@@ -116,8 +116,7 @@ const processSpans = (cells, count, prevRow = [], maxColspan = null) => {
116
116
  let cellText = cell.text;
117
117
  // Handle Rowspan - cells ending with ^ (but not superscript ^text^)
118
118
  // Check if it's a rowspan indicator (single ^ at end) vs superscript (^text^)
119
- const isRowspanIndicator = cellText.slice(-1) === '^' &&
120
- !cellText.match(/\^[^^\n\r]+\^$/); // Not a superscript pattern ^text^
119
+ const isRowspanIndicator = cellText.slice(-1) === '^' && !cellText.match(/\^[^^\n\r]+\^$/); // Not a superscript pattern ^text^
121
120
  if (isRowspanIndicator && prevRow.length > 0) {
122
121
  // Clean the ^ indicator from the cell text
123
122
  cell.text = cellText.slice(0, -1).trim();
@@ -374,129 +373,121 @@ function processRows(headerRows, bodyRows, alignment, colCount, lexer, maxColspa
374
373
  }
375
374
  return tokens;
376
375
  }
376
+ const { detectFooter, maxColspan } = DEFAULT_OPTIONS;
377
377
  // Adds support for extended tables in marked with row spanning, column spanning,
378
378
  // multi-row headers, and column alignment
379
- export function markedTable(options = {}) {
380
- const config = { ...DEFAULT_OPTIONS, ...options };
381
- const { detectFooter, maxColspan } = config;
382
- return {
383
- extensions: [
384
- {
385
- name: 'table',
386
- level: 'block',
387
- start(src) {
388
- // Check for table with potential header alignment
389
- let match = src.match(/^\n *([^\n ].*\|.*)\n/);
390
- if (match)
391
- return match.index;
392
- // Check for simple table without header alignment
393
- match = src.match(/^\n *(\|.*\|)\n/);
394
- if (match)
395
- return match.index;
379
+ export const markedTable = {
380
+ name: 'table',
381
+ level: 'block',
382
+ start(src) {
383
+ // Check for table with potential header alignment
384
+ let match = src.match(/^\n *([^\n ].*\|.*)\n/);
385
+ if (match)
386
+ return match.index;
387
+ // Check for simple table without header alignment
388
+ match = src.match(/^\n *(\|.*\|)\n/);
389
+ if (match)
390
+ return match.index;
391
+ return undefined;
392
+ },
393
+ tokenizer(src) {
394
+ // Try to match table with header and alignment first
395
+ let regex = new RegExp('^' +
396
+ '([^\\n ].*\\|.*\\n(?: *[^\\s].*\\n)*?)' + // Header
397
+ ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' + // Header Align
398
+ '(?:\\n((?:(?! *\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})' + // Body Cells
399
+ '(?:\\n+|$)| {0,3}#{1,6} | {0,3}>| {4}[^\\n]| {0,3}(?:`{3,}' +
400
+ '(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n| {0,3}(?:[*+-]|1[.)]) |' +
401
+ '<\\/?(?:address|article|aside|base|basefont|blockquote|body|' +
402
+ 'caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?: +|\\n|\\/?>)|<(?:script|pre|style|textarea|!--)).*(?:\\n|$))*)\\n*|$)');
403
+ let cap = regex.exec(src);
404
+ let hasHeaderAlignment = true;
405
+ // If no match with header alignment, try table without header alignment
406
+ if (!cap) {
407
+ // Simple regex for tables without header alignment
408
+ regex = /^(\|.*\|(?:\n\|.*\|)*)/;
409
+ cap = regex.exec(src);
410
+ hasHeaderAlignment = false;
411
+ }
412
+ if (!cap)
413
+ return undefined;
414
+ // Combine all captured groups to get complete table rows
415
+ let allTableContent = cap[1]; // Headers
416
+ if (cap[2])
417
+ allTableContent += '\n' + cap[2]; // Alignment row
418
+ if (cap[3])
419
+ allTableContent += '\n' + cap[3]; // Body rows
420
+ const allRows = allTableContent.replace(/\n$/, '').split('\n');
421
+ let headerRows = [];
422
+ let bodyRows = [];
423
+ let alignRow = [];
424
+ let alignment = [];
425
+ let colCount = 0;
426
+ if (hasHeaderAlignment) {
427
+ // Traditional table with header and alignment
428
+ // Parse all rows and identify which are headers vs body
429
+ let headerEndIndex = -1;
430
+ // Find the FIRST alignment row (contains dashes/underscores/asterisks)
431
+ for (let i = 0; i < allRows.length; i++) {
432
+ const row = allRows[i].trim();
433
+ const isAlignment = /^ *(\| *)?:?-+:? *(\| *:?-+:? *)*(\| *)?$/.test(row);
434
+ // Check if this row matches alignment pattern (contains only |, spaces, and alignment chars)
435
+ if (isAlignment) {
436
+ headerEndIndex = i;
437
+ alignRow = row.replace(/^ *\| *| *\| *$/g, '').split(/ *\| */);
438
+ break; // Stop at the first alignment row
439
+ }
440
+ }
441
+ if (headerEndIndex === -1) {
442
+ // No alignment row found, treat as simple table
443
+ bodyRows = allRows;
444
+ colCount = bodyRows[0].split('|').filter((cell) => cell.trim() !== '').length;
445
+ alignment = new Array(colCount).fill(null);
446
+ }
447
+ else {
448
+ // Found alignment row, split headers and body
449
+ // Filter out empty rows and the alignment row itself from headers
450
+ headerRows = allRows.slice(0, headerEndIndex).filter((row) => row.trim() !== '');
451
+ bodyRows = headerEndIndex + 1 < allRows.length ? allRows.slice(headerEndIndex + 1) : [];
452
+ // Use alignment row length as the authoritative column count
453
+ colCount = alignRow.length;
454
+ // Validate that we have a reasonable table structure
455
+ if (colCount === 0)
396
456
  return undefined;
397
- },
398
- tokenizer(src) {
399
- // Try to match table with header and alignment first
400
- let regex = new RegExp('^' +
401
- '([^\\n ].*\\|.*\\n(?: *[^\\s].*\\n)*?)' + // Header
402
- ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' + // Header Align
403
- '(?:\\n((?:(?! *\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})' + // Body Cells
404
- '(?:\\n+|$)| {0,3}#{1,6} | {0,3}>| {4}[^\\n]| {0,3}(?:`{3,}' +
405
- '(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n| {0,3}(?:[*+-]|1[.)]) |' +
406
- '<\\/?(?:address|article|aside|base|basefont|blockquote|body|' +
407
- 'caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?: +|\\n|\\/?>)|<(?:script|pre|style|textarea|!--)).*(?:\\n|$))*)\\n*|$)');
408
- let cap = regex.exec(src);
409
- let hasHeaderAlignment = true;
410
- // If no match with header alignment, try table without header alignment
411
- if (!cap) {
412
- // Simple regex for tables without header alignment
413
- regex = /^(\|.*\|(?:\n\|.*\|)*)/;
414
- cap = regex.exec(src);
415
- hasHeaderAlignment = false;
416
- }
417
- if (!cap)
418
- return null;
419
- // Combine all captured groups to get complete table rows
420
- let allTableContent = cap[1]; // Headers
421
- if (cap[2])
422
- allTableContent += '\n' + cap[2]; // Alignment row
423
- if (cap[3])
424
- allTableContent += '\n' + cap[3]; // Body rows
425
- const allRows = allTableContent.replace(/\n$/, '').split('\n');
426
- let headerRows = [];
427
- let bodyRows = [];
428
- let alignRow = [];
429
- let alignment = [];
430
- let colCount = 0;
431
- if (hasHeaderAlignment) {
432
- // Traditional table with header and alignment
433
- // Parse all rows and identify which are headers vs body
434
- let headerEndIndex = -1;
435
- // Find the FIRST alignment row (contains dashes/underscores/asterisks)
436
- for (let i = 0; i < allRows.length; i++) {
437
- const row = allRows[i].trim();
438
- const isAlignment = /^ *(\| *)?:?-+:? *(\| *:?-+:? *)*(\| *)?$/.test(row);
439
- // Check if this row matches alignment pattern (contains only |, spaces, and alignment chars)
440
- if (isAlignment) {
441
- headerEndIndex = i;
442
- alignRow = row.replace(/^ *\| *| *\| *$/g, '').split(/ *\| */);
443
- break; // Stop at the first alignment row
444
- }
445
- }
446
- if (headerEndIndex === -1) {
447
- // No alignment row found, treat as simple table
448
- bodyRows = allRows;
449
- colCount = bodyRows[0].split('|').filter((cell) => cell.trim() !== '').length;
450
- alignment = new Array(colCount).fill(null);
451
- }
452
- else {
453
- // Found alignment row, split headers and body
454
- // Filter out empty rows and the alignment row itself from headers
455
- headerRows = allRows.slice(0, headerEndIndex).filter((row) => row.trim() !== '');
456
- bodyRows =
457
- headerEndIndex + 1 < allRows.length ? allRows.slice(headerEndIndex + 1) : [];
458
- // Use alignment row length as the authoritative column count
459
- colCount = alignRow.length;
460
- // Validate that we have a reasonable table structure
461
- if (colCount === 0)
462
- return null;
463
- // Process alignment
464
- alignment = processAlignment(alignRow);
465
- }
466
- }
467
- else {
468
- // Table without header alignment - treat all rows as body rows
469
- bodyRows = allRows;
470
- const firstRowCells = bodyRows[0].split('|').filter((cell) => cell.trim() !== '');
471
- colCount = firstRowCells.length;
472
- alignment = new Array(colCount).fill(null); // No alignment for tables without headers
473
- }
474
- // Detect footer alignment row pattern in body rows (only for tables with header alignment)
475
- let shouldDetectFooter = false;
476
- let processedBodyRows = bodyRows;
477
- if (detectFooter && hasHeaderAlignment && bodyRows.length > 0) {
478
- // Check if any row matches the alignment pattern (contains only dashes, pipes, colons, and spaces)
479
- for (let i = bodyRows.length - 1; i >= 0; i--) {
480
- const row = bodyRows[i];
481
- if (/^ *\| *:?-+:? *(\| *:?-+:? *)*\| *$/.test(row)) {
482
- // Found footer alignment row - remove it and enable footer detection
483
- shouldDetectFooter = true;
484
- processedBodyRows = bodyRows.slice(0, i).concat(bodyRows.slice(i + 1));
485
- break;
486
- }
487
- }
488
- }
489
- // Process all rows and create table sections
490
- const tokens = processRows(headerRows, processedBodyRows, alignment, colCount, this.lexer, maxColspan, shouldDetectFooter);
491
- const item = {
492
- type: 'table',
493
- tokens,
494
- raw: cap[0],
495
- align: alignment
496
- };
497
- return item;
457
+ // Process alignment
458
+ alignment = processAlignment(alignRow);
459
+ }
460
+ }
461
+ else {
462
+ // Table without header alignment - treat all rows as body rows
463
+ bodyRows = allRows;
464
+ const firstRowCells = bodyRows[0].split('|').filter((cell) => cell.trim() !== '');
465
+ colCount = firstRowCells.length;
466
+ alignment = new Array(colCount).fill(null); // No alignment for tables without headers
467
+ }
468
+ // Detect footer alignment row pattern in body rows (only for tables with header alignment)
469
+ let shouldDetectFooter = false;
470
+ let processedBodyRows = bodyRows;
471
+ if (detectFooter && hasHeaderAlignment && bodyRows.length > 0) {
472
+ // Check if any row matches the alignment pattern (contains only dashes, pipes, colons, and spaces)
473
+ for (let i = bodyRows.length - 1; i >= 0; i--) {
474
+ const row = bodyRows[i];
475
+ if (/^ *\| *:?-+:? *(\| *:?-+:? *)*\| *$/.test(row)) {
476
+ // Found footer alignment row - remove it and enable footer detection
477
+ shouldDetectFooter = true;
478
+ processedBodyRows = bodyRows.slice(0, i).concat(bodyRows.slice(i + 1));
479
+ break;
498
480
  }
499
481
  }
500
- ]
501
- };
502
- }
482
+ }
483
+ // Process all rows and create table sections
484
+ const tokens = processRows(headerRows, processedBodyRows, alignment, colCount, this.lexer, maxColspan, shouldDetectFooter);
485
+ const item = {
486
+ type: 'table',
487
+ tokens,
488
+ raw: cap[0],
489
+ align: alignment
490
+ };
491
+ return item;
492
+ }
493
+ };
package/dist/theme.d.ts CHANGED
@@ -128,6 +128,18 @@ export declare const theme: {
128
128
  footnotePopover: {
129
129
  base: string;
130
130
  };
131
+ descriptionList: {
132
+ base: string;
133
+ };
134
+ description: {
135
+ base: string;
136
+ };
137
+ descriptionTerm: {
138
+ base: string;
139
+ };
140
+ descriptionDetail: {
141
+ base: string;
142
+ };
131
143
  };
132
144
  export declare const shadcnTheme: {
133
145
  link: {
@@ -257,6 +269,18 @@ export declare const shadcnTheme: {
257
269
  footnotePopover: {
258
270
  base: string;
259
271
  };
272
+ descriptionList: {
273
+ base: string;
274
+ };
275
+ description: {
276
+ base: string;
277
+ };
278
+ descriptionTerm: {
279
+ base: string;
280
+ };
281
+ descriptionDetail: {
282
+ base: string;
283
+ };
260
284
  };
261
285
  export type Theme = typeof theme;
262
286
  type DeepPartial<T> = {
@@ -391,5 +415,17 @@ export declare const mergeTheme: (customTheme?: DeepPartialTheme, baseTheme?: "t
391
415
  footnotePopover: {
392
416
  base: string;
393
417
  };
418
+ descriptionList: {
419
+ base: string;
420
+ };
421
+ description: {
422
+ base: string;
423
+ };
424
+ descriptionTerm: {
425
+ base: string;
426
+ };
427
+ descriptionDetail: {
428
+ base: string;
429
+ };
394
430
  };
395
431
  export {};
package/dist/theme.js CHANGED
@@ -128,6 +128,18 @@ export const theme = {
128
128
  },
129
129
  footnotePopover: {
130
130
  base: 'fixed z-50 max-h-[30vh] max-w-3xl overflow-y-auto rounded-lg bg-background p-4 shadow'
131
+ },
132
+ descriptionList: {
133
+ base: 'my-4 space-y-2'
134
+ },
135
+ description: {
136
+ base: 'border-l-2 border-gray-200 pl-4'
137
+ },
138
+ descriptionTerm: {
139
+ base: 'font-semibold text-gray-900'
140
+ },
141
+ descriptionDetail: {
142
+ base: 'text-gray-700 ml-4 leading-relaxed'
131
143
  }
132
144
  };
133
145
  export const shadcnTheme = {
@@ -247,7 +259,7 @@ export const shadcnTheme = {
247
259
  base: ''
248
260
  },
249
261
  em: {
250
- base: 'italic text-foreground'
262
+ base: 'italic'
251
263
  },
252
264
  del: {
253
265
  base: 'text-muted-foreground'
@@ -257,6 +269,18 @@ export const shadcnTheme = {
257
269
  },
258
270
  footnotePopover: {
259
271
  base: 'fixed z-50 max-h-[30vh] shadow max-w-3xl overflow-y-auto rounded-lg bg-background p-4'
272
+ },
273
+ descriptionList: {
274
+ base: 'my-4 space-y-2'
275
+ },
276
+ description: {
277
+ base: 'border-l-2 border-border pl-4'
278
+ },
279
+ descriptionTerm: {
280
+ base: 'font-semibold text-foreground'
281
+ },
282
+ descriptionDetail: {
283
+ base: 'text-muted-foreground ml-4 leading-relaxed'
260
284
  }
261
285
  };
262
286
  export const mergeTheme = (customTheme, baseTheme) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-streamdown",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -73,7 +73,6 @@
73
73
  "dependencies": {
74
74
  "@floating-ui/dom": "^1.7.4",
75
75
  "clsx": "^2.1.1",
76
- "html-url-attributes": "^3.0.1",
77
76
  "katex": "^0.16.22",
78
77
  "marked": "^16.2.1",
79
78
  "mermaid": "^11.11.0",