@promptctl/rich-js 0.11.0 → 0.12.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/dist/core/oklch.d.ts +34 -0
- package/dist/core/oklch.js +52 -4
- package/dist/core/strip.d.ts +19 -2
- package/dist/core/strip.js +46 -10
- package/dist/core/style.d.ts +2 -1
- package/dist/core/style.js +1 -1
- package/dist/core/text.js +3 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/core/oklch.d.ts
CHANGED
|
@@ -41,6 +41,13 @@ export interface ThemeKey {
|
|
|
41
41
|
/** Additive on lightness; applied *after* the scale. */
|
|
42
42
|
readonly lightnessShift: number;
|
|
43
43
|
}
|
|
44
|
+
/** How far `Oklch.mixAxes` moves each axis toward its target, each in [0, 1]. */
|
|
45
|
+
export interface OklchWeights {
|
|
46
|
+
readonly l: number;
|
|
47
|
+
readonly c: number;
|
|
48
|
+
readonly h: number;
|
|
49
|
+
readonly alpha: number;
|
|
50
|
+
}
|
|
44
51
|
export declare const IDENTITY: ThemeKey;
|
|
45
52
|
/**
|
|
46
53
|
* Flip lightness around the midpoint (`L' = 1 - L`) with hue and chroma
|
|
@@ -70,6 +77,7 @@ export declare function isIdentityKey(k: ThemeKey): boolean;
|
|
|
70
77
|
* sRGB-quantization step normalizes them.
|
|
71
78
|
*/
|
|
72
79
|
export declare class Oklch {
|
|
80
|
+
#private;
|
|
73
81
|
readonly l: number;
|
|
74
82
|
readonly c: number;
|
|
75
83
|
readonly h: number;
|
|
@@ -115,6 +123,32 @@ export declare class Oklch {
|
|
|
115
123
|
* there too.
|
|
116
124
|
*/
|
|
117
125
|
mix(toward: Oklch, t: number): Oklch;
|
|
126
|
+
/**
|
|
127
|
+
* `mix` with each axis moved its own share of the way: `weights.l` of the
|
|
128
|
+
* lightness gap, `weights.c` of the chroma gap, and so on, each in [0, 1].
|
|
129
|
+
* `mix(toward, t)` is `mixAxes` with every weight `t` — one interpolation,
|
|
130
|
+
* so the shorter-arc hue and the powerless-endpoint rule are the same
|
|
131
|
+
* whichever is called.
|
|
132
|
+
*
|
|
133
|
+
* It exists because perceptual axes are independent: a tint can take most of
|
|
134
|
+
* a hue's colourfulness while keeping close to the lightness it started at,
|
|
135
|
+
* which a single `t` cannot say — raising `t` for chroma drags lightness with
|
|
136
|
+
* it. Every weight is required: an axis left out would need a default, and
|
|
137
|
+
* "unchanged" (0) and "same as the others" are both plausible readings.
|
|
138
|
+
*
|
|
139
|
+
* A weak `h` beside a strong `c` shows the starting colour's hue at high
|
|
140
|
+
* chroma. The powerless rule covers only a truly achromatic start, so a
|
|
141
|
+
* near-grey whose hue is noise (a theme surface at c ≈ 0.004) keeps that
|
|
142
|
+
* noise in proportion. To land on the target's hue, pass `h: 1`.
|
|
143
|
+
*/
|
|
144
|
+
mixAxes(toward: Oklch, weights: OklchWeights): Oklch;
|
|
145
|
+
/**
|
|
146
|
+
* ΔE_OK — the Euclidean distance between this colour and `other` in OKLab
|
|
147
|
+
* (CSS Color 4's `deltaEOK`), where ~0.02 is the smallest difference the eye
|
|
148
|
+
* resolves. Alpha is not a coordinate of the space and does not count.
|
|
149
|
+
* Symmetric and pure.
|
|
150
|
+
*/
|
|
151
|
+
deltaE(other: Oklch): number;
|
|
118
152
|
/** Linear-sRGB coordinates for an explicit (l, C, h). Pure; `toRgba` passes
|
|
119
153
|
* already-normalized values so this never sees out-of-range inputs. */
|
|
120
154
|
private toLinearRgb;
|
package/dist/core/oklch.js
CHANGED
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
* nothing else from inside the project. Nothing in core/ depends back on
|
|
21
21
|
* this file.
|
|
22
22
|
*/
|
|
23
|
+
var _a;
|
|
23
24
|
import { ColorRgba } from "./color.js";
|
|
25
|
+
// The axes `mixAxes` checks, named once so a weights object's own keys — a
|
|
26
|
+
// missing one, or an extra field riding along — never decide what is checked.
|
|
27
|
+
const OKLCH_AXES = ["l", "c", "h", "alpha"];
|
|
24
28
|
export const IDENTITY = Object.freeze({
|
|
25
29
|
hueShift: 0,
|
|
26
30
|
chromaScale: 1,
|
|
@@ -144,7 +148,7 @@ export class Oklch {
|
|
|
144
148
|
const bLab = 0.0259040371 * lCube + 0.7827717662 * mCube - 0.8086757660 * sCube;
|
|
145
149
|
const C = Math.sqrt(aLab * aLab + bLab * bLab);
|
|
146
150
|
const H = wrapHue(Math.atan2(bLab, aLab) * (180 / Math.PI));
|
|
147
|
-
return new
|
|
151
|
+
return new _a(L, C, hueOf(C, H), a);
|
|
148
152
|
}
|
|
149
153
|
/**
|
|
150
154
|
* Polar → OKLab → linear → sRGB (with chroma-bisection gamut clamping).
|
|
@@ -181,7 +185,7 @@ export class Oklch {
|
|
|
181
185
|
const newL = clamp01(this.l * k.lightnessScale + k.lightnessShift);
|
|
182
186
|
const newC = Math.max(0, this.c * k.chromaScale);
|
|
183
187
|
const newH = wrapHue(this.h + k.hueShift);
|
|
184
|
-
return new
|
|
188
|
+
return new _a(newL, newC, hueOf(newC, newH), this.alpha);
|
|
185
189
|
}
|
|
186
190
|
/**
|
|
187
191
|
* The color `t` of the way from this one toward `toward`, in OKLCH.
|
|
@@ -208,6 +212,38 @@ export class Oklch {
|
|
|
208
212
|
if (!(t >= 0 && t <= 1)) {
|
|
209
213
|
throw new RangeError(`Oklch.mix: t must be in [0, 1]; got ${t}`);
|
|
210
214
|
}
|
|
215
|
+
return this.#interpolate(toward, t, t, t, t);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* `mix` with each axis moved its own share of the way: `weights.l` of the
|
|
219
|
+
* lightness gap, `weights.c` of the chroma gap, and so on, each in [0, 1].
|
|
220
|
+
* `mix(toward, t)` is `mixAxes` with every weight `t` — one interpolation,
|
|
221
|
+
* so the shorter-arc hue and the powerless-endpoint rule are the same
|
|
222
|
+
* whichever is called.
|
|
223
|
+
*
|
|
224
|
+
* It exists because perceptual axes are independent: a tint can take most of
|
|
225
|
+
* a hue's colourfulness while keeping close to the lightness it started at,
|
|
226
|
+
* which a single `t` cannot say — raising `t` for chroma drags lightness with
|
|
227
|
+
* it. Every weight is required: an axis left out would need a default, and
|
|
228
|
+
* "unchanged" (0) and "same as the others" are both plausible readings.
|
|
229
|
+
*
|
|
230
|
+
* A weak `h` beside a strong `c` shows the starting colour's hue at high
|
|
231
|
+
* chroma. The powerless rule covers only a truly achromatic start, so a
|
|
232
|
+
* near-grey whose hue is noise (a theme surface at c ≈ 0.004) keeps that
|
|
233
|
+
* noise in proportion. To land on the target's hue, pass `h: 1`.
|
|
234
|
+
*/
|
|
235
|
+
mixAxes(toward, weights) {
|
|
236
|
+
for (const axis of OKLCH_AXES) {
|
|
237
|
+
const w = weights[axis];
|
|
238
|
+
if (!(w >= 0 && w <= 1)) {
|
|
239
|
+
throw new RangeError(`Oklch.mixAxes: ${axis} weight must be in [0, 1]; got ${w}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return this.#interpolate(toward, weights.l, weights.c, weights.h, weights.alpha);
|
|
243
|
+
}
|
|
244
|
+
// The one interpolation both entry points share; each has already checked
|
|
245
|
+
// its own weights, so this is the arithmetic alone.
|
|
246
|
+
#interpolate(toward, wl, wc, wh, walpha) {
|
|
211
247
|
const thisHasHue = this.c >= ACHROMATIC_EPS;
|
|
212
248
|
const towardHasHue = toward.c >= ACHROMATIC_EPS;
|
|
213
249
|
const fromH = wrapHue(thisHasHue ? this.h : towardHasHue ? toward.h : 0);
|
|
@@ -219,8 +255,19 @@ export class Oklch {
|
|
|
219
255
|
const arc = toH - fromH;
|
|
220
256
|
const fromU = arc < -180 ? fromH - 360 : fromH;
|
|
221
257
|
const toU = arc > 180 ? toH - 360 : toH;
|
|
222
|
-
const c = lerp(this.c, toward.c,
|
|
223
|
-
return new
|
|
258
|
+
const c = lerp(this.c, toward.c, wc);
|
|
259
|
+
return new _a(lerp(this.l, toward.l, wl), c, hueOf(c, wrapHue(lerp(fromU, toU, wh))), lerp(this.alpha, toward.alpha, walpha));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* ΔE_OK — the Euclidean distance between this colour and `other` in OKLab
|
|
263
|
+
* (CSS Color 4's `deltaEOK`), where ~0.02 is the smallest difference the eye
|
|
264
|
+
* resolves. Alpha is not a coordinate of the space and does not count.
|
|
265
|
+
* Symmetric and pure.
|
|
266
|
+
*/
|
|
267
|
+
deltaE(other) {
|
|
268
|
+
const a = (this.h * Math.PI) / 180;
|
|
269
|
+
const b = (other.h * Math.PI) / 180;
|
|
270
|
+
return Math.hypot(this.l - other.l, this.c * Math.cos(a) - other.c * Math.cos(b), this.c * Math.sin(a) - other.c * Math.sin(b));
|
|
224
271
|
}
|
|
225
272
|
/** Linear-sRGB coordinates for an explicit (l, C, h). Pure; `toRgba` passes
|
|
226
273
|
* already-normalized values so this never sees out-of-range inputs. */
|
|
@@ -262,3 +309,4 @@ export class Oklch {
|
|
|
262
309
|
return lo;
|
|
263
310
|
}
|
|
264
311
|
}
|
|
312
|
+
_a = Oklch;
|
package/dist/core/strip.d.ts
CHANGED
|
@@ -58,12 +58,29 @@ export declare class Strip<T extends StyledRenderable = StyledRenderable> implem
|
|
|
58
58
|
constructor(items: readonly T[], joiner: Joiner<T>);
|
|
59
59
|
render(options: RenderOptions): Iterable<Segment>;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The least ΔE_OK two neighbouring backgrounds must differ by for the powerline
|
|
63
|
+
* arrow between them to be seen. Below it the arrow is drawn in a colour the
|
|
64
|
+
* eye cannot tell from its own background, so the joiner draws the divider
|
|
65
|
+
* instead. Twice the ~.02 threshold of a visible difference, because a seam is
|
|
66
|
+
* one cell wide.
|
|
67
|
+
*/
|
|
68
|
+
export declare const SEAM_MIN_DELTA_E = 0.04;
|
|
61
69
|
export interface PowerlineJoinerOptions {
|
|
62
|
-
/** Glyph used for every join
|
|
63
|
-
glyph
|
|
70
|
+
/** Glyph used for every join. */
|
|
71
|
+
glyph: string;
|
|
72
|
+
/**
|
|
73
|
+
* Glyph drawn between neighbours whose backgrounds the eye cannot tell apart,
|
|
74
|
+
* in the left item's text colour — the arrow itself would vanish into the
|
|
75
|
+
* shared background.
|
|
76
|
+
*/
|
|
77
|
+
divider: string;
|
|
64
78
|
}
|
|
79
|
+
/** The powerline pair: U+E0B0 (right-arrow) divided by U+E0B1 (thin right-arrow). */
|
|
80
|
+
export declare const POWERLINE_JOINER_GLYPHS: Readonly<PowerlineJoinerOptions>;
|
|
65
81
|
export declare class PowerlineJoiner<T extends StyledRenderable = StyledRenderable> implements Joiner<T> {
|
|
66
82
|
private readonly _glyph;
|
|
83
|
+
private readonly _divider;
|
|
67
84
|
constructor(options?: PowerlineJoinerOptions);
|
|
68
85
|
join(left: T | null, right: T | null): Renderable;
|
|
69
86
|
}
|
package/dist/core/strip.js
CHANGED
|
@@ -26,8 +26,9 @@
|
|
|
26
26
|
* styling, so the cell type does too.
|
|
27
27
|
*/
|
|
28
28
|
import { Segment } from "./segment.js";
|
|
29
|
-
import { Style } from "./style.js";
|
|
29
|
+
import { Style, SURFACE_BLACK } from "./style.js";
|
|
30
30
|
import { ColorSpec, blendRgb } from "./color.js";
|
|
31
|
+
import { Oklch } from "./oklch.js";
|
|
31
32
|
// --- Strip ---
|
|
32
33
|
export class Strip {
|
|
33
34
|
items;
|
|
@@ -100,10 +101,38 @@ function bgAsFg(edge) {
|
|
|
100
101
|
function paintableBg(bg) {
|
|
101
102
|
return bg !== undefined && !bg.isDefault ? bg : undefined;
|
|
102
103
|
}
|
|
104
|
+
// --- PowerlineJoiner ---
|
|
105
|
+
/**
|
|
106
|
+
* The least ΔE_OK two neighbouring backgrounds must differ by for the powerline
|
|
107
|
+
* arrow between them to be seen. Below it the arrow is drawn in a colour the
|
|
108
|
+
* eye cannot tell from its own background, so the joiner draws the divider
|
|
109
|
+
* instead. Twice the ~.02 threshold of a visible difference, because a seam is
|
|
110
|
+
* one cell wide.
|
|
111
|
+
*/
|
|
112
|
+
export const SEAM_MIN_DELTA_E = 0.04;
|
|
113
|
+
// Two backgrounds the eye cannot tell apart. What is measured is what is
|
|
114
|
+
// drawn: each colour flattened onto the substrate Style.toSgrCodes flattens it
|
|
115
|
+
// onto, so two alphas over one RGB are the two greys they render as. A palette
|
|
116
|
+
// colour has no value here, so two of them are the same only when they are the
|
|
117
|
+
// same palette slot — `type` + `number`, never the name, which spells one slot
|
|
118
|
+
// many ways ("red", "color(1)").
|
|
119
|
+
function indistinct(a, b) {
|
|
120
|
+
if (b === undefined)
|
|
121
|
+
return false;
|
|
122
|
+
const av = a.flattenAlpha(SURFACE_BLACK).value;
|
|
123
|
+
const bv = b.flattenAlpha(SURFACE_BLACK).value;
|
|
124
|
+
return av !== undefined && bv !== undefined
|
|
125
|
+
? Oklch.fromRgba(av).deltaE(Oklch.fromRgba(bv)) < SEAM_MIN_DELTA_E
|
|
126
|
+
: a.type === b.type && a.number === b.number;
|
|
127
|
+
}
|
|
128
|
+
/** The powerline pair: U+E0B0 (right-arrow) divided by U+E0B1 (thin right-arrow). */
|
|
129
|
+
export const POWERLINE_JOINER_GLYPHS = Object.freeze({ glyph: "\ue0b0", divider: "\ue0b1" });
|
|
103
130
|
export class PowerlineJoiner {
|
|
104
131
|
_glyph;
|
|
105
|
-
|
|
106
|
-
|
|
132
|
+
_divider;
|
|
133
|
+
constructor(options = POWERLINE_JOINER_GLYPHS) {
|
|
134
|
+
this._glyph = options.glyph;
|
|
135
|
+
this._divider = options.divider;
|
|
107
136
|
}
|
|
108
137
|
join(left, right) {
|
|
109
138
|
// [LAW:dataflow-not-control-flow] One expression for all three positions
|
|
@@ -117,20 +146,27 @@ export class PowerlineJoiner {
|
|
|
117
146
|
// colourless arrow is not drawn.)
|
|
118
147
|
// • no right bg — the end cap — bleeds the left colour out over the
|
|
119
148
|
// terminal background (fg = left bg, no bg).
|
|
120
|
-
// Equal REAL bgs still emit:
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
149
|
+
// Equal REAL bgs still emit: a same-bg seam between two distinct items is a
|
|
150
|
+
// structural boundary, never suppressed. The arrow would be drawn in its own
|
|
151
|
+
// background colour there and vanish, so the seam is the DIVIDER instead, in
|
|
152
|
+
// the left item's text colour — the vim-airline convention for neighbours
|
|
153
|
+
// that share a background. "Equal" is perceptual (SEAM_MIN_DELTA_E): an
|
|
154
|
+
// arrow a hair off its background is as invisible as one exactly on it.
|
|
155
|
+
// Background colour is paint, not structure; its ABSENCE (nothing to paint)
|
|
156
|
+
// is the only thing that elides the separator, and that is paint logic.
|
|
125
157
|
// "Absent" = no bg OR the terminal default (transparent) — paintableBg folds
|
|
126
158
|
// both to undefined so an explicit `… on default` cannot smuggle a separator.
|
|
127
159
|
const glyph = this._glyph;
|
|
160
|
+
const divider = this._divider;
|
|
128
161
|
return deferred(function* (options) {
|
|
129
|
-
const
|
|
162
|
+
const leftEdge = left?.edgeStyle("right", options);
|
|
163
|
+
const leftBg = paintableBg(leftEdge?.bgcolor);
|
|
130
164
|
if (leftBg === undefined)
|
|
131
165
|
return;
|
|
132
166
|
const rightBg = paintableBg(right?.edgeStyle("left", options).bgcolor);
|
|
133
|
-
yield
|
|
167
|
+
yield indistinct(leftBg, rightBg)
|
|
168
|
+
? new Segment(divider, new Style({ color: leftEdge?.color, bgcolor: rightBg }))
|
|
169
|
+
: new Segment(glyph, new Style({ color: leftBg, bgcolor: rightBg }));
|
|
134
170
|
});
|
|
135
171
|
}
|
|
136
172
|
}
|
package/dist/core/style.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Immutable style descriptors — colors, text attributes, links, metadata.
|
|
3
3
|
*/
|
|
4
|
-
import { ColorSpec, ColorDepth } from "./color.js";
|
|
4
|
+
import { ColorRgba, ColorSpec, ColorDepth } from "./color.js";
|
|
5
|
+
export declare const SURFACE_BLACK: ColorRgba;
|
|
5
6
|
/**
|
|
6
7
|
* Canonical text-attribute inventory. Single source of truth consumed by
|
|
7
8
|
* `Style.parse` / `Style.toString` and the template bindings — adding an
|
package/dist/core/style.js
CHANGED
|
@@ -8,7 +8,7 @@ import { OSC8_CLOSE, osc8Open } from "./osc8.js";
|
|
|
8
8
|
// canonical canvas color (black), inlined to avoid pulling in any preset
|
|
9
9
|
// theme constants. Preset themes live in `src/themes/` and depend on core,
|
|
10
10
|
// never the reverse.
|
|
11
|
-
const SURFACE_BLACK = new ColorRgba(0, 0, 0);
|
|
11
|
+
export const SURFACE_BLACK = new ColorRgba(0, 0, 0);
|
|
12
12
|
// --- Attribute definitions ---
|
|
13
13
|
/**
|
|
14
14
|
* Canonical text-attribute inventory. Single source of truth consumed by
|
package/dist/core/text.js
CHANGED
|
@@ -48,8 +48,9 @@ function hangingWhitespace(line) {
|
|
|
48
48
|
// [LAW:single-enforcer] RichText is the data-model trust boundary for link
|
|
49
49
|
// URLs: a `Style` is sanitized as it enters (`admitStyle`), and the `Style` a
|
|
50
50
|
// stored string resolves to is sanitized as it leaves for a render
|
|
51
|
-
// (`resolveStyle`). Wire-byte safety is enforced separately
|
|
52
|
-
//
|
|
51
|
+
// (`resolveStyle`). Wire-byte safety is enforced separately by `osc8Open`
|
|
52
|
+
// (core/osc8.ts), which every link producer calls and which applies the same
|
|
53
|
+
// `stripOscTerminators`.
|
|
53
54
|
function sanitizeStyleLink(style) {
|
|
54
55
|
const link = style.link;
|
|
55
56
|
if (!link)
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { CellCol, CodeUnit, CodePoint } from "./core/cells.js";
|
|
|
3
3
|
export { ColorRgba, ColorTable, ColorDepth, ColorSpec, ColorParseError, TerminalTheme, parseRgbHex, parseRgbaHex, blendRgb, resolveColorSystem, detectColorSystem, STANDARD_TABLE, EIGHT_BIT_TABLE, WINDOWS_TABLE, ANSI_COLOR_NAMES, } from "./core/color.js";
|
|
4
4
|
export type { DetectColorOptions } from "./core/color.js";
|
|
5
5
|
export { Oklch, IDENTITY, INVERT_LIGHTNESS, isIdentityKey, } from "./core/oklch.js";
|
|
6
|
-
export type { ThemeKey } from "./core/oklch.js";
|
|
6
|
+
export type { ThemeKey, OklchWeights } from "./core/oklch.js";
|
|
7
7
|
export { Palette } from "./themes/palette.js";
|
|
8
8
|
export { resolveColorRef, parseHexColor, ColorRefError, HEX_COLOR_RE, } from "./themes/colorRef.js";
|
|
9
9
|
export { buildPalette } from "./themes/buildPalette.js";
|
|
@@ -28,7 +28,7 @@ export type { Unsubscribe } from "./core/subscription.js";
|
|
|
28
28
|
export { Measurement, measureRenderables } from "./core/measure.js";
|
|
29
29
|
export { Span, RichText } from "./core/text.js";
|
|
30
30
|
export type { RichTextOptions } from "./core/text.js";
|
|
31
|
-
export { Strip, PowerlineJoiner, CapsuleJoiner, PlainJoiner, GradientJoiner, } from "./core/strip.js";
|
|
31
|
+
export { Strip, PowerlineJoiner, SEAM_MIN_DELTA_E, POWERLINE_JOINER_GLYPHS, CapsuleJoiner, PlainJoiner, GradientJoiner, } from "./core/strip.js";
|
|
32
32
|
export type { StyledRenderable, Joiner, PowerlineJoinerOptions, CapsuleJoinerOptions, PlainJoinerOptions, GradientJoinerOptions, } from "./core/strip.js";
|
|
33
33
|
export { renderToString, segmentToString, segmentsToString, } from "./core/render.js";
|
|
34
34
|
export type { RenderToStringOptions } from "./core/render.js";
|
package/dist/index.js
CHANGED
|
@@ -38,7 +38,7 @@ export { Measurement, measureRenderables } from "./core/measure.js";
|
|
|
38
38
|
// Text
|
|
39
39
|
export { Span, RichText } from "./core/text.js";
|
|
40
40
|
// Strip + Joiner
|
|
41
|
-
export { Strip, PowerlineJoiner, CapsuleJoiner, PlainJoiner, GradientJoiner, } from "./core/strip.js";
|
|
41
|
+
export { Strip, PowerlineJoiner, SEAM_MIN_DELTA_E, POWERLINE_JOINER_GLYPHS, CapsuleJoiner, PlainJoiner, GradientJoiner, } from "./core/strip.js";
|
|
42
42
|
// renderToString — stateless one-shot emission
|
|
43
43
|
export { renderToString, segmentToString, segmentsToString, } from "./core/render.js";
|
|
44
44
|
// OSC 8 hyperlink wire grammar — for consumers that read rendered bytes back
|