@promptctl/rich-js 0.24.0 → 0.24.1
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/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/animation.d.ts +78 -0
- package/dist/core/animation.js +107 -0
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +17 -11
- package/dist/core/cells.js +100 -25
- package/dist/core/console.js +5 -5
- package/dist/core/easing.d.ts +110 -10
- package/dist/core/easing.js +269 -36
- package/dist/core/json.js +2 -1
- package/dist/core/measure.js +2 -2
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +2 -2
- package/dist/core/protocol.d.ts +73 -19
- package/dist/core/protocol.js +56 -15
- package/dist/core/render.js +2 -1
- package/dist/core/segment.d.ts +18 -1
- package/dist/core/segment.js +45 -44
- package/dist/core/strip.d.ts +3 -2
- package/dist/core/strip.js +11 -7
- package/dist/core/style.js +17 -6
- package/dist/core/text.js +22 -14
- package/dist/core/timeline.d.ts +84 -0
- package/dist/core/timeline.js +144 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +69 -0
- package/dist/host/app.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +13 -4
- package/dist/renderables/align.d.ts +3 -2
- package/dist/renderables/align.js +3 -0
- package/dist/renderables/columns.d.ts +3 -2
- package/dist/renderables/columns.js +7 -2
- package/dist/renderables/constrain.d.ts +3 -2
- package/dist/renderables/constrain.js +5 -1
- package/dist/renderables/effect.d.ts +43 -58
- package/dist/renderables/effect.js +58 -64
- package/dist/renderables/effects.d.ts +16 -14
- package/dist/renderables/effects.js +64 -45
- package/dist/renderables/embed.js +3 -2
- package/dist/renderables/flexStrip.d.ts +3 -2
- package/dist/renderables/flexStrip.js +6 -1
- package/dist/renderables/frames.d.ts +40 -19
- package/dist/renderables/frames.js +48 -21
- package/dist/renderables/group.d.ts +3 -2
- package/dist/renderables/group.js +5 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +3 -2
- package/dist/renderables/layout.js +15 -2
- package/dist/renderables/list-view.d.ts +3 -2
- package/dist/renderables/list-view.js +12 -5
- package/dist/renderables/live.js +2 -2
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1421 -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 +8 -20
- package/dist/renderables/padding.js +32 -50
- package/dist/renderables/panel.d.ts +26 -26
- package/dist/renderables/panel.js +67 -103
- package/dist/renderables/progress.d.ts +4 -2
- package/dist/renderables/progress.js +11 -2
- package/dist/renderables/progressBar.js +2 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +24 -0
- package/dist/renderables/spinner.d.ts +26 -14
- package/dist/renderables/spinner.js +49 -37
- package/dist/renderables/syntax.js +2 -1
- package/dist/renderables/table.d.ts +19 -11
- package/dist/renderables/table.js +140 -156
- package/dist/renderables/traceback.d.ts +1 -1
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +6 -5
- package/dist/renderables/tree.js +11 -7
- package/dist/renderables/viewport.d.ts +13 -5
- package/dist/renderables/viewport.js +41 -20
- package/dist/template-bindings/animation-funcs.js +2 -2
- package/dist/template-bindings/index.js +2 -1
- package/dist/themes/ramp.d.ts +18 -3
- package/dist/themes/ramp.js +23 -2
- package/dist/widgets/focus-manager.d.ts +21 -14
- package/dist/widgets/focus-manager.js +28 -29
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/static-item.d.ts +4 -3
- package/dist/widgets/static-item.js +9 -7
- package/dist/widgets/text-input.d.ts +28 -18
- package/dist/widgets/text-input.js +144 -101
- package/dist/widgets/widget-app.d.ts +19 -3
- package/dist/widgets/widget-app.js +39 -21
- package/dist/widgets/widget-base.js +0 -3
- package/package.json +2 -5
|
@@ -7,9 +7,9 @@ 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
14
|
/** A `want` no budget can satisfy: the column takes every cell its weight earns. */
|
|
15
15
|
const UNBOUNDED = Number.MAX_SAFE_INTEGER;
|
|
@@ -106,8 +106,7 @@ function distribute(total, demands) {
|
|
|
106
106
|
* column evenly (`ratio_reduce(excess, [1] * n, widths, widths)`). Wants that
|
|
107
107
|
* fit come back as they are.
|
|
108
108
|
*
|
|
109
|
-
*
|
|
110
|
-
* table paid it first and it never gives up. One divergence: the reference's
|
|
109
|
+
* One divergence: the reference's
|
|
111
110
|
* even cut runs once, and where a column too narrow to give its share leaves
|
|
112
111
|
* cells over, the table draws past its width. Here the cut repeats over the
|
|
113
112
|
* columns with cells left, so the table fits. Each round either fits or
|
|
@@ -118,7 +117,7 @@ function reduceToFit(budget, demands) {
|
|
|
118
117
|
const excess = () => wants.reduce((sum, want) => sum + want, 0) - budget;
|
|
119
118
|
for (let over = excess(); over > 0; over = excess()) {
|
|
120
119
|
// 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 ?
|
|
120
|
+
const widths = wants.map((want, index) => (demands[index].wrapable && want > 0 ? want : 0));
|
|
122
121
|
const widest = Math.max(...widths);
|
|
123
122
|
if (widest === 0)
|
|
124
123
|
break;
|
|
@@ -130,23 +129,6 @@ function reduceToFit(budget, demands) {
|
|
|
130
129
|
}
|
|
131
130
|
return wants;
|
|
132
131
|
}
|
|
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
132
|
function layoutTable(outerWidth, demands, frame) {
|
|
151
133
|
// Plain `number`, not the `CellCol` `cells` hands back: this budget is spent
|
|
152
134
|
// down by arithmetic, and arithmetic on a branded type produces a number.
|
|
@@ -160,117 +142,87 @@ function layoutTable(outerWidth, demands, frame) {
|
|
|
160
142
|
// flag for the pair, so half a frame is not a shape this renderer can emit.
|
|
161
143
|
const edge = budget >= frame.edge * 2 ? frame.edge : 0;
|
|
162
144
|
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.
|
|
145
|
+
// Every column whose divider fits is drawn, in column order, at whatever
|
|
146
|
+
// width the division below leaves it — none at all included, as Rich draws a
|
|
147
|
+
// column it collapsed to nothing. The first divider the budget cannot hold
|
|
148
|
+
// is where the table ends; the rest are dropped, never drawn outside the
|
|
149
|
+
// frame.
|
|
150
|
+
let seated = demands.length > 0 ? 1 : 0;
|
|
151
|
+
while (seated < demands.length && budget >= frame.divider) {
|
|
152
|
+
take(frame.divider);
|
|
153
|
+
seated++;
|
|
154
|
+
}
|
|
155
|
+
// Every column's minimum is paid next, a declared width and a `noWrap`
|
|
156
|
+
// column alike, and a budget too small for them all cuts them in the
|
|
157
|
+
// reference's order, `reduceToFit`'s — never the table's frame. The rest of
|
|
158
|
+
// what each column wanted comes only after. A table whose columns all fit
|
|
159
|
+
// leaves this budget partly unspent.
|
|
181
160
|
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
161
|
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);
|
|
162
|
+
const holding = (...passes) => seatedDemands.map((_, index) => passes.reduce((sum, granted) => sum + granted[index], 0));
|
|
163
|
+
const needed = reduceToFit(budget, seatedDemands.map((demand) => ({ want: demand.minimum, wrapable: demand.wrapable })));
|
|
205
164
|
const beyond = distribute(budget - spent(needed), seatedDemands.map((demand, index) => ({
|
|
206
|
-
want: Math.max(0, demand.want -
|
|
165
|
+
want: Math.max(0, demand.want - needed[index]),
|
|
207
166
|
weight: demand.weight,
|
|
208
167
|
})));
|
|
209
168
|
const wanted = needed.map((width, index) => width + beyond[index]);
|
|
210
169
|
// The unspent part goes first to the columns with nothing to size to, each
|
|
211
170
|
// alike, up to its `fill` — the reference's `Measurement(1, max_width)`, a
|
|
212
171
|
// maximum of the whole offer that no content measured.
|
|
213
|
-
const afterWant = holding(wanted);
|
|
214
172
|
const filled = distribute(budget - spent(wanted), seatedDemands.map((demand, index) => ({
|
|
215
|
-
want: Math.max(0, demand.fill -
|
|
173
|
+
want: Math.max(0, demand.fill - wanted[index]),
|
|
216
174
|
weight: demand.fill > 0 ? 1 : 0,
|
|
217
175
|
})));
|
|
218
176
|
// Then to the ratio columns, by the reference's flexible split:
|
|
219
177
|
// `ratio_distribute(max_width - fixed_widths, ratios, flex_minimum)`. What
|
|
220
178
|
// 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.
|
|
179
|
+
// columns already hold included; each is held to at least its want, which is
|
|
180
|
+
// its padding and its floor. The floor is the reference's `column.width or
|
|
181
|
+
// 1`, reading `minWidth` where there is no `width`, where the reference
|
|
182
|
+
// reads none and draws a `min_width=12` ratio column five cells wide.
|
|
226
183
|
// A ratio column is paid the rest of its part out of the budget, in column
|
|
227
184
|
// order, so a part the minimums push past what is left is cut there rather
|
|
228
185
|
// than drawn past the width.
|
|
229
186
|
const afterFill = holding(wanted, filled);
|
|
230
187
|
const flexible = seatedDemands.flatMap((demand, index) => (demand.ratio > 0 ? [index] : []));
|
|
231
|
-
const padded = (index, content) => padLeft[index] + content + padRight[index];
|
|
232
188
|
// `UNBOUNDED` holds an unbounded offer, as it does for the stretch below;
|
|
233
189
|
// it caps the whole split, so the total stays an integer a double holds.
|
|
234
190
|
const pool = budget - spent(wanted) - spent(filled);
|
|
235
191
|
let unshared = Math.min(pool, UNBOUNDED);
|
|
236
|
-
const parts = ratioDistribute(Math.min(pool + flexible.reduce((sum, index) => sum +
|
|
192
|
+
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
193
|
const shared = seatedDemands.map(() => 0);
|
|
238
194
|
flexible.forEach((index, slot) => {
|
|
239
|
-
shared[index] = Math.min(Math.max(0, parts[slot] -
|
|
195
|
+
shared[index] = Math.min(Math.max(0, parts[slot] - afterFill[index]), unshared);
|
|
240
196
|
unshared -= shared[index];
|
|
241
197
|
});
|
|
242
198
|
// What the shares leave goes to the columns that stretch, by the width each
|
|
243
|
-
// already holds
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
199
|
+
// already holds: its natural width, since nothing is left over while any
|
|
200
|
+
// column is short, or the cap a fill stopped at. The split is the
|
|
201
|
+
// reference's `ratio_distribute(max_width - table_width, widths)`, rounding
|
|
202
|
+
// included; the columns it runs over are this port's. A table that does not
|
|
203
|
+
// expand has no column that stretches, and an empty list takes nothing,
|
|
204
|
+
// which is how it stays narrower than the width it was offered. The order is
|
|
205
|
+
// the reference's: Rich pads an expanding table only once
|
|
206
|
+
// `table_width < max_width`, never while it is collapsing a column.
|
|
251
207
|
const held = holding(wanted, filled, shared);
|
|
252
208
|
const stretchers = seatedDemands.flatMap((demand, index) => (demand.stretch ? [index] : []));
|
|
253
209
|
const stretches = ratioDistribute(
|
|
254
210
|
// An unbounded offer is held to `UNBOUNDED`, this model's own infinity, as
|
|
255
211
|
// `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) =>
|
|
212
|
+
Math.min(budget - spent(wanted) - spent(filled) - spent(shared), UNBOUNDED), stretchers.map((index) => held[index]));
|
|
257
213
|
// A table that does not fit gives back each column's `floor`: Rich
|
|
258
214
|
// re-measures a collapsed table's columns at the widths it gave them, and a
|
|
259
215
|
// column with nothing to draw measures 0 there however many cells the collapse
|
|
260
216
|
// left it. Those cells go unspent, as they do in Rich.
|
|
261
|
-
const natural = demands.reduce((sum, demand) => sum + demand.
|
|
217
|
+
const natural = demands.reduce((sum, demand) => sum + demand.want, frame.edge * 2 + Math.max(0, demands.length - 1) * frame.divider);
|
|
262
218
|
const squeezed = natural > outerWidth;
|
|
263
|
-
const
|
|
219
|
+
const cellWidths = held.map((width, index) => squeezed ? width - Math.min(width, seatedDemands[index].floor) : width);
|
|
264
220
|
stretchers.forEach((index, slot) => {
|
|
265
|
-
|
|
221
|
+
cellWidths[index] += stretches[slot];
|
|
266
222
|
});
|
|
267
|
-
const cellWidths = columns.map((width, index) => padded(index, width));
|
|
268
223
|
return {
|
|
269
224
|
edge,
|
|
270
225
|
divider: frame.divider,
|
|
271
|
-
padLeft,
|
|
272
|
-
padRight,
|
|
273
|
-
columns,
|
|
274
226
|
cellWidths,
|
|
275
227
|
totalWidth: edge * 2 +
|
|
276
228
|
Math.max(0, seated - 1) * frame.divider +
|
|
@@ -382,6 +334,10 @@ export class Column {
|
|
|
382
334
|
return col;
|
|
383
335
|
}
|
|
384
336
|
}
|
|
337
|
+
// A row's cell in column `index`: a column the row is short of draws a blank one.
|
|
338
|
+
function cellAt(row, index) {
|
|
339
|
+
return row.cells[index] ?? new EmbeddedText(undefined);
|
|
340
|
+
}
|
|
385
341
|
export class Table {
|
|
386
342
|
_columns;
|
|
387
343
|
_rows;
|
|
@@ -547,7 +503,7 @@ export class Table {
|
|
|
547
503
|
// Data rows
|
|
548
504
|
for (let rowIdx = 0; rowIdx < this._rows.length; rowIdx++) {
|
|
549
505
|
const row = this._rows[rowIdx];
|
|
550
|
-
const rowCells = this.
|
|
506
|
+
const rowCells = this._rowCells(row);
|
|
551
507
|
const rowStyle = stripes[rowIdx % stripes.length] ?? NULL_STYLE;
|
|
552
508
|
const cellStyles = columnStyles.map((s) => s.add(rowStyle));
|
|
553
509
|
yield* this._renderRow(options, rowCells, cellStyles, rowStyle, geometry, rowPadding(firstBody + rowIdx), box, "row", border);
|
|
@@ -678,8 +634,11 @@ export class Table {
|
|
|
678
634
|
* mode gets its own path through the width division.
|
|
679
635
|
*
|
|
680
636
|
* Cells are measured against the width left once the frame is paid, as the
|
|
681
|
-
* reference's `_measure_column` measures them. Padding
|
|
682
|
-
*
|
|
637
|
+
* reference's `_measure_column` measures them. Padding is part of every
|
|
638
|
+
* width the demand names, because the reference measures a cell wrapped in
|
|
639
|
+
* its `Padding`, and each sizing adds it where the reference does — a
|
|
640
|
+
* rowless column's minimum is the one width it never reaches. The floor is
|
|
641
|
+
* laid over whichever sizing the column has.
|
|
683
642
|
*/
|
|
684
643
|
_columnDemands(options, outerWidth) {
|
|
685
644
|
const frame = this._frame();
|
|
@@ -688,13 +647,12 @@ export class Table {
|
|
|
688
647
|
maxWidth: Math.max(0, outerWidth - frame.edge * 2 - frame.divider * Math.max(0, this._columns.length - 1)),
|
|
689
648
|
};
|
|
690
649
|
return this._columns.map((col, index) => {
|
|
691
|
-
const
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
return { ...sizing, minimum: sizing.minimum + floor, want: sizing.want + floor, pad, floor };
|
|
650
|
+
const sizing = this._columnSizing(col, index, inner, this._columnPadding(index));
|
|
651
|
+
const floor = sizing.want === 0 ? 1 : 0;
|
|
652
|
+
return { ...sizing, minimum: sizing.minimum + floor, want: sizing.want + floor, floor };
|
|
695
653
|
});
|
|
696
654
|
}
|
|
697
|
-
_columnSizing(col, index, options) {
|
|
655
|
+
_columnSizing(col, index, options, [left, right]) {
|
|
698
656
|
// A flexible column takes its share of whatever the bounded columns
|
|
699
657
|
// leave; every other column asks for its natural width, whether or not a
|
|
700
658
|
// neighbour is flexible. The split is the reference's —
|
|
@@ -703,17 +661,27 @@ export class Table {
|
|
|
703
661
|
// here a ratio is honoured either way. Its bounded part is its floor —
|
|
704
662
|
// the reference's `column.width or 1`, reading `minWidth` where there is
|
|
705
663
|
// 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
|
-
//
|
|
664
|
+
// table still pays it. A `width` beside a `ratio` is that floor, as the
|
|
665
|
+
// reference's `flex_minimum` reads it, held to the frame as a declared
|
|
666
|
+
// width is.
|
|
708
667
|
// [LAW:one-source-of-truth] `columnShare` decides first, so a column that
|
|
709
668
|
// `Column.flexible` calls elastic is one the division shares out.
|
|
669
|
+
const pad = left + right;
|
|
670
|
+
const padded = (content) => demandCells(content + pad);
|
|
671
|
+
// [LAW:single-enforcer] A width a column insists on, held to the width
|
|
672
|
+
// inside the frame — the reference's `with_maximum(max_width)` on a
|
|
673
|
+
// fixed-width column's measurement — so an even cut starts from what the
|
|
674
|
+
// table can hold. It holds a declared width and every ratio floor; a
|
|
675
|
+
// content column's `minWidth` is not held, as the reference clamps to
|
|
676
|
+
// `min_width` after `with_maximum`.
|
|
677
|
+
const held = (width) => Math.min(padded(demandCells(width)), options.maxWidth);
|
|
710
678
|
const share = columnShare(col);
|
|
711
679
|
if (share > 0) {
|
|
712
680
|
const floor = Math.max(1, demandCells(col.width ?? col.minWidth ?? 0));
|
|
681
|
+
const bid = held(floor);
|
|
713
682
|
return {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
want: floor,
|
|
683
|
+
minimum: bid,
|
|
684
|
+
want: bid,
|
|
717
685
|
weight: floor,
|
|
718
686
|
fill: 0,
|
|
719
687
|
ratio: share,
|
|
@@ -724,11 +692,10 @@ export class Table {
|
|
|
724
692
|
if (col.width !== undefined) {
|
|
725
693
|
// [LAW:single-enforcer] floored where it is parsed, the same rule
|
|
726
694
|
// `normalizePadding` applies to a negative padding side.
|
|
727
|
-
const
|
|
695
|
+
const bid = held(col.width);
|
|
728
696
|
return {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
want: declared,
|
|
697
|
+
minimum: bid,
|
|
698
|
+
want: bid,
|
|
732
699
|
weight: 0,
|
|
733
700
|
fill: 0,
|
|
734
701
|
ratio: 0,
|
|
@@ -736,8 +703,8 @@ export class Table {
|
|
|
736
703
|
wrapable: false,
|
|
737
704
|
};
|
|
738
705
|
}
|
|
739
|
-
const range = this._cellRange(col, index, options);
|
|
740
|
-
const natural = demandCells(this._bounded(col, range?.maximum ?? 1));
|
|
706
|
+
const range = this._cellRange(col, index, options, [left, right]);
|
|
707
|
+
const natural = demandCells(this._bounded(col, range?.maximum ?? padded(1), pad));
|
|
741
708
|
// `expand` is a stretch rather than a larger want: the column still
|
|
742
709
|
// competes for its natural width like any other, and only the cells left
|
|
743
710
|
// once every column has that are shared out. A larger want looks
|
|
@@ -749,25 +716,26 @@ export class Table {
|
|
|
749
716
|
// One deliberate divergence: Rich stretches declared-width columns as
|
|
750
717
|
// well, so `width: 6` under `expand` renders 21 cells wide — an option
|
|
751
718
|
// quietly meaning something else, the defect rich-justify-0cr exists to
|
|
752
|
-
// remove. A
|
|
719
|
+
// remove. A declared width's stretch is zero above.
|
|
753
720
|
//
|
|
754
721
|
// A column with no cell at all — no rows, and neither header nor footer
|
|
755
722
|
// drawn — has nothing to size to, which is not sizing to nothing. The
|
|
756
723
|
// reference measures it `Measurement(1, max_width)`: one cell of content,
|
|
757
724
|
// 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`.
|
|
725
|
+
// "no cells" as zero sized it to a two-cell box instead. The one cell and
|
|
726
|
+
// its padding are its want, so a `minWidth` is paid like any column's,
|
|
727
|
+
// and the offer is its `fill`, held to its `maxWidth`. Its minimum is the
|
|
728
|
+
// reference's bare 1, unpadded: `_measure_column` adds the padding only to
|
|
729
|
+
// a declared width and to the `min_width`/`max_width` clamp.
|
|
761
730
|
//
|
|
762
731
|
// A column that may not wrap has no narrower width than its natural one
|
|
763
732
|
// that keeps its content whole — the reference leaves `no_wrap` columns out
|
|
764
733
|
// of the ones it collapses.
|
|
765
734
|
return {
|
|
766
|
-
|
|
767
|
-
minimum: col.noWrap ? natural : Math.min(natural, demandCells(this._bounded(col, range?.minimum ?? 1))),
|
|
735
|
+
minimum: col.noWrap ? natural : Math.min(natural, demandCells(this._bounded(col, range?.minimum ?? 1, pad))),
|
|
768
736
|
want: natural,
|
|
769
737
|
weight: natural,
|
|
770
|
-
fill: range === undefined ? demandCells(this._bounded(col, UNBOUNDED)) : 0,
|
|
738
|
+
fill: range === undefined ? demandCells(this._bounded(col, UNBOUNDED, pad)) : 0,
|
|
771
739
|
ratio: 0,
|
|
772
740
|
stretch: this.expand,
|
|
773
741
|
wrapable: !col.noWrap,
|
|
@@ -793,6 +761,19 @@ export class Table {
|
|
|
793
761
|
const collapsed = this.collapsePadding && !last ? Math.max(0, top - bottom) : bottom;
|
|
794
762
|
return [!this.padEdge && first ? 0 : top, !this.padEdge && last ? 0 : collapsed];
|
|
795
763
|
}
|
|
764
|
+
// Every cell `render` draws, in the order it draws them: row by row, the
|
|
765
|
+
// header and footer when it shows them.
|
|
766
|
+
*contents() {
|
|
767
|
+
if (this.showHeader)
|
|
768
|
+
yield* this._columns.map((c) => c.header);
|
|
769
|
+
for (const row of this._rows)
|
|
770
|
+
yield* this._rowCells(row);
|
|
771
|
+
if (this.showFooter)
|
|
772
|
+
yield* this._columns.map((c) => c.footer);
|
|
773
|
+
}
|
|
774
|
+
_rowCells(row) {
|
|
775
|
+
return this._columns.map((_, index) => cellAt(row, index));
|
|
776
|
+
}
|
|
796
777
|
/**
|
|
797
778
|
* Every cell column `index` draws, in draw order.
|
|
798
779
|
*
|
|
@@ -808,18 +789,19 @@ export class Table {
|
|
|
808
789
|
if (this.showHeader)
|
|
809
790
|
yield col.header;
|
|
810
791
|
for (const row of this._rows)
|
|
811
|
-
yield row
|
|
792
|
+
yield cellAt(row, index);
|
|
812
793
|
if (this.showFooter)
|
|
813
794
|
yield col.footer;
|
|
814
795
|
}
|
|
815
796
|
/**
|
|
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.
|
|
797
|
+
* The widest any cell in a column draws, its padding included, and the
|
|
798
|
+
* narrowest the column can be without cutting one — the reference's
|
|
799
|
+
* `_measure_column` — or `undefined` when the column has no cell to measure.
|
|
800
|
+
* A column whose cells draw nothing measures its padding — a gutter asks for
|
|
801
|
+
* that and nothing else — which is not the same answer.
|
|
802
|
+
* [LAW:types-are-the-program]
|
|
821
803
|
*/
|
|
822
|
-
_cellRange(col, index, options) {
|
|
804
|
+
_cellRange(col, index, options, [left, right]) {
|
|
823
805
|
let range;
|
|
824
806
|
for (const cell of this._columnCells(col, index)) {
|
|
825
807
|
// The stamped cell, so the width a column asks for is the width its text
|
|
@@ -828,7 +810,10 @@ export class Table {
|
|
|
828
810
|
// [LAW:one-source-of-truth] Measured as the cell measures itself, so a
|
|
829
811
|
// multi-line cell asks for its widest line and a `Panel` for its frame;
|
|
830
812
|
// one that cannot measure itself asks for every cell there is, as in Rich.
|
|
831
|
-
|
|
813
|
+
// Wrapped in its padding, as the reference's `_get_cells` hands it to
|
|
814
|
+
// `_measure_column`, so a cell measures what it draws and never past the
|
|
815
|
+
// width it was offered.
|
|
816
|
+
const measured = Measurement.get(options, new Padding(cell, [0, right, 0, left]));
|
|
832
817
|
range = {
|
|
833
818
|
minimum: Math.max(range?.minimum ?? 0, measured.minimum),
|
|
834
819
|
maximum: Math.max(range?.maximum ?? 0, measured.maximum),
|
|
@@ -837,18 +822,20 @@ export class Table {
|
|
|
837
822
|
return range;
|
|
838
823
|
}
|
|
839
824
|
/**
|
|
840
|
-
* A natural width held to the column's own `minWidth`/`maxWidth
|
|
825
|
+
* A natural width held to the column's own `minWidth`/`maxWidth`, each
|
|
826
|
+
* widened by the column's `pad`, as the reference clamps a padded
|
|
827
|
+
* measurement to `min_width + padding_width`.
|
|
841
828
|
*
|
|
842
829
|
* A bound is a count of cells, read by the rule every width is read by: NaN
|
|
843
830
|
* and a negative are zero cells, so a NaN floor bounds nothing and a NaN
|
|
844
|
-
* ceiling
|
|
831
|
+
* ceiling holds the column to its padding alone, as `width: NaN` does. A bare `Math.max` returns
|
|
845
832
|
* NaN instead, and the column vanishes. [LAW:one-source-of-truth]
|
|
846
833
|
*/
|
|
847
|
-
_bounded(col, natural) {
|
|
848
|
-
return Math.min(Math.max(natural, cells(col.minWidth
|
|
834
|
+
_bounded(col, natural, pad) {
|
|
835
|
+
return Math.min(Math.max(natural, col.minWidth === undefined ? 0 : cells(col.minWidth) + pad), cells(col.maxWidth ?? Infinity) + pad);
|
|
849
836
|
}
|
|
850
837
|
*_renderRow(options, cells, styles, rowStyle, geometry, [top, bottom], box, level, border) {
|
|
851
|
-
const {
|
|
838
|
+
const { cellWidths } = geometry;
|
|
852
839
|
// [LAW:dataflow-not-control-flow] Header, body and footer share this path;
|
|
853
840
|
// the level crosses as a value the box answers with glyphs, not a branch.
|
|
854
841
|
const frame = box?.getContentChars(level);
|
|
@@ -859,39 +846,36 @@ export class Table {
|
|
|
859
846
|
const divider = frame && (frame.vertical.trim() === ""
|
|
860
847
|
? new Segment(frame.vertical, rowStyle.backgroundStyle.add(border))
|
|
861
848
|
: new Segment(frame.vertical, border));
|
|
862
|
-
// Render each cell
|
|
863
|
-
//
|
|
849
|
+
// Render each cell at the width the geometry gave its column. Columns the
|
|
850
|
+
// width could not seat are absent from `cellWidths` and so are never
|
|
864
851
|
// rendered at all.
|
|
865
|
-
const
|
|
866
|
-
const cellLines = columns.map((cellWidth, index) => {
|
|
852
|
+
const cellLines = cellWidths.map((cellWidth, index) => {
|
|
867
853
|
const col = this._columns[index];
|
|
868
|
-
const
|
|
854
|
+
const [left, right] = this._columnPadding(index);
|
|
855
|
+
// [LAW:single-enforcer] The padding on every side is part of the cell,
|
|
856
|
+
// drawn by `Padding` as the reference's `_get_cells` wraps each cell in
|
|
857
|
+
// one: its sides are paid before its content, so a cell too narrow for
|
|
858
|
+
// them draws as blanks, and a content line sits between its left and
|
|
859
|
+
// right blanks.
|
|
860
|
+
const padded = new Padding(cells[index] ?? embed(""), [top, right, bottom, left]);
|
|
869
861
|
// The render's own options with the column's canvas laid over them, so a
|
|
870
862
|
// cell resolves its style names against the same theme as the table. A
|
|
871
863
|
// table stacks its cells, so each is handed the table's rows as a
|
|
872
864
|
// ceiling, never a region to fill — the `Height` contract's
|
|
873
865
|
// `stackedHeight`, where the reference hands a cell `height=None`.
|
|
874
866
|
// 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
|
-
];
|
|
867
|
+
// A cell left no column draws nothing at all, its padding included, as
|
|
868
|
+
// the reference's `render_lines` draws nothing under one column.
|
|
869
|
+
const cellOptions = {
|
|
870
|
+
...options,
|
|
871
|
+
highlight: col.highlight,
|
|
872
|
+
maxWidth: cellWidth,
|
|
873
|
+
justify: col.justify,
|
|
874
|
+
overflow: col.overflow,
|
|
875
|
+
noWrap: col.noWrap,
|
|
876
|
+
height: stackedHeight(options.height),
|
|
877
|
+
};
|
|
878
|
+
return Segment.splitLines(render(padded, cellOptions));
|
|
895
879
|
});
|
|
896
880
|
const maxLines = cellLines.reduce((most, lines) => Math.max(most, lines.length), 1);
|
|
897
881
|
// How many blank lines stand above each cell's content. A header sits on
|
|
@@ -902,14 +886,14 @@ export class Table {
|
|
|
902
886
|
if (frame && geometry.edge === 1) {
|
|
903
887
|
yield new Segment(frame.left, border);
|
|
904
888
|
}
|
|
905
|
-
for (let colIdx = 0; colIdx <
|
|
889
|
+
for (let colIdx = 0; colIdx < cellWidths.length; colIdx++) {
|
|
906
890
|
if (colIdx > 0 && divider) {
|
|
907
891
|
yield divider;
|
|
908
892
|
}
|
|
909
893
|
const style = styles[colIdx];
|
|
910
894
|
// A cell with fewer lines than the row contributes blanks, so every
|
|
911
895
|
// column spans the same number of rows and the frame stays rectangular.
|
|
912
|
-
const line = cellLines[colIdx][lineIdx - drop[colIdx]] ?? blank(
|
|
896
|
+
const line = cellLines[colIdx][lineIdx - drop[colIdx]] ?? blank(cellWidths[colIdx]);
|
|
913
897
|
// The cell's style is the base its content's own spans layer over, and
|
|
914
898
|
// it covers the whole cell — its padding and its blank lines too, as
|
|
915
899
|
// the reference's does — so a background fills the column rather than
|
|
@@ -959,7 +943,7 @@ export class Table {
|
|
|
959
943
|
// lines, and `end="!!"` runs on into the top border there too. Splitting
|
|
960
944
|
// and re-ending each line moved a non-newline `end` onto the title's own
|
|
961
945
|
// row, wider than the table.
|
|
962
|
-
yield*
|
|
946
|
+
yield* render(source, {
|
|
963
947
|
...options,
|
|
964
948
|
maxWidth: tableWidth,
|
|
965
949
|
justify,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* sized and coloured a source-code excerpt this renderer does not produce.
|
|
17
17
|
*/
|
|
18
18
|
import { Segment } from "../core/segment.js";
|
|
19
|
-
import type
|
|
19
|
+
import { type Renderable, type RenderOptions } from "../core/protocol.js";
|
|
20
20
|
export interface TracebackOptions {
|
|
21
21
|
suppress?: string[];
|
|
22
22
|
maxFrames?: number;
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
import { Segment } from "../core/segment.js";
|
|
19
19
|
import { RichText } from "../core/text.js";
|
|
20
20
|
import { Pretty, UNSEEN_DATA_BOUNDS } from "../core/pretty.js";
|
|
21
|
-
import {
|
|
21
|
+
import { render } from "../core/protocol.js";
|
|
22
|
+
import { paddedContentWidth } from "./padding.js";
|
|
22
23
|
/**
|
|
23
24
|
* [LAW:parse-dont-validate] The checkpoint between whatever a `catch` caught —
|
|
24
25
|
* JavaScript lets a program throw any value — and the report. Anything with a
|
|
@@ -225,7 +226,7 @@ export class Traceback {
|
|
|
225
226
|
// width that is left on that line rather than overrunning it.
|
|
226
227
|
header.append(message({ ...options, maxWidth: Math.max(1, options.maxWidth - header.cellLength) }));
|
|
227
228
|
header.append("\n\n");
|
|
228
|
-
yield*
|
|
229
|
+
yield* render(header, options);
|
|
229
230
|
// A suppressed frame keeps its place and shows its file and line only: it
|
|
230
231
|
// is the same frame with no function name to show.
|
|
231
232
|
const frames = this.caught.frames.map((frame) => this.suppress.some((s) => frame.location.includes(s)) ? { ...frame, function: undefined } : frame);
|
|
@@ -253,10 +254,10 @@ export class Traceback {
|
|
|
253
254
|
// frame of its own. The width is divided as `Padding` divides it; the rows
|
|
254
255
|
// are not padded out on the right, as `Padding`'s are, because trailing
|
|
255
256
|
// blanks on every line of a crash report are bytes nobody asked for.
|
|
256
|
-
const {
|
|
257
|
-
const indent = new Segment("
|
|
257
|
+
const frameOptions = { ...options, maxWidth: paddedContentWidth(options.maxWidth, 2, 0) };
|
|
258
|
+
const indent = new Segment(" ");
|
|
258
259
|
for (const entry of entries) {
|
|
259
|
-
for (const line of Segment.splitLines(
|
|
260
|
+
for (const line of Segment.splitLines(render(entry, frameOptions))) {
|
|
260
261
|
yield indent;
|
|
261
262
|
yield* line;
|
|
262
263
|
yield Segment.line();
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
import { Segment } from "../core/segment.js";
|
|
5
5
|
import { Style } from "../core/style.js";
|
|
6
6
|
import { RichText } from "../core/text.js";
|
|
7
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
7
|
+
import type { Composite, Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
8
8
|
export interface TreeOptions {
|
|
9
9
|
expanded?: boolean;
|
|
10
10
|
hideRoot?: boolean;
|
|
11
11
|
guide_style?: string | Style;
|
|
12
12
|
style?: string | Style;
|
|
13
13
|
}
|
|
14
|
-
export declare class Tree implements Renderable, Measurable {
|
|
14
|
+
export declare class Tree implements Renderable, Measurable, Composite {
|
|
15
15
|
readonly label: Renderable;
|
|
16
16
|
readonly children: Tree[];
|
|
17
17
|
expanded: boolean;
|
|
@@ -21,6 +21,7 @@ export declare class Tree implements Renderable, Measurable {
|
|
|
21
21
|
constructor(label: string | RichText | Renderable, options?: TreeOptions);
|
|
22
22
|
add(label: string | RichText | Renderable, options?: TreeOptions): Tree;
|
|
23
23
|
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
24
|
+
contents(): Iterable<Renderable>;
|
|
24
25
|
/**
|
|
25
26
|
* The rows `render` and `measure` both read, entered in exactly one place so
|
|
26
27
|
* the two cannot start the walk differently. A hidden root takes its own row
|
|
@@ -59,9 +60,9 @@ export declare class Tree implements Renderable, Measurable {
|
|
|
59
60
|
* [LAW:single-enforcer] Tree also owns its row boundaries here rather than
|
|
60
61
|
* depending on label renderables to invent trailing newlines: the label's
|
|
61
62
|
* output is cut into lines and every line ends where Tree ends it. A label
|
|
62
|
-
* that emits nothing
|
|
63
|
-
* its own gets the blank row that line is, as in
|
|
64
|
-
* width when the label is justified.
|
|
63
|
+
* that emits nothing — one left no column, say — has no row, and one
|
|
64
|
+
* that ends in a newline of its own gets the blank row that line is, as in
|
|
65
|
+
* Rich — padded to the label's width when the label is justified.
|
|
65
66
|
*
|
|
66
67
|
* The node's style sits under the label's own, as Rich's `Styled` puts it, so
|
|
67
68
|
* markup in the label still wins; its background alone reaches under the
|