@promptctl/rich-js 0.24.1 → 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/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +175 -10
- package/dist/core/animation.js +283 -15
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cells.d.ts +23 -13
- package/dist/core/cells.js +58 -48
- 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 +44 -39
- package/dist/core/easing.d.ts +30 -0
- package/dist/core/easing.js +65 -20
- 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 +2 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +2 -2
- 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/pretty.js +9 -10
- package/dist/core/protocol.d.ts +100 -39
- package/dist/core/protocol.js +167 -21
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +25 -5
- package/dist/core/segment.d.ts +13 -5
- package/dist/core/segment.js +23 -9
- 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 +13 -16
- package/dist/core/strip.js +119 -110
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +57 -28
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +116 -124
- package/dist/core/timeline.d.ts +30 -5
- package/dist/core/timeline.js +43 -7
- package/dist/core/tween.d.ts +1 -1
- package/dist/core/tween.js +5 -3
- 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 +71 -37
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +24 -12
- package/dist/index.js +9 -4
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/align.d.ts +2 -3
- package/dist/renderables/align.js +0 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +3 -4
- package/dist/renderables/columns.js +2 -7
- package/dist/renderables/constrain.d.ts +2 -3
- package/dist/renderables/constrain.js +0 -3
- package/dist/renderables/effect.d.ts +132 -54
- package/dist/renderables/effect.js +188 -115
- package/dist/renderables/effects.d.ts +122 -11
- package/dist/renderables/effects.js +238 -94
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +16 -8
- package/dist/renderables/flexStrip.d.ts +2 -3
- package/dist/renderables/flexStrip.js +0 -4
- package/dist/renderables/frames.d.ts +35 -54
- package/dist/renderables/frames.js +43 -58
- 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 +3 -4
- package/dist/renderables/group.js +0 -3
- package/dist/renderables/layout.d.ts +12 -12
- package/dist/renderables/layout.js +40 -46
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +4 -5
- package/dist/renderables/list-view.js +9 -13
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +88 -41
- package/dist/renderables/markdown-parse.d.ts +4 -4
- package/dist/renderables/markdown-parse.js +52 -46
- package/dist/renderables/markdown-url.js +11 -11
- package/dist/renderables/padding.d.ts +4 -5
- package/dist/renderables/padding.js +0 -3
- package/dist/renderables/panel.d.ts +14 -16
- package/dist/renderables/panel.js +6 -14
- package/dist/renderables/progress.d.ts +83 -34
- package/dist/renderables/progress.js +182 -95
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +32 -9
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.js +2 -1
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +35 -14
- package/dist/renderables/spinner.js +65 -25
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +3 -2
- package/dist/renderables/table.d.ts +34 -40
- package/dist/renderables/table.js +41 -63
- package/dist/renderables/traceback.d.ts +2 -2
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +27 -25
- package/dist/renderables/tree.js +104 -58
- package/dist/renderables/viewport.d.ts +75 -44
- package/dist/renderables/viewport.js +145 -82
- 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 +5 -5
- package/dist/themes/ramp.d.ts +46 -3
- package/dist/themes/ramp.js +61 -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 +2 -0
- package/dist/widgets/focus-manager.js +6 -4
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -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 +2 -3
- package/dist/widgets/static-item.js +0 -3
- package/dist/widgets/text-input.d.ts +28 -32
- package/dist/widgets/text-input.js +360 -346
- 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 +14 -4
- package/dist/widgets/widget-app.js +36 -7
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -18
- package/package.json +8 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The effect catalogue: shimmer, pulse, sparkle and wheel, which loop for as
|
|
3
|
-
* long as they are drawn,
|
|
4
|
-
*
|
|
3
|
+
* long as they are drawn, fade-in and dissolve-out, which run once and are
|
|
4
|
+
* done, a blink and a bold pulse, which move an attribute on a beat, and a
|
|
5
|
+
* gradient, which lays a colour ramp across an element and may move it. Each is an `Effect` for `Effected` (`./effect.ts`); the defaults
|
|
5
6
|
* in `EFFECT_CURVES` are the ones signed off by eye in the effects-feel demo
|
|
6
7
|
* (`npm run effects-feel`), at 30 fps and at one frame a second, and
|
|
7
8
|
* `EFFECT_CATALOGUE` makes every one of them at those, by name, one way.
|
|
@@ -13,7 +14,10 @@
|
|
|
13
14
|
* One effect here is not ambience but a signal, and keeps none of this on
|
|
14
15
|
* purpose: `steppedInk` steps its ink through colours on a beat, the news
|
|
15
16
|
* that something is at its limit, and jumps from slot to slot where the
|
|
16
|
-
* loops hold still.
|
|
17
|
+
* loops hold still. Nor is `gradient` a light: it paints an ink rather than
|
|
18
|
+
* lighting it, so it may darken one, and a ramp scrolled across it jumps
|
|
19
|
+
* where the ramp wraps unless its ends are one colour. Of the list below it
|
|
20
|
+
* keeps the words. What makes the loops' motions familiar is what every
|
|
17
21
|
* curve of theirs keeps:
|
|
18
22
|
*
|
|
19
23
|
* - Nothing jumps. Every curve is continuous in time, built from smooth
|
|
@@ -50,8 +54,9 @@ import { Animation } from "../core/animation.js";
|
|
|
50
54
|
import { ColorDepth, ColorRgba, ColorSpec, contrastRatio, relativeLuminance } from "../core/color.js";
|
|
51
55
|
import { EASES, Phase, beat } from "../core/easing.js";
|
|
52
56
|
import { fbm, hash, noise, smoothstep } from "../core/noise.js";
|
|
53
|
-
import { Memo } from "../core/memo.js";
|
|
57
|
+
import { Memo, WeakMemo } from "../core/memo.js";
|
|
54
58
|
import { Oklch } from "../core/oklch.js";
|
|
59
|
+
import { Style } from "../core/style.js";
|
|
55
60
|
import { ensureContrast } from "../themes/colorMath.js";
|
|
56
61
|
import { Effected } from "./effect.js";
|
|
57
62
|
/** The contrast no touch takes a cell below, unless it rested below it: WCAG AA for body text. */
|
|
@@ -152,13 +157,11 @@ export function shares({ pairs, depth, theme }, colors, touch) {
|
|
|
152
157
|
* drawing what an earlier one drew costs no settling.
|
|
153
158
|
*/
|
|
154
159
|
export function onColors(loop, colors) {
|
|
155
|
-
const settled = new
|
|
160
|
+
const settled = new WeakMemo();
|
|
156
161
|
const settle = (drawn) => {
|
|
157
162
|
const lit = colors ?? drawn.colors;
|
|
158
163
|
const key = [drawnKey(drawn), "|", ...lit].join(" ");
|
|
159
|
-
|
|
160
|
-
settled.set(drawn.theme, memo);
|
|
161
|
-
return memo.get(key, () => shares(drawn, lit, loop.touch));
|
|
164
|
+
return settled.get(drawn.theme, () => new Memo()).get(key, () => shares(drawn, lit, loop.touch));
|
|
162
165
|
};
|
|
163
166
|
return stillOnSlots(atShares(loop, settle));
|
|
164
167
|
}
|
|
@@ -177,6 +180,59 @@ function drawnKey({ depth, pairs }) {
|
|
|
177
180
|
function pairKey({ fg, bg, terminal, reverse }) {
|
|
178
181
|
return `${fg.hex}/${bg.hex}/${terminal.fg}/${terminal.bg}/${reverse}`;
|
|
179
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* A ramp laid across the element: each lit cell's ink is the colour `ramp`
|
|
185
|
+
* shows at `t` at the cell's column, the element's leftmost cell reading
|
|
186
|
+
* the ramp's first stop and its rightmost its last, each at its cell's
|
|
187
|
+
* centre — or, where that colour would not read on the cell's ground, the
|
|
188
|
+
* nearest that does (`legibleInks`, as `steppedInk` writes its steps). A
|
|
189
|
+
* ramp that moves (`ColorRamp.moving`) scrolls along the element and turns
|
|
190
|
+
* its hues, and the frame changes when the ramp does — every frame for a
|
|
191
|
+
* loop, never again once a tween settles; a still one is
|
|
192
|
+
* `Animation.constant(ramp)`. Lit cells are those whose ink is one of
|
|
193
|
+
* `colors`, named as a style names them (`"#a6e3a1"`, `"bright_green"`),
|
|
194
|
+
* opaque, and matched as the screen shows them at the frame's depth — so
|
|
195
|
+
* below truecolor every ink that lands on the same palette colour is lit —
|
|
196
|
+
* by default every colour the element sets itself — never an ink the terminal supplies: a
|
|
197
|
+
* rule's line by its colour and not its title, a panel's border and not its
|
|
198
|
+
* body. The element is where its frame lists
|
|
199
|
+
* cells, as the fireflies read it, so a padded or confined element has the
|
|
200
|
+
* ramp across what it shows. On a sixteen-colour terminal nothing moves
|
|
201
|
+
* (`stillOnSlots`): a gradient there is jumps between slots, and the cells
|
|
202
|
+
* hold as drawn.
|
|
203
|
+
*/
|
|
204
|
+
export function gradient(ramp, colors) {
|
|
205
|
+
const legible = legibleInks();
|
|
206
|
+
// [LAW:parse-dont-validate] Parsed at the call, so a misspelt colour fails
|
|
207
|
+
// where it was written and not on some later frame.
|
|
208
|
+
const named = colors?.map(opaqueInk);
|
|
209
|
+
// [LAW:one-source-of-truth] A colour named as a style spells it is lit
|
|
210
|
+
// wherever a cell shows it, so it is resolved as `Effected` resolves a
|
|
211
|
+
// cell's ink: drawn at the frame's depth, then read on its terminal.
|
|
212
|
+
const shown = (spec, { depth, theme }) => (Style.fromColor(spec).drawnColors(depth).color ?? ColorSpec.default()).getTruecolor(theme, true).hex;
|
|
213
|
+
return stillOnSlots((drawn) => {
|
|
214
|
+
const lit = named === undefined ? drawn.colors : new Set(named.map((spec) => shown(spec, drawn)));
|
|
215
|
+
const inkOn = legible(drawn);
|
|
216
|
+
const [left, right] = extent(drawn.cells().map((cell) => cell.col));
|
|
217
|
+
return Animation.map(ramp, (now) => {
|
|
218
|
+
const first = now.stops[0].at;
|
|
219
|
+
const span = now.stops[now.stops.length - 1].at - first;
|
|
220
|
+
const along = (col) => now.at(first + (span * (col - left + 0.5)) / (right - left + 1));
|
|
221
|
+
return (look, cell) => (cell.terminal.fg || !lit.has(look.fg.hex) ? look : { ...look, fg: inkOn(along(cell.col), look, cell) });
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* `spec` parsed as an ink `gradient` lights. Opaque only: a translucent ink
|
|
227
|
+
* is drawn over the ground of each cell it is in, so it shows as no one
|
|
228
|
+
* colour to match, and naming one is refused here.
|
|
229
|
+
*/
|
|
230
|
+
function opaqueInk(spec) {
|
|
231
|
+
const parsed = ColorSpec.parse(spec);
|
|
232
|
+
if (parsed.value !== undefined && parsed.value.alpha < 1)
|
|
233
|
+
throw new RangeError(`gradient lights opaque colours, and ${spec} is translucent`);
|
|
234
|
+
return parsed;
|
|
235
|
+
}
|
|
180
236
|
/**
|
|
181
237
|
* A loop as an `Effect` on colours by hex, each touched at the share
|
|
182
238
|
* `share` gives it of the cell's strength in a frame drawing what `Drawn`
|
|
@@ -187,10 +243,10 @@ export function atShares(loop, share) {
|
|
|
187
243
|
const shared = share(drawn);
|
|
188
244
|
return Animation.continuous((t) => {
|
|
189
245
|
const field = loop.field(t, drawn);
|
|
190
|
-
return (
|
|
246
|
+
return (look, cell) => {
|
|
191
247
|
const w = Math.round(clamp01(field(cell)) * LEVELS) / LEVELS;
|
|
192
248
|
const moved = (color, terminal) => touchedAt(loop.touch, color, (terminal ? 0 : (shared.get(color.hex) ?? 0)) * w);
|
|
193
|
-
return { fg: moved(fg, cell.terminal.fg), bg: moved(bg, cell.terminal.bg) };
|
|
249
|
+
return { ...look, fg: moved(look.fg, cell.terminal.fg), bg: moved(look.bg, cell.terminal.bg) };
|
|
194
250
|
};
|
|
195
251
|
});
|
|
196
252
|
};
|
|
@@ -200,7 +256,7 @@ export function atShares(loop, share) {
|
|
|
200
256
|
* sixteen ANSI slots, or no colour, a move that shows at all is a jump to
|
|
201
257
|
* another slot, and a light on an element never jumps — so there it
|
|
202
258
|
* holds every cell still, and anywhere else it moves. [LAW:single-enforcer]
|
|
203
|
-
* `onColors` and `
|
|
259
|
+
* `onColors`, `wheel` and `gradient` draw through this, so every loop in the catalogue
|
|
204
260
|
* meets the sixteen-colour terminal here. `atShares` does not: a progress
|
|
205
261
|
* bar's pulse is the news that work is going on, and flashes a slot as
|
|
206
262
|
* Rich's does; nor does `steppedInk`, a signal, which steps slot to slot.
|
|
@@ -208,7 +264,7 @@ export function atShares(loop, share) {
|
|
|
208
264
|
* loop on such a terminal draws it once.
|
|
209
265
|
*/
|
|
210
266
|
function stillOnSlots(loop) {
|
|
211
|
-
return (drawn) => (onlySlots(drawn.depth) ? Animation.constant((
|
|
267
|
+
return (drawn) => (onlySlots(drawn.depth) ? Animation.constant((look) => look) : loop(drawn));
|
|
212
268
|
}
|
|
213
269
|
/** Whether `depth` writes nothing between the slots it has: the sixteen, at either sixteen-colour depth, or none. */
|
|
214
270
|
function onlySlots(depth) {
|
|
@@ -231,12 +287,21 @@ function touchedAt(touch, color, w) {
|
|
|
231
287
|
return w === 0 ? color : touch(color, w);
|
|
232
288
|
}
|
|
233
289
|
/** Throws, naming the field, unless `curve` has a positive, finite `seconds` and a `swing` in [0, 1]. */
|
|
234
|
-
function requireCurve(curve) {
|
|
290
|
+
export function requireCurve(curve) {
|
|
235
291
|
if (!(Number.isFinite(curve.seconds) && curve.seconds > 0))
|
|
236
292
|
throw new RangeError(`curve: seconds must be a positive number of seconds, got ${curve.seconds}`);
|
|
237
293
|
if (!(curve.swing >= 0 && curve.swing <= 1))
|
|
238
294
|
throw new RangeError(`curve: swing must be in [0, 1], got ${curve.swing}`);
|
|
239
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Throws, naming `what`, unless `z` is a finite place in the noise. Noise at
|
|
298
|
+
* a place that is not a number is not a number, which no effect could tell
|
|
299
|
+
* from a dim cell or an empty sky. [LAW:no-silent-failure]
|
|
300
|
+
*/
|
|
301
|
+
export function requireZ(what, z) {
|
|
302
|
+
if (!Number.isFinite(z))
|
|
303
|
+
throw new RangeError(`${what} z must be a finite place in the noise, got ${z}`);
|
|
304
|
+
}
|
|
240
305
|
/**
|
|
241
306
|
* Each effect's curve as it was signed off. Every loop's period is a prime
|
|
242
307
|
* number of seconds, so no two loops come back into step within a sitting.
|
|
@@ -340,6 +405,7 @@ const FLASH = [[0.24, 1], [0.6, 0]];
|
|
|
340
405
|
*/
|
|
341
406
|
export function pulse(curve, glow, z) {
|
|
342
407
|
requireCurve(curve);
|
|
408
|
+
requireZ("pulse", z);
|
|
343
409
|
// How much quicker or slower than `curve.seconds` this element beats.
|
|
344
410
|
const rate = 0.9 + 0.2 * hash(z, 3.1);
|
|
345
411
|
// How far, in beats, the rhythm wanders ahead and behind its metronome. Noise's
|
|
@@ -386,6 +452,7 @@ const bump = (d) => (Math.abs(d) >= 1 ? 0 : (1 - d * d) ** 3);
|
|
|
386
452
|
*/
|
|
387
453
|
export function shimmer(curve, width, glow, z) {
|
|
388
454
|
requireCurve(curve);
|
|
455
|
+
requireZ("shimmer", z);
|
|
389
456
|
const P = curve.seconds;
|
|
390
457
|
// Passes are events on slots of half a period. Whether slot `j` holds one,
|
|
391
458
|
// and how bright, is the sky's: slow noise over the slots, so sunny and
|
|
@@ -473,6 +540,7 @@ export function shimmer(curve, width, glow, z) {
|
|
|
473
540
|
*/
|
|
474
541
|
export function sparkle(curve, glow, z) {
|
|
475
542
|
requireCurve(curve);
|
|
543
|
+
requireZ("sparkle", z);
|
|
476
544
|
// The columns a firefly has to itself along a row of the lattice, and the
|
|
477
545
|
// rows between two rows of it: more than a halo's reach, so a one-row
|
|
478
546
|
// element is lit by one row of fireflies.
|
|
@@ -562,12 +630,12 @@ export function veiled(visibility, swing) {
|
|
|
562
630
|
const ground = Oklch.fromRgba(drawn.theme.backgroundColor);
|
|
563
631
|
return Animation.continuous((t) => {
|
|
564
632
|
const shows = visibility(t, drawn);
|
|
565
|
-
return (
|
|
633
|
+
return (look, cell) => {
|
|
566
634
|
const hidden = swing * (1 - clamp01(shows(cell)));
|
|
567
635
|
if (hidden === 0)
|
|
568
|
-
return
|
|
569
|
-
const bg = blend(Oklch.fromRgba(
|
|
570
|
-
return { fg: blend(Oklch.fromRgba(
|
|
636
|
+
return look;
|
|
637
|
+
const bg = blend(Oklch.fromRgba(look.bg), ground, hidden);
|
|
638
|
+
return { ...look, fg: blend(Oklch.fromRgba(look.fg), bg, hidden).toRgba(), bg: bg.toRgba() };
|
|
571
639
|
};
|
|
572
640
|
});
|
|
573
641
|
};
|
|
@@ -576,34 +644,68 @@ export function veiled(visibility, swing) {
|
|
|
576
644
|
* Where each cell falls in a transition's order, in [0, 1]: smooth noise over
|
|
577
645
|
* the element, so neighbouring cells arrive or leave together, in patches
|
|
578
646
|
* with soft edges, as ink spreads or mist thins. `z` places the element in
|
|
579
|
-
* the noise.
|
|
647
|
+
* the noise.
|
|
580
648
|
*/
|
|
581
649
|
function order(z) {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
// The noise is read through slower noise, so the patches reach out in
|
|
586
|
-
// curling tendrils rather than sitting as round blots.
|
|
587
|
-
const place = places.get(at) ??
|
|
588
|
-
clamp01(0.5 + 1.1 * fbm(cell.col * 0.09 + 1.4 * noise(cell.col * 0.035, cell.row * 0.2 + 2.6, z + 0.5), cell.row * 0.45, z, 3));
|
|
589
|
-
places.set(at, place);
|
|
590
|
-
return place;
|
|
591
|
-
};
|
|
650
|
+
// The noise is read through slower noise, so the patches reach out in
|
|
651
|
+
// curling tendrils rather than sitting as round blots.
|
|
652
|
+
return (cell) => clamp01(0.5 + 1.1 * fbm(cell.col * 0.09 + 1.4 * noise(cell.col * 0.035, cell.row * 0.2 + 2.6, z + 0.5), cell.row * 0.45, z, 3));
|
|
592
653
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
654
|
+
/**
|
|
655
|
+
* An effect that runs once: a fade-in or a dissolve-out over its curve's
|
|
656
|
+
* `seconds` from `t = 0`, its own clock as every effect's is — the caller
|
|
657
|
+
* draws it at the time since it began. `done(t)` is true from the moment
|
|
658
|
+
* every cell has arrived or gone, so the caller knows when to stop drawing an
|
|
659
|
+
* element that is leaving, or to drop the effect from one that has arrived.
|
|
660
|
+
*
|
|
661
|
+
* `schedule` is when its frame next changes on that clock, whatever the
|
|
662
|
+
* element draws: what a caller drawing the transition inside an animation —
|
|
663
|
+
* a lifecycle — says its frame does, since the frame it hands over carries
|
|
664
|
+
* no word of its own (`stampFrame`, `../core/animation.ts`). Until done it changes
|
|
665
|
+
* every frame, or on each hold of a beat (`onBeat`), and from then never.
|
|
666
|
+
* [LAW:one-source-of-truth] `done` is that schedule read as a fact — done
|
|
667
|
+
* from the first moment it never changes again — so no transition carries
|
|
668
|
+
* two answers to when it is over.
|
|
669
|
+
*/
|
|
670
|
+
export class Transition {
|
|
671
|
+
effect;
|
|
672
|
+
schedule;
|
|
673
|
+
constructor(effect, schedule) {
|
|
674
|
+
this.effect = effect;
|
|
675
|
+
this.schedule = schedule;
|
|
676
|
+
}
|
|
677
|
+
done(t) {
|
|
678
|
+
return this.schedule.nextChange(t) === Number.POSITIVE_INFINITY;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* `effect` run once over `seconds` as a `Transition`: what it draws until it
|
|
683
|
+
* is done, then held. [LAW:single-enforcer] The one rule for when a
|
|
684
|
+
* transition is done and what it answers after, for a colour effect and a
|
|
685
|
+
* glyph effect (`./glyph-effects.ts`) alike.
|
|
686
|
+
*/
|
|
687
|
+
export function transition(seconds, effect) {
|
|
688
|
+
// Moving every frame until `seconds`, then never: the sequence carries the switch.
|
|
689
|
+
const schedule = Animation.sequence(Animation.continuous(() => undefined), seconds, Animation.constant(undefined));
|
|
690
|
+
const ran = new Transition((drawn) => {
|
|
691
|
+
const moving = effect(drawn);
|
|
692
|
+
return Animation.of({
|
|
693
|
+
at: (t) => moving.at(t),
|
|
694
|
+
nextChange: (t) => (ran.done(t) ? Number.POSITIVE_INFINITY : moving.nextChange(t)),
|
|
695
|
+
// Done, the frame settled when the transition did; until then it is the effect's.
|
|
696
|
+
lastChange: (t) => (ran.done(t) ? schedule.lastChange(t) : moving.lastChange(t)),
|
|
697
|
+
// A one-shot, reduced, cuts from its start to its end, whatever the effect it runs would hold as a loop.
|
|
698
|
+
reduced: () => Animation.cut(moving.at(0), moving.at(seconds)),
|
|
699
|
+
});
|
|
700
|
+
}, schedule);
|
|
701
|
+
return ran;
|
|
702
|
+
}
|
|
703
|
+
export function onBeat(stepped, beat) {
|
|
704
|
+
// [LAW:types-are-the-program] An effect is a function and a transition an object: the representation is the discriminator.
|
|
705
|
+
if (typeof stepped === "function")
|
|
706
|
+
return (drawn) => Animation.onBeat(stepped(drawn), beat);
|
|
707
|
+
const { effect, schedule } = stepped;
|
|
708
|
+
return new Transition((drawn) => Animation.onBeat(effect(drawn), beat), Animation.onBeat(schedule, beat));
|
|
607
709
|
}
|
|
608
710
|
/**
|
|
609
711
|
* Of a transition's `seconds`, how long each cell's own change takes. Under
|
|
@@ -611,7 +713,7 @@ function transition(curve, effect) {
|
|
|
611
713
|
*/
|
|
612
714
|
const OWN = 0.45;
|
|
613
715
|
/**
|
|
614
|
-
* Each cell's own change in a transition, in
|
|
716
|
+
* Each cell's own change in a transition, in the frame at `t` listing `cells`: a cell
|
|
615
717
|
* starts as far through the time the others leave it as its `place` falls
|
|
616
718
|
* between the element's first and last, so on any element, however little of
|
|
617
719
|
* the order its cells span, the first starts at 0 and the last settles at
|
|
@@ -619,32 +721,27 @@ const OWN = 0.45;
|
|
|
619
721
|
* `seconds` alone. An element whose cells all share one place has no order to
|
|
620
722
|
* stagger, and its change takes the whole of `seconds`.
|
|
621
723
|
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
724
|
+
* [LAW:no-ambient-temporal-coupling] Worked out from the frame alone, so
|
|
725
|
+
* the same `t` over the same cells draws the same bytes whatever was drawn
|
|
726
|
+
* before — after a seek, in reverse, at any rate — and one transition is
|
|
727
|
+
* safe on any number of elements. An element that gains cells as it moves is
|
|
728
|
+
* spread over the order it has in each frame, so a cell already moving may
|
|
729
|
+
* move its start — back to unmoved, if a new cell comes first. Nothing
|
|
730
|
+
* steadier is there to spread over: the order's whole range would start a
|
|
731
|
+
* small element late and end it early, and the cells a frame hands reach
|
|
732
|
+
* across a wide frame's blank margin.
|
|
628
733
|
*/
|
|
629
734
|
function ownChange(curve, place) {
|
|
630
|
-
|
|
631
|
-
const key = (cell) => `${cell.row}:${cell.col}`;
|
|
632
|
-
return (cells) => {
|
|
735
|
+
return (t, cells) => {
|
|
633
736
|
const [first, last] = extent(cells.map(place));
|
|
634
737
|
const stagger = (last > first ? 1 - OWN : 0) * curve.seconds;
|
|
635
738
|
// One phase a frame, shifted per cell, so no cell builds its own.
|
|
636
739
|
const change = Phase.once(0, curve.seconds - stagger);
|
|
637
740
|
// A share of the way from the first place to the last, held to the order for a cell the frame does not list.
|
|
638
|
-
|
|
741
|
+
return (cell) => {
|
|
639
742
|
const along = last > first ? clamp01((place(cell) - first) / (last - first)) : 0;
|
|
640
|
-
return { along, progress:
|
|
743
|
+
return { along, progress: change(t - along * stagger) };
|
|
641
744
|
};
|
|
642
|
-
for (const cell of cells) {
|
|
643
|
-
const at = key(cell);
|
|
644
|
-
changes.set(at, changes.get(at) ?? settled(cell));
|
|
645
|
-
}
|
|
646
|
-
// A cell the frame hands but does not list shows nothing yet, so it is timed for this frame alone.
|
|
647
|
-
return (cell) => changes.get(key(cell)) ?? settled(cell);
|
|
648
745
|
};
|
|
649
746
|
}
|
|
650
747
|
/** The least and greatest of `values`; of none, a frame with no cell to place, both 0. */
|
|
@@ -664,10 +761,11 @@ function extent(values) {
|
|
|
664
761
|
*/
|
|
665
762
|
export function fadeIn(curve, z) {
|
|
666
763
|
requireCurve(curve);
|
|
764
|
+
requireZ("fadeIn", z);
|
|
667
765
|
const risen = ownChange(curve, order(1.7 + z));
|
|
668
|
-
return transition(curve, veiled((t, drawn) => {
|
|
669
|
-
const of = risen(drawn.cells());
|
|
670
|
-
return (cell) => curve.ease(of(cell).progress
|
|
766
|
+
return transition(curve.seconds, veiled((t, drawn) => {
|
|
767
|
+
const of = risen(t, drawn.cells());
|
|
768
|
+
return (cell) => curve.ease(of(cell).progress);
|
|
671
769
|
}, curve.swing));
|
|
672
770
|
}
|
|
673
771
|
/**
|
|
@@ -690,14 +788,15 @@ const DISSOLVE_SHAPE = { depth: 0.4, from: 0.5, to: 0.95, maskFrom: 0.35, maskTo
|
|
|
690
788
|
*/
|
|
691
789
|
export function dissolveOut(curve, z) {
|
|
692
790
|
requireCurve(curve);
|
|
791
|
+
requireZ("dissolveOut", z);
|
|
693
792
|
const eddy = order(9.3 + z);
|
|
694
793
|
const faded = ownChange(curve, order(4.2 + z));
|
|
695
794
|
const { depth, from, to, maskFrom, maskTo, late } = DISSOLVE_SHAPE;
|
|
696
|
-
return transition(curve, veiled((t, drawn) => {
|
|
697
|
-
const of = faded(drawn.cells());
|
|
795
|
+
return transition(curve.seconds, veiled((t, drawn) => {
|
|
796
|
+
const of = faded(t, drawn.cells());
|
|
698
797
|
return (cell) => {
|
|
699
798
|
const { along, progress } = of(cell);
|
|
700
|
-
const gone = curve.ease(progress
|
|
799
|
+
const gone = curve.ease(progress);
|
|
701
800
|
const q = clamp01((gone - from) / (to - from));
|
|
702
801
|
const back = depth * smoothstep(maskFrom, maskTo, eddy(cell)) * (1 - late + late * along) * Math.sin(Math.PI * q) ** 2;
|
|
703
802
|
return 1 - gone + back;
|
|
@@ -738,6 +837,7 @@ const STRAY = 180;
|
|
|
738
837
|
*/
|
|
739
838
|
export function wheel(curve, z) {
|
|
740
839
|
requireCurve(curve);
|
|
840
|
+
requireZ("wheel", z);
|
|
741
841
|
const P = curve.seconds;
|
|
742
842
|
// A segment's seed, from the colour it is.
|
|
743
843
|
const seed = (hex) => hash(Number.parseInt(hex.slice(1), 16) * 1e-4, 17.3 + z);
|
|
@@ -749,7 +849,7 @@ export function wheel(curve, z) {
|
|
|
749
849
|
return (360 * t) / P + curve.swing * STRAY * ((stray(s, t) - stray(s, 0)) / 1.5);
|
|
750
850
|
};
|
|
751
851
|
// A turned colour's bounds rest on what a frame draws, never on `t`: settled once for each.
|
|
752
|
-
const settled = new
|
|
852
|
+
const settled = new WeakMemo();
|
|
753
853
|
return stillOnSlots((drawn) => {
|
|
754
854
|
const { colors, fills, depth, theme } = drawn;
|
|
755
855
|
// Whether a slot shows one of the element's colours: never the terminal's, whatever its hex.
|
|
@@ -758,9 +858,7 @@ export function wheel(curve, z) {
|
|
|
758
858
|
const segmentOf = (color, other, theirs) => (fills.has(color.hex) || !theirs ? color.hex : other.hex);
|
|
759
859
|
// A colour as it turns with a segment: written one way wherever it is drawn in a frame.
|
|
760
860
|
const keyOf = (color, segment) => `${color.hex} ${segment}`;
|
|
761
|
-
const
|
|
762
|
-
settled.set(theme, memo);
|
|
763
|
-
const bounds = memo.get(drawnKey(drawn), () => turnedBounds(drawn.pairs, depth, (pair, side) => {
|
|
861
|
+
const bounds = settled.get(theme, () => new Memo()).get(drawnKey(drawn), () => turnedBounds(drawn.pairs, depth, (pair, side) => {
|
|
764
862
|
const other = side === "fg" ? "bg" : "fg";
|
|
765
863
|
return own(pair.terminal[side], pair[side]) ? keyOf(pair[side], segmentOf(pair[side], pair[other], own(pair.terminal[other], pair[other]))) : undefined;
|
|
766
864
|
}));
|
|
@@ -781,7 +879,7 @@ export function wheel(curve, z) {
|
|
|
781
879
|
return (shown, cell) => {
|
|
782
880
|
const mine = { fg: own(cell.terminal.fg, shown.fg), bg: own(cell.terminal.bg, shown.bg) };
|
|
783
881
|
const turn = (color, ours, other, theirs) => (ours ? write(color, segmentOf(color, other, theirs)) : color);
|
|
784
|
-
return { fg: turn(shown.fg, mine.fg, shown.bg, mine.bg), bg: turn(shown.bg, mine.bg, shown.fg, mine.fg) };
|
|
882
|
+
return { ...shown, fg: turn(shown.fg, mine.fg, shown.bg, mine.bg), bg: turn(shown.bg, mine.bg, shown.fg, mine.fg) };
|
|
785
883
|
};
|
|
786
884
|
});
|
|
787
885
|
});
|
|
@@ -861,12 +959,9 @@ function turnedBounds(pairs, depth, turnedAs) {
|
|
|
861
959
|
}
|
|
862
960
|
const measured = (color) => ({ color, looks: Oklch.fromRgba(color), stop: stop(color) });
|
|
863
961
|
/** `ColorSpec.indexed`'s colours, measured once for each theme and depth. */
|
|
864
|
-
const measuredMemo = new
|
|
962
|
+
const measuredMemo = new WeakMemo();
|
|
865
963
|
function measuredIndexed(depth, theme) {
|
|
866
|
-
|
|
867
|
-
const listed = measuredMemo.get(indexed) ?? indexed.map(([, color]) => measured(color));
|
|
868
|
-
measuredMemo.set(indexed, listed);
|
|
869
|
-
return listed;
|
|
964
|
+
return measuredMemo.get(ColorSpec.indexed(depth, theme), (indexed) => indexed.map(([, color]) => measured(color)));
|
|
870
965
|
}
|
|
871
966
|
/**
|
|
872
967
|
* The colour that looks nearest `color` (OKLab's ΔE): `rest`, or one of
|
|
@@ -904,7 +999,8 @@ function nearestOf(color, offered) {
|
|
|
904
999
|
* that does (`stillOnSlots`). A cell whose ground no offered colour reads
|
|
905
1000
|
* on keeps the ink it rested in.
|
|
906
1001
|
*
|
|
907
|
-
* The search is settled once for each
|
|
1002
|
+
* The search is settled once for each ground and step — below truecolor,
|
|
1003
|
+
* which colours read on a ground once for that ground — so a frame
|
|
908
1004
|
* inside a step redoes none, and nor does a later pass of the beat: at sixty
|
|
909
1005
|
* frames a second a one-second step is sixty identical frames, and the
|
|
910
1006
|
* frame changes when the beat does: the signal is the beat mapped to inks.
|
|
@@ -916,34 +1012,78 @@ export function steppedInk(steps) {
|
|
|
916
1012
|
// A copy, as `beat` keeps of the holds: what the signal plays stays what
|
|
917
1013
|
// `steps` said whatever the caller does to its array.
|
|
918
1014
|
const inks = steps.map((step) => step.color);
|
|
919
|
-
const
|
|
920
|
-
return (
|
|
921
|
-
const
|
|
922
|
-
settled.set(theme, memo);
|
|
1015
|
+
const legible = legibleInks();
|
|
1016
|
+
return (drawn) => {
|
|
1017
|
+
const inkOn = legible(drawn);
|
|
923
1018
|
return Animation.map(rhythm, (i) => {
|
|
924
1019
|
const step = inks[i];
|
|
925
|
-
return (
|
|
926
|
-
const pair = { ...colors, terminal: cell.terminal, reverse: cell.reverse };
|
|
927
|
-
return { fg: memo.get(`${depth} ${pairKey(pair)} ${step.hex}`, () => legibleInk(step, pair, depth, theme)), bg: colors.bg };
|
|
928
|
-
};
|
|
1020
|
+
return (look, cell) => ({ ...look, fg: inkOn(step, look, cell) });
|
|
929
1021
|
});
|
|
930
1022
|
};
|
|
931
1023
|
}
|
|
932
1024
|
/**
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
*
|
|
1025
|
+
* `legibleInk` for the frame `Drawn` describes, of an ink on a cell's look:
|
|
1026
|
+
* settled once for each terminal, depth, pair and ink, so a frame drawing
|
|
1027
|
+
* what an earlier one drew redoes no search. [LAW:single-enforcer] Every
|
|
1028
|
+
* effect that writes an ink of the caller's choosing writes it through
|
|
1029
|
+
* here — `steppedInk`'s steps and `gradient`'s ramp alike — so none of them
|
|
1030
|
+
* leaves a word unreadable. Below truecolor which of the depth's colours
|
|
1031
|
+
* read on a pair does not depend on the ink, so it is settled once for each
|
|
1032
|
+
* pair: a ramp, whose every frame offers inks no frame offered before,
|
|
1033
|
+
* then costs a nearest-colour scan a cell, not a contrast test of the whole
|
|
1034
|
+
* palette.
|
|
937
1035
|
*/
|
|
938
|
-
function
|
|
1036
|
+
function legibleInks() {
|
|
1037
|
+
const settled = new WeakMemo();
|
|
1038
|
+
return ({ depth, theme }) => {
|
|
1039
|
+
const memo = settled.get(theme, () => ({ inks: new Memo(), readable: new Memo() }));
|
|
1040
|
+
return (ink, look, cell) => {
|
|
1041
|
+
const pair = { fg: look.fg, bg: look.bg, terminal: cell.terminal, reverse: cell.reverse };
|
|
1042
|
+
const key = `${depth} ${pairKey(pair)}`;
|
|
1043
|
+
const legibleOf = (offered) => legibleAmong(pair, offered, depth, theme);
|
|
1044
|
+
return memo.inks.get(`${key} ${ink.hex}`, () => {
|
|
1045
|
+
const offered = depth === ColorDepth.TRUECOLOR
|
|
1046
|
+
? legibleOf([ink, ensureContrast(ink, pair.bg, floorOf(pair, depth))].map(measured))
|
|
1047
|
+
: memo.readable.get(key, () => legibleOf(measuredIndexed(depth, theme)));
|
|
1048
|
+
return nearestOf(ink, offered)?.color ?? pair.fg;
|
|
1049
|
+
});
|
|
1050
|
+
};
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Of `offered`, the colours that read on `pair`'s ground at `pair`'s floor,
|
|
1055
|
+
* as `Effected` writes them (`shows`). [LAW:one-source-of-truth] Measured the
|
|
1056
|
+
* way `shares` and `wheel` measure: the written colours, at the catalogue's
|
|
1057
|
+
* floor. An ink none of them is nearest stays `pair`'s own.
|
|
1058
|
+
*/
|
|
1059
|
+
function legibleAmong(pair, offered, depth, theme) {
|
|
939
1060
|
const floor = floorOf(pair, depth);
|
|
940
|
-
|
|
941
|
-
const reads = ({ color }) => {
|
|
1061
|
+
return offered.filter(({ color }) => {
|
|
942
1062
|
const shown = Effected.shows(pair, { fg: color, bg: pair.bg }, depth, theme);
|
|
943
1063
|
return contrastRatio(shown.fg, shown.bg) >= floor;
|
|
944
|
-
};
|
|
945
|
-
return nearestOf(step, offered.filter(reads))?.color ?? pair.fg;
|
|
1064
|
+
});
|
|
946
1065
|
}
|
|
1066
|
+
/**
|
|
1067
|
+
* `spec.attribute` blinking: off, the cell as drawn, so that hold's frame is
|
|
1068
|
+
* the identity and writes the element's own bytes; on, the attribute set.
|
|
1069
|
+
* It changes when its beat does, at a hold's end, and never between.
|
|
1070
|
+
* A blink and a bold pulse are this one function at two specs
|
|
1071
|
+
* (`ATTRIBUTE_DEFAULTS`) [LAW:one-type-per-behavior].
|
|
1072
|
+
*/
|
|
1073
|
+
export function blink({ attribute, off, on }) {
|
|
1074
|
+
const rhythm = beat([off, on]);
|
|
1075
|
+
const frames = [(look) => look, (look) => ({ ...look, attributes: { ...look.attributes, [attribute]: true } })];
|
|
1076
|
+
return () => Animation.map(rhythm, (i) => frames[i]);
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Each attribute effect's spec: a blink hides the words for half of each
|
|
1080
|
+
* second, as a terminal's own blink does, and a bold pulse holds them bold
|
|
1081
|
+
* for the last three tenths of each second.
|
|
1082
|
+
*/
|
|
1083
|
+
export const ATTRIBUTE_DEFAULTS = {
|
|
1084
|
+
blink: { attribute: "conceal", off: 0.5, on: 0.5 },
|
|
1085
|
+
boldPulse: { attribute: "bold", off: 0.7, on: 0.3 },
|
|
1086
|
+
};
|
|
947
1087
|
/**
|
|
948
1088
|
* Every effect in the catalogue by its name in `EFFECT_CURVES`, each made the
|
|
949
1089
|
* one way: for an element, at its signed-off curve unless handed another,
|
|
@@ -966,4 +1106,8 @@ export const EFFECT_CATALOGUE = {
|
|
|
966
1106
|
fade: (element, curve = EFFECT_CURVES.fade) => fadeIn(curve, element.z),
|
|
967
1107
|
dissolve: (element, curve = EFFECT_CURVES.dissolve) => dissolveOut(curve, element.z),
|
|
968
1108
|
},
|
|
1109
|
+
attributes: {
|
|
1110
|
+
blink: () => blink(ATTRIBUTE_DEFAULTS.blink),
|
|
1111
|
+
boldPulse: () => blink(ATTRIBUTE_DEFAULTS.boldPulse),
|
|
1112
|
+
},
|
|
969
1113
|
};
|
|
@@ -89,7 +89,7 @@ export declare class InlineLabel {
|
|
|
89
89
|
* measures it, so a cut to a border and the width a label asks for count
|
|
90
90
|
* the cells it will draw. [LAW:single-enforcer] The one reading of "set into
|
|
91
91
|
* a line", for a label read through the crossing above and for text a
|
|
92
|
-
* caller hands over already read (`
|
|
92
|
+
* caller hands over already read (`FrameSequence`).
|
|
93
93
|
*/
|
|
94
94
|
export declare function setIntoLine(text: RichText): RichText;
|
|
95
95
|
/**
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { cellCount, cellLen, setCellSize } from "../core/cells.js";
|
|
25
25
|
import { activeHighlighter, readStr } from "../core/markup.js";
|
|
26
|
-
import {
|
|
26
|
+
import { Memo } from "../core/memo.js";
|
|
27
|
+
import { measure, render } from "../core/protocol.js";
|
|
27
28
|
import { Segment } from "../core/segment.js";
|
|
28
29
|
import { RichText } from "../core/text.js";
|
|
29
30
|
/**
|
|
@@ -39,8 +40,10 @@ export class EmbeddedText {
|
|
|
39
40
|
// A string's reading under each markup setting, made the first time one is
|
|
40
41
|
// asked for. Parsing is the costly half of drawing a string, and a table
|
|
41
42
|
// measures every cell more than once per print; read on every call, a
|
|
42
|
-
// 500-row table of markup printed 75% slower.
|
|
43
|
-
|
|
43
|
+
// 500-row table of markup printed 75% slower. Kept as characters and spans,
|
|
44
|
+
// not as the `RichText`, whose state a memo may not hold: a memo is "same
|
|
45
|
+
// key, same value", and each call builds a text of its own from it.
|
|
46
|
+
_readings = new Memo();
|
|
44
47
|
constructor(content) {
|
|
45
48
|
if (typeof content === "string") {
|
|
46
49
|
this._source = content;
|
|
@@ -61,10 +64,15 @@ export class EmbeddedText {
|
|
|
61
64
|
text(options, base) {
|
|
62
65
|
if (typeof this._source !== "string")
|
|
63
66
|
return this._source.copy();
|
|
67
|
+
const source = this._source;
|
|
64
68
|
const markup = options.markup !== false;
|
|
65
|
-
const reading = this._readings.get(markup
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
const reading = this._readings.get(markup ? "markup" : "plain", () => {
|
|
70
|
+
const read = readStr(source, markup);
|
|
71
|
+
return { plain: read.plain, spans: [...read.spans] };
|
|
72
|
+
});
|
|
73
|
+
const text = new RichText(reading.plain);
|
|
74
|
+
for (const span of reading.spans)
|
|
75
|
+
text.stylize(span.style, span.start, span.end);
|
|
68
76
|
const highlighter = activeHighlighter(options);
|
|
69
77
|
if (base !== undefined && highlighter === undefined)
|
|
70
78
|
text.style = base;
|
|
@@ -81,7 +89,7 @@ export class EmbeddedText {
|
|
|
81
89
|
* measures, and no highlighter runs just to be thrown away.
|
|
82
90
|
*/
|
|
83
91
|
measure(options) {
|
|
84
|
-
return this.text({ ...options, highlight: false })
|
|
92
|
+
return measure(this.text({ ...options, highlight: false }), options);
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
/**
|
|
@@ -125,7 +133,7 @@ export class InlineLabel {
|
|
|
125
133
|
* measures it, so a cut to a border and the width a label asks for count
|
|
126
134
|
* the cells it will draw. [LAW:single-enforcer] The one reading of "set into
|
|
127
135
|
* a line", for a label read through the crossing above and for text a
|
|
128
|
-
* caller hands over already read (`
|
|
136
|
+
* caller hands over already read (`FrameSequence`).
|
|
129
137
|
*/
|
|
130
138
|
export function setIntoLine(text) {
|
|
131
139
|
text.end = "";
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* FlexStrip reuses it verbatim. There is no second concept of "join."
|
|
17
17
|
*/
|
|
18
18
|
import { Segment } from "../core/segment.js";
|
|
19
|
-
import type {
|
|
19
|
+
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
20
20
|
import { type Joiner, type StyledRenderable } from "../core/strip.js";
|
|
21
21
|
export type FlexAlign = "left" | "center" | "right" | "justify";
|
|
22
22
|
export interface FlexStripOptions<T extends StyledRenderable> {
|
|
@@ -27,13 +27,12 @@ export interface FlexStripOptions<T extends StyledRenderable> {
|
|
|
27
27
|
/** Line alignment when packed items don't fill the available width (default "left"). */
|
|
28
28
|
align?: FlexAlign;
|
|
29
29
|
}
|
|
30
|
-
export declare class FlexStrip<T extends StyledRenderable = StyledRenderable> implements Renderable, Measurable
|
|
30
|
+
export declare class FlexStrip<T extends StyledRenderable = StyledRenderable> implements Renderable, Measurable {
|
|
31
31
|
readonly items: readonly T[];
|
|
32
32
|
readonly joiner: Joiner<T> | undefined;
|
|
33
33
|
readonly gap: number;
|
|
34
34
|
readonly align: FlexAlign;
|
|
35
35
|
constructor(items: readonly T[], options?: FlexStripOptions<T>);
|
|
36
|
-
contents(): Iterable<Renderable>;
|
|
37
36
|
render(options: RenderOptions): Iterable<Segment>;
|
|
38
37
|
measure(options: RenderOptions): {
|
|
39
38
|
minimum: number;
|
|
@@ -56,10 +56,6 @@ export class FlexStrip {
|
|
|
56
56
|
this.gap = options?.gap ?? 0;
|
|
57
57
|
this.align = options?.align ?? "left";
|
|
58
58
|
}
|
|
59
|
-
// The items; a join is drawn from what the joiner emits for it, not held.
|
|
60
|
-
contents() {
|
|
61
|
-
return this.items;
|
|
62
|
-
}
|
|
63
59
|
*render(options) {
|
|
64
60
|
const items = this.items;
|
|
65
61
|
if (items.length === 0)
|