svelte-streamdown 1.0.9 → 2.0.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.
Files changed (37) hide show
  1. package/README.md +144 -135
  2. package/dist/Block.svelte +14 -54
  3. package/dist/Elements/Alert.svelte +1 -2
  4. package/dist/Elements/Code.svelte +2 -4
  5. package/dist/Elements/Code.svelte.d.ts +0 -2
  6. package/dist/Elements/Element.svelte +70 -25
  7. package/dist/Elements/FootnoteRef.svelte +19 -6
  8. package/dist/Elements/Image.svelte +1 -1
  9. package/dist/Elements/Link.svelte +1 -1
  10. package/dist/Elements/Math.svelte +13 -25
  11. package/dist/Elements/Math.svelte.d.ts +0 -2
  12. package/dist/Elements/Mermaid.svelte +105 -115
  13. package/dist/Elements/Mermaid.svelte.d.ts +0 -2
  14. package/dist/Elements/Table.svelte +1 -1
  15. package/dist/Streamdown.d.ts +52 -10
  16. package/dist/Streamdown.js +19 -1
  17. package/dist/Streamdown.svelte +3 -41
  18. package/dist/Streamdown.svelte.d.ts +1 -20
  19. package/dist/marked/index.d.ts +3 -20
  20. package/dist/marked/index.js +27 -19
  21. package/dist/marked/marked-alert.d.ts +2 -2
  22. package/dist/marked/marked-alert.js +6 -19
  23. package/dist/marked/marked-footnotes.js +1 -2
  24. package/dist/marked/marked-list.d.ts +2 -2
  25. package/dist/marked/marked-list.js +28 -11
  26. package/dist/marked/marked-math.js +1 -1
  27. package/dist/marked/marked-subsup.d.ts +1 -1
  28. package/dist/marked/marked-subsup.js +2 -1
  29. package/dist/marked/marked-table.d.ts +59 -0
  30. package/dist/marked/marked-table.js +495 -0
  31. package/dist/theme.d.ts +24 -6
  32. package/dist/theme.js +22 -10
  33. package/dist/utils/panzoom.svelte.d.ts +17 -18
  34. package/dist/utils/panzoom.svelte.js +0 -2
  35. package/dist/utils/useClickOutside.svelte.js +0 -1
  36. package/dist/utils/useKeyDown.svelte.js +4 -1
  37. package/package.json +1 -2
