@promptctl/rich-js 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +243 -0
- package/dist/core/animation.js +375 -0
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +40 -24
- package/dist/core/cells.js +151 -66
- package/dist/core/clock.d.ts +2 -2
- package/dist/core/clock.js +6 -4
- package/dist/core/color.d.ts +27 -10
- package/dist/core/color.js +21 -29
- package/dist/core/console.d.ts +28 -17
- package/dist/core/console.js +48 -43
- package/dist/core/easing.d.ts +140 -10
- package/dist/core/easing.js +328 -50
- package/dist/core/emoji.d.ts +1 -1
- package/dist/core/emoji.js +1 -2
- package/dist/core/export-html.js +2 -1
- package/dist/core/export-lines.js +3 -3
- package/dist/core/highlighter.d.ts +9 -8
- package/dist/core/highlighter.js +3 -2
- package/dist/core/json.js +3 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +3 -3
- package/dist/core/memo.d.ts +11 -2
- package/dist/core/memo.js +16 -0
- package/dist/core/oklch.js +17 -6
- package/dist/core/osc8.d.ts +1 -1
- package/dist/core/osc8.js +4 -5
- package/dist/core/paint.d.ts +2 -1
- package/dist/core/paint.js +4 -3
- package/dist/core/pattern.d.ts +21 -0
- package/dist/core/pattern.js +21 -0
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +11 -12
- package/dist/core/protocol.d.ts +136 -21
- package/dist/core/protocol.js +203 -16
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +27 -6
- package/dist/core/segment.d.ts +31 -6
- package/dist/core/segment.js +68 -53
- package/dist/core/spinnerData.d.ts +1 -1
- package/dist/core/state.d.ts +53 -0
- package/dist/core/state.js +82 -0
- package/dist/core/strip.d.ts +11 -13
- package/dist/core/strip.js +126 -113
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +74 -34
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +133 -133
- package/dist/core/timeline.d.ts +109 -0
- package/dist/core/timeline.js +180 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +71 -0
- package/dist/core/view.d.ts +92 -0
- package/dist/core/view.js +83 -0
- package/dist/host/app.d.ts +49 -21
- package/dist/host/app.js +72 -38
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +30 -15
- package/dist/index.js +20 -6
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +1 -1
- package/dist/renderables/columns.js +3 -3
- package/dist/renderables/constrain.js +2 -1
- package/dist/renderables/effect.d.ts +155 -92
- package/dist/renderables/effect.js +203 -136
- package/dist/renderables/effects.d.ts +137 -24
- package/dist/renderables/effects.js +276 -113
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +18 -9
- package/dist/renderables/flexStrip.js +2 -1
- package/dist/renderables/frames.d.ts +45 -43
- package/dist/renderables/frames.js +57 -45
- package/dist/renderables/glyph-effect.d.ts +158 -0
- package/dist/renderables/glyph-effect.js +237 -0
- package/dist/renderables/glyph-effects.d.ts +147 -0
- package/dist/renderables/glyph-effects.js +283 -0
- package/dist/renderables/group.d.ts +1 -1
- package/dist/renderables/group.js +2 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +10 -9
- package/dist/renderables/layout.js +42 -35
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +2 -2
- package/dist/renderables/list-view.js +11 -8
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +89 -42
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1427 -0
- package/dist/renderables/markdown-url.d.ts +19 -0
- package/dist/renderables/markdown-url.js +314 -0
- package/dist/renderables/markdown.d.ts +5 -7
- package/dist/renderables/markdown.js +356 -683
- package/dist/renderables/padding.d.ts +7 -20
- package/dist/renderables/padding.js +29 -50
- package/dist/renderables/panel.d.ts +30 -32
- package/dist/renderables/panel.js +65 -109
- package/dist/renderables/progress.d.ts +83 -32
- package/dist/renderables/progress.js +186 -90
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +33 -10
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +25 -0
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +57 -24
- package/dist/renderables/spinner.js +101 -49
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +5 -3
- package/dist/renderables/table.d.ts +48 -46
- package/dist/renderables/table.js +170 -208
- package/dist/renderables/traceback.d.ts +3 -3
- package/dist/renderables/traceback.js +12 -10
- package/dist/renderables/tree.d.ts +28 -25
- package/dist/renderables/tree.js +108 -58
- package/dist/renderables/viewport.d.ts +77 -38
- package/dist/renderables/viewport.js +156 -72
- package/dist/template-bindings/animation-funcs.d.ts +78 -9
- package/dist/template-bindings/animation-funcs.js +132 -14
- package/dist/template-bindings/index.d.ts +5 -5
- package/dist/template-bindings/index.js +7 -6
- package/dist/themes/ramp.d.ts +62 -4
- package/dist/themes/ramp.js +82 -4
- package/dist/themes/registry.js +5 -7
- package/dist/widgets/button.js +3 -2
- package/dist/widgets/checkbox.js +4 -3
- package/dist/widgets/dropdown.d.ts +3 -1
- package/dist/widgets/dropdown.js +20 -15
- package/dist/widgets/event-router.js +4 -2
- package/dist/widgets/focus-manager.d.ts +23 -14
- package/dist/widgets/focus-manager.js +34 -33
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -1
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/observable.d.ts +23 -0
- package/dist/widgets/observable.js +43 -0
- package/dist/widgets/slider.d.ts +1 -1
- package/dist/widgets/slider.js +12 -3
- package/dist/widgets/static-item.d.ts +1 -1
- package/dist/widgets/static-item.js +6 -7
- package/dist/widgets/text-input.d.ts +53 -47
- package/dist/widgets/text-input.js +456 -399
- package/dist/widgets/themed-widget.js +2 -1
- package/dist/widgets/toggle.js +4 -3
- package/dist/widgets/types.d.ts +10 -4
- package/dist/widgets/types.js +8 -2
- package/dist/widgets/widget-app.d.ts +32 -6
- package/dist/widgets/widget-app.js +74 -27
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -21
- package/package.json +9 -5
|
@@ -7,10 +7,11 @@ import { NULL_STYLE } from "../core/style.js";
|
|
|
7
7
|
import { HEAVY_HEAD } from "../core/box.js";
|
|
8
8
|
import { RichText } from "../core/text.js";
|
|
9
9
|
import { EmbeddedText, embed, present } from "./embed.js";
|
|
10
|
-
import { normalizePadding } from "./padding.js";
|
|
10
|
+
import { Padding, normalizePadding } from "./padding.js";
|
|
11
11
|
import { exactWeights, ratioDistribute, ratioReduce } from "./ratio.js";
|
|
12
|
-
import { getStyle, stackedHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
12
|
+
import { getStyle, render, stackedHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
13
13
|
import { Measurement } from "../core/measure.js";
|
|
14
|
+
import { ListState, State } from "../core/state.js";
|
|
14
15
|
/** A `want` no budget can satisfy: the column takes every cell its weight earns. */
|
|
15
16
|
const UNBOUNDED = Number.MAX_SAFE_INTEGER;
|
|
16
17
|
/**
|
|
@@ -106,8 +107,7 @@ function distribute(total, demands) {
|
|
|
106
107
|
* column evenly (`ratio_reduce(excess, [1] * n, widths, widths)`). Wants that
|
|
107
108
|
* fit come back as they are.
|
|
108
109
|
*
|
|
109
|
-
*
|
|
110
|
-
* table paid it first and it never gives up. One divergence: the reference's
|
|
110
|
+
* One divergence: the reference's
|
|
111
111
|
* even cut runs once, and where a column too narrow to give its share leaves
|
|
112
112
|
* cells over, the table draws past its width. Here the cut repeats over the
|
|
113
113
|
* columns with cells left, so the table fits. Each round either fits or
|
|
@@ -118,7 +118,7 @@ function reduceToFit(budget, demands) {
|
|
|
118
118
|
const excess = () => wants.reduce((sum, want) => sum + want, 0) - budget;
|
|
119
119
|
for (let over = excess(); over > 0; over = excess()) {
|
|
120
120
|
// A column with nothing left to give, or that may not wrap, is out of the round.
|
|
121
|
-
const widths = wants.map((want, index) => (demands[index].wrapable && want > 0 ?
|
|
121
|
+
const widths = wants.map((want, index) => (demands[index].wrapable && want > 0 ? want : 0));
|
|
122
122
|
const widest = Math.max(...widths);
|
|
123
123
|
if (widest === 0)
|
|
124
124
|
break;
|
|
@@ -130,23 +130,6 @@ function reduceToFit(budget, demands) {
|
|
|
130
130
|
}
|
|
131
131
|
return wants;
|
|
132
132
|
}
|
|
133
|
-
/**
|
|
134
|
-
* The most padding every column can have alike: each side is granted
|
|
135
|
-
* `min(want, level)`, and `level` is the highest those grants fit `budget` at.
|
|
136
|
-
* A padding level is bought for every column at once or not at all, so a
|
|
137
|
-
* budget too small for everyone's padding leaves it uneven by no column.
|
|
138
|
-
*/
|
|
139
|
-
function padLevel(wants, budget) {
|
|
140
|
-
const sorted = [...wants].sort((a, b) => a - b);
|
|
141
|
-
let left = budget;
|
|
142
|
-
for (const [rank, want] of sorted.entries()) {
|
|
143
|
-
const sharing = sorted.length - rank;
|
|
144
|
-
if (want * sharing > left)
|
|
145
|
-
return Math.floor(left / sharing);
|
|
146
|
-
left -= want;
|
|
147
|
-
}
|
|
148
|
-
return sorted.at(-1) ?? 0;
|
|
149
|
-
}
|
|
150
133
|
function layoutTable(outerWidth, demands, frame) {
|
|
151
134
|
// Plain `number`, not the `CellCol` `cells` hands back: this budget is spent
|
|
152
135
|
// down by arithmetic, and arithmetic on a branded type produces a number.
|
|
@@ -160,117 +143,87 @@ function layoutTable(outerWidth, demands, frame) {
|
|
|
160
143
|
// flag for the pair, so half a frame is not a shape this renderer can emit.
|
|
161
144
|
const edge = budget >= frame.edge * 2 ? frame.edge : 0;
|
|
162
145
|
take(edge * 2);
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
while (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Padding is bought one side at a time, to one level across every seated
|
|
179
|
-
// column (`padLevel`), so no column is padded while another that wants the
|
|
180
|
-
// same padding goes without.
|
|
146
|
+
// Every column whose divider fits is drawn, in column order, at whatever
|
|
147
|
+
// width the division below leaves it — none at all included, as Rich draws a
|
|
148
|
+
// column it collapsed to nothing. The first divider the budget cannot hold
|
|
149
|
+
// is where the table ends; the rest are dropped, never drawn outside the
|
|
150
|
+
// frame.
|
|
151
|
+
let seated = demands.length > 0 ? 1 : 0;
|
|
152
|
+
while (seated < demands.length && budget >= frame.divider) {
|
|
153
|
+
take(frame.divider);
|
|
154
|
+
seated++;
|
|
155
|
+
}
|
|
156
|
+
// Every column's minimum is paid next, a declared width and a `noWrap`
|
|
157
|
+
// column alike, and a budget too small for them all cuts them in the
|
|
158
|
+
// reference's order, `reduceToFit`'s — never the table's frame. The rest of
|
|
159
|
+
// what each column wanted comes only after. A table whose columns all fit
|
|
160
|
+
// leaves this budget partly unspent.
|
|
181
161
|
const seatedDemands = demands.slice(0, seated);
|
|
182
|
-
const takeLevelled = (wants) => {
|
|
183
|
-
const level = padLevel(wants, budget);
|
|
184
|
-
return wants.map((want) => take(Math.min(want, level)));
|
|
185
|
-
};
|
|
186
|
-
const padLeft = takeLevelled(seatedDemands.map((demand) => demand.pad[0]));
|
|
187
|
-
const padRight = takeLevelled(seatedDemands.map((demand) => demand.pad[1]));
|
|
188
|
-
// Reservations are paid in column order, each already holding the cell the
|
|
189
|
-
// seating pass gave it. A budget too small to cover them all runs out
|
|
190
|
-
// partway, which costs the trailing columns their width but never costs the
|
|
191
|
-
// table its frame.
|
|
192
|
-
const reserved = seatedDemands.map((demand, index) => take(demand.reserved - seats[index]));
|
|
193
|
-
// What is left to apportion is the rest of what each column wanted: its
|
|
194
|
-
// minimum first, for every column, and only then the width past it. A table
|
|
195
|
-
// whose columns all fit leaves this budget partly unspent.
|
|
196
162
|
const spent = (granted) => granted.reduce((sum, width) => sum + width, 0);
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
const needed = reduceToFit(budget, seatedDemands.map((demand, index) => ({
|
|
200
|
-
want: Math.max(0, demand.minimum - base[index]),
|
|
201
|
-
kept: padLeft[index] + base[index] + padRight[index],
|
|
202
|
-
wrapable: demand.wrapable,
|
|
203
|
-
})));
|
|
204
|
-
const afterNeeded = holding(needed);
|
|
163
|
+
const holding = (...passes) => seatedDemands.map((_, index) => passes.reduce((sum, granted) => sum + granted[index], 0));
|
|
164
|
+
const needed = reduceToFit(budget, seatedDemands.map((demand) => ({ want: demand.minimum, wrapable: demand.wrapable })));
|
|
205
165
|
const beyond = distribute(budget - spent(needed), seatedDemands.map((demand, index) => ({
|
|
206
|
-
want: Math.max(0, demand.want -
|
|
166
|
+
want: Math.max(0, demand.want - needed[index]),
|
|
207
167
|
weight: demand.weight,
|
|
208
168
|
})));
|
|
209
169
|
const wanted = needed.map((width, index) => width + beyond[index]);
|
|
210
170
|
// The unspent part goes first to the columns with nothing to size to, each
|
|
211
171
|
// alike, up to its `fill` — the reference's `Measurement(1, max_width)`, a
|
|
212
172
|
// maximum of the whole offer that no content measured.
|
|
213
|
-
const afterWant = holding(wanted);
|
|
214
173
|
const filled = distribute(budget - spent(wanted), seatedDemands.map((demand, index) => ({
|
|
215
|
-
want: Math.max(0, demand.fill -
|
|
174
|
+
want: Math.max(0, demand.fill - wanted[index]),
|
|
216
175
|
weight: demand.fill > 0 ? 1 : 0,
|
|
217
176
|
})));
|
|
218
177
|
// Then to the ratio columns, by the reference's flexible split:
|
|
219
178
|
// `ratio_distribute(max_width - fixed_widths, ratios, flex_minimum)`. What
|
|
220
179
|
// is split is every cell the bounded columns leave, the cells the ratio
|
|
221
|
-
// columns already hold
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
// `min_width=12` ratio column five cells wide.
|
|
180
|
+
// columns already hold included; each is held to at least its want, which is
|
|
181
|
+
// its padding and its floor. The floor is the reference's `column.width or
|
|
182
|
+
// 1`, reading `minWidth` where there is no `width`, where the reference
|
|
183
|
+
// reads none and draws a `min_width=12` ratio column five cells wide.
|
|
226
184
|
// A ratio column is paid the rest of its part out of the budget, in column
|
|
227
185
|
// order, so a part the minimums push past what is left is cut there rather
|
|
228
186
|
// than drawn past the width.
|
|
229
187
|
const afterFill = holding(wanted, filled);
|
|
230
188
|
const flexible = seatedDemands.flatMap((demand, index) => (demand.ratio > 0 ? [index] : []));
|
|
231
|
-
const padded = (index, content) => padLeft[index] + content + padRight[index];
|
|
232
189
|
// `UNBOUNDED` holds an unbounded offer, as it does for the stretch below;
|
|
233
190
|
// it caps the whole split, so the total stays an integer a double holds.
|
|
234
191
|
const pool = budget - spent(wanted) - spent(filled);
|
|
235
192
|
let unshared = Math.min(pool, UNBOUNDED);
|
|
236
|
-
const parts = ratioDistribute(Math.min(pool + flexible.reduce((sum, index) => sum +
|
|
193
|
+
const parts = ratioDistribute(Math.min(pool + flexible.reduce((sum, index) => sum + afterFill[index], 0), UNBOUNDED), flexible.map((index) => seatedDemands[index].ratio), flexible.map((index) => seatedDemands[index].want));
|
|
237
194
|
const shared = seatedDemands.map(() => 0);
|
|
238
195
|
flexible.forEach((index, slot) => {
|
|
239
|
-
shared[index] = Math.min(Math.max(0, parts[slot] -
|
|
196
|
+
shared[index] = Math.min(Math.max(0, parts[slot] - afterFill[index]), unshared);
|
|
240
197
|
unshared -= shared[index];
|
|
241
198
|
});
|
|
242
199
|
// What the shares leave goes to the columns that stretch, by the width each
|
|
243
|
-
// already holds
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
200
|
+
// already holds: its natural width, since nothing is left over while any
|
|
201
|
+
// column is short, or the cap a fill stopped at. The split is the
|
|
202
|
+
// reference's `ratio_distribute(max_width - table_width, widths)`, rounding
|
|
203
|
+
// included; the columns it runs over are this port's. A table that does not
|
|
204
|
+
// expand has no column that stretches, and an empty list takes nothing,
|
|
205
|
+
// which is how it stays narrower than the width it was offered. The order is
|
|
206
|
+
// the reference's: Rich pads an expanding table only once
|
|
207
|
+
// `table_width < max_width`, never while it is collapsing a column.
|
|
251
208
|
const held = holding(wanted, filled, shared);
|
|
252
209
|
const stretchers = seatedDemands.flatMap((demand, index) => (demand.stretch ? [index] : []));
|
|
253
210
|
const stretches = ratioDistribute(
|
|
254
211
|
// An unbounded offer is held to `UNBOUNDED`, this model's own infinity, as
|
|
255
212
|
// `demandCells` holds a want: `Infinity` is not an integer to split.
|
|
256
|
-
Math.min(budget - spent(wanted) - spent(filled) - spent(shared), UNBOUNDED), stretchers.map((index) =>
|
|
213
|
+
Math.min(budget - spent(wanted) - spent(filled) - spent(shared), UNBOUNDED), stretchers.map((index) => held[index]));
|
|
257
214
|
// A table that does not fit gives back each column's `floor`: Rich
|
|
258
215
|
// re-measures a collapsed table's columns at the widths it gave them, and a
|
|
259
216
|
// column with nothing to draw measures 0 there however many cells the collapse
|
|
260
217
|
// left it. Those cells go unspent, as they do in Rich.
|
|
261
|
-
const natural = demands.reduce((sum, demand) => sum + demand.
|
|
218
|
+
const natural = demands.reduce((sum, demand) => sum + demand.want, frame.edge * 2 + Math.max(0, demands.length - 1) * frame.divider);
|
|
262
219
|
const squeezed = natural > outerWidth;
|
|
263
|
-
const
|
|
220
|
+
const cellWidths = held.map((width, index) => squeezed ? width - Math.min(width, seatedDemands[index].floor) : width);
|
|
264
221
|
stretchers.forEach((index, slot) => {
|
|
265
|
-
|
|
222
|
+
cellWidths[index] += stretches[slot];
|
|
266
223
|
});
|
|
267
|
-
const cellWidths = columns.map((width, index) => padded(index, width));
|
|
268
224
|
return {
|
|
269
225
|
edge,
|
|
270
226
|
divider: frame.divider,
|
|
271
|
-
padLeft,
|
|
272
|
-
padRight,
|
|
273
|
-
columns,
|
|
274
227
|
cellWidths,
|
|
275
228
|
totalWidth: edge * 2 +
|
|
276
229
|
Math.max(0, seated - 1) * frame.divider +
|
|
@@ -304,10 +257,9 @@ export class Column {
|
|
|
304
257
|
noWrap;
|
|
305
258
|
overflow;
|
|
306
259
|
highlight;
|
|
307
|
-
_cells;
|
|
308
260
|
constructor(options) {
|
|
309
|
-
this.
|
|
310
|
-
this.
|
|
261
|
+
this._header = new State(embed(options?.header));
|
|
262
|
+
this._footer = new State(embed(options?.footer));
|
|
311
263
|
this.headerStyle = options?.headerStyle ?? NULL_STYLE;
|
|
312
264
|
this.footerStyle = options?.footerStyle ?? NULL_STYLE;
|
|
313
265
|
this.style = options?.style ?? NULL_STYLE;
|
|
@@ -319,7 +271,6 @@ export class Column {
|
|
|
319
271
|
this.noWrap = options?.noWrap ?? false;
|
|
320
272
|
this.overflow = options?.overflow ?? "ellipsis";
|
|
321
273
|
this.highlight = options?.highlight ?? false;
|
|
322
|
-
this._cells = [];
|
|
323
274
|
}
|
|
324
275
|
/**
|
|
325
276
|
* The two stamped cells, stamped on assignment rather than at the constructor.
|
|
@@ -339,30 +290,30 @@ export class Column {
|
|
|
339
290
|
* which is why the setters take `undefined` rather than defaulting around it.
|
|
340
291
|
*/
|
|
341
292
|
get header() {
|
|
342
|
-
return this._header;
|
|
293
|
+
return this._header.get();
|
|
343
294
|
}
|
|
344
295
|
set header(content) {
|
|
345
|
-
this._header
|
|
296
|
+
this._header.set(embed(content));
|
|
346
297
|
}
|
|
347
298
|
get footer() {
|
|
348
|
-
return this._footer;
|
|
299
|
+
return this._footer.get();
|
|
349
300
|
}
|
|
350
301
|
set footer(content) {
|
|
351
|
-
this._footer
|
|
302
|
+
this._footer.set(embed(content));
|
|
352
303
|
}
|
|
353
304
|
get flexible() {
|
|
354
305
|
return columnShare(this) > 0;
|
|
355
306
|
}
|
|
356
|
-
/** @internal */
|
|
357
|
-
addCell(cell) {
|
|
358
|
-
this._cells.push(cell);
|
|
359
|
-
}
|
|
360
|
-
/** @internal */
|
|
361
|
-
getCells() {
|
|
362
|
-
return this._cells;
|
|
363
|
-
}
|
|
364
307
|
copy() {
|
|
365
|
-
|
|
308
|
+
return new Column({
|
|
309
|
+
// Shared, not copied, as a row's cells are: `embed` already copied a
|
|
310
|
+
// caller's `RichText`, what it hands back is never changed, and `embed`
|
|
311
|
+
// passes a renderable through.
|
|
312
|
+
header: this.header,
|
|
313
|
+
footer: this.footer,
|
|
314
|
+
headerStyle: this.headerStyle,
|
|
315
|
+
footerStyle: this.footerStyle,
|
|
316
|
+
style: this.style,
|
|
366
317
|
justify: this.justify,
|
|
367
318
|
width: this.width,
|
|
368
319
|
minWidth: this.minWidth,
|
|
@@ -372,19 +323,16 @@ export class Column {
|
|
|
372
323
|
overflow: this.overflow,
|
|
373
324
|
highlight: this.highlight,
|
|
374
325
|
});
|
|
375
|
-
col.headerStyle = this.headerStyle;
|
|
376
|
-
col.footerStyle = this.footerStyle;
|
|
377
|
-
col.style = this.style;
|
|
378
|
-
// Shared, not copied, as a row's cells are: `embed` already copied a
|
|
379
|
-
// caller's `RichText`, and what it hands back is never changed.
|
|
380
|
-
col._header = this._header;
|
|
381
|
-
col._footer = this._footer;
|
|
382
|
-
return col;
|
|
383
326
|
}
|
|
384
327
|
}
|
|
328
|
+
// A row's cell in column `index`: a column the row is short of draws a blank one.
|
|
329
|
+
function cellAt(row, index) {
|
|
330
|
+
return row.cells[index] ?? new EmbeddedText(undefined);
|
|
331
|
+
}
|
|
385
332
|
export class Table {
|
|
386
|
-
|
|
387
|
-
|
|
333
|
+
// Rich's Table grows by addColumn and addRow after it is built.
|
|
334
|
+
_columns = new ListState();
|
|
335
|
+
_rows = new ListState();
|
|
388
336
|
box;
|
|
389
337
|
_title;
|
|
390
338
|
_caption;
|
|
@@ -415,8 +363,6 @@ export class Table {
|
|
|
415
363
|
return this._caption;
|
|
416
364
|
}
|
|
417
365
|
constructor(options) {
|
|
418
|
-
this._columns = [];
|
|
419
|
-
this._rows = [];
|
|
420
366
|
this.box = options?.box !== undefined ? options.box : HEAVY_HEAD;
|
|
421
367
|
// An empty title draws no row, as the reference's `if self.title` draws
|
|
422
368
|
// none; markup that styles nothing is a title, and draws a blank one.
|
|
@@ -449,10 +395,10 @@ export class Table {
|
|
|
449
395
|
this.highlight = options?.highlight ?? false;
|
|
450
396
|
}
|
|
451
397
|
get columns() {
|
|
452
|
-
return this._columns;
|
|
398
|
+
return this._columns.get();
|
|
453
399
|
}
|
|
454
400
|
get rowCount() {
|
|
455
|
-
return this._rows.length;
|
|
401
|
+
return this._rows.get().length;
|
|
456
402
|
}
|
|
457
403
|
addColumn(header, options) {
|
|
458
404
|
const col = new Column({ ...options, header: header ?? options?.header, highlight: options?.highlight ?? this.highlight });
|
|
@@ -481,20 +427,18 @@ export class Table {
|
|
|
481
427
|
// behind. [LAW:no-ambient-temporal-coupling]
|
|
482
428
|
const resolved = cells.map(embed);
|
|
483
429
|
// Auto-create columns if needed
|
|
484
|
-
while (this._columns.length < resolved.length) {
|
|
430
|
+
while (this._columns.get().length < resolved.length) {
|
|
485
431
|
this.addColumn();
|
|
486
432
|
}
|
|
487
|
-
this._rows.push({ cells: resolved, endSection });
|
|
433
|
+
this._rows.push({ cells: resolved, endSection: new State(endSection) });
|
|
488
434
|
return this;
|
|
489
435
|
}
|
|
490
436
|
addSection() {
|
|
491
|
-
|
|
492
|
-
this._rows[this._rows.length - 1].endSection = true;
|
|
493
|
-
}
|
|
437
|
+
this._rows.get().at(-1)?.endSection.set(true);
|
|
494
438
|
return this;
|
|
495
439
|
}
|
|
496
440
|
*render(rawOptions) {
|
|
497
|
-
if (this._columns.length === 0) {
|
|
441
|
+
if (this._columns.get().length === 0) {
|
|
498
442
|
yield Segment.line();
|
|
499
443
|
return;
|
|
500
444
|
}
|
|
@@ -527,17 +471,17 @@ export class Table {
|
|
|
527
471
|
// once per cell, as the reference's `_get_cells` does per column.
|
|
528
472
|
const headerStyle = getStyle(options, this.headerStyle);
|
|
529
473
|
const footerStyle = getStyle(options, this.footerStyle);
|
|
530
|
-
const columnStyles = this._columns.map((c) => getStyle(options, c.style));
|
|
474
|
+
const columnStyles = this._columns.get().map((c) => getStyle(options, c.style));
|
|
531
475
|
const stripes = this.rowStyles.map((s) => getStyle(options, s));
|
|
532
476
|
// Each row's padding above and below, by where it stands among every row
|
|
533
477
|
// the table draws, header and footer included, as the reference counts them.
|
|
534
478
|
const firstBody = this.showHeader ? 1 : 0;
|
|
535
|
-
const lastRow = firstBody + this._rows.length + (this.showFooter ? 1 : 0) - 1;
|
|
479
|
+
const lastRow = firstBody + this._rows.get().length + (this.showFooter ? 1 : 0) - 1;
|
|
536
480
|
const rowPadding = (position) => this._rowPadding(position === 0, position === lastRow);
|
|
537
481
|
// Header row
|
|
538
482
|
if (this.showHeader) {
|
|
539
|
-
const headerCells = this._columns.map((c) => c.header);
|
|
540
|
-
const headerStyles = this._columns.map((c) => headerStyle.add(getStyle(options, c.headerStyle)));
|
|
483
|
+
const headerCells = this._columns.get().map((c) => c.header);
|
|
484
|
+
const headerStyles = this._columns.get().map((c) => headerStyle.add(getStyle(options, c.headerStyle)));
|
|
541
485
|
yield* this._renderRow(options, headerCells, headerStyles, NULL_STYLE, geometry, rowPadding(0), box, "head", border);
|
|
542
486
|
// Header separator
|
|
543
487
|
if (box) {
|
|
@@ -545,15 +489,15 @@ export class Table {
|
|
|
545
489
|
}
|
|
546
490
|
}
|
|
547
491
|
// Data rows
|
|
548
|
-
for (let rowIdx = 0; rowIdx < this._rows.length; rowIdx++) {
|
|
549
|
-
const row = this._rows[rowIdx];
|
|
550
|
-
const rowCells = this.
|
|
492
|
+
for (let rowIdx = 0; rowIdx < this._rows.get().length; rowIdx++) {
|
|
493
|
+
const row = this._rows.get()[rowIdx];
|
|
494
|
+
const rowCells = this._rowCells(row);
|
|
551
495
|
const rowStyle = stripes[rowIdx % stripes.length] ?? NULL_STYLE;
|
|
552
496
|
const cellStyles = columnStyles.map((s) => s.add(rowStyle));
|
|
553
497
|
yield* this._renderRow(options, rowCells, cellStyles, rowStyle, geometry, rowPadding(firstBody + rowIdx), box, "row", border);
|
|
554
498
|
// Row separator
|
|
555
|
-
const showSep = this.showLines || row.endSection;
|
|
556
|
-
if (showSep && box && rowIdx < this._rows.length - 1) {
|
|
499
|
+
const showSep = this.showLines || row.endSection.get();
|
|
500
|
+
if (showSep && box && rowIdx < this._rows.get().length - 1) {
|
|
557
501
|
yield* box.getRow(geometry.cellWidths, "row", border, edge);
|
|
558
502
|
}
|
|
559
503
|
}
|
|
@@ -562,8 +506,8 @@ export class Table {
|
|
|
562
506
|
if (box) {
|
|
563
507
|
yield* box.getRow(geometry.cellWidths, "foot", border, edge);
|
|
564
508
|
}
|
|
565
|
-
const footerCells = this._columns.map((c) => c.footer);
|
|
566
|
-
const footerStyles = this._columns.map((c) => footerStyle.add(getStyle(options, c.footerStyle)));
|
|
509
|
+
const footerCells = this._columns.get().map((c) => c.footer);
|
|
510
|
+
const footerStyles = this._columns.get().map((c) => footerStyle.add(getStyle(options, c.footerStyle)));
|
|
567
511
|
yield* this._renderRow(options, footerCells, footerStyles, NULL_STYLE, geometry, rowPadding(lastRow), box, "foot", border);
|
|
568
512
|
}
|
|
569
513
|
// Bottom border
|
|
@@ -678,23 +622,25 @@ export class Table {
|
|
|
678
622
|
* mode gets its own path through the width division.
|
|
679
623
|
*
|
|
680
624
|
* Cells are measured against the width left once the frame is paid, as the
|
|
681
|
-
* reference's `_measure_column` measures them. Padding
|
|
682
|
-
*
|
|
625
|
+
* reference's `_measure_column` measures them. Padding is part of every
|
|
626
|
+
* width the demand names, because the reference measures a cell wrapped in
|
|
627
|
+
* its `Padding`, and each sizing adds it where the reference does — a
|
|
628
|
+
* rowless column's minimum is the one width it never reaches. The floor is
|
|
629
|
+
* laid over whichever sizing the column has.
|
|
683
630
|
*/
|
|
684
631
|
_columnDemands(options, outerWidth) {
|
|
685
632
|
const frame = this._frame();
|
|
686
633
|
const inner = {
|
|
687
634
|
...options,
|
|
688
|
-
maxWidth: Math.max(0, outerWidth - frame.edge * 2 - frame.divider * Math.max(0, this._columns.length - 1)),
|
|
635
|
+
maxWidth: Math.max(0, outerWidth - frame.edge * 2 - frame.divider * Math.max(0, this._columns.get().length - 1)),
|
|
689
636
|
};
|
|
690
|
-
return this._columns.map((col, index) => {
|
|
691
|
-
const
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
return { ...sizing, minimum: sizing.minimum + floor, want: sizing.want + floor, pad, floor };
|
|
637
|
+
return this._columns.get().map((col, index) => {
|
|
638
|
+
const sizing = this._columnSizing(col, index, inner, this._columnPadding(index));
|
|
639
|
+
const floor = sizing.want === 0 ? 1 : 0;
|
|
640
|
+
return { ...sizing, minimum: sizing.minimum + floor, want: sizing.want + floor, floor };
|
|
695
641
|
});
|
|
696
642
|
}
|
|
697
|
-
_columnSizing(col, index, options) {
|
|
643
|
+
_columnSizing(col, index, options, [left, right]) {
|
|
698
644
|
// A flexible column takes its share of whatever the bounded columns
|
|
699
645
|
// leave; every other column asks for its natural width, whether or not a
|
|
700
646
|
// neighbour is flexible. The split is the reference's —
|
|
@@ -703,17 +649,27 @@ export class Table {
|
|
|
703
649
|
// here a ratio is honoured either way. Its bounded part is its floor —
|
|
704
650
|
// the reference's `column.width or 1`, reading `minWidth` where there is
|
|
705
651
|
// no `width` — bid for in cells like any content column, so a squeezed
|
|
706
|
-
// table still pays it. A `width` beside a `ratio` is that floor,
|
|
707
|
-
//
|
|
652
|
+
// table still pays it. A `width` beside a `ratio` is that floor, as the
|
|
653
|
+
// reference's `flex_minimum` reads it, held to the frame as a declared
|
|
654
|
+
// width is.
|
|
708
655
|
// [LAW:one-source-of-truth] `columnShare` decides first, so a column that
|
|
709
656
|
// `Column.flexible` calls elastic is one the division shares out.
|
|
657
|
+
const pad = left + right;
|
|
658
|
+
const padded = (content) => demandCells(content + pad);
|
|
659
|
+
// [LAW:single-enforcer] A width a column insists on, held to the width
|
|
660
|
+
// inside the frame — the reference's `with_maximum(max_width)` on a
|
|
661
|
+
// fixed-width column's measurement — so an even cut starts from what the
|
|
662
|
+
// table can hold. It holds a declared width and every ratio floor; a
|
|
663
|
+
// content column's `minWidth` is not held, as the reference clamps to
|
|
664
|
+
// `min_width` after `with_maximum`.
|
|
665
|
+
const held = (width) => Math.min(padded(demandCells(width)), options.maxWidth);
|
|
710
666
|
const share = columnShare(col);
|
|
711
667
|
if (share > 0) {
|
|
712
668
|
const floor = Math.max(1, demandCells(col.width ?? col.minWidth ?? 0));
|
|
669
|
+
const bid = held(floor);
|
|
713
670
|
return {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
want: floor,
|
|
671
|
+
minimum: bid,
|
|
672
|
+
want: bid,
|
|
717
673
|
weight: floor,
|
|
718
674
|
fill: 0,
|
|
719
675
|
ratio: share,
|
|
@@ -724,11 +680,10 @@ export class Table {
|
|
|
724
680
|
if (col.width !== undefined) {
|
|
725
681
|
// [LAW:single-enforcer] floored where it is parsed, the same rule
|
|
726
682
|
// `normalizePadding` applies to a negative padding side.
|
|
727
|
-
const
|
|
683
|
+
const bid = held(col.width);
|
|
728
684
|
return {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
want: declared,
|
|
685
|
+
minimum: bid,
|
|
686
|
+
want: bid,
|
|
732
687
|
weight: 0,
|
|
733
688
|
fill: 0,
|
|
734
689
|
ratio: 0,
|
|
@@ -736,8 +691,8 @@ export class Table {
|
|
|
736
691
|
wrapable: false,
|
|
737
692
|
};
|
|
738
693
|
}
|
|
739
|
-
const range = this._cellRange(col, index, options);
|
|
740
|
-
const natural = demandCells(this._bounded(col, range?.maximum ?? 1));
|
|
694
|
+
const range = this._cellRange(col, index, options, [left, right]);
|
|
695
|
+
const natural = demandCells(this._bounded(col, range?.maximum ?? padded(1), pad));
|
|
741
696
|
// `expand` is a stretch rather than a larger want: the column still
|
|
742
697
|
// competes for its natural width like any other, and only the cells left
|
|
743
698
|
// once every column has that are shared out. A larger want looks
|
|
@@ -749,25 +704,26 @@ export class Table {
|
|
|
749
704
|
// One deliberate divergence: Rich stretches declared-width columns as
|
|
750
705
|
// well, so `width: 6` under `expand` renders 21 cells wide — an option
|
|
751
706
|
// quietly meaning something else, the defect rich-justify-0cr exists to
|
|
752
|
-
// remove. A
|
|
707
|
+
// remove. A declared width's stretch is zero above.
|
|
753
708
|
//
|
|
754
709
|
// A column with no cell at all — no rows, and neither header nor footer
|
|
755
710
|
// drawn — has nothing to size to, which is not sizing to nothing. The
|
|
756
711
|
// reference measures it `Measurement(1, max_width)`: one cell of content,
|
|
757
712
|
// and a maximum of the whole offer, so a rowless table fills it. Reading
|
|
758
|
-
// "no cells" as zero sized it to a two-cell box instead. The one cell
|
|
759
|
-
// its want, so a `minWidth` is paid like any column's,
|
|
760
|
-
// its `fill`, held to its `maxWidth`.
|
|
713
|
+
// "no cells" as zero sized it to a two-cell box instead. The one cell and
|
|
714
|
+
// its padding are its want, so a `minWidth` is paid like any column's,
|
|
715
|
+
// and the offer is its `fill`, held to its `maxWidth`. Its minimum is the
|
|
716
|
+
// reference's bare 1, unpadded: `_measure_column` adds the padding only to
|
|
717
|
+
// a declared width and to the `min_width`/`max_width` clamp.
|
|
761
718
|
//
|
|
762
719
|
// A column that may not wrap has no narrower width than its natural one
|
|
763
720
|
// that keeps its content whole — the reference leaves `no_wrap` columns out
|
|
764
721
|
// of the ones it collapses.
|
|
765
722
|
return {
|
|
766
|
-
|
|
767
|
-
minimum: col.noWrap ? natural : Math.min(natural, demandCells(this._bounded(col, range?.minimum ?? 1))),
|
|
723
|
+
minimum: col.noWrap ? natural : Math.min(natural, demandCells(this._bounded(col, range?.minimum ?? 1, pad))),
|
|
768
724
|
want: natural,
|
|
769
725
|
weight: natural,
|
|
770
|
-
fill: range === undefined ? demandCells(this._bounded(col, UNBOUNDED)) : 0,
|
|
726
|
+
fill: range === undefined ? demandCells(this._bounded(col, UNBOUNDED, pad)) : 0,
|
|
771
727
|
ratio: 0,
|
|
772
728
|
stretch: this.expand,
|
|
773
729
|
wrapable: !col.noWrap,
|
|
@@ -784,7 +740,7 @@ export class Table {
|
|
|
784
740
|
_columnPadding(index) {
|
|
785
741
|
const [, right, , left] = this.padding;
|
|
786
742
|
const first = index === 0;
|
|
787
|
-
const last = index === this._columns.length - 1;
|
|
743
|
+
const last = index === this._columns.get().length - 1;
|
|
788
744
|
const collapsed = this.collapsePadding && !first ? Math.max(0, left - right) : left;
|
|
789
745
|
return [!this.padEdge && first ? 0 : collapsed, !this.padEdge && last ? 0 : right];
|
|
790
746
|
}
|
|
@@ -793,6 +749,9 @@ export class Table {
|
|
|
793
749
|
const collapsed = this.collapsePadding && !last ? Math.max(0, top - bottom) : bottom;
|
|
794
750
|
return [!this.padEdge && first ? 0 : top, !this.padEdge && last ? 0 : collapsed];
|
|
795
751
|
}
|
|
752
|
+
_rowCells(row) {
|
|
753
|
+
return this._columns.get().map((_, index) => cellAt(row, index));
|
|
754
|
+
}
|
|
796
755
|
/**
|
|
797
756
|
* Every cell column `index` draws, in draw order.
|
|
798
757
|
*
|
|
@@ -807,19 +766,20 @@ export class Table {
|
|
|
807
766
|
*_columnCells(col, index) {
|
|
808
767
|
if (this.showHeader)
|
|
809
768
|
yield col.header;
|
|
810
|
-
for (const row of this._rows)
|
|
811
|
-
yield row
|
|
769
|
+
for (const row of this._rows.get())
|
|
770
|
+
yield cellAt(row, index);
|
|
812
771
|
if (this.showFooter)
|
|
813
772
|
yield col.footer;
|
|
814
773
|
}
|
|
815
774
|
/**
|
|
816
|
-
* The widest any cell in a column draws,
|
|
817
|
-
* without cutting one — the reference's
|
|
818
|
-
* when the column has no cell to measure.
|
|
819
|
-
*
|
|
820
|
-
* same answer.
|
|
775
|
+
* The widest any cell in a column draws, its padding included, and the
|
|
776
|
+
* narrowest the column can be without cutting one — the reference's
|
|
777
|
+
* `_measure_column` — or `undefined` when the column has no cell to measure.
|
|
778
|
+
* A column whose cells draw nothing measures its padding — a gutter asks for
|
|
779
|
+
* that and nothing else — which is not the same answer.
|
|
780
|
+
* [LAW:types-are-the-program]
|
|
821
781
|
*/
|
|
822
|
-
_cellRange(col, index, options) {
|
|
782
|
+
_cellRange(col, index, options, [left, right]) {
|
|
823
783
|
let range;
|
|
824
784
|
for (const cell of this._columnCells(col, index)) {
|
|
825
785
|
// The stamped cell, so the width a column asks for is the width its text
|
|
@@ -828,7 +788,10 @@ export class Table {
|
|
|
828
788
|
// [LAW:one-source-of-truth] Measured as the cell measures itself, so a
|
|
829
789
|
// multi-line cell asks for its widest line and a `Panel` for its frame;
|
|
830
790
|
// one that cannot measure itself asks for every cell there is, as in Rich.
|
|
831
|
-
|
|
791
|
+
// Wrapped in its padding, as the reference's `_get_cells` hands it to
|
|
792
|
+
// `_measure_column`, so a cell measures what it draws and never past the
|
|
793
|
+
// width it was offered.
|
|
794
|
+
const measured = Measurement.get(options, new Padding(cell, [0, right, 0, left]));
|
|
832
795
|
range = {
|
|
833
796
|
minimum: Math.max(range?.minimum ?? 0, measured.minimum),
|
|
834
797
|
maximum: Math.max(range?.maximum ?? 0, measured.maximum),
|
|
@@ -837,18 +800,20 @@ export class Table {
|
|
|
837
800
|
return range;
|
|
838
801
|
}
|
|
839
802
|
/**
|
|
840
|
-
* A natural width held to the column's own `minWidth`/`maxWidth
|
|
803
|
+
* A natural width held to the column's own `minWidth`/`maxWidth`, each
|
|
804
|
+
* widened by the column's `pad`, as the reference clamps a padded
|
|
805
|
+
* measurement to `min_width + padding_width`.
|
|
841
806
|
*
|
|
842
807
|
* A bound is a count of cells, read by the rule every width is read by: NaN
|
|
843
808
|
* and a negative are zero cells, so a NaN floor bounds nothing and a NaN
|
|
844
|
-
* ceiling
|
|
809
|
+
* ceiling holds the column to its padding alone, as `width: NaN` does. A bare `Math.max` returns
|
|
845
810
|
* NaN instead, and the column vanishes. [LAW:one-source-of-truth]
|
|
846
811
|
*/
|
|
847
|
-
_bounded(col, natural) {
|
|
848
|
-
return Math.min(Math.max(natural, cells(col.minWidth
|
|
812
|
+
_bounded(col, natural, pad) {
|
|
813
|
+
return Math.min(Math.max(natural, col.minWidth === undefined ? 0 : cells(col.minWidth) + pad), cells(col.maxWidth ?? Infinity) + pad);
|
|
849
814
|
}
|
|
850
815
|
*_renderRow(options, cells, styles, rowStyle, geometry, [top, bottom], box, level, border) {
|
|
851
|
-
const {
|
|
816
|
+
const { cellWidths } = geometry;
|
|
852
817
|
// [LAW:dataflow-not-control-flow] Header, body and footer share this path;
|
|
853
818
|
// the level crosses as a value the box answers with glyphs, not a branch.
|
|
854
819
|
const frame = box?.getContentChars(level);
|
|
@@ -859,39 +824,36 @@ export class Table {
|
|
|
859
824
|
const divider = frame && (frame.vertical.trim() === ""
|
|
860
825
|
? new Segment(frame.vertical, rowStyle.backgroundStyle.add(border))
|
|
861
826
|
: new Segment(frame.vertical, border));
|
|
862
|
-
// Render each cell
|
|
863
|
-
//
|
|
827
|
+
// Render each cell at the width the geometry gave its column. Columns the
|
|
828
|
+
// width could not seat are absent from `cellWidths` and so are never
|
|
864
829
|
// rendered at all.
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
const
|
|
868
|
-
|
|
830
|
+
const cellLines = cellWidths.map((cellWidth, index) => {
|
|
831
|
+
const col = this._columns.get()[index];
|
|
832
|
+
const [left, right] = this._columnPadding(index);
|
|
833
|
+
// [LAW:single-enforcer] The padding on every side is part of the cell,
|
|
834
|
+
// drawn by `Padding` as the reference's `_get_cells` wraps each cell in
|
|
835
|
+
// one: its sides are paid before its content, so a cell too narrow for
|
|
836
|
+
// them draws as blanks, and a content line sits between its left and
|
|
837
|
+
// right blanks.
|
|
838
|
+
const padded = new Padding(cells[index] ?? embed(""), [top, right, bottom, left]);
|
|
869
839
|
// The render's own options with the column's canvas laid over them, so a
|
|
870
840
|
// cell resolves its style names against the same theme as the table. A
|
|
871
841
|
// table stacks its cells, so each is handed the table's rows as a
|
|
872
842
|
// ceiling, never a region to fill — the `Height` contract's
|
|
873
843
|
// `stackedHeight`, where the reference hands a cell `height=None`.
|
|
874
844
|
// Highlighted as its column says, Rich's `highlight=column.highlight`.
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
const left = blank(padLeft[index]);
|
|
888
|
-
const right = blank(padRight[index]);
|
|
889
|
-
const padLine = () => blank(paddedWidths[index]);
|
|
890
|
-
return [
|
|
891
|
-
...Array.from({ length: top }, padLine),
|
|
892
|
-
...Segment.splitAndCropLines(segs, cellWidth).map((line) => [...left, ...line, ...right]),
|
|
893
|
-
...Array.from({ length: bottom }, padLine),
|
|
894
|
-
];
|
|
845
|
+
// A cell left no column draws nothing at all, its padding included, as
|
|
846
|
+
// the reference's `render_lines` draws nothing under one column.
|
|
847
|
+
const cellOptions = {
|
|
848
|
+
...options,
|
|
849
|
+
highlight: col.highlight,
|
|
850
|
+
maxWidth: cellWidth,
|
|
851
|
+
justify: col.justify,
|
|
852
|
+
overflow: col.overflow,
|
|
853
|
+
noWrap: col.noWrap,
|
|
854
|
+
height: stackedHeight(options.height),
|
|
855
|
+
};
|
|
856
|
+
return Segment.splitLines(render(padded, cellOptions));
|
|
895
857
|
});
|
|
896
858
|
const maxLines = cellLines.reduce((most, lines) => Math.max(most, lines.length), 1);
|
|
897
859
|
// How many blank lines stand above each cell's content. A header sits on
|
|
@@ -902,14 +864,14 @@ export class Table {
|
|
|
902
864
|
if (frame && geometry.edge === 1) {
|
|
903
865
|
yield new Segment(frame.left, border);
|
|
904
866
|
}
|
|
905
|
-
for (let colIdx = 0; colIdx <
|
|
867
|
+
for (let colIdx = 0; colIdx < cellWidths.length; colIdx++) {
|
|
906
868
|
if (colIdx > 0 && divider) {
|
|
907
869
|
yield divider;
|
|
908
870
|
}
|
|
909
871
|
const style = styles[colIdx];
|
|
910
872
|
// A cell with fewer lines than the row contributes blanks, so every
|
|
911
873
|
// column spans the same number of rows and the frame stays rectangular.
|
|
912
|
-
const line = cellLines[colIdx][lineIdx - drop[colIdx]] ?? blank(
|
|
874
|
+
const line = cellLines[colIdx][lineIdx - drop[colIdx]] ?? blank(cellWidths[colIdx]);
|
|
913
875
|
// The cell's style is the base its content's own spans layer over, and
|
|
914
876
|
// it covers the whole cell — its padding and its blank lines too, as
|
|
915
877
|
// the reference's does — so a background fills the column rather than
|
|
@@ -959,7 +921,7 @@ export class Table {
|
|
|
959
921
|
// lines, and `end="!!"` runs on into the top border there too. Splitting
|
|
960
922
|
// and re-ending each line moved a non-newline `end` onto the title's own
|
|
961
923
|
// row, wider than the table.
|
|
962
|
-
yield*
|
|
924
|
+
yield* render(source, {
|
|
963
925
|
...options,
|
|
964
926
|
maxWidth: tableWidth,
|
|
965
927
|
justify,
|