@flyingrobots/bijou 4.4.1 → 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 (249) hide show
  1. package/README.md +71 -1
  2. package/dist/adapters/test/index.d.ts.map +1 -1
  3. package/dist/adapters/test/index.js +1 -3
  4. package/dist/adapters/test/index.js.map +1 -1
  5. package/dist/adapters/test/runtime.d.ts.map +1 -1
  6. package/dist/adapters/test/runtime.js +4 -3
  7. package/dist/adapters/test/runtime.js.map +1 -1
  8. package/dist/context-theme.d.ts +25 -0
  9. package/dist/context-theme.d.ts.map +1 -1
  10. package/dist/context-theme.js +19 -0
  11. package/dist/context-theme.js.map +1 -1
  12. package/dist/context.d.ts +13 -1
  13. package/dist/context.d.ts.map +1 -1
  14. package/dist/context.js +24 -3
  15. package/dist/context.js.map +1 -1
  16. package/dist/core/active-binding-collection.d.ts +49 -0
  17. package/dist/core/active-binding-collection.d.ts.map +1 -0
  18. package/dist/core/active-binding-collection.js +264 -0
  19. package/dist/core/active-binding-collection.js.map +1 -0
  20. package/dist/core/app-shell-composition.d.ts +44 -0
  21. package/dist/core/app-shell-composition.d.ts.map +1 -0
  22. package/dist/core/app-shell-composition.js +123 -0
  23. package/dist/core/app-shell-composition.js.map +1 -0
  24. package/dist/core/binding-frame-update.d.ts +18 -0
  25. package/dist/core/binding-frame-update.d.ts.map +1 -0
  26. package/dist/core/binding-frame-update.js +133 -0
  27. package/dist/core/binding-frame-update.js.map +1 -0
  28. package/dist/core/binding-lifecycle.d.ts +65 -0
  29. package/dist/core/binding-lifecycle.d.ts.map +1 -0
  30. package/dist/core/binding-lifecycle.js +357 -0
  31. package/dist/core/binding-lifecycle.js.map +1 -0
  32. package/dist/core/binding.d.ts +192 -0
  33. package/dist/core/binding.d.ts.map +1 -0
  34. package/dist/core/binding.js +587 -0
  35. package/dist/core/binding.js.map +1 -0
  36. package/dist/core/block-metadata.d.ts +113 -0
  37. package/dist/core/block-metadata.d.ts.map +1 -0
  38. package/dist/core/block-metadata.js +376 -0
  39. package/dist/core/block-metadata.js.map +1 -0
  40. package/dist/core/block-tree-render.d.ts +17 -0
  41. package/dist/core/block-tree-render.d.ts.map +1 -0
  42. package/dist/core/block-tree-render.js +186 -0
  43. package/dist/core/block-tree-render.js.map +1 -0
  44. package/dist/core/component-metadata.d.ts +63 -0
  45. package/dist/core/component-metadata.d.ts.map +1 -0
  46. package/dist/core/component-metadata.js +158 -0
  47. package/dist/core/component-metadata.js.map +1 -0
  48. package/dist/core/components/alert-v3.d.ts.map +1 -1
  49. package/dist/core/components/alert-v3.js +5 -4
  50. package/dist/core/components/alert-v3.js.map +1 -1
  51. package/dist/core/components/alert.d.ts.map +1 -1
  52. package/dist/core/components/alert.js +7 -8
  53. package/dist/core/components/alert.js.map +1 -1
  54. package/dist/core/components/badge.d.ts.map +1 -1
  55. package/dist/core/components/badge.js +19 -18
  56. package/dist/core/components/badge.js.map +1 -1
  57. package/dist/core/components/box-v3.d.ts.map +1 -1
  58. package/dist/core/components/box-v3.js +29 -29
  59. package/dist/core/components/box-v3.js.map +1 -1
  60. package/dist/core/components/dag-edges.d.ts +28 -5
  61. package/dist/core/components/dag-edges.d.ts.map +1 -1
  62. package/dist/core/components/dag-edges.js +116 -22
  63. package/dist/core/components/dag-edges.js.map +1 -1
  64. package/dist/core/components/dag-layout.d.ts +3 -4
  65. package/dist/core/components/dag-layout.d.ts.map +1 -1
  66. package/dist/core/components/dag-layout.js +42 -9
  67. package/dist/core/components/dag-layout.js.map +1 -1
  68. package/dist/core/components/dag-render.d.ts +4 -3
  69. package/dist/core/components/dag-render.d.ts.map +1 -1
  70. package/dist/core/components/dag-render.js +209 -105
  71. package/dist/core/components/dag-render.js.map +1 -1
  72. package/dist/core/components/dag-source.d.ts +4 -0
  73. package/dist/core/components/dag-source.d.ts.map +1 -1
  74. package/dist/core/components/dag-source.js +10 -0
  75. package/dist/core/components/dag-source.js.map +1 -1
  76. package/dist/core/components/dag.d.ts +20 -4
  77. package/dist/core/components/dag.d.ts.map +1 -1
  78. package/dist/core/components/dag.js.map +1 -1
  79. package/dist/core/components/explainability.d.ts.map +1 -1
  80. package/dist/core/components/explainability.js +30 -87
  81. package/dist/core/components/explainability.js.map +1 -1
  82. package/dist/core/components/guided-flow.d.ts +32 -0
  83. package/dist/core/components/guided-flow.d.ts.map +1 -0
  84. package/dist/core/components/guided-flow.js +131 -0
  85. package/dist/core/components/guided-flow.js.map +1 -0
  86. package/dist/core/components/hyperlink.d.ts.map +1 -1
  87. package/dist/core/components/hyperlink.js +7 -4
  88. package/dist/core/components/hyperlink.js.map +1 -1
  89. package/dist/core/components/index.d.ts +5 -3
  90. package/dist/core/components/index.d.ts.map +1 -1
  91. package/dist/core/components/index.js +1 -0
  92. package/dist/core/components/index.js.map +1 -1
  93. package/dist/core/components/markdown-parse.d.ts +4 -0
  94. package/dist/core/components/markdown-parse.d.ts.map +1 -1
  95. package/dist/core/components/markdown-parse.js +66 -2
  96. package/dist/core/components/markdown-parse.js.map +1 -1
  97. package/dist/core/components/markdown-render.d.ts.map +1 -1
  98. package/dist/core/components/markdown-render.js +220 -14
  99. package/dist/core/components/markdown-render.js.map +1 -1
  100. package/dist/core/components/markdown.d.ts +4 -3
  101. package/dist/core/components/markdown.d.ts.map +1 -1
  102. package/dist/core/components/markdown.js +6 -5
  103. package/dist/core/components/markdown.js.map +1 -1
  104. package/dist/core/components/paginator.d.ts.map +1 -1
  105. package/dist/core/components/paginator.js +5 -3
  106. package/dist/core/components/paginator.js.map +1 -1
  107. package/dist/core/components/perf-overlay.d.ts.map +1 -1
  108. package/dist/core/components/perf-overlay.js +3 -2
  109. package/dist/core/components/perf-overlay.js.map +1 -1
  110. package/dist/core/components/preference-list.d.ts.map +1 -1
  111. package/dist/core/components/preference-list.js +75 -37
  112. package/dist/core/components/preference-list.js.map +1 -1
  113. package/dist/core/components/progress.d.ts.map +1 -1
  114. package/dist/core/components/progress.js +4 -3
  115. package/dist/core/components/progress.js.map +1 -1
  116. package/dist/core/components/separator-v3.d.ts.map +1 -1
  117. package/dist/core/components/separator-v3.js +11 -11
  118. package/dist/core/components/separator-v3.js.map +1 -1
  119. package/dist/core/components/separator.d.ts.map +1 -1
  120. package/dist/core/components/separator.js +4 -1
  121. package/dist/core/components/separator.js.map +1 -1
  122. package/dist/core/components/skeleton.d.ts.map +1 -1
  123. package/dist/core/components/skeleton.js +3 -2
  124. package/dist/core/components/skeleton.js.map +1 -1
  125. package/dist/core/components/surface-text.d.ts +7 -0
  126. package/dist/core/components/surface-text.d.ts.map +1 -1
  127. package/dist/core/components/surface-text.js +33 -25
  128. package/dist/core/components/surface-text.js.map +1 -1
  129. package/dist/core/components/table-v3.d.ts +5 -2
  130. package/dist/core/components/table-v3.d.ts.map +1 -1
  131. package/dist/core/components/table-v3.js +162 -37
  132. package/dist/core/components/table-v3.js.map +1 -1
  133. package/dist/core/components/table.d.ts +51 -7
  134. package/dist/core/components/table.d.ts.map +1 -1
  135. package/dist/core/components/table.js +655 -81
  136. package/dist/core/components/table.js.map +1 -1
  137. package/dist/core/fixture-promotion.d.ts +33 -0
  138. package/dist/core/fixture-promotion.d.ts.map +1 -0
  139. package/dist/core/fixture-promotion.js +62 -0
  140. package/dist/core/fixture-promotion.js.map +1 -0
  141. package/dist/core/forms/filter-interactive.d.ts.map +1 -1
  142. package/dist/core/forms/filter-interactive.js +2 -4
  143. package/dist/core/forms/filter-interactive.js.map +1 -1
  144. package/dist/core/forms/multiselect.d.ts.map +1 -1
  145. package/dist/core/forms/multiselect.js +2 -4
  146. package/dist/core/forms/multiselect.js.map +1 -1
  147. package/dist/core/forms/note.d.ts.map +1 -1
  148. package/dist/core/forms/note.js +2 -1
  149. package/dist/core/forms/note.js.map +1 -1
  150. package/dist/core/forms/select.d.ts.map +1 -1
  151. package/dist/core/forms/select.js +2 -4
  152. package/dist/core/forms/select.js.map +1 -1
  153. package/dist/core/forms/textarea-editor.d.ts.map +1 -1
  154. package/dist/core/forms/textarea-editor.js +7 -5
  155. package/dist/core/forms/textarea-editor.js.map +1 -1
  156. package/dist/core/layout/envelope.d.ts +174 -0
  157. package/dist/core/layout/envelope.d.ts.map +1 -0
  158. package/dist/core/layout/envelope.js +336 -0
  159. package/dist/core/layout/envelope.js.map +1 -0
  160. package/dist/core/layout/geometry.d.ts +39 -0
  161. package/dist/core/layout/geometry.d.ts.map +1 -0
  162. package/dist/core/layout/geometry.js +201 -0
  163. package/dist/core/layout/geometry.js.map +1 -0
  164. package/dist/core/mode-lowering.d.ts +48 -0
  165. package/dist/core/mode-lowering.d.ts.map +1 -0
  166. package/dist/core/mode-lowering.js +176 -0
  167. package/dist/core/mode-lowering.js.map +1 -0
  168. package/dist/core/numeric.d.ts +16 -0
  169. package/dist/core/numeric.d.ts.map +1 -0
  170. package/dist/core/numeric.js +37 -0
  171. package/dist/core/numeric.js.map +1 -0
  172. package/dist/core/render/differ.d.ts.map +1 -1
  173. package/dist/core/render/differ.js +15 -13
  174. package/dist/core/render/differ.js.map +1 -1
  175. package/dist/core/runtime-viewport.d.ts.map +1 -1
  176. package/dist/core/runtime-viewport.js +2 -3
  177. package/dist/core/runtime-viewport.js.map +1 -1
  178. package/dist/core/schema-block.d.ts +63 -0
  179. package/dist/core/schema-block.d.ts.map +1 -0
  180. package/dist/core/schema-block.js +361 -0
  181. package/dist/core/schema-block.js.map +1 -0
  182. package/dist/core/selection.d.ts +127 -0
  183. package/dist/core/selection.d.ts.map +1 -0
  184. package/dist/core/selection.js +350 -0
  185. package/dist/core/selection.js.map +1 -0
  186. package/dist/core/standard-blocks.d.ts +264 -0
  187. package/dist/core/standard-blocks.d.ts.map +1 -0
  188. package/dist/core/standard-blocks.js +2518 -0
  189. package/dist/core/standard-blocks.js.map +1 -0
  190. package/dist/core/story-capture.d.ts +48 -0
  191. package/dist/core/story-capture.d.ts.map +1 -0
  192. package/dist/core/story-capture.js +64 -0
  193. package/dist/core/story-capture.js.map +1 -0
  194. package/dist/core/text/grapheme.d.ts +51 -0
  195. package/dist/core/text/grapheme.d.ts.map +1 -1
  196. package/dist/core/text/grapheme.js +68 -0
  197. package/dist/core/text/grapheme.js.map +1 -1
  198. package/dist/core/text/icon-presentation.d.ts +10 -0
  199. package/dist/core/text/icon-presentation.d.ts.map +1 -0
  200. package/dist/core/text/icon-presentation.js +12 -0
  201. package/dist/core/text/icon-presentation.js.map +1 -0
  202. package/dist/core/text/index.d.ts +2 -1
  203. package/dist/core/text/index.d.ts.map +1 -1
  204. package/dist/core/text/index.js +2 -1
  205. package/dist/core/text/index.js.map +1 -1
  206. package/dist/core/text/wrap.d.ts.map +1 -1
  207. package/dist/core/text/wrap.js +37 -20
  208. package/dist/core/text/wrap.js.map +1 -1
  209. package/dist/core/theme/color.d.ts +32 -0
  210. package/dist/core/theme/color.d.ts.map +1 -1
  211. package/dist/core/theme/color.js +68 -0
  212. package/dist/core/theme/color.js.map +1 -1
  213. package/dist/core/theme/doctor.d.ts +37 -0
  214. package/dist/core/theme/doctor.d.ts.map +1 -0
  215. package/dist/core/theme/doctor.js +175 -0
  216. package/dist/core/theme/doctor.js.map +1 -0
  217. package/dist/core/theme/dtcg.d.ts.map +1 -1
  218. package/dist/core/theme/dtcg.js +10 -1
  219. package/dist/core/theme/dtcg.js.map +1 -1
  220. package/dist/core/theme/graph-types.d.ts +5 -0
  221. package/dist/core/theme/graph-types.d.ts.map +1 -1
  222. package/dist/core/theme/graph.d.ts.map +1 -1
  223. package/dist/core/theme/graph.js +52 -5
  224. package/dist/core/theme/graph.js.map +1 -1
  225. package/dist/core/theme/index.d.ts +4 -1
  226. package/dist/core/theme/index.d.ts.map +1 -1
  227. package/dist/core/theme/index.js +3 -1
  228. package/dist/core/theme/index.js.map +1 -1
  229. package/dist/core/theme/presets.d.ts +3 -2
  230. package/dist/core/theme/presets.d.ts.map +1 -1
  231. package/dist/core/theme/presets.js +22 -29
  232. package/dist/core/theme/presets.js.map +1 -1
  233. package/dist/core/theme/tokens.d.ts +1 -1
  234. package/dist/core/theme/tokens.d.ts.map +1 -1
  235. package/dist/factory.d.ts +3 -0
  236. package/dist/factory.d.ts.map +1 -1
  237. package/dist/factory.js +4 -6
  238. package/dist/factory.js.map +1 -1
  239. package/dist/index.d.ts +23 -6
  240. package/dist/index.d.ts.map +1 -1
  241. package/dist/index.js +25 -6
  242. package/dist/index.js.map +1 -1
  243. package/dist/ports/context.d.ts +6 -1
  244. package/dist/ports/context.d.ts.map +1 -1
  245. package/dist/ports/surface.d.ts +6 -4
  246. package/dist/ports/surface.d.ts.map +1 -1
  247. package/dist/ports/surface.js +40 -23
  248. package/dist/ports/surface.js.map +1 -1
  249. package/package.json +2 -2