@@ -0,0 +1,495 @@
1
+ // Default configuration options for the extended tables extension
2
+ export const DEFAULT_OPTIONS = {
3
+ useTheadTbody: true,
4
+ useTfoot: false,
5
+ detectFooter: true,
6
+ maxColspan: null,
7
+ handleComplexSpans: true
8
+ };
9
+ // Creates an HTML table cell with appropriate attributes
10
+ export const getTableCell = (text, cell, type, align) => {
11
+ if (!cell.rowspan)
12
+ return '';
13
+ const tag = `<${type}` +
14
+ `${cell.colspan > 1 ? ` colspan=${cell.colspan}` : ''}` +
15
+ `${cell.rowspan > 1 ? ` rowspan=${cell.rowspan}` : ''}` +
16
+ `${align ? ` align=${align}` : ''}>`;
17
+ return `${tag + text}</${type}>\n`;
18
+ };
19
+ function splitRow(src) {
20
+ const out = [];
21
+ let buf = '';
22
+ let esc = false;
23
+ let inCode = false;
24
+ let fence = 0;
25
+ for (let i = 0; i < src.length; i++) {
26
+ const ch = src[i];
27
+ if (esc) {
28
+ buf += ch;
29
+ esc = false;
30
+ continue;
31
+ }
32
+ if (ch === '\\') {
33
+ esc = true;
34
+ buf += ch;
35
+ continue;
36
+ }
37
+ if (ch === '`') {
38
+ // count backticks
39
+ let run = 1;
40
+ while (i + run < src.length && src[i + run] === '`')
41
+ run++;
42
+ if (!inCode) {
43
+ inCode = true;
44
+ fence = run;
45
+ }
46
+ else if (run >= fence) {
47
+ inCode = false;
48
+ fence = 0;
49
+ }
50
+ buf += src.slice(i, i + run);
51
+ i += run - 1;
52
+ continue;
53
+ }
54
+ if (ch === '|' && !inCode) {
55
+ out.push(buf.trim());
56
+ buf = '';
57
+ continue;
58
+ }
59
+ buf += ch;
60
+ }
61
+ out.push(buf.trim());
62
+ return out;
63
+ }
64
+ // Splits a table row into cells and processes row/column spans
65
+ export const splitCells = (tableRow, count, prevRow = null, maxColspan = null) => {
66
+ // Split by pipe, but handle escaped pipes and empty cells
67
+ const cells = splitRow(tableRow);
68
+ // Remove first/last cell if it's empty (from leading/trailing pipes)
69
+ if (cells.length > 0 && !cells[0])
70
+ cells.shift();
71
+ if (cells.length > 0 && !cells[cells.length - 1])
72
+ cells.pop();
73
+ return processSpans(cells, count, prevRow || [], maxColspan);
74
+ };
75
+ // Process row and column spans in table cells
76
+ const processSpans = (cells, count, prevRow = [], maxColspan = null) => {
77
+ let numCols = 0;
78
+ let i, j, trimmedCell, prevCell;
79
+ const processedCells = [];
80
+ // Track colspan cells that need rowspan
81
+ const colspanCells = new Map();
82
+ // First pass: Process each cell's colspan and merge consecutive empty cells
83
+ let cellIndex = 0;
84
+ const mergedIndices = new Set();
85
+ for (i = 0; i < cells.length; i++) {
86
+ // Skip cells that were merged into previous colspans
87
+ if (mergedIndices.has(i))
88
+ continue;
89
+ trimmedCell = cells[i];
90
+ // Count consecutive empty cells for colspan
91
+ let colspan = 1;
92
+ if (!trimmedCell.trim()) {
93
+ // Count how many consecutive empty cells we have
94
+ let j = i + 1;
95
+ while (j < cells.length && !cells[j].trim()) {
96
+ colspan++;
97
+ mergedIndices.add(j); // Mark as merged
98
+ j++;
99
+ }
100
+ }
101
+ // Apply maxColspan limit if specified
102
+ if (maxColspan !== null && colspan > maxColspan)
103
+ colspan = maxColspan;
104
+ processedCells[cellIndex] = {
105
+ rowspan: 1,
106
+ colspan: colspan,
107
+ text: trimmedCell.trim().replace(/\\\|/g, '|'),
108
+ position: numCols // Store original column position for better tracking
109
+ };
110
+ numCols += processedCells[cellIndex].colspan;
111
+ cellIndex++;
112
+ }
113
+ // Second pass: Process rowspan by matching cells by position
114
+ for (i = 0; i < processedCells.length; i++) {
115
+ const cell = processedCells[i];
116
+ let cellText = cell.text;
117
+ // Handle Rowspan - cells ending with ^
118
+ if (cellText.slice(-1) === '^' && prevRow.length > 0) {
119
+ // Clean the ^ indicator from the cell text
120
+ cell.text = cellText.slice(0, -1).trim();
121
+ cellText = cell.text;
122
+ let targetFound = false;
123
+ const startPosition = cell.position || 0;
124
+ const endPosition = startPosition + cell.colspan - 1;
125
+ // Try to find a matching cell or combination of cells in previous row
126
+ for (j = 0; j < prevRow.length; j++) {
127
+ prevCell = prevRow[j];
128
+ const prevStartPosition = prevCell.position || 0;
129
+ const prevEndPosition = prevStartPosition + prevCell.colspan - 1;
130
+ // Check for position overlap between cells
131
+ if ((startPosition >= prevStartPosition && startPosition <= prevEndPosition) ||
132
+ (endPosition >= prevStartPosition && endPosition <= prevEndPosition) ||
133
+ (prevStartPosition >= startPosition && prevEndPosition <= endPosition)) {
134
+ // Complex case: Handle rowspan for colspan cells
135
+ if (cell.colspan > 1 && prevCell.colspan > 1) {
136
+ // If the cell spans exactly match, simple case
137
+ if (cell.colspan === prevCell.colspan && cell.position === prevCell.position) {
138
+ cell.rowSpanTarget = prevCell.rowSpanTarget ?? prevCell;
139
+ // Only append text if it's different from the target cell
140
+ const textToAppend = cell.text.slice(0, -1).trim();
141
+ const targetText = cell.rowSpanTarget.text.trim();
142
+ // Don't append if the text is the same or already contained (common case for rowspan indicators)
143
+ if (textToAppend &&
144
+ textToAppend !== targetText &&
145
+ !targetText.includes(textToAppend)) {
146
+ cell.rowSpanTarget.text = targetText + (targetText ? ' ' : '') + textToAppend;
147
+ }
148
+ cell.rowSpanTarget.rowspan += 1;
149
+ cell.rowspan = 0;
150
+ targetFound = true;
151
+ break;
152
+ }
153
+ else {
154
+ // More complex case: Track colspan cells that need rowspan for next row
155
+ const key = `${cell.position}-${cell.colspan}`;
156
+ colspanCells.set(key, {
157
+ original: prevCell,
158
+ newCell: cell
159
+ });
160
+ // Keep the cell visible for now, will be merged in rendering
161
+ }
162
+ }
163
+ else {
164
+ // Standard case of single column cell with rowspan
165
+ cell.rowSpanTarget = prevCell.rowSpanTarget ?? prevCell;
166
+ // Only append text if it's different from the target cell
167
+ const textToAppend = cell.text.slice(0, -1).trim();
168
+ const targetText = cell.rowSpanTarget.text.trim();
169
+ // Don't append if the text is the same or already contained (common case for rowspan indicators)
170
+ if (textToAppend && textToAppend !== targetText && !targetText.includes(textToAppend)) {
171
+ cell.rowSpanTarget.text = targetText + (targetText ? ' ' : '') + textToAppend;
172
+ }
173
+ cell.rowSpanTarget.rowspan += 1;
174
+ cell.rowspan = 0;
175
+ targetFound = true;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ // If no target was found but it's a rowspan cell, clean the ^ indicator
181
+ if (!targetFound && cell.rowspan > 0) {
182
+ cell.text = cell.text.slice(0, -1);
183
+ }
184
+ }
185
+ }
186
+ // Process any complex colspan+rowspan combinations we tracked
187
+ colspanCells.forEach((spanData) => {
188
+ const { original, newCell } = spanData;
189
+ if (original && newCell) {
190
+ // Here we could apply more sophisticated merging logic
191
+ // For now, just mark that these cells have a relationship
192
+ newCell.complexRowSpan = true;
193
+ newCell.relatedCell = original;
194
+ }
195
+ });
196
+ // Normalize column count
197
+ return normalizeColumnCount(processedCells, count, numCols);
198
+ };
199
+ // Ensures the row has the correct number of columns
200
+ const normalizeColumnCount = (cells, count, numCols) => {
201
+ // If count is null, don't normalize
202
+ if (count === null)
203
+ return cells;
204
+ if (numCols > count) {
205
+ // We need to keep track of total column count
206
+ let currentColCount = 0;
207
+ const cellsToKeep = [];
208
+ for (const cell of cells) {
209
+ if (currentColCount + cell.colspan <= count) {
210
+ // This cell fits completely
211
+ cellsToKeep.push(cell);
212
+ currentColCount += cell.colspan;
213
+ }
214
+ else if (currentColCount < count) {
215
+ // This cell partially fits - adjust its colspan
216
+ const adjustedCell = { ...cell };
217
+ adjustedCell.colspan = count - currentColCount;
218
+ cellsToKeep.push(adjustedCell);
219
+ currentColCount = count;
220
+ }
221
+ else {
222
+ // This cell doesn't fit at all
223
+ break;
224
+ }
225
+ }
226
+ return cellsToKeep;
227
+ }
228
+ else {
229
+ while (numCols < count) {
230
+ cells.push({
231
+ colspan: 1,
232
+ rowspan: 1,
233
+ text: '',
234
+ position: numCols
235
+ });
236
+ numCols += 1;
237
+ }
238
+ }
239
+ return cells;
240
+ };
241
+ // Process alignment indicators in table headers
242
+ function processAlignment(alignRow) {
243
+ const alignment = [];
244
+ for (let i = 0; i < alignRow.length; i++) {
245
+ if (/^ *-+: *$/.test(alignRow[i])) {
246
+ alignment[i] = 'right';
247
+ }
248
+ else if (/^ *:-+: *$/.test(alignRow[i])) {
249
+ alignment[i] = 'center';
250
+ }
251
+ else if (/^ *:-+ *$/.test(alignRow[i])) {
252
+ alignment[i] = 'left';
253
+ }
254
+ else {
255
+ alignment[i] = null;
256
+ }
257
+ }
258
+ return alignment;
259
+ }
260
+ // Convert working cell to TH
261
+ function workingCellToTH(cell, align) {
262
+ return {
263
+ type: 'th',
264
+ rowspan: cell.rowspan,
265
+ colspan: cell.colspan,
266
+ text: cell.text,
267
+ position: cell.position,
268
+ tokens: cell.tokens,
269
+ rowSpanTarget: cell.rowSpanTarget,
270
+ complexRowSpan: cell.complexRowSpan,
271
+ relatedCell: cell.relatedCell,
272
+ align
273
+ };
274
+ }
275
+ // Convert working cell to TD
276
+ function workingCellToTD(cell, align) {
277
+ return {
278
+ type: 'td',
279
+ rowspan: cell.rowspan,
280
+ colspan: cell.colspan,
281
+ text: cell.text,
282
+ position: cell.position,
283
+ tokens: cell.tokens,
284
+ rowSpanTarget: cell.rowSpanTarget,
285
+ complexRowSpan: cell.complexRowSpan,
286
+ relatedCell: cell.relatedCell,
287
+ align
288
+ };
289
+ }
290
+ // Process table rows and add inline tokens to cells
291
+ function processRows(headerRows, bodyRows, alignment, colCount, lexer, maxColspan, detectFooter) {
292
+ const tokens = [];
293
+ // Process header rows
294
+ const processedHeaderRows = [];
295
+ for (let i = 0; i < headerRows.length; i++) {
296
+ const prevRow = i > 0 ? processedHeaderRows[i - 1] : null;
297
+ processedHeaderRows[i] = splitCells(headerRows[i], colCount, prevRow, maxColspan);
298
+ }
299
+ // Convert header rows to THead (only if we have header rows)
300
+ if (processedHeaderRows.length > 0) {
301
+ const theadRows = processedHeaderRows.map((row) => ({
302
+ type: 'tr',
303
+ tokens: row.map((cell) => {
304
+ // Use the cell's position to get the correct alignment
305
+ const cellAlignment = cell.position !== undefined ? alignment[cell.position] : null;
306
+ const th = workingCellToTH(cell, cellAlignment);
307
+ // Add inline tokens
308
+ th.tokens = lexer.inline(th.text, th.tokens);
309
+ return th;
310
+ })
311
+ }));
312
+ tokens.push({
313
+ type: 'thead',
314
+ tokens: theadRows
315
+ });
316
+ }
317
+ // Process body rows
318
+ if (bodyRows.length > 0) {
319
+ const processedBodyRows = [];
320
+ for (let i = 0; i < bodyRows.length; i++) {
321
+ const prevRow = i > 0 ? processedBodyRows[i - 1] : processedHeaderRows[processedHeaderRows.length - 1];
322
+ processedBodyRows[i] = splitCells(bodyRows[i], colCount, prevRow, maxColspan);
323
+ }
324
+ // Handle footer detection
325
+ let tbodyRows = processedBodyRows;
326
+ let tfootRows = [];
327
+ if (detectFooter && processedBodyRows.length > 0) {
328
+ const lastRowIndex = processedBodyRows.length - 1;
329
+ tfootRows = [processedBodyRows[lastRowIndex]];
330
+ tbodyRows = processedBodyRows.slice(0, lastRowIndex);
331
+ }
332
+ // Convert body rows to TBody if there are any
333
+ if (tbodyRows.length > 0) {
334
+ const tbodyRowTokens = tbodyRows.map((row) => ({
335
+ type: 'tr',
336
+ tokens: row.map((cell) => {
337
+ // Use the cell's position to get the correct alignment
338
+ const cellAlignment = cell.position !== undefined ? alignment[cell.position] : null;
339
+ const td = workingCellToTD(cell, cellAlignment);
340
+ // Add inline tokens
341
+ td.tokens = lexer.inline(td.text, td.tokens);
342
+ return td;
343
+ })
344
+ }));
345
+ tokens.push({
346
+ type: 'tbody',
347
+ tokens: tbodyRowTokens
348
+ });
349
+ }
350
+ // Convert footer rows to TFoot if there are any
351
+ if (tfootRows.length > 0) {
352
+ const tfootRowTokens = tfootRows.map((row) => ({
353
+ type: 'tr',
354
+ tokens: row.map((cell) => {
355
+ // Use the cell's position to get the correct alignment
356
+ const cellAlignment = cell.position !== undefined ? alignment[cell.position] : null;
357
+ const td = workingCellToTD(cell, cellAlignment);
358
+ // Add inline tokens
359
+ td.tokens = lexer.inline(td.text, td.tokens);
360
+ return td;
361
+ })
362
+ }));
363
+ tokens.push({
364
+ type: 'tfoot',
365
+ tokens: tfootRowTokens
366
+ });
367
+ }
368
+ }
369
+ return tokens;
370
+ }
371
+ // Adds support for extended tables in marked with row spanning, column spanning,
372
+ // multi-row headers, and column alignment
373
+ export function markedTable(options = {}) {
374
+ const config = { ...DEFAULT_OPTIONS, ...options };
375
+ const { detectFooter, maxColspan } = config;
376
+ return {
377
+ extensions: [
378
+ {
379
+ name: 'table',
380
+ level: 'block',
381
+ start(src) {
382
+ // Check for table with potential header alignment
383
+ let match = src.match(/^\n *([^\n ].*\|.*)\n/);
384
+ if (match)
385
+ return match.index;
386
+ // Check for simple table without header alignment
387
+ match = src.match(/^\n *(\|.*\|)\n/);
388
+ if (match)
389
+ return match.index;
390
+ return undefined;
391
+ },
392
+ tokenizer(src) {
393
+ // Try to match table with header and alignment first
394
+ let regex = new RegExp('^' +
395
+ '([^\\n ].*\\|.*\\n(?: *[^\\s].*\\n)*?)' + // Header
396
+ ' {0,3}(?:\\| *)?(:?-+:? *(?:\\| *:?-+:? *)*)(?:\\| *)?' + // Header Align
397
+ '(?:\\n((?:(?! *\\n| {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})' + // Body Cells
398
+ '(?:\\n+|$)| {0,3}#{1,6} | {0,3}>| {4}[^\\n]| {0,3}(?:`{3,}' +
399
+ '(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n| {0,3}(?:[*+-]|1[.)]) |' +
400
+ '<\\/?(?:address|article|aside|base|basefont|blockquote|body|' +
401
+ '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*|$)');
402
+ let cap = regex.exec(src);
403
+ let hasHeaderAlignment = true;
404
+ // If no match with header alignment, try table without header alignment
405
+ if (!cap) {
406
+ // Simple regex for tables without header alignment
407
+ regex = /^(\|.*\|(?:\n\|.*\|)*)/;
408
+ cap = regex.exec(src);
409
+ hasHeaderAlignment = false;
410
+ }
411
+ if (!cap)
412
+ return null;
413
+ // Combine all captured groups to get complete table rows
414
+ let allTableContent = cap[1]; // Headers
415
+ if (cap[2])
416
+ allTableContent += '\n' + cap[2]; // Alignment row
417
+ if (cap[3])
418
+ allTableContent += '\n' + cap[3]; // Body rows
419
+ const allRows = allTableContent.replace(/\n$/, '').split('\n');
420
+ let headerRows = [];
421
+ let bodyRows = [];
422
+ let alignRow = [];
423
+ let alignment = [];
424
+ let colCount = 0;
425
+ if (hasHeaderAlignment) {
426
+ // Traditional table with header and alignment
427
+ // Parse all rows and identify which are headers vs body
428
+ let headerEndIndex = -1;
429
+ // Find the FIRST alignment row (contains dashes/underscores/asterisks)
430
+ for (let i = 0; i < allRows.length; i++) {
431
+ const row = allRows[i].trim();
432
+ const isAlignment = /^ *(\| *)?:?-+:? *(\| *:?-+:? *)*(\| *)?$/.test(row);
433
+ // Check if this row matches alignment pattern (contains only |, spaces, and alignment chars)
434
+ if (isAlignment) {
435
+ headerEndIndex = i;
436
+ alignRow = row.replace(/^ *\| *| *\| *$/g, '').split(/ *\| */);
437
+ break; // Stop at the first alignment row
438
+ }
439
+ }
440
+ if (headerEndIndex === -1) {
441
+ // No alignment row found, treat as simple table
442
+ bodyRows = allRows;
443
+ colCount = bodyRows[0].split('|').filter((cell) => cell.trim() !== '').length;
444
+ alignment = new Array(colCount).fill(null);
445
+ }
446
+ else {
447
+ // Found alignment row, split headers and body
448
+ // Filter out empty rows and the alignment row itself from headers
449
+ headerRows = allRows.slice(0, headerEndIndex).filter((row) => row.trim() !== '');
450
+ bodyRows =
451
+ 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)
456
+ return null;
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;
480
+ }
481
+ }
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
+ };
490
+ return item;
491
+ }
492
+ }
493
+ ]
494
+ };
495
+ }
package/dist/theme.d.ts CHANGED
@@ -70,10 +70,16 @@ export declare const theme: {
70
70
  base: string;
71
71
  table: string;
72
72
  };
73
- tableRow: {
73
+ thead: {
74
74
  base: string;
75
75
  };
76
- tableHead: {
76
+ tbody: {
77
+ base: string;
78
+ };
79
+ tfoot: {
80
+ base: string;
81
+ };
82
+ tr: {
77
83
  base: string;
78
84
  };
79
85
  td: {
@@ -191,10 +197,16 @@ export declare const shadcnTheme: {
191
197
  base: string;
192
198
  table: string;
193
199
  };
194
- tableRow: {
200
+ thead: {
201
+ base: string;
202
+ };
203
+ tbody: {
195
204
  base: string;
196
205
  };
197
- tableHead: {
206
+ tfoot: {
207
+ base: string;
208
+ };
209
+ tr: {
198
210
  base: string;
199
211
  };
200
212
  td: {
@@ -313,10 +325,16 @@ export declare const mergeTheme: (customTheme?: Partial<Theme>, baseTheme?: "tai
313
325
  base: string;
314
326
  table: string;
315
327
  };
316
- tableRow: {
328
+ thead: {
329
+ base: string;
330
+ };
331
+ tbody: {
332
+ base: string;
333
+ };
334
+ tfoot: {
317
335
  base: string;
318
336
  };
319
- tableHead: {
337
+ tr: {
320
338
  base: string;
321
339
  };
322
340
  td: {
package/dist/theme.js CHANGED
@@ -71,17 +71,23 @@ export const theme = {
71
71
  base: 'overflow-x-auto max-w-full my-4 border border-gray-200 rounded-lg',
72
72
  table: 'w-full border-collapse min-w-full'
73
73
  },
74
- tableRow: {
75
- base: 'border-gray-200 border-b hover:bg-gray-100/50 transition-colors'
76
- },
77
- tableHead: {
74
+ thead: {
78
75
  base: 'bg-gray-200/80'
79
76
  },
77
+ tbody: {
78
+ base: ''
79
+ },
80
+ tfoot: {
81
+ base: 'bg-gray-100/50 border-t border-gray-300'
82
+ },
83
+ tr: {
84
+ base: 'border-gray-200 border-b hover:bg-gray-100/50 transition-colors'
85
+ },
80
86
  td: {
81
87
  base: 'px-4 py-3 text-sm min-w-[200px] max-w-[400px] break-words'
82
88
  },
83
89
  th: {
84
- base: 'text-left px-4 py-3 text-sm text-foreground min-w-[200px] max-w-[400px] break-words'
90
+ base: 'px-4 py-3 text-sm text-foreground min-w-[200px] max-w-[400px] break-words'
85
91
  },
86
92
  sup: {
87
93
  base: 'text-sm'
@@ -192,17 +198,23 @@ export const shadcnTheme = {
192
198
  base: 'overflow-x-auto max-w-full my-4 rounded-lg border border-border',
193
199
  table: 'w-full border-collapse min-w-full'
194
200
  },
195
- tableRow: {
196
- base: 'border-border border-b hover:bg-muted/50 transition-colors'
197
- },
198
- tableHead: {
201
+ thead: {
199
202
  base: 'bg-muted/80'
200
203
  },
204
+ tbody: {
205
+ base: ''
206
+ },
207
+ tfoot: {
208
+ base: 'bg-muted/50 border-t border-border'
209
+ },
210
+ tr: {
211
+ base: 'border-border border-b hover:bg-muted/50 transition-colors'
212
+ },
201
213
  td: {
202
214
  base: 'px-4 py-3 text-sm text-foreground min-w-[200px] max-w-[400px] break-words'
203
215
  },
204
216
  th: {
205
- base: 'text-left px-4 py-3 text-sm text-foreground min-w-[200px] max-w-[400px] break-words'
217
+ base: 'px-4 py-3 text-sm text-foreground min-w-[200px] max-w-[400px] break-words'
206
218
  },
207
219
  sup: {
208
220
  base: 'text-sm'
@@ -8,29 +8,28 @@ export interface PanzoomOptions {
8
8
  initialY?: number;
9
9
  activateMouseWheel?: boolean;
10
10
  }
11
- export interface PanzoomInstance {
12
- attach: (node: HTMLElement | SVGSVGElement) => () => void;
11
+ export interface ExpandOptions {
12
+ padding?: number;
13
+ duration?: number;
14
+ easing?: string;
15
+ zIndex?: number;
16
+ fitRatio?: number;
17
+ }
18
+ export declare const usePanzoom: (opts?: PanzoomOptions) => {
19
+ attach: (target: HTMLElement | SVGSVGElement) => () => void;
13
20
  zoomToFit: (padding?: number) => void;
14
21
  zoomBy: (factor: number) => void;
15
22
  zoomIn: (factor?: number) => void;
16
23
  zoomOut: (factor?: number) => void;
17
24
  moveBy: (dx: number, dy: number) => void;
18
- setTransform: (x: number, y: number, scale: number) => void;
25
+ setTransform: (nx: number, ny: number, ns: number) => void;
19
26
  expand: () => void;
20
27
  collapse: () => void;
21
- toggleExpand: (options?: ExpandOptions | number) => Promise<void>;
28
+ toggleExpand: () => Promise<void>;
29
+ readonly transform: {
30
+ readonly x: number;
31
+ readonly y: number;
32
+ readonly scale: number;
33
+ };
22
34
  readonly expanded: boolean;
23
- readonly transform: Readonly<{
24
- x: number;
25
- y: number;
26
- scale: number;
27
- }>;
28
- }
29
- export interface ExpandOptions {
30
- padding?: number;
31
- duration?: number;
32
- easing?: string;
33
- zIndex?: number;
34
- fitRatio?: number;
35
- }
36
- export declare const usePanzoom: (opts?: PanzoomOptions) => PanzoomInstance;
35
+ };
@@ -429,7 +429,6 @@ export const usePanzoom = (opts = {}) => {
429
429
  apply();
430
430
  }
431
431
  function zoomBy(factor) {
432
- console.log('zoomBy', factor);
433
432
  if (!node)
434
433
  return;
435
434
  // Use zoomAt with the center of the container for consistent behavior
@@ -448,7 +447,6 @@ export const usePanzoom = (opts = {}) => {
448
447
  zoomBy(factor);
449
448
  }
450
449
  function zoomOut(factor = 1.25) {
451
- console.log('zoomOut', factor);
452
450
  // zoom out by inverse multiplier
453
451
  if (factor <= 0)
454
452
  return;
@@ -32,7 +32,6 @@ export const useClickOutside = (props) => {
32
32
  if (listener && refs.size === 0) {
33
33
  listener();
34
34
  listener = null;
35
- console.log('clean up', listener);
36
35
  }
37
36
  };
38
37
  }