@flyingrobots/bijou 5.0.0 → 7.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 (111) hide show
  1. package/README.md +62 -0
  2. package/dist/core/active-binding-collection.d.ts +49 -0
  3. package/dist/core/active-binding-collection.d.ts.map +1 -0
  4. package/dist/core/active-binding-collection.js +264 -0
  5. package/dist/core/active-binding-collection.js.map +1 -0
  6. package/dist/core/app-shell-composition.d.ts +44 -0
  7. package/dist/core/app-shell-composition.d.ts.map +1 -0
  8. package/dist/core/app-shell-composition.js +123 -0
  9. package/dist/core/app-shell-composition.js.map +1 -0
  10. package/dist/core/binding-frame-update.d.ts +18 -0
  11. package/dist/core/binding-frame-update.d.ts.map +1 -0
  12. package/dist/core/binding-frame-update.js +133 -0
  13. package/dist/core/binding-frame-update.js.map +1 -0
  14. package/dist/core/binding-lifecycle.d.ts +65 -0
  15. package/dist/core/binding-lifecycle.d.ts.map +1 -0
  16. package/dist/core/binding-lifecycle.js +357 -0
  17. package/dist/core/binding-lifecycle.js.map +1 -0
  18. package/dist/core/binding.d.ts +192 -0
  19. package/dist/core/binding.d.ts.map +1 -0
  20. package/dist/core/binding.js +587 -0
  21. package/dist/core/binding.js.map +1 -0
  22. package/dist/core/block-metadata.d.ts +113 -0
  23. package/dist/core/block-metadata.d.ts.map +1 -0
  24. package/dist/core/block-metadata.js +376 -0
  25. package/dist/core/block-metadata.js.map +1 -0
  26. package/dist/core/block-tree-render.d.ts +17 -0
  27. package/dist/core/block-tree-render.d.ts.map +1 -0
  28. package/dist/core/block-tree-render.js +186 -0
  29. package/dist/core/block-tree-render.js.map +1 -0
  30. package/dist/core/component-metadata.d.ts +63 -0
  31. package/dist/core/component-metadata.d.ts.map +1 -0
  32. package/dist/core/component-metadata.js +158 -0
  33. package/dist/core/component-metadata.js.map +1 -0
  34. package/dist/core/components/dag-edges.d.ts +9 -3
  35. package/dist/core/components/dag-edges.d.ts.map +1 -1
  36. package/dist/core/components/dag-edges.js +41 -10
  37. package/dist/core/components/dag-edges.js.map +1 -1
  38. package/dist/core/components/dag-render.d.ts +1 -1
  39. package/dist/core/components/dag-render.d.ts.map +1 -1
  40. package/dist/core/components/dag-render.js +193 -72
  41. package/dist/core/components/dag-render.js.map +1 -1
  42. package/dist/core/components/dag-source.d.ts +4 -0
  43. package/dist/core/components/dag-source.d.ts.map +1 -1
  44. package/dist/core/components/dag-source.js +10 -0
  45. package/dist/core/components/dag-source.js.map +1 -1
  46. package/dist/core/components/dag.d.ts +13 -0
  47. package/dist/core/components/dag.d.ts.map +1 -1
  48. package/dist/core/components/dag.js.map +1 -1
  49. package/dist/core/components/index.d.ts +2 -2
  50. package/dist/core/components/index.d.ts.map +1 -1
  51. package/dist/core/components/index.js.map +1 -1
  52. package/dist/core/components/table-v3.d.ts +1 -1
  53. package/dist/core/components/table-v3.d.ts.map +1 -1
  54. package/dist/core/components/table-v3.js +136 -21
  55. package/dist/core/components/table-v3.js.map +1 -1
  56. package/dist/core/components/table.d.ts +36 -6
  57. package/dist/core/components/table.d.ts.map +1 -1
  58. package/dist/core/components/table.js +530 -74
  59. package/dist/core/components/table.js.map +1 -1
  60. package/dist/core/fixture-promotion.d.ts +33 -0
  61. package/dist/core/fixture-promotion.d.ts.map +1 -0
  62. package/dist/core/fixture-promotion.js +62 -0
  63. package/dist/core/fixture-promotion.js.map +1 -0
  64. package/dist/core/layout/envelope.d.ts +174 -0
  65. package/dist/core/layout/envelope.d.ts.map +1 -0
  66. package/dist/core/layout/envelope.js +336 -0
  67. package/dist/core/layout/envelope.js.map +1 -0
  68. package/dist/core/mode-lowering.d.ts +48 -0
  69. package/dist/core/mode-lowering.d.ts.map +1 -0
  70. package/dist/core/mode-lowering.js +176 -0
  71. package/dist/core/mode-lowering.js.map +1 -0
  72. package/dist/core/schema-block.d.ts +63 -0
  73. package/dist/core/schema-block.d.ts.map +1 -0
  74. package/dist/core/schema-block.js +361 -0
  75. package/dist/core/schema-block.js.map +1 -0
  76. package/dist/core/selection.d.ts +127 -0
  77. package/dist/core/selection.d.ts.map +1 -0
  78. package/dist/core/selection.js +350 -0
  79. package/dist/core/selection.js.map +1 -0
  80. package/dist/core/standard-blocks.d.ts +264 -0
  81. package/dist/core/standard-blocks.d.ts.map +1 -0
  82. package/dist/core/standard-blocks.js +2518 -0
  83. package/dist/core/standard-blocks.js.map +1 -0
  84. package/dist/core/story-capture.d.ts +48 -0
  85. package/dist/core/story-capture.d.ts.map +1 -0
  86. package/dist/core/story-capture.js +64 -0
  87. package/dist/core/story-capture.js.map +1 -0
  88. package/dist/core/text/wrap.d.ts.map +1 -1
  89. package/dist/core/text/wrap.js +37 -20
  90. package/dist/core/text/wrap.js.map +1 -1
  91. package/dist/core/theme/doctor.d.ts +37 -0
  92. package/dist/core/theme/doctor.d.ts.map +1 -0
  93. package/dist/core/theme/doctor.js +175 -0
  94. package/dist/core/theme/doctor.js.map +1 -0
  95. package/dist/core/theme/dtcg.d.ts.map +1 -1
  96. package/dist/core/theme/dtcg.js +10 -1
  97. package/dist/core/theme/dtcg.js.map +1 -1
  98. package/dist/core/theme/index.d.ts +2 -0
  99. package/dist/core/theme/index.d.ts.map +1 -1
  100. package/dist/core/theme/index.js +2 -0
  101. package/dist/core/theme/index.js.map +1 -1
  102. package/dist/core/theme/presets.d.ts.map +1 -1
  103. package/dist/core/theme/presets.js +4 -0
  104. package/dist/core/theme/presets.js.map +1 -1
  105. package/dist/core/theme/tokens.d.ts +1 -1
  106. package/dist/core/theme/tokens.d.ts.map +1 -1
  107. package/dist/index.d.ts +17 -2
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +17 -0
  110. package/dist/index.js.map +1 -1
  111. package/package.json +2 -2
