@promptctl/rich-js 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +3 -2
  2. package/dist/core/color.d.ts +1 -0
  3. package/dist/core/color.js +3 -2
  4. package/dist/core/noise.d.ts +31 -0
  5. package/dist/core/noise.js +94 -0
  6. package/dist/core/paint.js +13 -2
  7. package/dist/core/pretty.d.ts +23 -2
  8. package/dist/core/pretty.js +54 -18
  9. package/dist/core/protocol.d.ts +2 -0
  10. package/dist/core/protocol.js +3 -2
  11. package/dist/core/selection.d.ts +12 -0
  12. package/dist/core/selection.js +14 -0
  13. package/dist/core/strip.d.ts +103 -72
  14. package/dist/core/strip.js +240 -212
  15. package/dist/core/style.js +9 -7
  16. package/dist/host/app.d.ts +68 -2
  17. package/dist/host/app.js +125 -15
  18. package/dist/host/index.d.ts +3 -1
  19. package/dist/host/index.js +1 -0
  20. package/dist/host/input.d.ts +84 -0
  21. package/dist/host/input.js +498 -0
  22. package/dist/host/terminal-host.d.ts +12 -0
  23. package/dist/host/terminal-host.js +2 -0
  24. package/dist/index.d.ts +9 -4
  25. package/dist/index.js +6 -3
  26. package/dist/node/terminal-host.d.ts +7 -0
  27. package/dist/node/terminal-host.js +7 -0
  28. package/dist/renderables/effect.d.ts +23 -8
  29. package/dist/renderables/effect.js +48 -26
  30. package/dist/renderables/effects.d.ts +252 -0
  31. package/dist/renderables/effects.js +565 -0
  32. package/dist/renderables/embed.d.ts +11 -5
  33. package/dist/renderables/embed.js +13 -6
  34. package/dist/renderables/list-view.d.ts +42 -0
  35. package/dist/renderables/list-view.js +78 -0
  36. package/dist/renderables/progress.d.ts +6 -0
  37. package/dist/renderables/progress.js +11 -4
  38. package/dist/renderables/progressBar.d.ts +22 -2
  39. package/dist/renderables/progressBar.js +51 -7
  40. package/dist/renderables/rule.d.ts +2 -2
  41. package/dist/renderables/rule.js +12 -10
  42. package/dist/renderables/table.js +5 -7
  43. package/dist/renderables/viewport.d.ts +30 -0
  44. package/dist/renderables/viewport.js +59 -3
  45. package/dist/widgets/dropdown.js +3 -3
  46. package/dist/widgets/event-router.d.ts +31 -40
  47. package/dist/widgets/event-router.js +52 -483
  48. package/dist/widgets/index.d.ts +2 -1
  49. package/dist/widgets/types.d.ts +8 -25
  50. package/dist/widgets/types.js +8 -0
  51. package/dist/widgets/widget-app.d.ts +22 -7
  52. package/dist/widgets/widget-app.js +22 -12
  53. package/package.json +22 -18
@@ -22,8 +22,9 @@
22
22
  * cell (`Style.shiftedBy`), so hit testing still finds the owner.
23
23
  * - A cell whose colours come back unchanged keeps its original `ColorSpec`,
24
24
  * and a segment whose every cell is unchanged is yielded as it was, so the
25
- * identity effect is byte-identical. Neighbouring cells given equal colours
26
- * stay one segment: a segment is cut only where the colours change.
25
+ * identity effect is byte-identical. Neighbouring cells the output depth
26
+ * writes alike stay one segment: a segment is cut only where the written
27
+ * colours change, so two colours that land on one index are one run.
27
28
  * - New colours are built with `ColorSpec.fromRgba`, never parsed from a
28
29
  * string, so no frame adds an entry to a parse cache.
29
30
  * - With no colour output (`colorSystem: null`) there is nothing for an
@@ -45,14 +46,25 @@ export interface CellColors {
45
46
  readonly bg: ColorRgba;
46
47
  }
47
48
  /**
48
- * Where a cell is in the output of the renderable an effect wraps, and its
49
- * seed: a number in [0, 1) that is the same for that cell on every frame, for
50
- * an effect that varies cell by cell (a per-cell offset in time, a sparkle).
49
+ * Where a cell is in the output of the renderable an effect wraps, its seed:
50
+ * a number in [0, 1) that is the same for that cell on every frame, for an
51
+ * effect that varies cell by cell (a per-cell offset in time, a sparkle) —
52
+ * and which of its on-screen colours are the terminal's.
51
53
  */