@@ -1,18 +1,42 @@
1
- import { graphemeWidth } from '../text/grapheme.js';
2
- import { clipToWidth } from '../text/clip.js';
1
+ import { RESET_SGR } from '../ansi.js';
2
+ import { ANSI_OSC8_RE, ANSI_SGR_RE, graphemeClusterWidth, segmentGraphemes, stripAnsi, } from '../text/grapheme.js';
3
3
  import { wrapToWidth } from '../text/wrap.js';
4
4
  import { resolveCtx } from '../resolve-ctx.js';
5
5
  import { renderByMode } from '../mode-render.js';
6
6
  import { makeBgFill } from '../bg-fill.js';
7
+ import { sanitizeNonNegativeInt, sanitizeOptionalNonNegativeInt, sanitizeOptionalPositiveInt, sanitizePositiveInt, } from '../numeric.js';
7
8
  import { resolveOverflowBehavior } from './overflow.js';
8
9
  /**
9
- * Calculate the visible (non-ANSI) character length of a string.
10
+ * Structural width added by the interactive table frame for a given column count.
11
+ *
12
+ * Each column contributes two padding spaces and one vertical divider, with one
13
+ * extra border character for the outer edge.
14
+ */
15
+ export function interactiveTableBorderOverhead(columnCount) {
16
+ return Math.max(0, columnCount) * 3 + 1;
17
+ }
18
+ /**
19
+ * Total rendered width of an interactive table for the supplied column widths.
20
+ */
21
+ export function measureInteractiveTableWidth(columnWidths) {
22
+ return columnWidths.reduce((total, width) => total + width, 0)
23
+ + interactiveTableBorderOverhead(columnWidths.length);
24
+ }
25
+ function isTableOptions(value) {
26
+ return !Array.isArray(value);
27
+ }
28
+ /**
29
+ * Calculate the visible (non-ANSI) display width of a string.
10
30
  *
11
31
  * @param str - Input string potentially containing ANSI codes.
12
- * @returns The number of visible characters.
32
+ * @returns The number of visible terminal columns.
13
33
  */