@@ -1,8 +1,10 @@
1
1
  import { RESET_SGR } from '../ansi.js';
2
- import { ANSI_OSC8_RE, ANSI_SGR_RE, graphemeClusterWidth, segmentGraphemes, stripAnsi } from '../text/grapheme.js';
2
+ import { ANSI_OSC8_RE, ANSI_SGR_RE, graphemeClusterWidth, segmentGraphemes, stripAnsi, } from '../text/grapheme.js';
3
+ import { wrapToWidth } from '../text/wrap.js';
3
4
  import { resolveCtx } from '../resolve-ctx.js';
4
5
  import { renderByMode } from '../mode-render.js';
5
6
  import { makeBgFill } from '../bg-fill.js';
7
+ import { sanitizeNonNegativeInt, sanitizeOptionalNonNegativeInt, sanitizeOptionalPositiveInt, sanitizePositiveInt, } from '../numeric.js';
6
8
  import { resolveOverflowBehavior } from './overflow.js';
7
9
  /**
8
10
  * Structural width added by the interactive table frame for a given column count.
@@ -24,10 +26,10 @@ function isTableOptions(value) {
24
26
  return !Array.isArray(value);
25
27
  }
26
28
  /**
27
- * Calculate the visible (non-ANSI) character length of a string.
29
+ * Calculate the visible (non-ANSI) display width of a string.
28
30
  *
29
31
  * @param str - Input string potentially containing ANSI codes.
30
- * @returns The number of visible characters.
32
+ * @returns The number of visible terminal columns.
31
33
  */