52
54
  export interface EffectCell {
53
55
  readonly row: number;
54
56
  readonly col: number;
55
57
  readonly seed: number;
58
+ /**
59
+ * Whether the glyph's colour and the colour behind it, as the screen shows
60
+ * them, are the terminal's own: a slot the element's style leaves unset or
61
+ * sets to `default`. Such a colour is resolved through the theme, so its
62
+ * hex can equal one the element wrote; this is what tells them apart.
63
+ */
64
+ readonly terminal: {
65
+ readonly fg: boolean;
66
+ readonly bg: boolean;
67
+ };
56
68
  }
57
69
  /** A cell's colours at time `t`, in seconds. Return `colors` to leave it be. */
58
70
  export type Effect = (colors: CellColors, cell: EffectCell, t: number) => CellColors;
@@ -84,13 +96,16 @@ export declare class Effected implements Renderable, Measurable {
84
96
  maximum: number;
85
97
  };
86
98
  /**
87
- * The style that draws a run's slot colours `to` where the segment drew
99
+ * The specs that draw a cell's slot colours `to` where the segment drew
88
100
  * `from` with `wire`: a slot the effect left alone keeps its written spec,
89
101
  * and a moved one is laid on what is beneath it — the ground on
90
102
  * `SURFACE_BLACK`, the glyph on that ground, as the writer lays any
91
103
  * translucent colour (`Style.drawnColors`) — then drawn in the colour nearest it at `depth`
92
- * that the terminal shows (`ColorSpec.matchOn`), so a colour moved a little
93
- * off a theme slot or the default colour is still drawn as that one.
104
+ * that the terminal shows (`ColorSpec.matchOn`). A move the depth cannot
105
+ * show is no move: where the written spec is as near the moved colour as
106
+ * that match, the slot keeps it, so below truecolor a colour moved a little
107
+ * off any colour it can write — a theme slot, the default colour, the cube —
108
+ * is still drawn as that one.
94
109
  */
95
110
  private respec;
96
111
  /** A cell's seed: FNV-1a of the key and the position, scaled into [0, 1). */
@@ -22,8 +22,9 @@
22
22
  * cell (`Style.shiftedBy`), so hit testing still finds the owner.
23
23
  * - A cell whose colours come back unchanged keeps its original `ColorSpec`,
24
24
  * and a segment whose every cell is unchanged is yielded as it was, so the
25
- * identity effect is byte-identical. Neighbouring cells given equal colours
26
- * stay one segment: a segment is cut only where the colours change.
25
+ * identity effect is byte-identical. Neighbouring cells the output depth
26
+ * writes alike stay one segment: a segment is cut only where the written
27
+ * colours change, so two colours that land on one index are one run.
27
28
  * - New colours are built with `ColorSpec.fromRgba`, never parsed from a
28
29
  * string, so no frame adds an entry to a parse cache.
29
30
  * - With no colour output (`colorSystem: null`) there is nothing for an
@@ -33,7 +34,7 @@
33
34
  * what the child drew, so a sweep crosses the element, not the screen.
34
35
  */
35
36
  import { cellLen, graphemes } from "../core/cells.js";
36
- import { ColorDepth, ColorSpec, SURFACE_BLACK } from "../core/color.js";
37
+ import { ColorDepth, ColorSpec, SURFACE_BLACK, rgbDistance } from "../core/color.js";
37
38
  import { fnv1a } from "../core/fnv1a.js";
38
39
  import { Measurement } from "../core/measure.js";
39
40
  import { Segment } from "../core/segment.js";
