@promptctl/rich-js 0.26.0 → 0.28.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 +1 -0
- package/THIRD-PARTY-NOTICES +42 -0
- package/dist/core/animation.d.ts +52 -15
- package/dist/core/animation.js +82 -37
- package/dist/core/ansi.d.ts +15 -8
- package/dist/core/ansi.js +106 -28
- package/dist/core/bidi-data.d.ts +14 -0
- package/dist/core/bidi-data.js +385 -0
- package/dist/core/bidi.d.ts +89 -0
- package/dist/core/bidi.js +615 -0
- package/dist/core/box.d.ts +9 -1
- package/dist/core/box.js +11 -0
- package/dist/core/cell-widths.d.ts +3 -2
- package/dist/core/cell-widths.js +93 -93
- package/dist/core/cells.d.ts +20 -0
- package/dist/core/cells.js +93 -5
- package/dist/core/color.d.ts +26 -18
- package/dist/core/color.js +37 -34
- package/dist/core/console.d.ts +26 -1
- package/dist/core/console.js +26 -6
- package/dist/core/control-codes.d.ts +61 -0
- package/dist/core/control-codes.js +157 -0
- package/dist/core/easing.js +6 -1
- package/dist/core/env.d.ts +6 -0
- package/dist/core/env.js +9 -1
- package/dist/core/export-html.js +4 -3
- package/dist/core/export-lines.d.ts +2 -0
- package/dist/core/export-lines.js +7 -3
- package/dist/core/export-svg.js +6 -5
- package/dist/core/json.js +9 -2
- package/dist/core/markup.js +4 -3
- package/dist/core/oklch.d.ts +2 -0
- package/dist/core/oklch.js +3 -3
- package/dist/core/orient.d.ts +68 -0
- package/dist/core/orient.js +224 -0
- package/dist/core/osc8.d.ts +12 -5
- package/dist/core/osc8.js +14 -7
- package/dist/core/pretty.js +22 -18
- package/dist/core/protocol.d.ts +53 -22
- package/dist/core/protocol.js +14 -6
- package/dist/core/render.d.ts +8 -3
- package/dist/core/render.js +19 -5
- package/dist/core/seal.d.ts +26 -0
- package/dist/core/seal.js +75 -0
- package/dist/core/segment.d.ts +9 -1
- package/dist/core/segment.js +11 -1
- package/dist/core/style.d.ts +29 -1
- package/dist/core/style.js +41 -5
- package/dist/core/text-order.d.ts +51 -0
- package/dist/core/text-order.js +50 -0
- package/dist/core/text.d.ts +10 -0
- package/dist/core/text.js +133 -87
- package/dist/core/timeline.js +6 -6
- package/dist/core/view.d.ts +23 -9
- package/dist/core/view.js +37 -12
- package/dist/core/wrap.d.ts +24 -6
- package/dist/core/wrap.js +82 -16
- package/dist/host/app.d.ts +48 -7
- package/dist/host/app.js +116 -11
- package/dist/host/index.d.ts +3 -2
- package/dist/host/index.js +1 -0
- package/dist/host/input.d.ts +35 -2
- package/dist/host/input.js +88 -4
- package/dist/host/terminal-colors.d.ts +18 -0
- package/dist/host/terminal-colors.js +46 -0
- package/dist/host/terminal-host.d.ts +2 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.js +2 -2
- package/dist/renderables/change.d.ts +1 -3
- package/dist/renderables/change.js +4 -6
- package/dist/renderables/effect.d.ts +61 -60
- package/dist/renderables/effect.js +91 -81
- package/dist/renderables/effects.d.ts +37 -8
- package/dist/renderables/effects.js +193 -55
- package/dist/renderables/frames.d.ts +1 -10
- package/dist/renderables/frames.js +1 -16
- package/dist/renderables/glyph-effect.d.ts +12 -13
- package/dist/renderables/glyph-effect.js +34 -32
- package/dist/renderables/glyph-effects.d.ts +27 -8
- package/dist/renderables/glyph-effects.js +75 -7
- package/dist/renderables/lifecycle.d.ts +40 -32
- package/dist/renderables/lifecycle.js +87 -48
- package/dist/renderables/live.d.ts +3 -3
- package/dist/renderables/live.js +4 -4
- package/dist/renderables/markdown.js +4 -2
- package/dist/renderables/panel.js +6 -4
- package/dist/renderables/progress.d.ts +11 -4
- package/dist/renderables/progress.js +29 -17
- package/dist/renderables/progressBar.d.ts +3 -3
- package/dist/renderables/progressBar.js +16 -13
- package/dist/renderables/syntax.js +7 -2
- package/dist/renderables/table.js +2 -1
- package/dist/template-bindings/animation-funcs.d.ts +49 -18
- package/dist/template-bindings/animation-funcs.js +115 -32
- package/dist/template-bindings/index.d.ts +7 -5
- package/dist/template-bindings/index.js +8 -5
- package/dist/template-bindings/palette-funcs.d.ts +1 -1
- package/dist/template-bindings/palette-funcs.js +1 -1
- package/dist/themes/buildPalette.d.ts +15 -39
- package/dist/themes/buildPalette.js +196 -67
- package/dist/themes/colorMath.d.ts +10 -4
- package/dist/themes/colorMath.js +33 -1
- package/dist/themes/data/index.js +1 -4
- package/dist/themes/data/textual-ansi.js +0 -143
- package/dist/themes/data/types.d.ts +3 -1
- package/dist/themes/palette.d.ts +22 -6
- package/dist/themes/palette.js +13 -21
- package/dist/themes/ramp.d.ts +1 -1
- package/dist/themes/ramp.js +1 -1
- package/dist/themes/registry.d.ts +7 -10
- package/dist/themes/registry.js +9 -30
- package/dist/themes/terminalThemes.d.ts +4 -10
- package/dist/themes/terminalThemes.js +31 -46
- package/dist/themes/transpose.d.ts +5 -10
- package/dist/themes/transpose.js +13 -24
- package/dist/widgets/button.d.ts +1 -1
- package/dist/widgets/button.js +6 -6
- package/dist/widgets/checkbox.js +4 -4
- package/dist/widgets/dropdown.d.ts +1 -1
- package/dist/widgets/dropdown.js +7 -5
- package/dist/widgets/event-router.js +2 -2
- package/dist/widgets/ink.d.ts +5 -4
- package/dist/widgets/ink.js +6 -9
- package/dist/widgets/text-input.d.ts +1 -1
- package/dist/widgets/text-input.js +41 -32
- package/dist/widgets/themed-widget.js +3 -3
- package/dist/widgets/toggle.d.ts +1 -1
- package/dist/widgets/toggle.js +5 -5
- package/dist/widgets/types.d.ts +2 -2
- package/dist/widgets/widget-app.d.ts +3 -3
- package/dist/widgets/widget-app.js +5 -5
- package/package.json +5 -4
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* (`JoinedCell`, `../core/strip.ts`), the way the joiner drew it. Shimmer,
|
|
18
18
|
* pulse, fade, dissolve and a blinking attribute are all this one shape,
|
|
19
19
|
* differing by what the function does with its inputs
|
|
20
|
-
* [LAW:one-type-per-behavior]; two effects stack
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* [LAW:one-type-per-behavior]; two effects stack as layers of one effect
|
|
21
|
+
* (`layered`, `./effects.ts`), each moving what the last left and recognising
|
|
22
|
+
* a cell by what the element drew (`EffectCell.pair`), or by nesting one
|
|
23
|
+
* wrapper in another, which measures the inner one's output instead.
|
|
23
24
|
*
|
|
24
25
|
* A cell's look is one value, its colours and its attributes together
|
|
25
26
|
* (`CellLook`), rather than an attribute stage beside the colour one, so
|
|
@@ -69,60 +70,67 @@
|
|
|
69
70
|
* what the child drew, so a sweep crosses the element, not the screen.
|
|
70
71
|
*/
|
|
71
72
|
import { drawnBy, shiftAnchor } from "../core/anchor.js";
|
|
72
|
-
import { Animation,
|
|
73
|
+
import { Animation, holdOf, placedRender, shownAt } from "../core/animation.js";
|
|
73
74
|
import { cellLen, graphemes } from "../core/cells.js";
|
|
74
|
-
import { ColorDepth, ColorSpec, SURFACE_BLACK } from "../core/color.js";
|
|
75
|
+
import { ColorDepth, ColorSpec, resolveTerminal, SURFACE_BLACK } from "../core/color.js";
|
|
75
76
|
import { fnv1a } from "../core/fnv1a.js";
|
|
76
77
|
import { Measurement } from "../core/measure.js";
|
|
77
78
|
import { render } from "../core/protocol.js";
|
|
78
79
|
import { Segment } from "../core/segment.js";
|
|
79
80
|
import { JOIN, joinedCell } from "../core/strip.js";
|
|
80
|
-
import { ATTRIBUTE_NAMES, Style } from "../core/style.js";
|
|
81
|
+
import { ATTRIBUTE_NAMES, NEVER_CHANGES, Style } from "../core/style.js";
|
|
81
82
|
import { finiteTime } from "../core/easing.js";
|
|
82
|
-
/**
|
|
83
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The hold an effect drawn at `t` tells: its animation's there, or held for
|
|
85
|
+
* good over no cell, since it changes how cells look and never which exist.
|
|
86
|
+
* What drew none says for itself when it will (a lifecycle not yet entered).
|
|
87
|
+
*/
|
|
88
|
+
export function holdOver(anyCell, shown, t) {
|
|
89
|
+
return anyCell ? holdOf(shown, t) : NEVER_CHANGES;
|
|
90
|
+
}
|
|
84
91
|
/**
|
|
85
92
|
* An `Effected` at `t`, or an element through an effect at a time an
|
|
86
93
|
* animation's `at` was handed — a lifecycle's time since it entered: `renderable`
|
|
87
|
-
* with its cells' looks passed through `effect` at `t
|
|
88
|
-
* when that changes, since
|
|
89
|
-
*
|
|
94
|
+
* with its cells' looks passed through `effect` at `t`. Its cells say nothing
|
|
95
|
+
* of when that changes, since `t` is not the frame's clock: it tells the hold
|
|
96
|
+
* it drew in (`RenderOptions.onMoved`), and whatever placed the animation
|
|
97
|
+
* carries that onto the frame's and stamps it. [LAW:one-type-per-behavior]
|
|
90
98
|
*/
|
|
91
99
|
export class Recolored {
|
|
92
100
|
renderable;
|
|
93
101
|
effect;
|
|
94
|
-
|
|
102
|
+
_t;
|
|
95
103
|
key;
|
|
96
|
-
theme;
|
|
97
104
|
constructor(renderable, effect, options) {
|
|
98
105
|
this.renderable = renderable;
|
|
99
106
|
this.effect = effect;
|
|
100
|
-
this.
|
|
107
|
+
this._t = finiteTime("Effected", options.t);
|
|
101
108
|
this.key = options.key;
|
|
102
|
-
this.theme = options.theme;
|
|
103
109
|
}
|
|
104
110
|
render(options) {
|
|
105
|
-
return this.moved(options).segments;
|
|
106
|
-
}
|
|
107
|
-
/** The frame the effect draws at `t`, and the hold it says that is in. */
|
|
108
|
-
moved(options) {
|
|
109
111
|
const segments = render(this.renderable, options);
|
|
110
112
|
// With no colour output nothing the effect does is written, so the child
|
|
111
113
|
// is what is shown, and it changes when the child says it does.
|
|
112
|
-
if (options.colorSystem === null)
|
|
113
|
-
|
|
114
|
+
if (options.colorSystem === null) {
|
|
115
|
+
options.onMoved?.(this._t, this.key, NEVER_CHANGES);
|
|
116
|
+
return segments;
|
|
117
|
+
}
|
|
114
118
|
// No depth named is no downgrade, as `drawnColors` reads it.
|
|
115
119
|
const depth = options.colorSystem ?? ColorDepth.TRUECOLOR;
|
|
120
|
+
// [LAW:one-source-of-truth] Every colour below resolves on the frame's
|
|
121
|
+
// terminal, so two effects in a frame cannot disagree.
|
|
122
|
+
const terminal = resolveTerminal(options.terminal, depth);
|
|
116
123
|
// [LAW:one-source-of-truth] The frame is measured before any cell is
|
|
117
124
|
// handed to the effect, off the same looks its cells are drawn from, and
|
|
118
125
|
// the one measure is what the effect's animation is made from.
|
|
119
|
-
const looked = looks(segments, depth,
|
|
126
|
+
const looked = looks(segments, depth, terminal);
|
|
120
127
|
const walked = walk(looked);
|
|
121
|
-
const
|
|
122
|
-
const shown = shownAt(
|
|
123
|
-
|
|
128
|
+
const drawn = measured(looked, () => walked, depth, terminal);
|
|
129
|
+
const shown = shownAt(this.effect(drawn), this._t, options);
|
|
130
|
+
options.onMoved?.(this._t, this.key, holdOver(walked.some(({ glyphs }) => glyphs.some(({ cell }) => cell !== undefined)), shown, this._t));
|
|
131
|
+
return this._recolored(walked, shown.value, drawn);
|
|
124
132
|
}
|
|
125
|
-
*_recolored(walked, frame,
|
|
133
|
+
*_recolored(walked, frame, drawn) {
|
|
126
134
|
// [LAW:one-source-of-truth] Every cell but a join cell that follows its
|
|
127
135
|
// neighbours is painted first, so that cell is resolved from the cells
|
|
128
136
|
// beside it as the effect left them, wherever in the frame they fall.
|
|
@@ -133,8 +141,7 @@ export class Recolored {
|
|
|
133
141
|
if (joined?.follows === true)
|
|
134
142
|
return { joined };
|
|
135
143
|
const from = { fg: look.pair.fg, bg: look.pair.bg, attributes: look.attributes };
|
|
136
|
-
|
|
137
|
-
return { painted: glyphs.map(({ cell }) => (cell === undefined ? from : returned(frame(from, { ...cell, seed: cellSeed(this.key, cell.row, cell.col), terminal, reverse })))) };
|
|
144
|
+
return { painted: glyphs.map(({ cell }) => (cell === undefined ? from : returned(frame(from, { ...cell, seed: cellSeed(this.key, cell.row, cell.col), pair: look.pair })))) };
|
|
138
145
|
});
|
|
139
146
|
const beside = edgesBeside(walked, drawings);
|
|
140
147
|
for (const [i, { segment, look, glyphs }] of walked.entries()) {
|
|
@@ -142,13 +149,13 @@ export class Recolored {
|
|
|
142
149
|
if (look === undefined || drawing === undefined)
|
|
143
150
|
yield segment;
|
|
144
151
|
else if ("joined" in drawing)
|
|
145
|
-
yield this._rejoined(segment, look, drawing.joined, glyphs, beside, frame,
|
|
152
|
+
yield this._rejoined(segment, look, drawing.joined, glyphs, beside, frame, drawn);
|
|
146
153
|
else
|
|
147
|
-
yield* this._repainted(segment, look, glyphs, drawing.painted,
|
|
154
|
+
yield* this._repainted(segment, look, glyphs, drawing.painted, drawn);
|
|
148
155
|
}
|
|
149
156
|
}
|
|
150
157
|
/** `segment`'s cells drawn in the looks the effect painted them. */
|
|
151
|
-
*_repainted(segment, look, glyphs, moved,
|
|
158
|
+
*_repainted(segment, look, glyphs, moved, drawn) {
|
|
152
159
|
const { style: base, wire, slots, pair, attributes } = look;
|
|
153
160
|
// [LAW:one-source-of-truth] A run is cells the wire writes alike: each
|
|
154
161
|
// cell is drawn at the output depth first and merged on the colours that
|
|
@@ -161,7 +168,7 @@ export class Recolored {
|
|
|
161
168
|
// Back from the screen to the style's slots: the same swap undoes itself.
|
|
162
169
|
const colors = onScreen(now, pair.reverse);
|
|
163
170
|
const last = runs[runs.length - 1];
|
|
164
|
-
const writes = last !== undefined && sameColors(last.colors, colors) ? last.drawn : this.respec(wire, slots, colors,
|
|
171
|
+
const writes = last !== undefined && sameColors(last.colors, colors) ? last.drawn : this.respec(wire, slots, colors, drawn);
|
|
165
172
|
if (last !== undefined && writesAlike(last.drawn, writes) && sameAttributes(last.attributes, now.attributes)) {
|
|
166
173
|
last.text += glyph;
|
|
167
174
|
last.cells += cellLen(glyph);
|
|
@@ -191,13 +198,13 @@ export class Recolored {
|
|
|
191
198
|
* nothing of keeps the edge the cell was drawn from, so with neither moved
|
|
192
199
|
* the cell is as it was drawn.
|
|
193
200
|
*/
|
|
194
|
-
_rejoined(segment, look, joined, glyphs, beside, frame,
|
|
201
|
+
_rejoined(segment, look, joined, glyphs, beside, frame, measure) {
|
|
202
|
+
const { depth, terminal } = measure;
|
|
195
203
|
const cells = glyphs.flatMap(({ cell }) => (cell === undefined ? [] : [cell]));
|
|
196
204
|
// A join cell that draws no cell shows nothing an effect could move.
|
|
197
205
|
const [at] = cells;
|
|
198
206
|
if (at === undefined)
|
|
199
207
|
return segment;
|
|
200
|
-
const { theme } = this;
|
|
201
208
|
// [LAW:one-source-of-truth] A moved edge is the spec the depth writes its
|
|
202
209
|
// slot in, the spec the joiner reads of an item drawn so: a palette slot
|
|
203
210
|
// stays that slot, which the lift tells apart by number, never an RGB read
|
|
@@ -209,12 +216,12 @@ export class Recolored {
|
|
|
209
216
|
const edgeOf = (spec, foreground) => {
|
|
210
217
|
if (!spec?.isDefault)
|
|
211
218
|
return spec;
|
|
212
|
-
const own = ColorSpec.fromRgba(spec.getTruecolor(
|
|
219
|
+
const own = ColorSpec.fromRgba(spec.getTruecolor(terminal, foreground));
|
|
213
220
|
terminals.add(own);
|
|
214
221
|
return own;
|
|
215
222
|
};
|
|
216
223
|
const moved = (from, to) => {
|
|
217
|
-
const specs = movedSpecs(from, to, depth,
|
|
224
|
+
const specs = movedSpecs(from, to, depth, terminal);
|
|
218
225
|
return specs.fg === undefined && specs.bg === undefined ? undefined : { color: edgeOf(specs.fg, true), bgcolor: edgeOf(specs.bg, false) };
|
|
219
226
|
};
|
|
220
227
|
// [LAW:one-source-of-truth] A neighbour the frame shows no cell of beside
|
|
@@ -228,9 +235,9 @@ export class Recolored {
|
|
|
228
235
|
// comes here [LAW:no-silent-failure].
|
|
229
236
|
const unseen = (edge) => {
|
|
230
237
|
const wire = edge.drawnColors(depth);
|
|
231
|
-
const slots = { fg: (wire.color ?? DEFAULT).getTruecolor(
|
|
232
|
-
const
|
|
233
|
-
return moved(slots, returned(frame({ ...slots, attributes: cellAttributes(edge) }, { ...at, seed: cellSeed(this.key, at.row, at.col), terminal, reverse: false })));
|
|
238
|
+
const slots = { fg: (wire.color ?? DEFAULT).getTruecolor(terminal, true), bg: (wire.bgcolor ?? DEFAULT).getTruecolor(terminal, false) };
|
|
239
|
+
const own = { fg: wire.color?.isDefault ?? true, bg: wire.bgcolor?.isDefault ?? true };
|
|
240
|
+
return moved(slots, returned(frame({ ...slots, attributes: cellAttributes(edge) }, { ...at, seed: cellSeed(this.key, at.row, at.col), pair: { ...slots, terminal: own, reverse: false } })));
|
|
234
241
|
};
|
|
235
242
|
const moveOf = (side) => {
|
|
236
243
|
const read = joined.reads[side];
|
|
@@ -256,7 +263,7 @@ export class Recolored {
|
|
|
256
263
|
const was = joined.style;
|
|
257
264
|
const drawn = colors.drawnColors(depth);
|
|
258
265
|
const { wire, slots } = look;
|
|
259
|
-
const rounded = this.respec(wire, slots, { fg: colors.color?.getTruecolor(
|
|
266
|
+
const rounded = this.respec(wire, slots, { fg: colors.color?.getTruecolor(terminal, true) ?? slots.fg, bg: colors.bgcolor?.getTruecolor(terminal, false) ?? slots.bg }, measure);
|
|
260
267
|
const written = (now, before, wrote, drew, round) => {
|
|
261
268
|
if (sameSpec(now, before))
|
|
262
269
|
return wrote;
|
|
@@ -280,42 +287,39 @@ export class Recolored {
|
|
|
280
287
|
* `from` with `wire`: each moved slot in the spec `movedSpecs` writes it
|
|
281
288
|
* in, the rest as `wire` wrote them.
|
|
282
289
|
*/
|
|
283
|
-
respec(wire, from, to, depth) {
|
|
284
|
-
const moved = movedSpecs(from, to, depth,
|
|
290
|
+
respec(wire, from, to, { depth, terminal }) {
|
|
291
|
+
const moved = movedSpecs(from, to, depth, terminal);
|
|
285
292
|
return { color: moved.fg ?? wire.color, bgcolor: moved.bg ?? wire.bgcolor };
|
|
286
293
|
}
|
|
287
294
|
}
|
|
288
295
|
/**
|
|
289
296
|
* `renderable` under `effect` over time: an `Animation<Renderable>` whose
|
|
290
297
|
* value at `t` is the element through the effect at `t`, placed wherever a
|
|
291
|
-
* renderable goes.
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
* depth (`
|
|
296
|
-
*
|
|
297
|
-
* host sleeps.
|
|
298
|
+
* renderable goes. An `Effect` is made from what the element draws, so with
|
|
299
|
+
* nothing drawn its answer to when it changes is `t`, at every time: early,
|
|
300
|
+
* never false. Placed, on its own or as a part of any combinator, it says
|
|
301
|
+
* exactly: the frame's drawing told the effect's hold at that time and the
|
|
302
|
+
* output's depth (`Moves`), so at a depth that shows no motion a loop says it
|
|
303
|
+
* never changes there and the host sleeps.
|
|
298
304
|
*/
|
|
299
305
|
export class Effected {
|
|
300
306
|
renderable;
|
|
301
307
|
effect;
|
|
302
308
|
key;
|
|
303
|
-
theme;
|
|
304
309
|
constructor(renderable, effect, options) {
|
|
305
310
|
this.renderable = renderable;
|
|
306
311
|
this.effect = effect;
|
|
307
312
|
this.key = options.key;
|
|
308
|
-
this.theme = options.theme;
|
|
309
313
|
}
|
|
310
314
|
/** The element through the effect at `t`, unstamped. */
|
|
311
315
|
at(t) {
|
|
312
|
-
return new Recolored(this.renderable, this.effect, { t, key: this.key
|
|
316
|
+
return new Recolored(this.renderable, this.effect, { t, key: this.key });
|
|
313
317
|
}
|
|
314
|
-
nextChange(t) {
|
|
315
|
-
return t;
|
|
318
|
+
nextChange(t, moves) {
|
|
319
|
+
return moves?.(t, this.key)?.nextChange ?? t;
|
|
316
320
|
}
|
|
317
|
-
lastChange(t) {
|
|
318
|
-
return t;
|
|
321
|
+
lastChange(t, moves) {
|
|
322
|
+
return moves?.(t, this.key)?.lastChange ?? t;
|
|
319
323
|
}
|
|
320
324
|
/** The element under the effect with its motion taken out. */
|
|
321
325
|
reduced() {
|
|
@@ -324,23 +328,22 @@ export class Effected {
|
|
|
324
328
|
}
|
|
325
329
|
/** Drawn at the frame's time, its cells stamped with the effect's exact hold there. */
|
|
326
330
|
render(options) {
|
|
327
|
-
|
|
328
|
-
return stampFrame(segments, hold);
|
|
331
|
+
return placedRender(this, options);
|
|
329
332
|
}
|
|
330
333
|
measure(options) {
|
|
331
334
|
return Measurement.get(options, this.renderable);
|
|
332
335
|
}
|
|
333
336
|
/**
|
|
334
337
|
* The colours the screen shows for `pair` once it is drawn moved to `to`,
|
|
335
|
-
* both as the screen shows them, at `depth` on `
|
|
338
|
+
* both as the screen shows them, at `depth` on `terminal`: what an effect
|
|
336
339
|
* moving a pair's colours leaves its words reading in.
|
|
337
340
|
* [LAW:one-source-of-truth] Read through `movedSpecs`, the one writer a
|
|
338
341
|
* moved slot is written with.
|
|
339
342
|
*/
|
|
340
|
-
static shows(pair, to, depth,
|
|
343
|
+
static shows(pair, to, depth, terminal) {
|
|
341
344
|
const from = onScreen(pair, pair.reverse);
|
|
342
|
-
const moved = movedSpecs(from, onScreen(to, pair.reverse), depth,
|
|
343
|
-
return onScreen({ fg: moved.fg?.getTruecolor(
|
|
345
|
+
const moved = movedSpecs(from, onScreen(to, pair.reverse), depth, terminal);
|
|
346
|
+
return onScreen({ fg: moved.fg?.getTruecolor(terminal, true) ?? from.fg, bg: moved.bg?.getTruecolor(terminal, false) ?? from.bg }, pair.reverse);
|
|
344
347
|
}
|
|
345
348
|
}
|
|
346
349
|
/**
|
|
@@ -352,17 +355,17 @@ export function cellSeed(key, row, col) {
|
|
|
352
355
|
return Number.parseInt(fnv1a(`${key}:${row}:${col}`), 16) / 0x1_0000_0000;
|
|
353
356
|
}
|
|
354
357
|
const DEFAULT = ColorSpec.default();
|
|
355
|
-
/** Each of `segments` with how it is drawn at `depth` on `
|
|
356
|
-
function looks(segments, depth,
|
|
358
|
+
/** Each of `segments` with how it is drawn at `depth` on `terminal`; a control segment draws nothing. */
|
|
359
|
+
function looks(segments, depth, terminal) {
|
|
357
360
|
return [...segments].map((segment) => {
|
|
358
361
|
if (segment.isControl)
|
|
359
362
|
return { segment, look: undefined };
|
|
360
363
|
const style = segment.style ?? Style.null();
|
|
361
364
|
const wire = style.drawnColors(depth);
|
|
362
|
-
const slots = { fg: (wire.color ?? DEFAULT).getTruecolor(
|
|
365
|
+
const slots = { fg: (wire.color ?? DEFAULT).getTruecolor(terminal, true), bg: (wire.bgcolor ?? DEFAULT).getTruecolor(terminal, false) };
|
|
363
366
|
const reverse = style.reverse === true;
|
|
364
|
-
const
|
|
365
|
-
return { segment, look: { style, wire, slots, pair: { ...onScreen(slots, reverse), terminal, reverse }, attributes: cellAttributes(style), seam: drawnBy(style.anchor, JOIN) } };
|
|
367
|
+
const own = onScreen({ fg: wire.color?.isDefault ?? true, bg: wire.bgcolor?.isDefault ?? true }, reverse);
|
|
368
|
+
return { segment, look: { style, wire, slots, pair: { ...onScreen(slots, reverse), terminal: own, reverse }, attributes: cellAttributes(style), seam: drawnBy(style.anchor, JOIN) } };
|
|
366
369
|
});
|
|
367
370
|
}
|
|
368
371
|
/**
|
|
@@ -381,10 +384,11 @@ export function walkGlyphs(segments) {
|
|
|
381
384
|
if (segment.isControl)
|
|
382
385
|
return [];
|
|
383
386
|
const anchor = segment.style?.anchor;
|
|
387
|
+
const boxFrame = segment.style?.boxFrame;
|
|
384
388
|
// Cells into the segment, as `base.shiftedBy` counts them in the render.
|
|
385
389
|
let into = 0;
|
|
386
390
|
return [...graphemes(segment.text)].map((glyph) => {
|
|
387
|
-
const cell = glyph === "\n" ? undefined : { row, col, anchor: anchor && shiftAnchor(anchor, into) };
|
|
391
|
+
const cell = glyph === "\n" ? undefined : { row, col, anchor: anchor && shiftAnchor(anchor, into), boxFrame };
|
|
388
392
|
const width = cellLen(glyph);
|
|
389
393
|
[row, col] = glyph === "\n" ? [row + 1, 0] : [row, col + width];
|
|
390
394
|
into += width;
|
|
@@ -451,12 +455,12 @@ function drawsLines(style) {
|
|
|
451
455
|
/**
|
|
452
456
|
* What `looked` draws: `Drawn`'s measure, taken once per frame. Its `all`
|
|
453
457
|
* and `cells` are listed off `walked` the first time either is asked for,
|
|
454
|
-
* since
|
|
458
|
+
* since an effect on attributes alone never asks, and
|
|
455
459
|
* a glyph walk is what measuring the words alone, a segment at a time, is
|
|
456
460
|
* spared (`drawnOf`).
|
|
457
461
|
*/
|
|
458
|
-
function measured(looked, walked, depth,
|
|
459
|
-
const ground =
|
|
462
|
+
function measured(looked, walked, depth, terminal) {
|
|
463
|
+
const ground = terminal.backgroundColor.hex;
|
|
460
464
|
// [LAW:one-source-of-truth] One listing, each cell with whether it shows; `all` and `cells` are read off it.
|
|
461
465
|
let everything;
|
|
462
466
|
const list = () => walked().flatMap(({ look, glyphs }) => {
|
|
@@ -480,7 +484,7 @@ function measured(looked, walked, depth, theme) {
|
|
|
480
484
|
colors: new Set([...own("fg"), ...own("bg")]),
|
|
481
485
|
fills: new Set(own("bg")),
|
|
482
486
|
depth,
|
|
483
|
-
|
|
487
|
+
terminal,
|
|
484
488
|
};
|
|
485
489
|
}
|
|
486
490
|
/**
|
|
@@ -503,18 +507,24 @@ export function confined(effect, ...owners) {
|
|
|
503
507
|
return false;
|
|
504
508
|
};
|
|
505
509
|
// The frame as the effect sees it: the owners' cells alone. [LAW:one-source-of-truth] One narrowing, handed to both stages.
|
|
506
|
-
const narrowed = (drawn) =>
|
|
510
|
+
const narrowed = (drawn) => {
|
|
511
|
+
let all;
|
|
512
|
+
let cells;
|
|
513
|
+
return { ...drawn, all: () => (all ??= drawn.all().filter(owned)), cells: () => (cells ??= drawn.cells().filter(owned)) };
|
|
514
|
+
};
|
|
507
515
|
// The owners' cells change when the effect's frame does.
|
|
508
516
|
return (drawn) => Animation.map(effect(narrowed(drawn)), (frame) => (look, cell) => (owned(cell) ? frame(look, cell) : look));
|
|
509
517
|
}
|
|
510
518
|
/**
|
|
511
|
-
* What `segments`, drawn at `depth` on `
|
|
512
|
-
* as `Effected` measures its child for every frame
|
|
513
|
-
*
|
|
519
|
+
* What `segments`, drawn at `depth` on `terminal`, draw their words in, measured
|
|
520
|
+
* as `Effected` measures its child for every frame, so `options.terminal` means
|
|
521
|
+
* here what it means on a frame. Measure an element's output under an effect to
|
|
522
|
+
* read the contrast its words are left at.
|
|
514
523
|
*/
|
|
515
|
-
export function drawnOf(segments, depth,
|
|
516
|
-
const
|
|
517
|
-
|
|
524
|
+
export function drawnOf(segments, depth, terminal) {
|
|
525
|
+
const on = resolveTerminal(terminal, depth);
|
|
526
|
+
const looked = looks(segments, depth, on);
|
|
527
|
+
return measured(looked, () => walk(looked), depth, on);
|
|
518
528
|
}
|
|
519
529
|
/**
|
|
520
530
|
* A style's colour slots as the screen shows them, or the reverse: `reverse`
|
|
@@ -531,11 +541,11 @@ function onScreen(slots, reverse) {
|
|
|
531
541
|
* (`ColorSpec.matchMove`). A slot the depth shows no move in has none, and
|
|
532
542
|
* keeps the spec it was written in.
|
|
533
543
|
*/
|
|
534
|
-
function movedSpecs(from, to, depth,
|
|
544
|
+
function movedSpecs(from, to, depth, terminal) {
|
|
535
545
|
// [LAW:one-source-of-truth] The writer's surface, so one translucent colour draws one way in a frame.
|
|
536
546
|
const ground = to.bg.compositeOver(SURFACE_BLACK);
|
|
537
547
|
const glyph = to.fg.compositeOver(ground);
|
|
538
|
-
return { fg: ColorSpec.matchMove(from.fg, glyph, depth,
|
|
548
|
+
return { fg: ColorSpec.matchMove(from.fg, glyph, depth, terminal, true), bg: ColorSpec.matchMove(from.bg, ground, depth, terminal, false) };
|
|
539
549
|
}
|
|
540
550
|
/**
|
|
541
551
|
* Whether two cells' colours are written alike. A slot with no colour is not
|
|
@@ -73,7 +73,7 @@ export interface Loop {
|
|
|
73
73
|
* How much of `touch` each of `colors` (hex) can take: the largest share in
|
|
74
74
|
* [0, 1] at which every pair `drawn` shows it in, touched at the same
|
|
75
75
|
* strength, still reads at its resting contrast or `LEGIBLE`, whichever is
|
|
76
|
-
* lower, in the colours `Effected` writes it in at `drawn`'s depth and
|
|
76
|
+
* lower, in the colours `Effected` writes it in at `drawn`'s depth and terminal.
|
|
77
77
|
* A colour in a pair but not in `colors` takes none, and so does one
|
|
78
78
|
* the terminal supplies, whatever its hex, as in `onColors`. One of `colors`
|
|
79
79
|
* no pair writes has nothing to be measured against, and takes it all.
|
|
@@ -85,7 +85,7 @@ export interface Loop {
|
|
|
85
85
|
* the fill room to glow — and a darker colour left as it is always holds,
|
|
86
86
|
* because its lighter partner was settled against exactly that.
|
|
87
87
|
*/
|
|
88
|
-
export declare function shares({ pairs, depth,
|
|
88
|
+
export declare function shares({ pairs, depth, terminal }: Drawn, colors: ReadonlySet<string>, touch: Touch): Map<string, number>;
|
|
89
89
|
/**
|
|
90
90
|
* A loop as an `Effect` on the element's own colours, by hex, wherever a cell
|
|
91
91
|
* shows them, each touched at its share of the cell's strength: the share
|
|
@@ -93,10 +93,10 @@ export declare function shares({ pairs, depth, theme }: Drawn, colors: ReadonlyS
|
|
|
93
93
|
* (`Drawn`), touched as `Effected` writes them at the frame's depth — at 256
|
|
94
94
|
* colours a touched colour lands on a colour the slot can write, and the
|
|
95
95
|
* contrast it spends is that colour's; on a sixteen-colour terminal nothing
|
|
96
|
-
* moves (`
|
|
96
|
+
* moves (`stillWhereNothingMoves`). `colors` are the ones lit, by default every colour the element
|
|
97
97
|
* sets itself. A powerline strip's fill is the ground of its cells and the
|
|
98
98
|
* ink of the seam glyphs between them, so the arrow moves with the cell it
|
|
99
|
-
* points out of. A colour the terminal supplies (`
|
|
99
|
+
* points out of. A colour the terminal supplies (`Pair.terminal`) is
|
|
100
100
|
* not the element's, whatever its hex, and takes no share.
|
|
101
101
|
*
|
|
102
102
|
* Settling shares is costly, so it is done once for each set of colours an
|
|
@@ -122,7 +122,7 @@ export declare function onColors(loop: Loop, colors?: ReadonlySet<string>): Effe
|
|
|
122
122
|
* body. The element is where its frame lists
|
|
123
123
|
* cells, as the fireflies read it, so a padded or confined element has the
|
|
124
124
|
* ramp across what it shows. On a sixteen-colour terminal nothing moves
|
|
125
|
-
* (`
|
|
125
|
+
* (`stillWhereNothingMoves`): a gradient there is jumps between slots, and the cells
|
|
126
126
|
* hold as drawn.
|
|
127
127
|
*/
|
|
128
128
|
export declare function gradient(ramp: Animation<ColorRamp>, colors?: readonly string[]): Effect;
|
|
@@ -264,7 +264,7 @@ export declare function sparkle(curve: Curve, glow: ColorRgba, z: number): Loop;
|
|
|
264
264
|
export type Visibility = (t: number, drawn: Drawn) => (cell: EffectCell) => number;
|
|
265
265
|
/**
|
|
266
266
|
* A cell mixed toward the ground of the terminal the frame is drawn on
|
|
267
|
-
* (`Drawn.
|
|
267
|
+
* (`Drawn.terminal`) by how hidden it is: its fill toward the ground, and its ink
|
|
268
268
|
* toward that fill, so an element with fills of its own — a powerline strip —
|
|
269
269
|
* arrives and leaves whole, fill and words together, never as bare coloured
|
|
270
270
|
* blocks. Invisible is ink and fill equal to the ground. `swing` scales the
|
|
@@ -360,9 +360,38 @@ export declare function dissolveOut(curve: Curve, z: number): Transition;
|
|
|
360
360
|
* is drawn in at its floor (`floorOf`) — its resting contrast or WCAG AA,
|
|
361
361
|
* whichever is lower — as `shares` keeps the light's (`turnedBounds`). The
|
|
362
362
|
* turn only happens to the element's own colours (`Drawn.colors`), never the
|
|
363
|
-
* terminal's. On a sixteen-colour terminal nothing turns (`
|
|
363
|
+
* terminal's. On a sixteen-colour terminal nothing turns (`stillWhereNothingMoves`).
|
|
364
364
|
*/
|
|
365
365
|
export declare function wheel(curve: Curve, z: number): Effect;
|
|
366
|
+
/**
|
|
367
|
+
* Several effects on one element, as one effect: each layer is handed the look
|
|
368
|
+
* the one before it left and the same cell, so later layers are on top, and each
|
|
369
|
+
* recognises a cell by what the element drew (`EffectCell.pair`), never by the
|
|
370
|
+
* colour it is handed. A hue a layer turned onto another of the element's
|
|
371
|
+
* colours is not mistaken for it, and turns once. It changes when the soonest
|
|
372
|
+
* layer does, and with no layers it leaves every cell as drawn.
|
|
373
|
+
*
|
|
374
|
+
* Every word reads at its floor (`floorOf`), whatever the layers did and in
|
|
375
|
+
* whatever order. Each loop proves that for colours as the element drew them,
|
|
376
|
+
* and a colour another layer moved voids the proof: light lands at another
|
|
377
|
+
* luminance on a turned hue. So once the layers have moved a cell, its ground
|
|
378
|
+
* lands where its inks can still read (`groundBounds`), a function of the colour
|
|
379
|
+
* alone so one fill stays one fill, and an ink that does not read on it slides
|
|
380
|
+
* to the luminance that does, on the side of the ground it stands on while that
|
|
381
|
+
* side has room. A layer that takes an ink across its ground crosses it here too:
|
|
382
|
+
* nothing reads at the crossing, so the word jumps it. [LAW:single-enforcer]
|
|
383
|
+
* Both are measured as `Effected` writes them, and both hand back what they were
|
|
384
|
+
* given where it already holds, so a layer that proved its own floor is drawn as
|
|
385
|
+
* it would be alone. A frame that takes a ground past its bounds is the one
|
|
386
|
+
* exception, whatever its ink reads: a swap of ink and ground lands at the floor,
|
|
387
|
+
* since a bound cannot ask what one cell's ink does without splitting a fill.
|
|
388
|
+
* The floor outranks a colour being the terminal's: a terminal
|
|
389
|
+
* ink is moved here where a ground under it left it unreadable. The rule is the
|
|
390
|
+
* same for one layer as for five: a veil (`veiled`, or a transition's `effect`)
|
|
391
|
+
* stops at the floor here, so a transition goes in an `Effected` around this
|
|
392
|
+
* one, as a lifecycle places it.
|
|
393
|
+
*/
|
|
394
|
+
export declare function layered(...effects: readonly Effect[]): Effect;
|
|
366
395
|
/** One step of a signal: the ink it draws, and how long it is held, in seconds. */
|
|
367
396
|
export interface ColorStep {
|
|
368
397
|
readonly color: ColorRgba;
|
|
@@ -381,7 +410,7 @@ export interface ColorStep {
|
|
|
381
410
|
* (`ensureContrast`, the one slide a template's `readableOn` makes too);
|
|
382
411
|
* below truecolor, the nearest of the colours the depth has an index for, so on a
|
|
383
412
|
* sixteen-colour terminal it steps from slot to slot, the one effect here
|
|
384
|
-
* that does (`
|
|
413
|
+
* that does (`stillWhereNothingMoves`). A cell whose ground no offered colour reads
|
|
385
414
|
* on keeps the ink it rested in.
|
|
386
415
|
*
|
|
387
416
|
* The search is settled once for each ground and step — below truecolor,
|