32
34
  function visibleLength(str) {
33
35
  let width = 0;
@@ -47,21 +49,181 @@ function padRight(str, width) {
47
49
  const visible = visibleLength(str);
48
50
  return visible >= width ? str : str + ' '.repeat(width - visible);
49
51
  }
52
+ function padLeft(str, width) {
53
+ const visible = visibleLength(str);
54
+ return visible >= width ? str : ' '.repeat(width - visible) + str;
55
+ }
56
+ function padCenter(str, width) {
57
+ const visible = visibleLength(str);
58
+ if (visible >= width)
59
+ return str;
60
+ const remaining = width - visible;
61
+ const left = Math.floor(remaining / 2);
62
+ return ' '.repeat(left) + str + ' '.repeat(remaining - left);
63
+ }
64
+ function alignCell(str, width, align) {
65
+ if (align === 'right')
66
+ return padLeft(str, width);
67
+ if (align === 'center')
68
+ return padCenter(str, width);
69
+ return padRight(str, width);
70
+ }
50
71
  function normalizeColumnWidth(width) {
51
- if (width == null)
52
- return undefined;
53
- if (!Number.isFinite(width))
54
- return 0;
55
- return Math.max(0, Math.floor(width));
72
+ return sanitizeOptionalNonNegativeInt(width);
73
+ }
74
+ function normalizePositiveWeight(weight) {
75
+ if (weight == null || !Number.isFinite(weight))
76
+ return 1;
77
+ return Math.max(0, Math.floor(weight));
78
+ }
79
+ function maxVisibleLineWidth(value) {
80
+ const lines = String(value ?? '').split('\n');
81
+ return lines.reduce((max, line) => Math.max(max, visibleLength(line)), 0);
82
+ }
83
+ function normalizeRows(rows) {
84
+ return (rows ?? []).map(row => (row ?? []).map(cell => String(cell ?? '')));
85
+ }
86
+ function resolveColumns(suppliedColumns, rows, variant) {
87
+ const columns = suppliedColumns ?? [];
88
+ if (columns.length > 0)
89
+ return [...columns];
90
+ if (variant === 'definition') {
91
+ return [
92
+ { header: 'Field', minWidth: 5 },
93
+ { header: 'Value', minWidth: 5, weight: 3 },
94
+ ];
95
+ }
96
+ const inferredCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
97
+ return Array.from({ length: inferredCount }, () => ({ header: '' }));
56
98
  }
57
- function formatCellLines(value, width, overflow, fixedWidth) {
99
+ function normalizeTable(options) {
100
+ const rows = normalizeRows(options.rows);
101
+ const variant = options.variant ?? 'box';
102
+ const columns = resolveColumns(options.columns, rows, variant);
103
+ return {
104
+ columns,
105
+ rows,
106
+ showHeader: variant === 'definition' || columns.some(column => (column.header ?? '').length > 0),
107
+ };
108
+ }
109
+ function structuralOverhead(variant, columnCount, columnGap) {
110
+ if (columnCount <= 0)
111
+ return variant === 'box' || variant === 'ascii-grid' ? 2 : 0;
112
+ if (variant === 'box' || variant === 'ascii-grid') {
113
+ return interactiveTableBorderOverhead(columnCount);
114
+ }
115
+ if (variant === 'markdown') {
116
+ return columnCount * 3 + 1;
117
+ }
118
+ return Math.max(0, columnCount - 1) * columnGap;
119
+ }
120
+ function resolveTargetWidth(options, ctx, layout) {
121
+ const explicitWidth = sanitizeOptionalPositiveInt(options.width);
122
+ if (explicitWidth !== undefined)
123
+ return explicitWidth;
124
+ const explicitMaxWidth = sanitizeOptionalPositiveInt(options.maxWidth);
125
+ if (layout === 'intrinsic')
126
+ return explicitMaxWidth;
127
+ const runtimeWidth = sanitizePositiveInt(ctx.runtime.columns, 80);
128
+ return explicitMaxWidth === undefined ? runtimeWidth : Math.min(runtimeWidth, explicitMaxWidth);
129
+ }
130
+ function columnPreferredWidth(column, rows, columnIndex) {
131
+ const fixedWidth = normalizeColumnWidth(column.width);
132
+ if (fixedWidth !== undefined)
133
+ return fixedWidth;
134
+ let preferred = Math.max(1, maxVisibleLineWidth(column.header ?? ''));
135
+ for (const row of rows) {
136
+ preferred = Math.max(preferred, maxVisibleLineWidth(row[columnIndex] ?? ''));
137
+ }
138
+ const maxWidth = sanitizeOptionalNonNegativeInt(column.maxWidth);
139
+ if (maxWidth !== undefined)
140
+ preferred = Math.min(preferred, maxWidth);
141
+ const minWidth = sanitizeOptionalNonNegativeInt(column.minWidth);
142
+ if (minWidth !== undefined)
143
+ preferred = Math.max(preferred, Math.min(minWidth, maxWidth ?? minWidth));
144
+ return preferred;
145
+ }
146
+ function columnMinWidth(column, preferredWidth) {
147
+ const fixedWidth = normalizeColumnWidth(column.width);
148
+ if (fixedWidth !== undefined)
149
+ return fixedWidth;
150
+ const maxWidth = sanitizeOptionalNonNegativeInt(column.maxWidth);
151
+ const rawMinWidth = sanitizeOptionalNonNegativeInt(column.minWidth) ?? Math.min(1, preferredWidth);
152
+ const constrainedMin = Math.min(rawMinWidth, maxWidth ?? rawMinWidth);
153
+ return Math.min(preferredWidth, constrainedMin);
154
+ }
155
+ function fitColumnWidths(columns, rows, variant, layout, targetWidth, columnGap) {
156
+ const preferred = columns.map((column, index) => columnPreferredWidth(column, rows, index));
157
+ if (layout === 'intrinsic' && targetWidth === undefined)
158
+ return preferred;
159
+ const contentTarget = targetWidth === undefined
160
+ ? undefined
161
+ : Math.max(0, targetWidth - structuralOverhead(variant, columns.length, columnGap));
162
+ if (contentTarget === undefined)
163
+ return preferred;
164
+ const preferredTotal = preferred.reduce((sum, width) => sum + width, 0);
165
+ if (preferredTotal <= contentTarget)
166
+ return preferred;
167
+ const minimum = columns.map((column, index) => columnMinWidth(column, preferred[index]));
168
+ const minimumTotal = minimum.reduce((sum, width) => sum + width, 0);
169
+ if (minimumTotal >= contentTarget)
170
+ return minimum;
171
+ const widths = [...minimum];
172
+ const remainingCapacity = preferred.map((width, index) => Math.max(0, width - minimum[index]));
173
+ let remainingBudget = contentTarget - minimumTotal;
174
+ while (remainingBudget > 0) {
175
+ const active = remainingCapacity
176
+ .map((capacity, index) => ({ capacity, index }))
177
+ .filter(entry => entry.capacity > 0);
178
+ if (active.length === 0)
179
+ break;
180
+ const totalWeight = active.reduce((sum, entry) => {
181
+ return sum + Math.max(1, normalizePositiveWeight(columns[entry.index]?.weight));
182
+ }, 0);
183
+ let used = 0;
184
+ const remainders = [];
185
+ for (const entry of active) {
186
+ const weight = Math.max(1, normalizePositiveWeight(columns[entry.index]?.weight));
187
+ const exact = (remainingBudget * weight) / totalWeight;
188
+ const add = Math.min(entry.capacity, Math.floor(exact));
189
+ if (add > 0) {
190
+ widths[entry.index] += add;
191
+ remainingCapacity[entry.index] -= add;
192
+ used += add;
193
+ }
194
+ remainders.push({
195
+ index: entry.index,
196
+ remainder: exact - Math.floor(exact),
197
+ capacity: remainingCapacity[entry.index],
198
+ });
199
+ }
200
+ if (used === 0) {
201
+ const sortedRemainders = remainders
202
+ .filter(entry => entry.capacity > 0)
203
+ .sort((left, right) => {
204
+ if (right.remainder !== left.remainder)
205
+ return right.remainder - left.remainder;
206
+ return remainingCapacity[right.index] - remainingCapacity[left.index];
207
+ });
208
+ const next = sortedRemainders[0];
209
+ if (!next)
210
+ break;
211
+ widths[next.index]++;
212
+ remainingCapacity[next.index]--;
213
+ used = 1;
214
+ }
215
+ remainingBudget -= used;
216
+ }
217
+ return widths;
218
+ }
219
+ function formatCellLines(value, width, overflow, wrapMode) {
58
220
  const safeValue = value ?? '';
59
- if (!fixedWidth)
60
- return safeValue.split('\n');
61
221
  if (overflow === 'truncate') {
62
- return safeValue.split('\n').map((line) => clipCellToWidth(line, width));
222
+ return safeValue.split('\n').map(line => clipCellToWidth(line, width));
63
223
  }
64
- return safeValue.split('\n').flatMap((line) => wrapCellToWidth(line, width));
224
+ if (wrapMode === 'word')
225
+ return wrapToWidth(safeValue, width);
226
+ return safeValue.split('\n').flatMap(line => wrapCellToWidth(line, width));
65
227
  }
66
228
  const TABLE_EMOJI_PRESENTATION_RE = /\p{Emoji_Presentation}/u;
67
229
  function tableGraphemeWidth(grapheme) {
@@ -72,6 +234,11 @@ function tableGraphemeWidth(grapheme) {
72
234
  function isOsc8Escape(raw) {
73
235
  return raw.startsWith('\x1b]8;;');
74
236
  }
237
+ const OSC8_CLOSE = '\x1b]8;;\x1b\\';
238
+ const OSC8_CLOSE_BEL = '\x1b]8;;\x07';
239
+ function isOsc8Close(raw) {
240
+ return raw === OSC8_CLOSE || raw === OSC8_CLOSE_BEL;
241
+ }
75
242
  function isResetEscape(raw) {
76
243
  return raw === RESET_SGR;
77
244
  }
@@ -106,6 +273,19 @@ function tokenizeTableText(str) {
106
273
  }
107
274
  return tokens;
108
275
  }
276
+ function activeTableAnsiPrefix(activeOsc8, activeStyle) {
277
+ return activeOsc8 + activeStyle;
278
+ }
279
+ function finalizeTableWrappedLine(raw, activeOsc8, activeStyle) {
280
+ let result = raw;
281
+ if (activeOsc8.length > 0 && !result.endsWith(OSC8_CLOSE) && !result.endsWith(OSC8_CLOSE_BEL)) {
282
+ result += OSC8_CLOSE;
283
+ }
284
+ if (activeStyle.length > 0 && !result.endsWith(RESET_SGR)) {
285
+ result += RESET_SGR;
286
+ }
287
+ return result;
288
+ }
109
289
  function clipCellToWidth(str, maxWidth) {
110
290
  if (maxWidth <= 0)
111
291
  return '';
@@ -113,15 +293,21 @@ function clipCellToWidth(str, maxWidth) {
113
293
  let visible = 0;
114
294
  let result = '';
115
295
  let hasStyle = false;
296
+ let activeOsc8 = '';
116
297
  for (const token of tokens) {
117
298
  if (token.kind === 'ansi') {
118
299
  result += token.raw;
119
- if (!isOsc8Escape(token.raw)) {
120
- hasStyle = true;
300
+ if (isOsc8Escape(token.raw)) {
301
+ activeOsc8 = isOsc8Close(token.raw) ? '' : token.raw;
302
+ }
303
+ else {
304
+ hasStyle = !isResetEscape(token.raw);
121
305
  }
122
306
  continue;
123
307
  }
124
308
  if (visible + token.width > maxWidth) {
309
+ if (activeOsc8.length > 0)
310
+ result += OSC8_CLOSE;
125
311
  if (hasStyle && !result.endsWith(RESET_SGR))
126
312
  result += RESET_SGR;
127
313
  break;
@@ -129,7 +315,9 @@ function clipCellToWidth(str, maxWidth) {
129
315
  result += token.raw;
130
316
  visible += token.width;
131
317
  }
132
- return result;
318
+ return activeOsc8.length > 0 || hasStyle
319
+ ? finalizeTableWrappedLine(result, activeOsc8, hasStyle ? RESET_SGR : '')
320
+ : result;
133
321
  }
134
322
  function wrapCellToWidth(str, maxWidth) {
135
323
  if (maxWidth <= 0)
@@ -141,17 +329,20 @@ function wrapCellToWidth(str, maxWidth) {
141
329
  let current = '';
142
330
  let currentWidth = 0;
143
331
  let activeStyle = '';
332
+ let activeOsc8 = '';
144
333
  for (const token of tokens) {
145
334
  if (token.kind === 'ansi') {
146
335
  current += token.raw;
147
- if (isOsc8Escape(token.raw))
336
+ if (isOsc8Escape(token.raw)) {
337
+ activeOsc8 = isOsc8Close(token.raw) ? '' : token.raw;
148
338
  continue;
339
+ }
149
340
  activeStyle = isResetEscape(token.raw) ? '' : activeStyle + token.raw;
150
341
  continue;
151
342
  }
152
343
  if (currentWidth + token.width > maxWidth && currentWidth > 0) {
153
- lines.push(activeStyle.length === 0 || current.endsWith(RESET_SGR) ? current : current + RESET_SGR);
154
- current = activeStyle + token.raw;
344
+ lines.push(finalizeTableWrappedLine(current, activeOsc8, activeStyle));
345
+ current = activeTableAnsiPrefix(activeOsc8, activeStyle) + token.raw;
155
346
  currentWidth = token.width;
156
347
  continue;
157
348
  }
@@ -159,10 +350,320 @@ function wrapCellToWidth(str, maxWidth) {
159
350
  currentWidth += token.width;
160
351
  }
161
352
  if (current.length > 0) {
162
- lines.push(activeStyle.length === 0 || current.endsWith(RESET_SGR) ? current : current + RESET_SGR);
353
+ lines.push(finalizeTableWrappedLine(current, activeOsc8, activeStyle));
163
354
  }
164
355
  return lines.length > 0 ? lines : [''];
165
356
  }
357
+ function buildFittedTable(options, ctx, tableData, variant, optionsOverride = {}) {
358
+ const columns = optionsOverride.columns ?? tableData.columns;
359
+ const rows = optionsOverride.rows ?? tableData.rows;
360
+ const layout = optionsOverride.layout ?? options.layout ?? 'auto';
361
+ const columnGap = sanitizeNonNegativeInt(options.columnGap, 2);
362
+ const overflow = resolveOverflowBehavior(options.overflow, ctx.resolveBCSS({ type: 'Table', id: options.id, classes: options.class?.split(' ') }));
363
+ const wrapMode = options.wrap ?? 'word';
364
+ const widths = fitColumnWidths(columns, rows, variant, layout, resolveTargetWidth(options, ctx, layout), columnGap);
365
+ const headerToken = options.headerToken ?? ctx.ui('tableHeader');
366
+ const styleHeaders = optionsOverride.styleHeaders ?? true;
367
+ const headerLines = columns.map((column, index) => {
368
+ const header = column.header ?? '';
369
+ const value = styleHeaders ? ctx.style.styled(headerToken, header) : header;
370
+ return formatCellLines(value, widths[index] ?? 0, overflow, wrapMode);
371
+ });
372
+ const headerHeight = headerLines.reduce((max, lines) => Math.max(max, lines.length), 1);
373
+ const fittedRows = rows.map((row) => {
374
+ const cells = widths.map((width, index) => {
375
+ return formatCellLines(row[index] ?? '', width, overflow, wrapMode);
376
+ });
377
+ const height = cells.reduce((max, lines) => Math.max(max, lines.length), 1);
378
+ return { cells, height };
379
+ });
380
+ return {
381
+ columns: columns.map((column, index) => ({
382
+ header: column.header ?? '',
383
+ width: widths[index] ?? 0,
384
+ align: column.align ?? 'left',
385
+ })),
386
+ widths,
387
+ headerLines,
388
+ headerHeight,
389
+ rows: fittedRows,
390
+ columnGap,
391
+ showHeader: tableData.showHeader,
392
+ };
393
+ }
394
+ function renderGridTable(model, options, ctx, variant, styled = true) {
395
+ const borderToken = options.borderToken ?? ctx.border('muted');
396
+ const bc = (s) => styled ? ctx.style.styled(borderToken, s) : s;
397
+ const chars = variant === 'box'
398
+ ? {
399
+ h: '\u2500',
400
+ v: '\u2502',
401
+ topLeft: '\u250c',
402
+ topMid: '\u252c',
403
+ topRight: '\u2510',
404
+ midLeft: '\u251c',
405
+ midMid: '\u253c',
406
+ midRight: '\u2524',
407
+ bottomLeft: '\u2514',
408
+ bottomMid: '\u2534',
409
+ bottomRight: '\u2518',
410
+ }
411
+ : {
412
+ h: '-',
413
+ v: '|',
414
+ topLeft: '+',
415
+ topMid: '+',
416
+ topRight: '+',
417
+ midLeft: '+',
418
+ midMid: '+',
419
+ midRight: '+',
420
+ bottomLeft: '+',
421
+ bottomMid: '+',
422
+ bottomRight: '+',
423
+ };
424
+ const hLine = (left, mid, right) => {
425
+ const segments = model.widths.map(w => chars.h.repeat(w + 2));
426
+ return bc(left + segments.join(mid) + right);
427
+ };
428
+ const top = hLine(chars.topLeft, chars.topMid, chars.topRight);
429
+ const midSep = hLine(chars.midLeft, chars.midMid, chars.midRight);
430
+ const bottom = hLine(chars.bottomLeft, chars.bottomMid, chars.bottomRight);
431
+ const hdrBgFill = styled ? makeBgFill(options.headerBgToken, ctx) : undefined;
432
+ const headerRows = Array.from({ length: model.headerHeight }, (_row, rowIndex) => {
433
+ const headerCells = model.columns.map((column, i) => ' ' + alignCell(model.headerLines[i]?.[rowIndex] ?? '', column.width, column.align) + ' ');
434
+ const rawHeaderRow = bc(chars.v) + headerCells.join(bc(chars.v)) + bc(chars.v);
435
+ return hdrBgFill ? hdrBgFill(rawHeaderRow) : rawHeaderRow;
436
+ });
437
+ const dataRows = model.rows.flatMap((row) => {
438
+ return Array.from({ length: row.height }, (_line, lineIndex) => {
439
+ const cells = model.columns.map((column, i) => ' ' + alignCell(row.cells[i]?.[lineIndex] ?? '', column.width, column.align) + ' ');
440
+ return bc(chars.v) + cells.join(bc(chars.v)) + bc(chars.v);
441
+ });
442
+ });
443
+ return model.showHeader
444
+ ? [top, ...headerRows, midSep, ...dataRows, bottom].join('\n')
445
+ : [top, ...dataRows, bottom].join('\n');
446
+ }
447
+ function renderBorderlessRow(model, cells, rowIndex) {
448
+ const gap = ' '.repeat(model.columnGap);
449
+ return model.columns
450
+ .map((column, index) => alignCell(cells[index]?.[rowIndex] ?? '', column.width, column.align))
451
+ .join(gap)
452
+ .trimEnd();
453
+ }
454
+ function renderRule(model, char, ctx, options) {
455
+ const borderToken = options.borderToken ?? ctx.border('muted');
456
+ const line = model.widths.map(width => char.repeat(width)).join(' '.repeat(model.columnGap)).trimEnd();
457
+ return ctx.style.styled(borderToken, line);
458
+ }
459
+ function renderRuledTable(model, options, ctx, includeRowRules) {
460
+ const lines = model.showHeader
461
+ ? [
462
+ ...Array.from({ length: model.headerHeight }, (_row, index) => {
463
+ return renderBorderlessRow(model, model.headerLines, index);
464
+ }),
465
+ renderRule(model, '\u2501', ctx, options),
466
+ ]
467
+ : [];
468
+ for (let rowIndex = 0; rowIndex < model.rows.length; rowIndex++) {
469
+ const row = model.rows[rowIndex];
470
+ for (let lineIndex = 0; lineIndex < row.height; lineIndex++) {
471
+ lines.push(renderBorderlessRow(model, row.cells, lineIndex));
472
+ }
473
+ if (includeRowRules && rowIndex < model.rows.length - 1) {
474
+ lines.push(renderRule(model, '\u2500', ctx, options));
475
+ }
476
+ }
477
+ return lines.join('\n');
478
+ }
479
+ function renderHeaderRuleTable(model) {
480
+ const lines = model.showHeader
481
+ ? [
482
+ ...Array.from({ length: model.headerHeight }, (_row, index) => {
483
+ return renderBorderlessRow(model, model.headerLines, index);
484
+ }),
485
+ model.widths.map(width => '-'.repeat(width)).join(' '.repeat(model.columnGap)).trimEnd(),
486
+ ]
487
+ : [];
488
+ for (const row of model.rows) {
489
+ for (let lineIndex = 0; lineIndex < row.height; lineIndex++) {
490
+ lines.push(renderBorderlessRow(model, row.cells, lineIndex));
491
+ }
492
+ }
493
+ return lines.join('\n');
494
+ }
495
+ function renderPlainTable(model) {
496
+ const lines = model.showHeader
497
+ ? Array.from({ length: model.headerHeight }, (_row, index) => {
498
+ return renderBorderlessRow(model, model.headerLines, index);
499
+ })
500
+ : [];
501
+ for (const row of model.rows) {
502
+ for (let lineIndex = 0; lineIndex < row.height; lineIndex++) {
503
+ lines.push(renderBorderlessRow(model, row.cells, lineIndex));
504
+ }
505
+ }
506
+ return lines.join('\n');
507
+ }
508
+ function markdownSeparator(width, align) {
509
+ const segmentWidth = Math.max(3, width + 2);
510
+ if (align === 'right')
511
+ return '-'.repeat(segmentWidth - 1) + ':';
512
+ if (align === 'center')
513
+ return ':' + '-'.repeat(segmentWidth - 2) + ':';
514
+ return '-'.repeat(segmentWidth);
515
+ }
516
+ function renderMarkdownTable(model) {
517
+ const renderWidths = model.columns.map(column => Math.max(1, column.width));
518
+ const rowLine = (cells, lineIndex) => {
519
+ return '|' + model.columns.map((column, index) => {
520
+ const value = cells[index]?.[lineIndex] ?? '';
521
+ return ' ' + alignCell(value, renderWidths[index], column.align) + ' ';
522
+ }).join('|') + '|';
523
+ };
524
+ const headerRows = Array.from({ length: model.headerHeight }, (_row, index) => {
525
+ return rowLine(model.headerLines, index);
526
+ });
527
+ const separator = '|' + model.columns.map((column, index) => {
528
+ return markdownSeparator(renderWidths[index], column.align);
529
+ }).join('|') + '|';
530
+ const rows = model.rows.flatMap((row) => {
531
+ return Array.from({ length: row.height }, (_line, index) => rowLine(row.cells, index));
532
+ });
533
+ return model.showHeader ? [...headerRows, separator, ...rows].join('\n') : rows.join('\n');
534
+ }
535
+ function renderExpandedTable(tableData, options, ctx) {
536
+ const borderToken = options.borderToken ?? ctx.border('muted');
537
+ const lines = [];
538
+ const columnLabel = (index) => {
539
+ return tableData.showHeader ? tableData.columns[index]?.header ?? '' : `Column ${index + 1}`;
540
+ };
541
+ const preferredLabelWidth = tableData.columns.reduce((max, _column, index) => {
542
+ return Math.max(max, visibleLength(columnLabel(index)));
543
+ }, 0);
544
+ const layout = options.layout ?? 'auto';
545
+ const targetWidth = resolveTargetWidth(options, ctx, layout);
546
+ const ruleWidth = targetWidth ?? 40;
547
+ const separator = targetWidth === undefined || targetWidth >= 3
548
+ ? ' | '
549
+ : ' '.repeat(Math.max(0, targetWidth - 1));
550
+ const availableContentWidth = targetWidth === undefined
551
+ ? undefined
552
+ : Math.max(0, targetWidth - separator.length);
553
+ const labelWidth = targetWidth === undefined
554
+ ? preferredLabelWidth
555
+ : Math.min(preferredLabelWidth, Math.floor((availableContentWidth ?? 0) / 2));
556
+ const valueWidth = targetWidth === undefined
557
+ ? undefined
558
+ : Math.max(0, targetWidth - labelWidth - separator.length);
559
+ const overflow = resolveOverflowBehavior(options.overflow, ctx.resolveBCSS({ type: 'Table', id: options.id, classes: options.class?.split(' ') }));
560
+ const wrapMode = options.wrap ?? 'word';
561
+ for (let rowIndex = 0; rowIndex < tableData.rows.length; rowIndex++) {
562
+ const title = `-[ RECORD ${rowIndex + 1} ]`;
563
+ const clippedTitle = targetWidth === undefined ? title : clipCellToWidth(title, ruleWidth);
564
+ lines.push(ctx.style.styled(borderToken, clippedTitle + '-'.repeat(Math.max(0, ruleWidth - visibleLength(clippedTitle)))));
565
+ const row = tableData.rows[rowIndex];
566
+ for (let columnIndex = 0; columnIndex < tableData.columns.length; columnIndex++) {
567
+ const rawLabel = columnLabel(columnIndex);
568
+ const label = padRight(targetWidth === undefined ? rawLabel : clipCellToWidth(rawLabel, labelWidth), labelWidth);
569
+ const rawValue = row[columnIndex] ?? '';
570
+ const valueLines = valueWidth === undefined
571
+ ? rawValue.split('\n')
572
+ : formatCellLines(rawValue, valueWidth, overflow, wrapMode);
573
+ for (let lineIndex = 0; lineIndex < valueLines.length; lineIndex++) {
574
+ const lineLabel = lineIndex === 0 ? label : ' '.repeat(labelWidth);
575
+ lines.push(`${lineLabel}${separator}${valueLines[lineIndex] ?? ''}`.trimEnd());
576
+ }
577
+ }
578
+ }
579
+ return lines.join('\n');
580
+ }
581
+ function markdownEscapeCell(value) {
582
+ return stripAnsi(String(value ?? ''))
583
+ .replace(/\\/g, '\\\\')
584
+ .replace(/\|/g, '\\|')
585
+ .replace(/\r\n?/g, '\n')
586
+ .replace(/\n/g, '<br>');
587
+ }
588
+ function markdownTableData(tableData) {
589
+ return {
590
+ columns: tableData.columns.map(column => ({
591
+ ...column,
592
+ header: markdownEscapeCell(column.header ?? ''),
593
+ })),
594
+ rows: tableData.rows.map(row => row.map(cell => markdownEscapeCell(cell))),
595
+ showHeader: tableData.showHeader,
596
+ };
597
+ }
598
+ function renderVisualTable(options, ctx, tableData, variant) {
599
+ if (variant === 'markdown') {
600
+ const markdownData = markdownTableData(tableData);
601
+ const markdownModel = buildFittedTable(options, ctx, markdownData, 'markdown', {
602
+ styleHeaders: false,
603
+ });
604
+ return renderMarkdownTable(markdownModel);
605
+ }
606
+ if (variant === 'expanded') {
607
+ return renderExpandedTable(tableData, options, ctx);
608
+ }
609
+ const model = buildFittedTable(options, ctx, tableData, variant);
610
+ switch (variant) {
611
+ case 'ascii-grid':
612
+ return renderGridTable(model, options, ctx, 'ascii-grid');
613
+ case 'ruled':
614
+ return renderRuledTable(model, options, ctx, true);
615
+ case 'header-rule':
616
+ return renderHeaderRuleTable(model);
617
+ case 'plain':
618
+ return renderPlainTable(model);
619
+ case 'definition':
620
+ return renderRuledTable(model, options, ctx, true);
621
+ case 'box':
622
+ default:
623
+ return renderGridTable(model, options, ctx, 'box');
624
+ }
625
+ }
626
+ function escapeTsvCell(value) {
627
+ return String(value ?? '')
628
+ .replace(/\\/g, '\\\\')
629
+ .replace(/\t/g, '\\t')
630
+ .replace(/\r/g, '\\r')
631
+ .replace(/\n/g, '\\n');
632
+ }
633
+ function escapeCsvCell(value) {
634
+ const text = String(value ?? '');
635
+ if (!/[",\r\n]/.test(text))
636
+ return text;
637
+ return `"${text.replace(/"/g, '""')}"`;
638
+ }
639
+ function renderSeparatedPipe(tableData, separator, escapeCell) {
640
+ const headerLine = tableData.columns.map(column => escapeCell(column.header ?? '')).join(separator);
641
+ const dataLines = tableData.rows.map(row => row.map(escapeCell).join(separator));
642
+ if (!tableData.showHeader)
643
+ return dataLines.join('\n');
644
+ return [headerLine, ...dataLines].join('\n');
645
+ }
646
+ function renderPipeTable(options, ctx, tableData) {
647
+ const format = options.pipeFormat ?? 'tsv';
648
+ if (format === 'csv')
649
+ return renderSeparatedPipe(tableData, ',', escapeCsvCell);
650
+ if (format === 'markdown') {
651
+ const markdownData = markdownTableData(tableData);
652
+ const markdownModel = buildFittedTable(options, ctx, markdownData, 'markdown', {
653
+ layout: options.layout ?? (options.width !== undefined || options.maxWidth !== undefined ? 'auto' : 'intrinsic'),
654
+ styleHeaders: false,
655
+ });
656
+ return renderMarkdownTable(markdownModel);
657
+ }
658
+ if (format === 'ascii-grid') {
659
+ const model = buildFittedTable(options, ctx, tableData, 'ascii-grid', {
660
+ layout: options.layout ?? (options.width !== undefined || options.maxWidth !== undefined ? 'auto' : 'intrinsic'),
661
+ styleHeaders: false,
662
+ });
663
+ return renderGridTable(model, options, ctx, 'ascii-grid', false);
664
+ }
665
+ return renderSeparatedPipe(tableData, '\t', escapeTsvCell);
666
+ }
166
667
  export function table(optionsOrColumns, rowData, context) {
167
668
  let options;
168
669
  if (isTableOptions(optionsOrColumns)) {
@@ -172,67 +673,22 @@ export function table(optionsOrColumns, rowData, context) {
172
673
  options = { columns: [...optionsOrColumns], rows: rowData ?? [], ctx: context };
173
674
  }
174
675
  const ctx = resolveCtx(options.ctx);
175
- const columns = options.columns ?? [];
176
- const rows = (options.rows ?? []).map(row => (row ?? []).map(cell => cell ?? ''));
676
+ const tableData = normalizeTable(options);
177
677
  return renderByMode(ctx.mode, {
178
- pipe: () => {
179
- const headerLine = columns.map((c) => c.header ?? '').join('\t');
180
- const dataLines = rows.map((row) => row.join('\t'));
181
- return [headerLine, ...dataLines].join('\n');
182
- },
678
+ pipe: () => renderPipeTable(options, ctx, tableData),
183
679
  accessible: () => {
184
680
  const lines = [];
185
- for (let i = 0; i < rows.length; i++) {
186
- const row = rows[i];
187
- const pairs = columns.map((col, j) => `${col.header ?? ''}=${row[j] ?? ''}`);
681
+ for (let i = 0; i < tableData.rows.length; i++) {
682
+ const row = tableData.rows[i];
683
+ const pairs = tableData.columns.map((col, j) => {
684
+ const label = tableData.showHeader ? col.header ?? '' : `Column ${j + 1}`;
685
+ return `${label}=${row[j] ?? ''}`;
686
+ });
188
687
  lines.push(`Row ${i + 1}: ${pairs.join(', ')}`);
189
688
  }
190
689
  return lines.join('\n');
191
690
  },
192
- interactive: () => {
193
- const headerToken = options.headerToken ?? ctx.ui('tableHeader');
194
- const borderToken = options.borderToken ?? ctx.border('muted');
195
- const overflow = resolveOverflowBehavior(options.overflow, ctx.resolveBCSS({ type: 'Table', id: options.id, classes: options.class?.split(' ') }));
196
- const bc = (s) => ctx.style.styled(borderToken, s);
197
- const colWidths = columns.map((col, i) => {
198
- const normalizedWidth = normalizeColumnWidth(col.width);
199
- if (normalizedWidth !== undefined)
200
- return normalizedWidth;
201
- let max = visibleLength(col.header ?? '');
202
- for (const row of rows) {
203
- const cell = row[i] ?? '';
204
- max = Math.max(max, visibleLength(cell));
205
- }
206
- return max;
207
- });
208
- const h = '\u2500';
209
- const v = '\u2502';
210
- const hLine = (left, mid, right) => {
211
- const segments = colWidths.map((w) => h.repeat(w + 2));
212
- return bc(left + segments.join(mid) + right);
213
- };
214
- const top = hLine('\u250c', '\u252c', '\u2510');
215
- const midSep = hLine('\u251c', '\u253c', '\u2524');
216
- const bottom = hLine('\u2514', '\u2534', '\u2518');
217
- const hdrBgFill = makeBgFill(options.headerBgToken, ctx);
218
- const headerLinesByColumn = columns.map((col, i) => formatCellLines(ctx.style.styled(headerToken, col.header ?? ''), colWidths[i], overflow, col.width !== undefined));
219
- const headerHeight = headerLinesByColumn.reduce((max, lines) => Math.max(max, lines.length), 1);
220
- const headerRows = Array.from({ length: headerHeight }, (_row, rowIndex) => {
221
- const headerCells = columns.map((_col, i) => ' ' + padRight(headerLinesByColumn[i][rowIndex] ?? '', colWidths[i]) + ' ');
222
- const rawHeaderRow = bc(v) + headerCells.join(bc(v)) + bc(v);
223
- return hdrBgFill ? hdrBgFill(rawHeaderRow) : rawHeaderRow;
224
- });
225
- const dataRows = rows.flatMap((row) => {
226
- const cellLines = colWidths.map((w, i) => formatCellLines(row[i] ?? '', w, overflow, columns[i]?.width !== undefined));
227
- const rowHeight = cellLines.reduce((max, lines) => Math.max(max, lines.length), 1);
228
- return Array.from({ length: rowHeight }, (_line, lineIndex) => {
229
- const cells = colWidths.map((w, i) => ' ' + padRight(cellLines[i][lineIndex] ?? '', w) + ' ');
230
- return bc(v) + cells.join(bc(v)) + bc(v);
231
- });
232
- });
233
- const lines = [top, ...headerRows, midSep, ...dataRows, bottom];
234
- return lines.join('\n');
235
- },
691
+ interactive: () => renderVisualTable(options, ctx, tableData, options.variant ?? 'box'),
236
692
  }, options);
237
693
  }
238
694
  //# sourceMappingURL=table.js.map