@@ -78,40 +79,44 @@ export class Effected {
78
79
  const wire = base.drawnColors(options.colorSystem ?? undefined);
79
80
  const ink = (wire.color ?? DEFAULT).getTruecolor(this.theme, true);
80
81
  const paper = (wire.bgcolor ?? DEFAULT).getTruecolor(this.theme, false);
81
- const from = onScreen({ fg: ink, bg: paper }, base);
82
- // Each grapheme's colours, merged into runs of equal colours as they
83
- // come; `undefined` is a run left as the segment drew it.
82
+ const slots = { fg: ink, bg: paper };
83
+ const from = onScreen(slots, base);
84
+ const terminal = onScreen({ fg: wire.color?.isDefault ?? true, bg: wire.bgcolor?.isDefault ?? true }, base);
85
+ // [LAW:one-source-of-truth] A run is cells the wire writes alike: each
86
+ // cell is drawn at the output depth first and merged on the colours that
87
+ // writes, so two truecolors landing on one index stay one run. A cell
88
+ // the effect gave its neighbour's colours is drawn as that neighbour was.
84
89
  const runs = [];
85
90
  for (const glyph of graphemes(segment.text)) {
86
- let colors;
91
+ let colors = slots;
87
92
  if (glyph === "\n") {
88
93
  row++;
89
94
  col = 0;
90
95
  }
91
96
  else {
92
- const to = this.effect(from, { row, col, seed: this.seed(row, col) }, this.t);
93
- colors = sameColors(to, from) ? undefined : to;
97
+ // Back from the screen to the style's slots: the same swap undoes itself.
98
+ colors = onScreen(this.effect(from, { row, col, seed: this.seed(row, col), terminal }, this.t), base);
94
99
  col += cellLen(glyph);
95
100
  }
96
101
  const last = runs[runs.length - 1];
97
- if (last !== undefined && sameRun(last.colors, colors)) {
102
+ const drawn = last !== undefined && sameColors(last.colors, colors) ? last.drawn : this.respec(wire, slots, colors, depth);
103
+ if (last !== undefined && writesAlike(last.drawn, drawn)) {
98
104
  last.text += glyph;
99
105
  last.cells += cellLen(glyph);
106
+ last.colors = colors;
100
107
  }
101
108
  else {
102
- runs.push({ text: glyph, cells: cellLen(glyph), colors });
109
+ runs.push({ text: glyph, cells: cellLen(glyph), colors, drawn });
103
110
  }
104
111
  }
105
- if (runs.every((run) => run.colors === undefined)) {
112
+ if (runs.every((run) => writesAlike(run.drawn, wire))) {
106
113
  yield segment;
107
114
  continue;
108
115
  }
109
116
  let offset = 0;
110
117
  for (const run of runs) {
111
118
  const shifted = base.shiftedBy(offset);
112
- // Back from the screen to the style's slots: the same swap undoes itself.
113
- const slots = run.colors === undefined ? undefined : onScreen(run.colors, base);
114
- const style = slots === undefined ? shifted : shifted.add(this.respec(wire, { fg: ink, bg: paper }, slots, depth));
119
+ const style = writesAlike(run.drawn, wire) ? shifted : shifted.add(Style.fromColor(run.drawn.color, run.drawn.bgcolor));
115
120
  yield new Segment(run.text, style);
116
121
  offset += run.cells;
117
122
  }
@@ -121,20 +126,26 @@ export class Effected {
121
126
  return Measurement.get(options, this.renderable);
122
127
  }
123
128
  /**
124
- * The style that draws a run's slot colours `to` where the segment drew
129
+ * The specs that draw a cell's slot colours `to` where the segment drew
125
130
  * `from` with `wire`: a slot the effect left alone keeps its written spec,
126
131
  * and a moved one is laid on what is beneath it — the ground on
127
132
  * `SURFACE_BLACK`, the glyph on that ground, as the writer lays any
128
133
  * translucent colour (`Style.drawnColors`) — then drawn in the colour nearest it at `depth`
129
- * that the terminal shows (`ColorSpec.matchOn`), so a colour moved a little
130
- * off a theme slot or the default colour is still drawn as that one.
134
+ * that the terminal shows (`ColorSpec.matchOn`). A move the depth cannot
135
+ * show is no move: where the written spec is as near the moved colour as
136
+ * that match, the slot keeps it, so below truecolor a colour moved a little
137
+ * off any colour it can write — a theme slot, the default colour, the cube —
138
+ * is still drawn as that one.
131
139
  */
132
140
  respec(wire, from, to, depth) {
133
141
  // [LAW:one-source-of-truth] The writer's surface, so one translucent colour draws one way in a frame.
134
142
  const ground = to.bg.compositeOver(SURFACE_BLACK);
135
143
  const glyph = to.fg.compositeOver(ground);
136
- const drawn = (spec, was, now, foreground) => sameColor(was, now) ? spec : ColorSpec.matchOn(now, depth, this.theme, foreground);
137
- return Style.fromColor(drawn(wire.color, from.fg, glyph, true), drawn(wire.bgcolor, from.bg, ground, false));
144
+ const drawn = (spec, was, now, foreground) => {
145
+ const match = ColorSpec.matchOn(now, depth, this.theme, foreground);
146
+ return rgbDistance(was, now) <= rgbDistance(match.getTruecolor(this.theme, foreground), now) ? spec : match;
147
+ };
148
+ return { color: drawn(wire.color, from.fg, glyph, true), bgcolor: drawn(wire.bgcolor, from.bg, ground, false) };
138
149
  }
139
150
  /** A cell's seed: FNV-1a of the key and the position, scaled into [0, 1). */
140
151
  seed(row, col) {
@@ -146,15 +157,26 @@ const DEFAULT = ColorSpec.default();
146
157
  * A style's colour slots as the screen shows them, or the reverse: `reverse`
147
158
  * swaps glyph and ground, and swapping twice is where you started.
148
159
  */
149
- function onScreen(colors, style) {
150
- return style.reverse === true ? { fg: colors.bg, bg: colors.fg } : colors;
160
+ function onScreen(slots, style) {
161
+ return style.reverse === true ? { fg: slots.bg, bg: slots.fg } : slots;
151
162
  }
152
- function sameColor(a, b) {
153
- return a.red === b.red && a.green === b.green && a.blue === b.blue && a.alpha === b.alpha;
163
+ /**
164
+ * Whether two cells' colours are written alike. A slot with no colour is not
165
+ * the default colour: it writes nothing, so a style a container lays beneath
166
+ * (`Segment.applyStyle`) still shows through it, where `39`/`49` would not.
167
+ */
168
+ function writesAlike(a, b) {
169
+ return slotWritesAlike(a.color, b.color, true) && slotWritesAlike(a.bgcolor, b.bgcolor, false);
170
+ }
171
+ function slotWritesAlike(a, b, foreground) {
172
+ return a === b || written(a, foreground) === written(b, foreground);
173
+ }
174
+ function written(spec, foreground) {
175
+ return spec?.getAnsiCodes(foreground).join(";") ?? "";
154
176
  }
155
177
  function sameColors(a, b) {
156
178
  return sameColor(a.fg, b.fg) && sameColor(a.bg, b.bg);
157
179
  }
158
- function sameRun(a, b) {
159
- return a === undefined || b === undefined ? a === b : sameColors(a, b);
180
+ function sameColor(a, b) {
181
+ return a.red === b.red && a.green === b.green && a.blue === b.blue && a.alpha === b.alpha;
160
182
  }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * The effect catalogue: shimmer, pulse, sparkle and wheel, which loop for as
3
+ * long as they are drawn, and fade-in and dissolve-out, which run once and
4
+ * are done. Each is an `Effect` for `Effected` (`./effect.ts`); the defaults
5
+ * in `EFFECT_CURVES` are the ones signed off by eye in the effects-feel demo
6
+ * (`npm run effects-feel`), at 30 fps and at one frame a second.
7
+ *
8
+ * Each effect is something a first-time observer already knows from life —
9
+ * a slow glow, sunlight moving on water, the colours of daylight, fireflies, ink
10
+ * blooming in water, mist lifting — because a motion the eye recognises
11
+ * reads as calm and alive, where a motion it has to learn reads as a widget.
12
+ * What makes those motions familiar is what every curve here keeps:
13
+ *
14
+ * - Nothing jumps. Every curve is continuous in time, built from smooth
15
+ * noise (`core/noise.ts`) and smooth waveforms, so at one frame a second a cell
16
+ * moves a little and at thirty it glides.
17
+ * - Nothing repeats exactly. Rhythms are perturbed by slow noise, and each
18
+ * breath, firefly's flash and pass of light is an event of its own, with
19
+ * its own strength and moment, the way no breath or gust is the last one
20
+ * again. A weaker event is a shorter one, so its strength costs nothing
21
+ * in smoothness.
22
+ * - Neighbours move together. Variation is spatially coherent — patches,
23
+ * filaments, fronts — never per-cell static.
24
+ * - Light only ever lights, and never takes the words away. Ink and the
25
+ * fill under it both move toward the light and never darken, each by its
26
+ * own share: the share a colour can take while every cell it is drawn in
27
+ * still reads at its resting contrast or WCAG AA, whichever is lower. A
28
+ * cell with contrast to spare glows brightly; one with none barely moves.
29
+ * - Elements are apart. Each sits at its own `z` in the noise, so two
30
+ * elements under one effect do not move in lockstep.
31
+ *
32
+ * Colour moves are in OKLCH, where equal steps look equal. A curve's `ease`
33
+ * maps its intensity in [0, 1] — how strongly the effect acts on a cell at a
34
+ * moment — onto its swing, for the loops and the transitions alike.
35
+ *
36
+ * [LAW:no-ambient-temporal-coupling] No curve reads a clock; `t` is the
37
+ * effect's argument, so the frame owner decides when to sample.
38
+ *
39
+ * Every import below names something the package exports, but for `noise`,
40
+ * which the docs' effects playground page supplies itself: each of its
41
+ * cards runs this file's own declarations as the program a visitor edits
42
+ * (`examples/effects-playground/programs.ts`), on the published library.
43
+ */
44
+ import { ColorRgba } from "../core/color.js";
45
+ import { type Ease } from "../core/easing.js";
46
+ import type { CellColors, Effect, EffectCell } from "./effect.js";
47
+ /** What a loop does to one colour at strength `w` in [0, 1]: at 0, nothing. */
48
+ export type Touch = (color: ColorRgba, w: number) => ColorRgba;
49
+ /** How strongly a loop acts on a cell at a moment, in [0, 1]; `onColors` holds a field outside it to it. */
50
+ export type Field = (cell: EffectCell, t: number) => number;
51
+ /** A looping effect: what it does to a colour, and where and when, how strongly. */
52
+ export interface Loop {
53
+ readonly touch: Touch;
54
+ readonly field: Field;
55
+ }
56
+ /**
57
+ * A cell's ink and ground as the screen shows them, and which of the two are
58
+ * the terminal's: what an `Effect` is handed for that cell.
59
+ */
60
+ export interface Pair extends CellColors {
61
+ readonly terminal: EffectCell["terminal"];
62
+ }
63
+ /**
64
+ * How much of `touch` each of `colors` (hex) can take: the largest share in
65
+ * [0, 1] at which every pair in `pairs` that shows it, touched at the same
66
+ * strength, still reads at its resting contrast or `LEGIBLE`, whichever is
67
+ * lower. A colour in a pair but not in `colors` takes none, and so does one
68
+ * the terminal supplies, whatever its hex, as in `onColors`. One of `colors`
69
+ * no pair writes has nothing to be measured against, and throws.
70
+ *
71
+ * Colours are settled lightest first, each against every partner: one
72
+ * already settled at its share, one not yet settled as it is. So the lighter
73
+ * colour of a cell takes the light first and its darker partner spends
74
+ * whatever contrast is left — pale lettering lifting off a dark fill gives
75
+ * the fill room to glow — and a darker colour left as it is always holds,
76
+ * because its lighter partner was settled against exactly that.
77
+ */
78
+ export declare function shares(pairs: readonly Pair[], colors: ReadonlySet<string>, touch: Touch): Map<string, number>;
79
+ /**
80
+ * A loop as an `Effect` on a subject's own colours, by hex, wherever a cell
81
+ * shows them, each touched at its share of the cell's strength. A powerline
82
+ * strip's fill is the ground of its cells and the ink of the seam glyphs
83
+ * between them, so the arrow moves with the cell it points out of. A colour
84
+ * the terminal supplies (`EffectCell.terminal`) is not the subject's, whatever
85
+ * its hex, and takes no share.
86
+ */
87
+ export declare function onColors(share: ReadonlyMap<string, number>, loop: Loop): Effect;
88
+ /** How the curves below are tuned: one period (or duration), ease and swing. */
89
+ export interface Curve {
90
+ /** Seconds: a looping effect's period, a transition's duration. */
91
+ readonly seconds: number;
92
+ /** Intensity in [0, 1] to the fraction of `swing` applied. One that overshoots, as a CSS `cubic-bezier` may, saturates: an effect never acts past its swing. */
93
+ readonly ease: Ease;
94
+ /** How far the move goes at full intensity, in [0, 1]; what 1 means is the effect's. */
95
+ readonly swing: number;
96
+ }
97
+ /**
98
+ * Each effect's curve as it was signed off. Every loop's period is a prime
99
+ * number of seconds, so no two loops come back into step within a sitting.
100
+ * The periods are long on purpose: at one frame a second a loop's colour
101
+ * moves by no more than a just-noticeable step from one frame to the next.
102
+ */
103
+ export declare const EFFECT_CURVES: {
104
+ readonly shimmer: {
105
+ readonly seconds: 109;
106
+ readonly ease: Ease;
107
+ readonly swing: 0.9;
108
+ };
109
+ readonly pulse: {
110
+ readonly seconds: 43;
111
+ readonly ease: Ease;
112
+ readonly swing: 0.85;
113
+ };
114
+ readonly sparkle: {
115
+ readonly seconds: 139;
116
+ readonly ease: Ease;
117
+ readonly swing: 0.95;
118
+ };
119
+ readonly wheel: {
120
+ readonly seconds: 907;
121
+ readonly ease: Ease;
122
+ readonly swing: 1;
123
+ };
124
+ readonly fade: {
125
+ readonly seconds: 20;
126
+ readonly ease: Ease;
127
+ readonly swing: 1;
128
+ };
129
+ readonly dissolve: {
130
+ readonly seconds: 30;
131
+ readonly ease: Ease;
132
+ readonly swing: 1;
133
+ };
134
+ };
135
+ /** The lights the loops were signed off casting, warm as sunlight and fireflies are. */
136
+ export declare const EFFECT_LIGHTS: {
137
+ readonly sun: ColorRgba;
138
+ readonly firefly: ColorRgba;
139
+ };
140
+ /** How far a shimmer's light reaches either side of its centre, in columns, as signed off. */
141
+ export declare const SHIMMER_WIDTH = 24;
142
+ /**
143
+ * A colour under `glow`: the one way every light here falls. It moves `w` of
144
+ * the way toward the light's colour, and never below its own lightness —
145
+ * light can warm a colour and lift it, never darken it. A colour already
146
+ * lighter than the light only warms, and white, which has no warmer shade at
147
+ * its lightness, stays white.
148
+ */
149
+ export declare function light(glow: ColorRgba): Touch;
150
+ /**
151
+ * Pulse, as a gentle glow on one element of a screen: the colours handed to
152
+ * it, an element's own, warm into `light` and settle back, over and over, the
153
+ * way an indicator breathes while something is waiting. Swing: how far into
154
+ * the light it goes at the top of a beat, 0–1. The element glows as one —
155
+ * every cell it is drawn in at once — and the colours not handed to it, the
156
+ * rest of the screen, stay as they are, so what pulses is what was chosen.
157
+ * Each element keeps a time of its own, a little quicker or slower than
158
+ * another's by `z`, and no beat is quite the last: the rhythm wanders ahead
159
+ * and behind its metronome and each beat goes a little deeper or shallower
160
+ * than the one before. A beat starts and ends in rest, so `t = 0` draws the
161
+ * element untouched.
162
+ */
163
+ export declare function pulse(curve: Curve, glow: ColorRgba, z: number): Loop;
164
+ /**
165
+ * Shimmer, as sunlight moving across water. Swing: how far into `light` the
166
+ * brightest glint goes, 0–1. A soft band of light, `width` columns either
167
+ * side of its centre, crosses columns 0 to `span`, entering and leaving
168
+ * fully off the row, `STRIDE` columns in about three quarters of a period. Its passes
169
+ * come the way light comes and goes on water as clouds move: while the sun
170
+ * is out they are bright and follow close on one another, one sometimes
171
+ * catching the last; under cloud they thin to faint ones with the row still
172
+ * for a while between, and the light is never gone for long. Each crosses at its own unhurried pace, drifting a little ahead
173
+ * and behind as it goes. Inside a band the light is a faint glow broken
174
+ * into a glitter path — bright filaments where two ripples cross — carried
175
+ * on a slow current and re-forming as they go, so the glints dance while
176
+ * the band glides. Before the first pass enters, the row is untouched.
177
+ */
178
+ export declare function shimmer(curve: Curve, span: number, width: number, glow: ColorRgba, z: number): Loop;
179
+ /**
180
+ * Sparkle, as fireflies. Swing: how far into the firefly's `light` a glow
181
+ * goes at its brightest, 0–1. About one firefly to every eight columns of
182
+ * `span`, each keeping near a home of its own. A firefly flashes — kindles,
183
+ * drifts a few cells glowing, slowing as it fades — then flies on in the
184
+ * dark and flashes again somewhere a few cells off, the way fireflies at
185
+ * dusk are seen: a short lit stroke, then gone, each time a little
186
+ * elsewhere. Each keeps its own irregular time, now and then letting a turn
187
+ * pass dark, so most moments a few are lit, at different brightnesses. Its
188
+ * light is a soft halo several cells wide, brightest where it is, so as it
189
+ * drifts the glow slides between cells rather than hopping. `seconds` is a firefly's
190
+ * turn: its brightest flash, rise and fall, takes `FLASH` of it, and a
191
+ * dimmer one less.
192
+ */
193
+ export declare function sparkle(curve: Curve, span: number, glow: ColorRgba, z: number): Loop;
194
+ /**
195
+ * How much of a cell shows at `t`: 1 is the cell as drawn, 0 is the cell gone
196
+ * to the terminal's ground. Read per cell, so cells can arrive or leave apart. One
197
+ * outside [0, 1] is held to it.
198
+ */
199
+ export type Visibility = (cell: EffectCell, t: number) => number;
200
+ /**
201
+ * A cell mixed toward the terminal's `ground` by how hidden it is: its fill
202
+ * toward the ground, and its ink toward that fill, so an element with fills
203
+ * of its own — a powerline strip — arrives and leaves whole, fill and words
204
+ * together, never as bare coloured blocks. Invisible is ink and fill equal
205
+ * to the ground. `swing` scales the hiding — 1 reaches fully invisible. A
206
+ * cell wholly shown is returned as it was.
207
+ */
208
+ export declare function veiled(visibility: Visibility, swing: number, ground: ColorRgba): Effect;
209
+ /**
210
+ * An effect that runs once: a fade-in or a dissolve-out from its start time
211
+ * over its curve's `seconds`. `done(t)` is true from the moment every cell
212
+ * has arrived or gone, so the caller knows when to stop drawing an element
213
+ * that is leaving, or to drop the effect from one that has arrived.
214
+ */
215
+ export interface Transition {
216
+ readonly effect: Effect;
217
+ done(t: number): boolean;
218
+ }
219
+ /**
220
+ * Fade-in starting at `start`, as ink blooming in water: patches of the
221
+ * element surface first and the rest follows outward from them, each cell
222
+ * rising smoothly over its own part of the duration. Whole at
223
+ * `start + seconds`.
224
+ */
225
+ export declare function fadeIn(curve: Curve, start: number, z: number, ground: ColorRgba): Transition;
226
+ /**
227
+ * Dissolve-out starting at `start`, as mist lifting: the element thins in
228
+ * drifting patches, each cell fading smoothly over its own part of the
229
+ * duration, until nothing is left at `start + seconds`. Mist does not lift
230
+ * in one breath: towards the end, patches of what is left — the later a cell
231
+ * goes, the likelier — rise back part way, as the last of it eddies, before
232
+ * they thin away for good.
233
+ */
234
+ export declare function dissolveOut(curve: Curve, start: number, z: number, ground: ColorRgba): Transition;
235
+ /**
236
+ * Wheel, as the colour of daylight going round: every hue turning the whole
237
+ * way round once a period — far too slowly to be seen moving, so a glance
238
+ * sees a steady palette and an hour's absence a different one — each
239
+ * segment of the element at its own pace. Swing: how far the segments stray
240
+ * from one another, 0–1, 1 being half a turn apart. A segment is a fill of
241
+ * the element's own — one of `fills`, the colours it draws as a ground — and
242
+ * it turns at its own pace wherever it is drawn: as the ground of its cells,
243
+ * and as the ink of the seam glyph pointing out of it into the next, so a
244
+ * powerline cap keeps the colour of the cell it caps. An ink that is no fill
245
+ * turns with the ground it sits on, so a cell's words and fill turn
246
+ * together; on the terminal's ground, a run of text's ink is a segment of
247
+ * its own. A segment's stray is slow noise of its own, at `STRAY_PERIODS`,
248
+ * so no two keep step, and the row never shows one spread of hues twice.
249
+ * Lightness and chroma are kept, so the words read as they did; the turn
250
+ * only happens to `colors`, the element's own, never the terminal's.
251
+ */
252
+ export declare function wheel(curve: Curve, colors: ReadonlySet<string>, fills: ReadonlySet<string>, z: number): Effect;