@promptctl/rich-js 0.16.0 → 0.18.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 +14 -27
- package/package.json +7 -3
- package/dist/core/box.d.ts +0 -144
- package/dist/core/box.js +0 -391
- package/dist/core/cells.d.ts +0 -148
- package/dist/core/cells.js +0 -266
- package/dist/core/color.d.ts +0 -229
- package/dist/core/color.js +0 -992
- package/dist/core/console.d.ts +0 -142
- package/dist/core/console.js +0 -477
- package/dist/core/emoji.d.ts +0 -24
- package/dist/core/emoji.js +0 -391
- package/dist/core/export-html.d.ts +0 -24
- package/dist/core/export-html.js +0 -106
- package/dist/core/export-lines.d.ts +0 -121
- package/dist/core/export-lines.js +0 -141
- package/dist/core/highlighter.d.ts +0 -33
- package/dist/core/highlighter.js +0 -124
- package/dist/core/json.d.ts +0 -27
- package/dist/core/json.js +0 -53
- package/dist/core/markup.d.ts +0 -97
- package/dist/core/markup.js +0 -619
- package/dist/core/measure.d.ts +0 -27
- package/dist/core/measure.js +0 -61
- package/dist/core/oklch.d.ts +0 -161
- package/dist/core/oklch.js +0 -312
- package/dist/core/osc8.d.ts +0 -68
- package/dist/core/osc8.js +0 -90
- package/dist/core/pretty.d.ts +0 -141
- package/dist/core/pretty.js +0 -472
- package/dist/core/protocol.d.ts +0 -137
- package/dist/core/protocol.js +0 -101
- package/dist/core/render.d.ts +0 -69
- package/dist/core/render.js +0 -140
- package/dist/core/segment.d.ts +0 -142
- package/dist/core/segment.js +0 -433
- package/dist/core/spinnerData.d.ts +0 -10
- package/dist/core/spinnerData.js +0 -115
- package/dist/core/strip.d.ts +0 -139
- package/dist/core/strip.js +0 -302
- package/dist/core/style.d.ts +0 -149
- package/dist/core/style.js +0 -741
- package/dist/core/subscription.d.ts +0 -12
- package/dist/core/subscription.js +0 -12
- package/dist/core/text.d.ts +0 -255
- package/dist/core/text.js +0 -990
- package/dist/core/wrap.d.ts +0 -41
- package/dist/core/wrap.js +0 -127
- package/dist/host/host-stream.d.ts +0 -30
- package/dist/host/host-stream.js +0 -35
- package/dist/host/index.d.ts +0 -3
- package/dist/host/index.js +0 -2
- package/dist/host/terminal-host.d.ts +0 -143
- package/dist/host/terminal-host.js +0 -132
- package/dist/index.d.ts +0 -87
- package/dist/index.js +0 -104
- package/dist/node/prompt.d.ts +0 -21
- package/dist/node/prompt.js +0 -30
- package/dist/node/save.d.ts +0 -17
- package/dist/node/save.js +0 -21
- package/dist/node/terminal-host.d.ts +0 -84
- package/dist/node/terminal-host.js +0 -160
- package/dist/node/traceback.d.ts +0 -27
- package/dist/node/traceback.js +0 -112
- package/dist/renderables/align.d.ts +0 -16
- package/dist/renderables/align.js +0 -46
- package/dist/renderables/columns.d.ts +0 -61
- package/dist/renderables/columns.js +0 -168
- package/dist/renderables/constrain.d.ts +0 -15
- package/dist/renderables/constrain.js +0 -37
- package/dist/renderables/flexStrip.d.ts +0 -41
- package/dist/renderables/flexStrip.js +0 -200
- package/dist/renderables/group.d.ts +0 -11
- package/dist/renderables/group.js +0 -15
- package/dist/renderables/layout.d.ts +0 -92
- package/dist/renderables/layout.js +0 -315
- package/dist/renderables/live.d.ts +0 -47
- package/dist/renderables/live.js +0 -131
- package/dist/renderables/markdown.d.ts +0 -24
- package/dist/renderables/markdown.js +0 -182
- package/dist/renderables/padding.d.ts +0 -38
- package/dist/renderables/padding.js +0 -140
- package/dist/renderables/panel.d.ts +0 -116
- package/dist/renderables/panel.js +0 -322
- package/dist/renderables/progress.d.ts +0 -90
- package/dist/renderables/progress.js +0 -241
- package/dist/renderables/progressBar.d.ts +0 -31
- package/dist/renderables/progressBar.js +0 -52
- package/dist/renderables/prompt.d.ts +0 -49
- package/dist/renderables/prompt.js +0 -125
- package/dist/renderables/rule.d.ts +0 -24
- package/dist/renderables/rule.js +0 -90
- package/dist/renderables/spinner.d.ts +0 -29
- package/dist/renderables/spinner.js +0 -64
- package/dist/renderables/status.d.ts +0 -23
- package/dist/renderables/status.js +0 -60
- package/dist/renderables/syntax.d.ts +0 -35
- package/dist/renderables/syntax.js +0 -114
- package/dist/renderables/table.d.ts +0 -182
- package/dist/renderables/table.js +0 -697
- package/dist/renderables/traceback.d.ts +0 -31
- package/dist/renderables/traceback.js +0 -109
- package/dist/renderables/tree.d.ts +0 -54
- package/dist/renderables/tree.js +0 -159
- package/dist/template-bindings/color-funcs.d.ts +0 -87
- package/dist/template-bindings/color-funcs.js +0 -219
- package/dist/template-bindings/helpers.d.ts +0 -24
- package/dist/template-bindings/helpers.js +0 -48
- package/dist/template-bindings/index.d.ts +0 -107
- package/dist/template-bindings/index.js +0 -144
- package/dist/template-bindings/palette-funcs.d.ts +0 -94
- package/dist/template-bindings/palette-funcs.js +0 -151
- package/dist/template-bindings/style-funcs.d.ts +0 -28
- package/dist/template-bindings/style-funcs.js +0 -151
- package/dist/themes/buildPalette.d.ts +0 -36
- package/dist/themes/buildPalette.js +0 -56
- package/dist/themes/colorMath.d.ts +0 -82
- package/dist/themes/colorMath.js +0 -254
- package/dist/themes/colorRef.d.ts +0 -51
- package/dist/themes/colorRef.js +0 -96
- package/dist/themes/data/atom-one-dark.d.ts +0 -3
- package/dist/themes/data/atom-one-dark.js +0 -158
- package/dist/themes/data/atom-one-light.d.ts +0 -3
- package/dist/themes/data/atom-one-light.js +0 -158
- package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
- package/dist/themes/data/catppuccin-frappe.js +0 -158
- package/dist/themes/data/catppuccin-latte.d.ts +0 -3
- package/dist/themes/data/catppuccin-latte.js +0 -158
- package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
- package/dist/themes/data/catppuccin-macchiato.js +0 -158
- package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
- package/dist/themes/data/catppuccin-mocha.js +0 -158
- package/dist/themes/data/cyberpunk.d.ts +0 -17
- package/dist/themes/data/cyberpunk.js +0 -191
- package/dist/themes/data/default.d.ts +0 -3
- package/dist/themes/data/default.js +0 -158
- package/dist/themes/data/dracula.d.ts +0 -3
- package/dist/themes/data/dracula.js +0 -158
- package/dist/themes/data/flexoki.d.ts +0 -3
- package/dist/themes/data/flexoki.js +0 -158
- package/dist/themes/data/gruvbox.d.ts +0 -3
- package/dist/themes/data/gruvbox.js +0 -158
- package/dist/themes/data/index.d.ts +0 -35
- package/dist/themes/data/index.js +0 -64
- package/dist/themes/data/monokai.d.ts +0 -3
- package/dist/themes/data/monokai.js +0 -158
- package/dist/themes/data/nord.d.ts +0 -3
- package/dist/themes/data/nord.js +0 -158
- package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
- package/dist/themes/data/rose-pine-dawn.js +0 -158
- package/dist/themes/data/rose-pine-moon.d.ts +0 -3
- package/dist/themes/data/rose-pine-moon.js +0 -158
- package/dist/themes/data/rose-pine.d.ts +0 -3
- package/dist/themes/data/rose-pine.js +0 -158
- package/dist/themes/data/solarized-dark.d.ts +0 -3
- package/dist/themes/data/solarized-dark.js +0 -158
- package/dist/themes/data/solarized-light.d.ts +0 -3
- package/dist/themes/data/solarized-light.js +0 -158
- package/dist/themes/data/svg-export.d.ts +0 -3
- package/dist/themes/data/svg-export.js +0 -158
- package/dist/themes/data/textual-ansi.d.ts +0 -3
- package/dist/themes/data/textual-ansi.js +0 -158
- package/dist/themes/data/textual-dark.d.ts +0 -3
- package/dist/themes/data/textual-dark.js +0 -158
- package/dist/themes/data/textual-light.d.ts +0 -3
- package/dist/themes/data/textual-light.js +0 -158
- package/dist/themes/data/tokyo-night.d.ts +0 -3
- package/dist/themes/data/tokyo-night.js +0 -158
- package/dist/themes/data/types.d.ts +0 -15
- package/dist/themes/data/types.js +0 -1
- package/dist/themes/palette.d.ts +0 -21
- package/dist/themes/palette.js +0 -28
- package/dist/themes/ramp.d.ts +0 -83
- package/dist/themes/ramp.js +0 -122
- package/dist/themes/registry.d.ts +0 -43
- package/dist/themes/registry.js +0 -82
- package/dist/themes/terminalThemes.d.ts +0 -54
- package/dist/themes/terminalThemes.js +0 -68
- package/dist/themes/transpose.d.ts +0 -87
- package/dist/themes/transpose.js +0 -138
- package/dist/widgets/button.d.ts +0 -49
- package/dist/widgets/button.js +0 -206
- package/dist/widgets/checkbox.d.ts +0 -41
- package/dist/widgets/checkbox.js +0 -155
- package/dist/widgets/dropdown.d.ts +0 -67
- package/dist/widgets/dropdown.js +0 -453
- package/dist/widgets/event-router.d.ts +0 -78
- package/dist/widgets/event-router.js +0 -622
- package/dist/widgets/focus-manager.d.ts +0 -26
- package/dist/widgets/focus-manager.js +0 -193
- package/dist/widgets/index.d.ts +0 -22
- package/dist/widgets/index.js +0 -15
- package/dist/widgets/screen.d.ts +0 -98
- package/dist/widgets/screen.js +0 -542
- package/dist/widgets/slider.d.ts +0 -57
- package/dist/widgets/slider.js +0 -270
- package/dist/widgets/static-item.d.ts +0 -40
- package/dist/widgets/static-item.js +0 -60
- package/dist/widgets/text-input.d.ts +0 -274
- package/dist/widgets/text-input.js +0 -1162
- package/dist/widgets/toggle.d.ts +0 -44
- package/dist/widgets/toggle.js +0 -179
- package/dist/widgets/types.d.ts +0 -108
- package/dist/widgets/types.js +0 -31
- package/dist/widgets/widget-base.d.ts +0 -42
- package/dist/widgets/widget-base.js +0 -173
package/dist/core/pretty.js
DELETED
|
@@ -1,472 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pretty — formats JavaScript data structures with highlighting.
|
|
3
|
-
*
|
|
4
|
-
* Lives in `core/` beside `markup` and `emoji` because it does their job: turn
|
|
5
|
-
* foreign input into `RichText`. It composes no other renderable — the trait
|
|
6
|
-
* every file in `renderables/` shares and this one does not — and its imports
|
|
7
|
-
* are core primitives only. `Console.print` accepts `unknown` and must turn any
|
|
8
|
-
* of it into something renderable, so the formatter has to sit where `console`
|
|
9
|
-
* can reach it without an upward edge. [LAW:one-way-deps]
|
|
10
|
-
*
|
|
11
|
-
* Two traversals share this file, and the split is the whole design. `_format`
|
|
12
|
-
* lays a value out across lines; `_oneLine` answers only "does this fit on one
|
|
13
|
-
* line, and if so what is it?". They meet at `_shape`, which describes a
|
|
14
|
-
* container without committing to either layout. One traversal answering both
|
|
15
|
-
* questions is what made this formatter exponential: every child was rendered
|
|
16
|
-
* once to probe it and again to place it, so a node at depth d was visited 2^d
|
|
17
|
-
* times.
|
|
18
|
-
*/
|
|
19
|
-
import { cellLen } from "./cells.js";
|
|
20
|
-
import { RichText } from "./text.js";
|
|
21
|
-
import { ReprHighlighter } from "./highlighter.js";
|
|
22
|
-
const reprHighlighter = new ReprHighlighter();
|
|
23
|
-
const rootFrame = (maxWidth) => ({
|
|
24
|
-
inset: 0,
|
|
25
|
-
level: 0,
|
|
26
|
-
maxWidth,
|
|
27
|
-
column: 0,
|
|
28
|
-
reserve: 0,
|
|
29
|
-
open: new WeakSet(),
|
|
30
|
-
});
|
|
31
|
-
/**
|
|
32
|
-
* The column reached after `text` is appended to a line currently at `column`.
|
|
33
|
-
*
|
|
34
|
-
* A hole's formatted value can itself span multiple lines — a nested
|
|
35
|
-
* container that failed its own compact try. What comes after it (a Map
|
|
36
|
-
* entry's tail, the next hole) sits after `text`'s *last* line, not at
|
|
37
|
-
* `column + cellLen(text)`, so a multi-line insert resets the column to that
|
|
38
|
-
* last line's width instead of accumulating across lines that were never on
|
|
39
|
-
* the same row.
|
|
40
|
-
*/
|
|
41
|
-
function lineColumn(column, text) {
|
|
42
|
-
const lastNewline = text.lastIndexOf("\n");
|
|
43
|
-
return lastNewline === -1 ? column + cellLen(text) : cellLen(text.slice(lastNewline + 1));
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* The text, when it is a single line no wider than `budget` — otherwise `null`.
|
|
47
|
-
*
|
|
48
|
-
* The one definition of "fits on one line" in this file, and it has to name
|
|
49
|
-
* both halves: `cellLen` scores a newline as zero cells, so a width check alone
|
|
50
|
-
* accepted multi-line text as compact. That is how a `Map` nested in an object
|
|
51
|
-
* used to render as `{ m: Map {` — a one-line object with an expansion wedged
|
|
52
|
-
* inside it and the closing brace back at column 0.
|
|
53
|
-
*/
|
|
54
|
-
function fitOneLine(text, budget) {
|
|
55
|
-
return text.includes("\n") || cellLen(text) > budget ? null : text;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* The elements of an indexed sequence, or `null` for anything that isn't one.
|
|
59
|
-
*
|
|
60
|
-
* A typed array is an array with a fixed element type, and neither of the two
|
|
61
|
-
* questions asked elsewhere in this file recognises it: `Array.isArray` says
|
|
62
|
-
* false, and its own `toString` answers the bare `1,2,3` — no brackets, nothing
|
|
63
|
-
* for the highlighter to colour per element. Both spellings reach the one arm
|
|
64
|
-
* that knows what a sequence looks like. `DataView` is excluded because it is a
|
|
65
|
-
* window onto bytes, not a sequence of values.
|
|
66
|
-
*
|
|
67
|
-
* Returned as `ArrayLike` rather than a materialised array because both
|
|
68
|
-
* spellings already are one. Converting here would box every element of a
|
|
69
|
-
* multi-megabyte `Buffer` to keep the first `maxLength` of them — bounding the
|
|
70
|
-
* output while leaving the cost of producing it unbounded.
|
|
71
|
-
*/
|
|
72
|
-
function indexedElements(value) {
|
|
73
|
-
if (Array.isArray(value))
|
|
74
|
-
return value;
|
|
75
|
-
if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
|
|
76
|
-
return value;
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* The first `limit` items of a sequence, pulled and no more.
|
|
82
|
-
*
|
|
83
|
-
* A `Map` or `Set` reaches its size through `.size`, which iterates nothing, so
|
|
84
|
-
* spreading one only ever served to throw the tail away — `print` of a
|
|
85
|
-
* half-million-entry cache walked all of it to show a hundred. `Infinity` is
|
|
86
|
-
* the unbounded case and never breaks.
|
|
87
|
-
*/
|
|
88
|
-
function take(source, limit) {
|
|
89
|
-
const taken = [];
|
|
90
|
-
if (limit <= 0)
|
|
91
|
-
return taken;
|
|
92
|
-
for (const item of source) {
|
|
93
|
-
taken.push(item);
|
|
94
|
-
if (taken.length >= limit)
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
return taken;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* What a value renders as when reading it threw instead of producing it.
|
|
101
|
-
*
|
|
102
|
-
* `Pretty` reflects on data it did not create, and every reflection it performs
|
|
103
|
-
* — a property getter, `Object.keys`, an iterator, `toString` — can throw. The
|
|
104
|
-
* message travels into the output, so the failure stays visible and attributed
|
|
105
|
-
* to the position that produced it. [LAW:no-silent-failure] the error is
|
|
106
|
-
* carried rather than swallowed; a marker naming its cause is not an
|
|
107
|
-
* answer-shaped void, and a diagnostic that takes the program down over one
|
|
108
|
-
* lazily-computed field is unusable on the objects it is most wanted for.
|
|
109
|
-
*/
|
|
110
|
-
function threw(error) {
|
|
111
|
-
return `[Threw: ${error instanceof Error ? error.message : String(error)}]`;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Does this value carry its own string form, or must we reflect on its keys?
|
|
115
|
-
*
|
|
116
|
-
* A `Date`, an `Error`, a `RegExp`, or any object that defines `toString`
|
|
117
|
-
* answers the display question itself, and reflecting on such a value throws
|
|
118
|
-
* that answer away — `Object.keys(new Date())` is empty, so key-reflection
|
|
119
|
-
* renders it `{}`. Inheriting `Object.prototype.toString` is the opposite
|
|
120
|
-
* signal: it yields `[object Object]`, a non-answer, so the keys are all the
|
|
121
|
-
* information there is.
|
|
122
|
-
*
|
|
123
|
-
* Both clauses are load-bearing. The identity check separates the two
|
|
124
|
-
* populations; the `typeof` check is what makes `Object.create(null)` — which
|
|
125
|
-
* has no `toString` at all — reflect rather than throw.
|
|
126
|
-
*/
|
|
127
|
-
function describesItself(value) {
|
|
128
|
-
const asRecord = value;
|
|
129
|
-
return (typeof asRecord[Symbol.toPrimitive] === "function" ||
|
|
130
|
-
(typeof asRecord.toString === "function" &&
|
|
131
|
-
asRecord.toString !== Object.prototype.toString));
|
|
132
|
-
}
|
|
133
|
-
/** What joins a container's positions on one line. Its width is charged for, so it is named once. */
|
|
134
|
-
const SEPARATOR = ", ";
|
|
135
|
-
/**
|
|
136
|
-
* What joins a container's positions across lines, once it has expanded —
|
|
137
|
-
* `SEPARATOR` without the trailing space a newline already provides.
|
|
138
|
-
* rich-pretty-xms: named, and derived rather than a second hand-typed
|
|
139
|
-
* literal, so the width it costs a non-last slot's own compact try —
|
|
140
|
-
* reserved via `Frame.reserve` — cannot drift from what `_formatObject`
|
|
141
|
-
* actually joins with.
|
|
142
|
-
*/
|
|
143
|
-
const EXPAND_SEPARATOR = SEPARATOR.trimEnd();
|
|
144
|
-
/** The marker for positions the bound dropped, or nothing when it dropped none. */
|
|
145
|
-
const elided = (dropped) => dropped > 0 ? [{ head: `... +${dropped}`, holes: [] }] : [];
|
|
146
|
-
export class Pretty {
|
|
147
|
-
data;
|
|
148
|
-
indent;
|
|
149
|
-
expandAll;
|
|
150
|
-
maxLength;
|
|
151
|
-
maxString;
|
|
152
|
-
maxDepth;
|
|
153
|
-
indentGuides;
|
|
154
|
-
highlighter;
|
|
155
|
-
constructor(data, options) {
|
|
156
|
-
this.data = data;
|
|
157
|
-
this.indent = options?.indent ?? 4;
|
|
158
|
-
this.expandAll = options?.expandAll ?? false;
|
|
159
|
-
this.maxLength = options?.maxLength;
|
|
160
|
-
this.maxString = options?.maxString;
|
|
161
|
-
this.maxDepth = options?.maxDepth ?? Infinity;
|
|
162
|
-
this.indentGuides = options?.indentGuides !== false;
|
|
163
|
-
this.highlighter = options?.highlighter ?? reprHighlighter;
|
|
164
|
-
}
|
|
165
|
-
*render(options) {
|
|
166
|
-
const formatted = this._format(this.data, rootFrame(options.maxWidth));
|
|
167
|
-
const text = new RichText(formatted, { end: "" });
|
|
168
|
-
this.highlighter.highlight(text);
|
|
169
|
-
if (this.indentGuides) {
|
|
170
|
-
this._addIndentGuides(text);
|
|
171
|
-
}
|
|
172
|
-
yield* text.render(options);
|
|
173
|
-
}
|
|
174
|
-
measure(_options) {
|
|
175
|
-
const formatted = this._format(this.data, rootFrame(_options.maxWidth));
|
|
176
|
-
const lines = formatted.split("\n");
|
|
177
|
-
let max = 0;
|
|
178
|
-
for (const line of lines) {
|
|
179
|
-
max = Math.max(max, cellLen(line));
|
|
180
|
-
}
|
|
181
|
-
return { minimum: 1, maximum: Math.min(max, _options.maxWidth) };
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* The text a value shows without being reflected on, or `null` when it is an
|
|
185
|
-
* object and the shape arms own it.
|
|
186
|
-
*
|
|
187
|
-
* Every kind answered here renders the same wherever it sits — no
|
|
188
|
-
* indentation, no width to fit — which is why one method serves both
|
|
189
|
-
* traversals.
|
|
190
|
-
*/
|
|
191
|
-
_scalar(value) {
|
|
192
|
-
if (value === null)
|
|
193
|
-
return "null";
|
|
194
|
-
if (value === undefined)
|
|
195
|
-
return "undefined";
|
|
196
|
-
switch (typeof value) {
|
|
197
|
-
case "string": {
|
|
198
|
-
// The dropped count is an annotation about the value, not part of it,
|
|
199
|
-
// so it goes after the closing quote — the way `maxLength`'s `... +N`
|
|
200
|
-
// sits beside the kept entries rather than inside the last one. Inside
|
|
201
|
-
// the quotes it reads as content, and copying it out yields a string
|
|
202
|
-
// the program never held.
|
|
203
|
-
if (this.maxString === undefined || value.length <= this.maxString) {
|
|
204
|
-
return JSON.stringify(value);
|
|
205
|
-
}
|
|
206
|
-
return JSON.stringify(value.slice(0, this.maxString)) + `+${value.length - this.maxString}`;
|
|
207
|
-
}
|
|
208
|
-
case "number":
|
|
209
|
-
case "bigint":
|
|
210
|
-
case "boolean":
|
|
211
|
-
return String(value);
|
|
212
|
-
case "symbol":
|
|
213
|
-
return value.toString();
|
|
214
|
-
case "function":
|
|
215
|
-
return `[Function: ${value.name || "anonymous"}]`;
|
|
216
|
-
}
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* A container, or the text standing in for one that is empty or past the
|
|
221
|
-
* depth cap.
|
|
222
|
-
*
|
|
223
|
-
* `positions` is a thunk because both of those answers are reachable from
|
|
224
|
-
* `size` alone, and reaching a position is not free: a `Map` or `Set` reaches
|
|
225
|
-
* its own through an iterator, so enumerating one only to elide it drains a
|
|
226
|
-
* container to print `Set {...}`. `size` also says how many positions were
|
|
227
|
-
* dropped, which is where the elision marker comes from — one derivation, so
|
|
228
|
-
* the count and the bound cannot disagree.
|
|
229
|
-
*/
|
|
230
|
-
_container(open, close, pad, size, level, positions) {
|
|
231
|
-
if (size === 0)
|
|
232
|
-
return { kind: "text", text: open + close };
|
|
233
|
-
if (level >= this.maxDepth)
|
|
234
|
-
return { kind: "text", text: open + "..." + close };
|
|
235
|
-
const slots = positions();
|
|
236
|
-
return { kind: "container", open, close, pad, slots: [...slots, ...elided(size - slots.length)] };
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* The brackets and positions of an object, layout-free. See `Shape`.
|
|
240
|
-
*
|
|
241
|
-
* `bound` is the most positions the caller could ever use. Laying out passes
|
|
242
|
-
* `Infinity`, because it prints every position it is handed. A probe passes
|
|
243
|
-
* what is left of its line, which is already more positions than a fitting
|
|
244
|
-
* one could hold — `SEPARATOR` charges two cells apiece, so a container with
|
|
245
|
-
* more positions than `budget` overruns however narrow its contents are.
|
|
246
|
-
* Reaching past it only ever feeds a join that returns `null`, and reaching a
|
|
247
|
-
* position is what drains a `Map` or `Set`.
|
|
248
|
-
*/
|
|
249
|
-
_shape(value, level, bound) {
|
|
250
|
-
// Clamped because a probe's budget goes negative once a line is overrun,
|
|
251
|
-
// and `slice` reads a negative end as counting from the far end — it would
|
|
252
|
-
// keep all but the last few keys where the other arms take none.
|
|
253
|
-
const cap = Math.max(0, Math.min(this.maxLength ?? Infinity, bound));
|
|
254
|
-
const elements = indexedElements(value);
|
|
255
|
-
if (elements !== null) {
|
|
256
|
-
return this._container("[", "]", "", elements.length, level, () => {
|
|
257
|
-
// Positions rather than values: each index is read in its own slot, so
|
|
258
|
-
// one throwing accessor costs its own slot, not the whole sequence.
|
|
259
|
-
const shown = Math.min(elements.length, cap);
|
|
260
|
-
return Array.from({ length: shown }, (_, i) => ({
|
|
261
|
-
head: "",
|
|
262
|
-
holes: [{ read: () => elements[i], tail: "" }],
|
|
263
|
-
}));
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
if (value instanceof Map) {
|
|
267
|
-
return this._container("Map {", "}", " ", value.size, level, () => take(value.entries(), cap).map(([k, v]) => ({
|
|
268
|
-
head: "",
|
|
269
|
-
holes: [{ read: () => k, tail: " => " }, { read: () => v, tail: "" }],
|
|
270
|
-
})));
|
|
271
|
-
}
|
|
272
|
-
if (value instanceof Set) {
|
|
273
|
-
return this._container("Set {", "}", " ", value.size, level, () => take(value, cap).map((v) => ({
|
|
274
|
-
head: "",
|
|
275
|
-
holes: [{ read: () => v, tail: "" }],
|
|
276
|
-
})));
|
|
277
|
-
}
|
|
278
|
-
// Objects that answer the display question themselves. Sits below the
|
|
279
|
-
// Array/Map/Set arms deliberately: an array also overrides `toString`, but
|
|
280
|
-
// "1,2,3" is a poorer answer than the structural form above.
|
|
281
|
-
if (describesItself(value))
|
|
282
|
-
return { kind: "text", text: String(value) };
|
|
283
|
-
// Plain objects — no self-description, so the keys are the whole story.
|
|
284
|
-
const obj = value;
|
|
285
|
-
const keys = Object.keys(obj);
|
|
286
|
-
return this._container("{", "}", " ", keys.length, level, () => keys.slice(0, cap).map((k) => ({
|
|
287
|
-
head: `${k}: `,
|
|
288
|
-
holes: [{ read: () => obj[k], tail: "" }],
|
|
289
|
-
})));
|
|
290
|
-
}
|
|
291
|
-
/** The laid-out form of a value, expanded across lines wherever one line will not do. */
|
|
292
|
-
_format(value, at) {
|
|
293
|
-
const scalar = this._scalar(value);
|
|
294
|
-
if (scalar !== null)
|
|
295
|
-
return scalar;
|
|
296
|
-
const object = value;
|
|
297
|
-
if (at.open.has(object))
|
|
298
|
-
return "[Circular]";
|
|
299
|
-
at.open.add(object);
|
|
300
|
-
try {
|
|
301
|
-
return this._formatObject(object, at);
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
// The container's *shape* would not be read — `Object.keys`, an iterator,
|
|
305
|
-
// `toString`. Nothing can be enumerated, so the whole container degrades;
|
|
306
|
-
// a value that merely would not be read degrades alone, in its own slot.
|
|
307
|
-
return threw(error);
|
|
308
|
-
}
|
|
309
|
-
finally {
|
|
310
|
-
at.open.delete(object);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/** The arms for a non-null object, with `value` already on the open path. */
|
|
314
|
-
_formatObject(value, at) {
|
|
315
|
-
const shape = this._shape(value, at.level, Infinity);
|
|
316
|
-
if (shape.kind === "text")
|
|
317
|
-
return shape.text;
|
|
318
|
-
if (!this.expandAll) {
|
|
319
|
-
// rich-pretty-xms: budget from `at.column` and `at.reserve`, not from
|
|
320
|
-
// `at.inset` alone. `at.column` is where *this* value's text actually
|
|
321
|
-
// starts on its line, which already includes whatever `_expandSlot`
|
|
322
|
-
// prepended for us (a key and its separator, a Map entry's `" => "`,
|
|
323
|
-
// ...); `at.reserve` is what a fixed piece of literal text — the rest
|
|
324
|
-
// of this hole's own tail, or the trailing `,` a non-last slot gets
|
|
325
|
-
// joined with — is known to cost right after, before this value's own
|
|
326
|
-
// text even starts. Deriving the budget from the indent alone assumed
|
|
327
|
-
// nothing precedes *or follows* the value on its line, which is only
|
|
328
|
-
// true at the root and for the last hole of an array's last element.
|
|
329
|
-
const compact = this._joinOneLine(shape, {
|
|
330
|
-
level: at.level + 1,
|
|
331
|
-
budget: at.maxWidth - at.column - at.reserve,
|
|
332
|
-
open: at.open,
|
|
333
|
-
});
|
|
334
|
-
if (compact !== null)
|
|
335
|
-
return compact;
|
|
336
|
-
}
|
|
337
|
-
const indentStr = " ".repeat(this.indent * at.inset);
|
|
338
|
-
const innerIndent = " ".repeat(this.indent * (at.inset + 1));
|
|
339
|
-
// Every non-last slot gets `EXPAND_SEPARATOR` appended right after it
|
|
340
|
-
// below, on the same line as whatever its own last character was — the
|
|
341
|
-
// last slot doesn't. Two frames, not one per slot: `reserve` is the only
|
|
342
|
-
// field that varies, and it only ever takes these two values.
|
|
343
|
-
const base = { inset: at.inset + 1, level: at.level + 1, maxWidth: at.maxWidth, column: cellLen(innerIndent), open: at.open };
|
|
344
|
-
const midFrame = { ...base, reserve: cellLen(EXPAND_SEPARATOR) };
|
|
345
|
-
const lastFrame = { ...base, reserve: 0 };
|
|
346
|
-
const lastSlot = shape.slots.length - 1;
|
|
347
|
-
const parts = shape.slots.map((slot, i) => innerIndent + this._expandSlot(slot, i === lastSlot ? lastFrame : midFrame));
|
|
348
|
-
return shape.open + "\n" + parts.join(EXPAND_SEPARATOR + "\n") + "\n" + indentStr + shape.close;
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* One position, with every value in it laid out.
|
|
352
|
-
*
|
|
353
|
-
* `at.column` is where `slot.head` begins. Rather than hand-tracking a
|
|
354
|
-
* second, parallel "column so far" alongside `out` — two values a future
|
|
355
|
-
* edit could update out of step, silently reintroducing a stale-budget bug
|
|
356
|
-
* of the same shape this file just fixed — each hole derives its own
|
|
357
|
-
* column fresh from `out` via `lineColumn`, the one place "text just
|
|
358
|
-
* emitted" becomes "column now". [LAW:one-source-of-truth] `out` is
|
|
359
|
-
* already the complete record; `column` is a read of it, not a second copy
|
|
360
|
-
* of the same fact. `_joinOneLine`'s probe sibling tracks the analogous
|
|
361
|
-
* thing via `budget` shrinking instead, because a probe already discards
|
|
362
|
-
* anything that overruns and so never needs to know where a multi-line
|
|
363
|
-
* insert's last line ends.
|
|
364
|
-
*
|
|
365
|
-
* Each hole's `reserve` is its own `tail` plus, only for the slot's last
|
|
366
|
-
* hole, whatever `at.reserve` already asked this whole slot to leave room
|
|
367
|
-
* for (`_formatObject`'s trailing `,`). An earlier hole's tail is never
|
|
368
|
-
* folded into a later hole's reserve — it is spent, not carried, the moment
|
|
369
|
-
* `out` grows past it.
|
|
370
|
-
*/
|
|
371
|
-
_expandSlot(slot, at) {
|
|
372
|
-
let out = slot.head;
|
|
373
|
-
const lastHole = slot.holes.length - 1;
|
|
374
|
-
for (let i = 0; i < slot.holes.length; i++) {
|
|
375
|
-
const hole = slot.holes[i];
|
|
376
|
-
// This is the read that costs the least when it fails: neighbours are
|
|
377
|
-
// unaffected, so `{ a: 1, b: [Threw: …], c: 3 }` still shows everything
|
|
378
|
-
// that could be read.
|
|
379
|
-
let text;
|
|
380
|
-
try {
|
|
381
|
-
const reserve = cellLen(hole.tail) + (i === lastHole ? at.reserve : 0);
|
|
382
|
-
text = this._format(hole.read(), { ...at, column: lineColumn(at.column, out), reserve });
|
|
383
|
-
}
|
|
384
|
-
catch (error) {
|
|
385
|
-
text = threw(error);
|
|
386
|
-
}
|
|
387
|
-
out += text + hole.tail;
|
|
388
|
-
}
|
|
389
|
-
return out;
|
|
390
|
-
}
|
|
391
|
-
/** The one-line form of a value, or `null` when it will not fit `at.budget`. */
|
|
392
|
-
_oneLine(value, at) {
|
|
393
|
-
const scalar = this._scalar(value);
|
|
394
|
-
if (scalar !== null)
|
|
395
|
-
return fitOneLine(scalar, at.budget);
|
|
396
|
-
const object = value;
|
|
397
|
-
if (at.open.has(object))
|
|
398
|
-
return fitOneLine("[Circular]", at.budget);
|
|
399
|
-
at.open.add(object);
|
|
400
|
-
try {
|
|
401
|
-
const shape = this._shape(object, at.level, at.budget);
|
|
402
|
-
if (shape.kind === "text")
|
|
403
|
-
return fitOneLine(shape.text, at.budget);
|
|
404
|
-
return this._joinOneLine(shape, { level: at.level + 1, budget: at.budget, open: at.open });
|
|
405
|
-
}
|
|
406
|
-
catch (error) {
|
|
407
|
-
return fitOneLine(threw(error), at.budget);
|
|
408
|
-
}
|
|
409
|
-
finally {
|
|
410
|
-
at.open.delete(object);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* A container's positions on one line, or `null` as soon as they overrun.
|
|
415
|
-
*
|
|
416
|
-
* The budget is spent as it goes and each position is asked for only what is
|
|
417
|
-
* left, so a subtree that has already outgrown the line is abandoned where it
|
|
418
|
-
* outgrew it rather than formatted in full and then measured.
|
|
419
|
-
*
|
|
420
|
-
* The brackets are charged before anything is read, and that ordering is what
|
|
421
|
-
* bounds the traversal: a budget checked only against the finished text still
|
|
422
|
-
* reads the whole subtree to produce text it then throws away. Charged first,
|
|
423
|
-
* every level costs at least the two cells of its own brackets, so a probe
|
|
424
|
-
* descends at most `budget / 2` levels however deep the data goes.
|
|
425
|
-
*/
|
|
426
|
-
_joinOneLine(shape, at) {
|
|
427
|
-
let used = cellLen(shape.open) + cellLen(shape.close) + 2 * cellLen(shape.pad);
|
|
428
|
-
if (used > at.budget)
|
|
429
|
-
return null;
|
|
430
|
-
const pieces = [];
|
|
431
|
-
for (const slot of shape.slots) {
|
|
432
|
-
used += pieces.length > 0 ? cellLen(SEPARATOR) : 0;
|
|
433
|
-
const piece = this._slotOneLine(slot, { ...at, budget: at.budget - used });
|
|
434
|
-
if (piece === null)
|
|
435
|
-
return null;
|
|
436
|
-
used += cellLen(piece);
|
|
437
|
-
pieces.push(piece);
|
|
438
|
-
}
|
|
439
|
-
return shape.open + shape.pad + pieces.join(SEPARATOR) + shape.pad + shape.close;
|
|
440
|
-
}
|
|
441
|
-
/** One position on one line, or `null` when any value in it will not fit. */
|
|
442
|
-
_slotOneLine(slot, at) {
|
|
443
|
-
let out = slot.head;
|
|
444
|
-
for (const hole of slot.holes) {
|
|
445
|
-
const left = at.budget - cellLen(out);
|
|
446
|
-
let text;
|
|
447
|
-
try {
|
|
448
|
-
text = this._oneLine(hole.read(), { ...at, budget: left });
|
|
449
|
-
}
|
|
450
|
-
catch (error) {
|
|
451
|
-
text = fitOneLine(threw(error), left);
|
|
452
|
-
}
|
|
453
|
-
if (text === null)
|
|
454
|
-
return null;
|
|
455
|
-
out += text + hole.tail;
|
|
456
|
-
}
|
|
457
|
-
return fitOneLine(out, at.budget);
|
|
458
|
-
}
|
|
459
|
-
_addIndentGuides(text) {
|
|
460
|
-
const lines = text.plain.split("\n");
|
|
461
|
-
let offset = 0;
|
|
462
|
-
for (const line of lines) {
|
|
463
|
-
const leadingSpaces = line.length - line.trimStart().length;
|
|
464
|
-
for (let i = 0; i < leadingSpaces; i += this.indent) {
|
|
465
|
-
if (i + offset < text.length) {
|
|
466
|
-
text.stylize("repr.indent", offset + i, offset + i + 1);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
offset += line.length + 1; // +1 for newline
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
package/dist/core/protocol.d.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rendering protocol interfaces — Renderable, Measurable, RenderOptions.
|
|
3
|
-
* [LAW:one-source-of-truth] These interfaces are the single authority for the rendering contract.
|
|
4
|
-
*/
|
|
5
|
-
import type { Segment } from "./segment.js";
|
|
6
|
-
import { type Style, type StyleSyntaxError, type Theme } from "./style.js";
|
|
7
|
-
import type { ColorDepth } from "./color.js";
|
|
8
|
-
export interface RenderOptions {
|
|
9
|
-
/**
|
|
10
|
-
* The cells this renderable may occupy. A count of cells, so a non-negative
|
|
11
|
-
* integer — but the field is a plain `number` because it is public API and a
|
|
12
|
-
* caller can write anything into it. `withCellWidth` is where that becomes
|
|
13
|
-
* true rather than assumed; see its comment for why every renderable must
|
|
14
|
-
* call it rather than trusting this declaration.
|
|
15
|
-
*/
|
|
16
|
-
maxWidth: number;
|
|
17
|
-
minWidth?: number;
|
|
18
|
-
height?: number;
|
|
19
|
-
maxHeight?: number;
|
|
20
|
-
isTerminal?: boolean;
|
|
21
|
-
encoding?: string;
|
|
22
|
-
legacyWindows?: boolean;
|
|
23
|
-
asciiOnly?: boolean;
|
|
24
|
-
justify?: "left" | "center" | "right" | "full";
|
|
25
|
-
overflow?: "fold" | "crop" | "ellipsis";
|
|
26
|
-
noWrap?: boolean;
|
|
27
|
-
highlight?: unknown;
|
|
28
|
-
markup?: unknown;
|
|
29
|
-
/**
|
|
30
|
-
* The depth the output will be encoded at, `null` when it carries no colour.
|
|
31
|
-
* A renderable choosing between two ways of drawing something — the strip's
|
|
32
|
-
* arrow or its divider — decides on the colours the terminal will draw at
|
|
33
|
-
* this depth, which at 256 colours or fewer are not the colours it was
|
|
34
|
-
* handed. Absent means truecolor.
|
|
35
|
-
*/
|
|
36
|
-
colorSystem?: ColorDepth | null;
|
|
37
|
-
/**
|
|
38
|
-
* The names a style string may use. A `Console` passes its own; absent, the
|
|
39
|
-
* built-in defaults apply. Read it through `getStyle` rather than directly.
|
|
40
|
-
*/
|
|
41
|
-
theme?: Theme;
|
|
42
|
-
/**
|
|
43
|
-
* Called each time a render degrades a style string it cannot parse to
|
|
44
|
-
* unstyled, once per resolution — a style the render reads twice is reported
|
|
45
|
-
* twice. Absent, the failure passes silently. A handler that throws makes
|
|
46
|
-
* the render strict: the error leaves `render`.
|
|
47
|
-
*/
|
|
48
|
-
onStyleError?: StyleErrorHandler;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Receives a style error a render would otherwise absorb: the parse failure,
|
|
52
|
-
* and the whole style string it came from — the error names only the token
|
|
53
|
-
* that failed, and `"bold rd"` loses its `bold` too.
|
|
54
|
-
*
|
|
55
|
-
* [LAW:no-mode-explosion] Strict mode is not a second option beside this one.
|
|
56
|
-
* It is a handler that throws, so there is no "strict and a callback" state
|
|
57
|
-
* whose order anyone has to define.
|
|
58
|
-
*/
|
|
59
|
-
export type StyleErrorHandler = (error: StyleSyntaxError, style: string) => void;
|
|
60
|
-
/**
|
|
61
|
-
* The style a `string | Style` stands for in this render.
|
|
62
|
-
*
|
|
63
|
-
* [LAW:single-enforcer] Every renderable resolves a style name here, at render
|
|
64
|
-
* time, because only the render knows whose theme it is drawing for. Resolved
|
|
65
|
-
* when a renderable was built instead, a name was fixed to the built-in
|
|
66
|
-
* defaults before any `Console` could offer its theme, and `new Console({
|
|
67
|
-
* theme })` changed nothing.
|
|
68
|
-
*/
|
|
69
|
-
export declare function getStyle(options: RenderOptions, style: string | Style): Style;
|
|
70
|
-
/**
|
|
71
|
-
* The options a renderable should work from: the caller's, with `maxWidth`
|
|
72
|
-
* parsed into an actual count of cells.
|
|
73
|
-
*
|
|
74
|
-
* [LAW:parse-dont-validate] The stamp has to travel, which is the whole reason
|
|
75
|
-
* this returns options rather than a number. Parsing `options.maxWidth` into a
|
|
76
|
-
* local leaves the caller's raw value sitting in `options` for whatever the
|
|
77
|
-
* renderable forwards it to — and every renderable forwards it, to a child's
|
|
78
|
-
* `render`, to `Measurement.get`, to a nested layout. `Columns` was fixed that
|
|
79
|
-
* way first and still threw `Invalid array length` on a NaN width, because it
|
|
80
|
-
* handed the unparsed original to `Measurement.get` and got a NaN column count
|
|
81
|
-
* back. Replace the field, and nothing downstream can see the number the
|
|
82
|
-
* caller actually wrote.
|
|
83
|
-
*
|
|
84
|
-
* [LAW:single-enforcer] This is the width checkpoint for the render contract.
|
|
85
|
-
* There is no second one: a renderable that re-derives its own answer is how
|
|
86
|
-
* `Panel`, `Table`, `Tree`, `Columns` and `Layout` came to disagree about what
|
|
87
|
-
* a NaN width means — one collapsed to a cell of garbage, two threw, one
|
|
88
|
-
* ignored the request and emitted its full natural width.
|
|
89
|
-
*/
|
|
90
|
-
export declare function withCellWidth(options: RenderOptions): RenderOptions;
|
|
91
|
-
/**
|
|
92
|
-
* The options a renderable should *lay out against*: `withCellWidth`, with an
|
|
93
|
-
* unbounded offer resolved to the renderable's own natural width.
|
|
94
|
-
*
|
|
95
|
-
* [LAW:parse-dont-validate] `cellCount` cannot finish the job alone. It floors
|
|
96
|
-
* a negative width and NaN to zero from the number alone, but `Infinity` is not
|
|
97
|
-
* a quantity it can floor — the only finite answer is "as wide as this
|
|
98
|
-
* renderable's content wants", which is a question about the renderable and not
|
|
99
|
-
* about the number. So the parse is completed here, where the renderable is in
|
|
100
|
-
* hand, and the stamped options travel exactly as `withCellWidth`'s do.
|
|
101
|
-
*
|
|
102
|
-
* [LAW:single-enforcer] One checkpoint for the whole rule, not one per
|
|
103
|
-
* renderable. `Panel`, `Padding`, `Columns` and `Layout` each expand into the
|
|
104
|
-
* width they are offered, and each independently reached `" ".repeat(Infinity)`
|
|
105
|
-
* — Columns twice over, since its column *count* is derived from the width too
|
|
106
|
-
* and `new Array(Infinity)` throws a different error again.
|
|
107
|
-
*
|
|
108
|
-
* The natural width comes from `measure`, which every one of them already
|
|
109
|
-
* implements, and the recursion terminates because `measure` reports content
|
|
110
|
-
* rather than the offer: `Panel.measure({maxWidth: Infinity})` is `{9, 9}` for
|
|
111
|
-
* nine cells of content and frame, not `{9, Infinity}`.
|
|
112
|
-
*
|
|
113
|
-
* So this belongs at the top of `render` and never at the top of `measure`:
|
|
114
|
-
* `measure` is the method being asked, and asking it through here would ask it
|
|
115
|
-
* with itself. `measure` parses with `withCellWidth` and reports a natural
|
|
116
|
-
* width of its own — that is the half of the contract that makes this half work.
|
|
117
|
-
*
|
|
118
|
-
* A `maximum` of `Infinity` means the renderable genuinely cannot answer. Two
|
|
119
|
-
* ways in: it wraps a `Renderable` with no `measure`, so nothing in the tree
|
|
120
|
-
* knows how wide the content wants to be; or something inside it asked for an
|
|
121
|
-
* unbounded width of its own, as a `Table` column declared
|
|
122
|
-
* `{ ratio: Infinity }` does. [LAW:no-silent-failure] Both are unanswerable
|
|
123
|
-
* rather than zero, and saying so names the cause — where `String.repeat` and
|
|
124
|
-
* `new Array` only ever name their own argument.
|
|
125
|
-
*/
|
|
126
|
-
export declare function withBoundedWidth(options: RenderOptions, self: Measurable): RenderOptions;
|
|
127
|
-
export interface Renderable {
|
|
128
|
-
render(options: RenderOptions): Iterable<Segment>;
|
|
129
|
-
}
|
|
130
|
-
export interface Measurable {
|
|
131
|
-
measure(options: RenderOptions): {
|
|
132
|
-
minimum: number;
|
|
133
|
-
maximum: number;
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
export declare function isRenderable(obj: unknown): obj is Renderable;
|
|
137
|
-
export declare function isMeasurable(obj: unknown): obj is Measurable;
|