14
34
  function visibleLength(str) {
15
- return graphemeWidth(str);
35
+ let width = 0;
36
+ for (const grapheme of segmentGraphemes(stripAnsi(str))) {
37
+ width += tableGraphemeWidth(grapheme);
38
+ }
39
+ return width;
16
40
  }
17
41
  /**
18
42
  * Right-pad a string to a target width, accounting for ANSI escape sequences.
@@ -25,96 +49,646 @@ function padRight(str, width) {
25
49
  const visible = visibleLength(str);
26
50
  return visible >= width ? str : str + ' '.repeat(width - visible);
27
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
+ }
28
71
  function normalizeColumnWidth(width) {
29
- if (width == null)
30
- return undefined;
31
- if (!Number.isFinite(width))
32
- return 0;
33
- 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: '' }));
98
+ }
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);
34
129
  }
35
- function formatCellLines(value, width, overflow, fixedWidth) {
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) {
36
220
  const safeValue = value ?? '';
37
- if (!fixedWidth)
38
- return safeValue.split('\n');
39
221
  if (overflow === 'truncate') {
40
- return safeValue.split('\n').map((line) => clipToWidth(line, width));
222
+ return safeValue.split('\n').map(line => clipCellToWidth(line, width));
41
223
  }
42
- return safeValue.split('\n').flatMap((line) => wrapToWidth(line, width));
224
+ if (wrapMode === 'word')
225
+ return wrapToWidth(safeValue, width);
226
+ return safeValue.split('\n').flatMap(line => wrapCellToWidth(line, width));
43
227
  }
44
- /**
45
- * Render a table with unicode box-drawing borders.
46
- *
47
- * Output adapts to the current output mode:
48
- * - `interactive` / `static` — bordered table with styled headers.
49
- * - `pipe` — tab-separated values (TSV).
50
- * - `accessible` — key-value pairs per row for screen readers.
51
- *
52
- * @param options - Table configuration including columns and row data.
53
- * @returns The rendered table string.
54
- */
55
- export function table(options) {
228
+ const TABLE_EMOJI_PRESENTATION_RE = /\p{Emoji_Presentation}/u;
229
+ function tableGraphemeWidth(grapheme) {
230
+ if (TABLE_EMOJI_PRESENTATION_RE.test(grapheme))
231
+ return 2;
232
+ return graphemeClusterWidth(grapheme);
233
+ }
234
+ function isOsc8Escape(raw) {
235
+ return raw.startsWith('\x1b]8;;');
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
+ }
242
+ function isResetEscape(raw) {
243
+ return raw === RESET_SGR;
244
+ }
245
+ function tokenizeTableText(str) {
246
+ const regex = new RegExp(`${ANSI_SGR_RE.source}|${ANSI_OSC8_RE.source}`, 'g');
247
+ const tokens = [];
248
+ let lastIndex = 0;
249
+ for (const match of str.matchAll(regex)) {
250
+ const index = match.index ?? 0;
251
+ if (index > lastIndex) {
252
+ const raw = str.slice(lastIndex, index);
253
+ for (const grapheme of segmentGraphemes(raw)) {
254
+ tokens.push({
255
+ kind: 'grapheme',
256
+ raw: grapheme,
257
+ width: tableGraphemeWidth(grapheme),
258
+ });
259
+ }
260
+ }
261
+ tokens.push({ kind: 'ansi', raw: match[0] });
262
+ lastIndex = index + match[0].length;
263
+ }
264
+ if (lastIndex < str.length) {
265
+ const raw = str.slice(lastIndex);
266
+ for (const grapheme of segmentGraphemes(raw)) {
267
+ tokens.push({
268
+ kind: 'grapheme',
269
+ raw: grapheme,
270
+ width: tableGraphemeWidth(grapheme),
271
+ });
272
+ }
273
+ }
274
+ return tokens;
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
+ }
289
+ function clipCellToWidth(str, maxWidth) {
290
+ if (maxWidth <= 0)
291
+ return '';
292
+ const tokens = tokenizeTableText(str);
293
+ let visible = 0;
294
+ let result = '';
295
+ let hasStyle = false;
296
+ let activeOsc8 = '';
297
+ for (const token of tokens) {
298
+ if (token.kind === 'ansi') {
299
+ result += token.raw;
300
+ if (isOsc8Escape(token.raw)) {
301
+ activeOsc8 = isOsc8Close(token.raw) ? '' : token.raw;
302
+ }
303
+ else {
304
+ hasStyle = !isResetEscape(token.raw);
305
+ }
306
+ continue;
307
+ }
308
+ if (visible + token.width > maxWidth) {
309
+ if (activeOsc8.length > 0)
310
+ result += OSC8_CLOSE;
311
+ if (hasStyle && !result.endsWith(RESET_SGR))
312
+ result += RESET_SGR;
313
+ break;
314
+ }
315
+ result += token.raw;
316
+ visible += token.width;
317
+ }
318
+ return activeOsc8.length > 0 || hasStyle
319
+ ? finalizeTableWrappedLine(result, activeOsc8, hasStyle ? RESET_SGR : '')
320
+ : result;
321
+ }
322
+ function wrapCellToWidth(str, maxWidth) {
323
+ if (maxWidth <= 0)
324
+ return [''];
325
+ const tokens = tokenizeTableText(str);
326
+ if (tokens.length === 0)
327
+ return [''];
328
+ const lines = [];
329
+ let current = '';
330
+ let currentWidth = 0;
331
+ let activeStyle = '';
332
+ let activeOsc8 = '';
333
+ for (const token of tokens) {
334
+ if (token.kind === 'ansi') {
335
+ current += token.raw;
336
+ if (isOsc8Escape(token.raw)) {
337
+ activeOsc8 = isOsc8Close(token.raw) ? '' : token.raw;
338
+ continue;
339
+ }
340
+ activeStyle = isResetEscape(token.raw) ? '' : activeStyle + token.raw;
341
+ continue;
342
+ }
343
+ if (currentWidth + token.width > maxWidth && currentWidth > 0) {
344
+ lines.push(finalizeTableWrappedLine(current, activeOsc8, activeStyle));
345
+ current = activeTableAnsiPrefix(activeOsc8, activeStyle) + token.raw;
346
+ currentWidth = token.width;
347
+ continue;
348
+ }
349
+ current += token.raw;
350
+ currentWidth += token.width;
351
+ }
352
+ if (current.length > 0) {
353
+ lines.push(finalizeTableWrappedLine(current, activeOsc8, activeStyle));
354
+ }
355
+ return lines.length > 0 ? lines : [''];
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
+ }
667
+ export function table(optionsOrColumns, rowData, context) {
668
+ let options;
669
+ if (isTableOptions(optionsOrColumns)) {
670
+ options = optionsOrColumns;
671
+ }
672
+ else {
673
+ options = { columns: [...optionsOrColumns], rows: rowData ?? [], ctx: context };
674
+ }
56
675
  const ctx = resolveCtx(options.ctx);
57
- const columns = options.columns ?? [];
58
- const rows = (options.rows ?? []).map(row => (row ?? []).map(cell => cell ?? ''));
676
+ const tableData = normalizeTable(options);
59
677
  return renderByMode(ctx.mode, {
60
- pipe: () => {
61
- const headerLine = columns.map((c) => c.header ?? '').join('\t');
62
- const dataLines = rows.map((row) => row.join('\t'));
63
- return [headerLine, ...dataLines].join('\n');
64
- },
678
+ pipe: () => renderPipeTable(options, ctx, tableData),
65
679
  accessible: () => {
66
680
  const lines = [];
67
- for (let i = 0; i < rows.length; i++) {
68
- const row = rows[i];
69
- 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
+ });
70
687
  lines.push(`Row ${i + 1}: ${pairs.join(', ')}`);
71
688
  }
72
689
  return lines.join('\n');
73
690
  },
74
- interactive: () => {
75
- const headerToken = options.headerToken ?? ctx.ui('tableHeader');
76
- const borderToken = options.borderToken ?? ctx.border('muted');
77
- const overflow = resolveOverflowBehavior(options.overflow, ctx.resolveBCSS({ type: 'Table', id: options.id, classes: options.class?.split(' ') }));
78
- const bc = (s) => ctx.style.styled(borderToken, s);
79
- const colWidths = columns.map((col, i) => {
80
- const normalizedWidth = normalizeColumnWidth(col.width);
81
- if (normalizedWidth !== undefined)
82
- return normalizedWidth;
83
- let max = visibleLength(col.header ?? '');
84
- for (const row of rows) {
85
- const cell = row[i] ?? '';
86
- max = Math.max(max, visibleLength(cell));
87
- }
88
- return max;
89
- });
90
- const h = '\u2500';
91
- const v = '\u2502';
92
- const hLine = (left, mid, right) => {
93
- const segments = colWidths.map((w) => h.repeat(w + 2));
94
- return bc(left + segments.join(mid) + right);
95
- };
96
- const top = hLine('\u250c', '\u252c', '\u2510');
97
- const midSep = hLine('\u251c', '\u253c', '\u2524');
98
- const bottom = hLine('\u2514', '\u2534', '\u2518');
99
- const hdrBgFill = makeBgFill(options.headerBgToken, ctx);
100
- const headerLinesByColumn = columns.map((col, i) => formatCellLines(ctx.style.styled(headerToken, col.header ?? ''), colWidths[i], overflow, col.width !== undefined));
101
- const headerHeight = headerLinesByColumn.reduce((max, lines) => Math.max(max, lines.length), 1);
102
- const headerRows = Array.from({ length: headerHeight }, (_row, rowIndex) => {
103
- const headerCells = columns.map((_col, i) => ' ' + padRight(headerLinesByColumn[i][rowIndex] ?? '', colWidths[i]) + ' ');
104
- const rawHeaderRow = bc(v) + headerCells.join(bc(v)) + bc(v);
105
- return hdrBgFill ? hdrBgFill(rawHeaderRow) : rawHeaderRow;
106
- });
107
- const dataRows = rows.flatMap((row) => {
108
- const cellLines = colWidths.map((w, i) => formatCellLines(row[i] ?? '', w, overflow, columns[i]?.width !== undefined));
109
- const rowHeight = cellLines.reduce((max, lines) => Math.max(max, lines.length), 1);
110
- return Array.from({ length: rowHeight }, (_line, lineIndex) => {
111
- const cells = colWidths.map((w, i) => ' ' + padRight(cellLines[i][lineIndex] ?? '', w) + ' ');
112
- return bc(v) + cells.join(bc(v)) + bc(v);
113
- });
114
- });
115
- const lines = [top, ...headerRows, midSep, ...dataRows, bottom];
116
- return lines.join('\n');
117
- },
691
+ interactive: () => renderVisualTable(options, ctx, tableData, options.variant ?? 'box'),
118
692
  }, options);
119
693
  }
120
694
  //# sourceMappingURL=table.js.map