@promptctl/rich-js 0.18.0 → 0.19.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 +9 -7
- package/dist/core/anchor.d.ts +45 -0
- package/dist/core/anchor.js +39 -0
- package/dist/core/ansi.d.ts +65 -0
- package/dist/core/ansi.js +231 -0
- package/dist/core/box.d.ts +145 -0
- package/dist/core/box.js +372 -0
- package/dist/core/cells.d.ts +192 -0
- package/dist/core/cells.js +334 -0
- package/dist/core/clock.d.ts +48 -0
- package/dist/core/clock.js +42 -0
- package/dist/core/color.d.ts +326 -0
- package/dist/core/color.js +1225 -0
- package/dist/core/console.d.ts +196 -0
- package/dist/core/console.js +596 -0
- package/dist/core/easing.d.ts +86 -0
- package/dist/core/easing.js +171 -0
- package/dist/core/emoji.d.ts +24 -0
- package/dist/core/emoji.js +391 -0
- package/dist/core/env.d.ts +16 -0
- package/dist/core/env.js +16 -0
- package/dist/core/export-html.d.ts +59 -0
- package/dist/core/export-html.js +197 -0
- package/dist/core/export-lines.d.ts +130 -0
- package/dist/core/export-lines.js +159 -0
- package/dist/core/export-svg.d.ts +49 -0
- package/dist/core/export-svg.js +213 -0
- package/dist/core/fnv1a.d.ts +10 -0
- package/dist/core/fnv1a.js +17 -0
- package/dist/core/highlighter.d.ts +33 -0
- package/dist/core/highlighter.js +190 -0
- package/dist/core/json.d.ts +27 -0
- package/dist/core/json.js +53 -0
- package/dist/core/markup.d.ts +140 -0
- package/dist/core/markup.js +654 -0
- package/dist/core/measure.d.ts +27 -0
- package/dist/core/measure.js +67 -0
- package/dist/core/memo.d.ts +19 -0
- package/dist/core/memo.js +34 -0
- package/dist/core/oklch.d.ts +161 -0
- package/dist/core/oklch.js +312 -0
- package/dist/core/osc8.d.ts +68 -0
- package/dist/core/osc8.js +78 -0
- package/dist/core/paint.d.ts +107 -0
- package/dist/core/paint.js +150 -0
- package/dist/core/place.d.ts +30 -0
- package/dist/core/place.js +49 -0
- package/dist/core/pretty.d.ts +247 -0
- package/dist/core/pretty.js +906 -0
- package/dist/core/protocol.d.ts +305 -0
- package/dist/core/protocol.js +183 -0
- package/dist/core/render.d.ts +87 -0
- package/dist/core/render.js +105 -0
- package/dist/core/segment.d.ts +162 -0
- package/dist/core/segment.js +488 -0
- package/dist/core/spinnerData.d.ts +12 -0
- package/dist/core/spinnerData.js +117 -0
- package/dist/core/strip.d.ts +152 -0
- package/dist/core/strip.js +326 -0
- package/dist/core/style.d.ts +189 -0
- package/dist/core/style.js +758 -0
- package/dist/core/subscription.d.ts +12 -0
- package/dist/core/subscription.js +12 -0
- package/dist/core/text.d.ts +283 -0
- package/dist/core/text.js +1097 -0
- package/dist/core/wrap.d.ts +46 -0
- package/dist/core/wrap.js +140 -0
- package/dist/host/app.d.ts +133 -0
- package/dist/host/app.js +285 -0
- package/dist/host/host-environment.d.ts +26 -0
- package/dist/host/host-environment.js +44 -0
- package/dist/host/index.d.ts +6 -0
- package/dist/host/index.js +3 -0
- package/dist/host/terminal-host.d.ts +180 -0
- package/dist/host/terminal-host.js +186 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +114 -0
- package/dist/node/prompt.d.ts +34 -0
- package/dist/node/prompt.js +51 -0
- package/dist/node/save.d.ts +18 -0
- package/dist/node/save.js +24 -0
- package/dist/node/terminal-host.d.ts +114 -0
- package/dist/node/terminal-host.js +227 -0
- package/dist/node/traceback.d.ts +38 -0
- package/dist/node/traceback.js +98 -0
- package/dist/renderables/align.d.ts +16 -0
- package/dist/renderables/align.js +26 -0
- package/dist/renderables/columns.d.ts +77 -0
- package/dist/renderables/columns.js +191 -0
- package/dist/renderables/constrain.d.ts +15 -0
- package/dist/renderables/constrain.js +31 -0
- package/dist/renderables/effect.d.ts +98 -0
- package/dist/renderables/effect.js +160 -0
- package/dist/renderables/embed.d.ts +107 -0
- package/dist/renderables/embed.js +160 -0
- package/dist/renderables/flexStrip.d.ts +41 -0
- package/dist/renderables/flexStrip.js +200 -0
- package/dist/renderables/group.d.ts +14 -0
- package/dist/renderables/group.js +22 -0
- package/dist/renderables/layout.d.ts +75 -0
- package/dist/renderables/layout.js +223 -0
- package/dist/renderables/live.d.ts +74 -0
- package/dist/renderables/live.js +188 -0
- package/dist/renderables/markdown.d.ts +38 -0
- package/dist/renderables/markdown.js +587 -0
- package/dist/renderables/padding.d.ts +59 -0
- package/dist/renderables/padding.js +147 -0
- package/dist/renderables/panel.d.ts +114 -0
- package/dist/renderables/panel.js +303 -0
- package/dist/renderables/progress.d.ts +178 -0
- package/dist/renderables/progress.js +362 -0
- package/dist/renderables/progressBar.d.ts +31 -0
- package/dist/renderables/progressBar.js +64 -0
- package/dist/renderables/prompt.d.ts +71 -0
- package/dist/renderables/prompt.js +165 -0
- package/dist/renderables/ratio.d.ts +123 -0
- package/dist/renderables/ratio.js +213 -0
- package/dist/renderables/rule.d.ts +26 -0
- package/dist/renderables/rule.js +86 -0
- package/dist/renderables/spinner.d.ts +57 -0
- package/dist/renderables/spinner.js +103 -0
- package/dist/renderables/status.d.ts +29 -0
- package/dist/renderables/status.js +46 -0
- package/dist/renderables/syntax.d.ts +65 -0
- package/dist/renderables/syntax.js +166 -0
- package/dist/renderables/table.d.ts +232 -0
- package/dist/renderables/table.js +973 -0
- package/dist/renderables/traceback.d.ts +32 -0
- package/dist/renderables/traceback.js +279 -0
- package/dist/renderables/tree.d.ts +76 -0
- package/dist/renderables/tree.js +177 -0
- package/dist/renderables/viewport.d.ts +114 -0
- package/dist/renderables/viewport.js +196 -0
- package/dist/template-bindings/color-funcs.d.ts +109 -0
- package/dist/template-bindings/color-funcs.js +243 -0
- package/dist/template-bindings/helpers.d.ts +26 -0
- package/dist/template-bindings/helpers.js +33 -0
- package/dist/template-bindings/index.d.ts +119 -0
- package/dist/template-bindings/index.js +162 -0
- package/dist/template-bindings/palette-funcs.d.ts +96 -0
- package/dist/template-bindings/palette-funcs.js +145 -0
- package/dist/template-bindings/style-funcs.d.ts +28 -0
- package/dist/template-bindings/style-funcs.js +157 -0
- package/dist/themes/buildPalette.d.ts +57 -0
- package/dist/themes/buildPalette.js +100 -0
- package/dist/themes/colorMath.d.ts +94 -0
- package/dist/themes/colorMath.js +280 -0
- package/dist/themes/colorRef.d.ts +36 -0
- package/dist/themes/colorRef.js +78 -0
- package/dist/themes/data/ansi-tables.d.ts +14 -0
- package/dist/themes/data/ansi-tables.js +49 -0
- package/dist/themes/data/atom-one-dark.d.ts +3 -0
- package/dist/themes/data/atom-one-dark.js +177 -0
- package/dist/themes/data/atom-one-light.d.ts +3 -0
- package/dist/themes/data/atom-one-light.js +177 -0
- package/dist/themes/data/catppuccin-frappe.d.ts +3 -0
- package/dist/themes/data/catppuccin-frappe.js +177 -0
- package/dist/themes/data/catppuccin-latte.d.ts +3 -0
- package/dist/themes/data/catppuccin-latte.js +177 -0
- package/dist/themes/data/catppuccin-macchiato.d.ts +3 -0
- package/dist/themes/data/catppuccin-macchiato.js +177 -0
- package/dist/themes/data/catppuccin-mocha.d.ts +3 -0
- package/dist/themes/data/catppuccin-mocha.js +177 -0
- package/dist/themes/data/cyberpunk.d.ts +17 -0
- package/dist/themes/data/cyberpunk.js +193 -0
- package/dist/themes/data/default.d.ts +3 -0
- package/dist/themes/data/default.js +160 -0
- package/dist/themes/data/dracula.d.ts +3 -0
- package/dist/themes/data/dracula.js +177 -0
- package/dist/themes/data/flexoki.d.ts +3 -0
- package/dist/themes/data/flexoki.js +177 -0
- package/dist/themes/data/gruvbox.d.ts +3 -0
- package/dist/themes/data/gruvbox.js +177 -0
- package/dist/themes/data/index.d.ts +35 -0
- package/dist/themes/data/index.js +64 -0
- package/dist/themes/data/monokai.d.ts +3 -0
- package/dist/themes/data/monokai.js +177 -0
- package/dist/themes/data/nord.d.ts +3 -0
- package/dist/themes/data/nord.js +177 -0
- package/dist/themes/data/rose-pine-dawn.d.ts +3 -0
- package/dist/themes/data/rose-pine-dawn.js +177 -0
- package/dist/themes/data/rose-pine-moon.d.ts +3 -0
- package/dist/themes/data/rose-pine-moon.js +177 -0
- package/dist/themes/data/rose-pine.d.ts +3 -0
- package/dist/themes/data/rose-pine.js +177 -0
- package/dist/themes/data/solarized-dark.d.ts +3 -0
- package/dist/themes/data/solarized-dark.js +177 -0
- package/dist/themes/data/solarized-light.d.ts +3 -0
- package/dist/themes/data/solarized-light.js +177 -0
- package/dist/themes/data/svg-export.d.ts +3 -0
- package/dist/themes/data/svg-export.js +177 -0
- package/dist/themes/data/textual-ansi.d.ts +3 -0
- package/dist/themes/data/textual-ansi.js +160 -0
- package/dist/themes/data/textual-dark.d.ts +3 -0
- package/dist/themes/data/textual-dark.js +160 -0
- package/dist/themes/data/textual-light.d.ts +3 -0
- package/dist/themes/data/textual-light.js +160 -0
- package/dist/themes/data/tokyo-night.d.ts +3 -0
- package/dist/themes/data/tokyo-night.js +177 -0
- package/dist/themes/data/types.d.ts +31 -0
- package/dist/themes/data/types.js +10 -0
- package/dist/themes/palette.d.ts +45 -0
- package/dist/themes/palette.js +71 -0
- package/dist/themes/ramp.d.ts +70 -0
- package/dist/themes/ramp.js +103 -0
- package/dist/themes/registry.d.ts +44 -0
- package/dist/themes/registry.js +105 -0
- package/dist/themes/terminalThemes.d.ts +49 -0
- package/dist/themes/terminalThemes.js +63 -0
- package/dist/themes/transpose.d.ts +87 -0
- package/dist/themes/transpose.js +138 -0
- package/dist/widgets/button.d.ts +45 -0
- package/dist/widgets/button.js +177 -0
- package/dist/widgets/checkbox.d.ts +38 -0
- package/dist/widgets/checkbox.js +135 -0
- package/dist/widgets/dropdown.d.ts +64 -0
- package/dist/widgets/dropdown.js +416 -0
- package/dist/widgets/event-router.d.ts +71 -0
- package/dist/widgets/event-router.js +629 -0
- package/dist/widgets/focus-manager.d.ts +45 -0
- package/dist/widgets/focus-manager.js +191 -0
- package/dist/widgets/hit.d.ts +64 -0
- package/dist/widgets/hit.js +113 -0
- package/dist/widgets/index.d.ts +20 -0
- package/dist/widgets/index.js +14 -0
- package/dist/widgets/ink.d.ts +22 -0
- package/dist/widgets/ink.js +31 -0
- package/dist/widgets/slider.d.ts +54 -0
- package/dist/widgets/slider.js +247 -0
- package/dist/widgets/static-item.d.ts +34 -0
- package/dist/widgets/static-item.js +54 -0
- package/dist/widgets/text-input.d.ts +283 -0
- package/dist/widgets/text-input.js +1174 -0
- package/dist/widgets/themed-widget.d.ts +20 -0
- package/dist/widgets/themed-widget.js +89 -0
- package/dist/widgets/toggle.d.ts +41 -0
- package/dist/widgets/toggle.js +157 -0
- package/dist/widgets/types.d.ts +87 -0
- package/dist/widgets/types.js +30 -0
- package/dist/widgets/widget-app.d.ts +65 -0
- package/dist/widgets/widget-app.js +206 -0
- package/dist/widgets/widget-base.d.ts +42 -0
- package/dist/widgets/widget-base.js +172 -0
- package/package.json +16 -2
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WidgetApp — an `App` whose view has widgets in it: keys reach the focused
|
|
3
|
+
* widget, the pointer reaches the widget that drew the cell under it, and a
|
|
4
|
+
* change to anything a frame read paints the next one.
|
|
5
|
+
*
|
|
6
|
+
* A widget goes wherever a renderable goes — in a `Panel`, a `Layout` pane, a
|
|
7
|
+
* `Table` cell — and nothing lays it out a second time. Everything added here
|
|
8
|
+
* reads the frame the view drew:
|
|
9
|
+
*
|
|
10
|
+
* - Focus moves in document order: the order widgets start to render in
|
|
11
|
+
* (`RenderOptions.onDraw`), among those with a cell on the frame on screen.
|
|
12
|
+
* Not reading order — in a `Layout` split into columns, reading order
|
|
13
|
+
* zigzags between the columns row by row.
|
|
14
|
+
* - Overlays are painted over the view's frame, in document order, directly
|
|
15
|
+
* below their owner's footprint (`OverlayRenderable`). Paint order is the
|
|
16
|
+
* z-order: a later overlay covers an earlier one.
|
|
17
|
+
* - A pointer event goes to the innermost widget that drew the cell under it
|
|
18
|
+
* (`widgetAt`), in that widget's own coordinates; the wheel goes to the
|
|
19
|
+
* innermost `Viewport` that can move (`scrollTargetAt`).
|
|
20
|
+
*
|
|
21
|
+
* [LAW:locality-or-seam] `App` knows nothing of widgets — `host/` depends on
|
|
22
|
+
* `core/` alone — and this reaches it only through the seams it already has:
|
|
23
|
+
* the view is a renderable, and `onPaint` hears the frame.
|
|
24
|
+
*/
|
|
25
|
+
import { Reaction } from "mobx";
|
|
26
|
+
import { Segment } from "../core/segment.js";
|
|
27
|
+
import { App } from "../host/app.js";
|
|
28
|
+
import { DefaultFocusManager } from "./focus-manager.js";
|
|
29
|
+
import { EventRouter } from "./event-router.js";
|
|
30
|
+
import { footprintOf, ownersOn } from "./hit.js";
|
|
31
|
+
import { hasOverlay } from "./types.js";
|
|
32
|
+
import { WidgetBase } from "./widget-base.js";
|
|
33
|
+
export class WidgetApp {
|
|
34
|
+
focusManager;
|
|
35
|
+
focus;
|
|
36
|
+
app;
|
|
37
|
+
router;
|
|
38
|
+
// Paints the next frame when anything the last one read changes.
|
|
39
|
+
reaction;
|
|
40
|
+
// The widgets the view drew for the frame on screen, in document order.
|
|
41
|
+
drawn = [];
|
|
42
|
+
constructor(options) {
|
|
43
|
+
const { view } = options;
|
|
44
|
+
this.reaction = new Reaction("WidgetApp", () => this.app.refresh());
|
|
45
|
+
this.app = new App({
|
|
46
|
+
...options,
|
|
47
|
+
view: (t) => ({ render: (renderOptions) => this.compose(() => view(t), renderOptions) }),
|
|
48
|
+
});
|
|
49
|
+
this.focus = new DefaultFocusManager(() => {
|
|
50
|
+
const owners = ownersOn(this.app.frame);
|
|
51
|
+
return this.drawn.filter((widget) => owners.has(widget));
|
|
52
|
+
});
|
|
53
|
+
this.focusManager = this.focus;
|
|
54
|
+
this.router = new EventRouter({ source: this, host: options.host });
|
|
55
|
+
this.app.onPaint(() => {
|
|
56
|
+
this.focus.settle(this.drawn);
|
|
57
|
+
this.router.framePainted();
|
|
58
|
+
});
|
|
59
|
+
// [LAW:single-enforcer] `settle` judges every focus move, so every move
|
|
60
|
+
// paints — including one onto a widget no frame has read yet.
|
|
61
|
+
this.focus.onChange(() => this.app.refresh());
|
|
62
|
+
}
|
|
63
|
+
get phase() {
|
|
64
|
+
return this.app.phase;
|
|
65
|
+
}
|
|
66
|
+
/** The rows on screen now, as painted: what a pointer event lands on. */
|
|
67
|
+
get frame() {
|
|
68
|
+
return this.app.frame;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Take the terminal, paint the first frame, and route input to its widgets
|
|
72
|
+
* until the app stops — `App.run`, with the input.
|
|
73
|
+
*/
|
|
74
|
+
async run() {
|
|
75
|
+
// App refuses a second run; this one must not end the input of the first.
|
|
76
|
+
if (this.app.phase !== "idle")
|
|
77
|
+
return this.app.run();
|
|
78
|
+
// Listening before the app starts, so a first frame that throws — and
|
|
79
|
+
// stops the host — is not followed by a subscription that restarts it.
|
|
80
|
+
this.router.start();
|
|
81
|
+
const running = this.app.run();
|
|
82
|
+
try {
|
|
83
|
+
await running;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
this.router.stop();
|
|
87
|
+
this.reaction.dispose();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
stop() {
|
|
91
|
+
this.app.stop();
|
|
92
|
+
}
|
|
93
|
+
fail(error) {
|
|
94
|
+
this.app.fail(error);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Paint the next frame, as `App.refresh` does. A change to an observable
|
|
98
|
+
* the view read needs none; one no observable carries — a `Viewport`
|
|
99
|
+
* scrolled from a key handler — does.
|
|
100
|
+
*/
|
|
101
|
+
refresh() {
|
|
102
|
+
this.app.refresh();
|
|
103
|
+
}
|
|
104
|
+
suspend() {
|
|
105
|
+
return this.app.suspend();
|
|
106
|
+
}
|
|
107
|
+
/** Paint at the app's rate until the returned function is called, as `App.animate` does. */
|
|
108
|
+
animate() {
|
|
109
|
+
return this.app.animate();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Hear every key. A `high` handler hears it before the focused widget, a
|
|
113
|
+
* `normal` one after it — and after Tab has moved focus — and a handler
|
|
114
|
+
* that calls `event.stop()` ends the key there.
|
|
115
|
+
*/
|
|
116
|
+
onKey(handler, options) {
|
|
117
|
+
return this.router.onKey(handler, options);
|
|
118
|
+
}
|
|
119
|
+
/** Hear every pointer event, in terminal cells, before any widget does. */
|
|
120
|
+
onMouse(handler) {
|
|
121
|
+
return this.router.onMouse(handler);
|
|
122
|
+
}
|
|
123
|
+
compose(view, options) {
|
|
124
|
+
const drawn = new Set();
|
|
125
|
+
// [LAW:parse-dont-validate] An owner is any object; only a `WidgetBase`
|
|
126
|
+
// reports itself as a widget, so this is where `object` becomes one.
|
|
127
|
+
const onDraw = (owner) => {
|
|
128
|
+
if (owner instanceof WidgetBase)
|
|
129
|
+
drawn.add(owner);
|
|
130
|
+
};
|
|
131
|
+
// The view, its render and the overlays are all tracked, so an observable
|
|
132
|
+
// any of them reads — a label, `focused`, the state that picks what the
|
|
133
|
+
// view shows — paints the next frame when it changes.
|
|
134
|
+
// An overlay reports what it draws too: a widget in it is on the frame,
|
|
135
|
+
// and visiting `drawn` in insertion order paints its overlay in turn.
|
|
136
|
+
const render = { ...options, onDraw };
|
|
137
|
+
const lines = tracked(this.reaction, () => {
|
|
138
|
+
const base = Segment.splitLines(view().render(render));
|
|
139
|
+
for (const widget of drawn)
|
|
140
|
+
paintOverlay(base, widget, render);
|
|
141
|
+
return base;
|
|
142
|
+
});
|
|
143
|
+
this.drawn = [...drawn];
|
|
144
|
+
return lines.flatMap((line) => [...line, Segment.line()]);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `fn`, with every observable it reads subscribed to by `reaction`.
|
|
149
|
+
*
|
|
150
|
+
* [LAW:no-silent-failure] mobx reports an error thrown inside `track` and
|
|
151
|
+
* carries on, which would paint an empty frame for a view that threw. The
|
|
152
|
+
* error is carried out instead, and fails the app as `App` fails on any frame
|
|
153
|
+
* that throws.
|
|
154
|
+
*/
|
|
155
|
+
function tracked(reaction, fn) {
|
|
156
|
+
// Written inside `track`, which the compiler cannot see run.
|
|
157
|
+
let outcome = {
|
|
158
|
+
ok: false,
|
|
159
|
+
error: new Error("WidgetApp: a frame drawn after the app stopped"),
|
|
160
|
+
};
|
|
161
|
+
reaction.track(() => {
|
|
162
|
+
try {
|
|
163
|
+
outcome = { ok: true, value: fn() };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
outcome = { ok: false, error };
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
if (!outcome.ok)
|
|
170
|
+
throw outcome.error;
|
|
171
|
+
return outcome.value;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Paint `widget`'s overlay over `lines`, below its footprint and at the column
|
|
175
|
+
* of its row 0, each overlay row stamped as the widget's row below its last —
|
|
176
|
+
* so a click there reaches the widget, in its own coordinates. A widget whose
|
|
177
|
+
* row 0 is not on the frame has nowhere to hang an overlay, and paints none.
|
|
178
|
+
*/
|
|
179
|
+
function paintOverlay(lines, widget, options) {
|
|
180
|
+
if (!hasOverlay(widget))
|
|
181
|
+
return;
|
|
182
|
+
const at = footprintOf(lines, widget);
|
|
183
|
+
if (!at)
|
|
184
|
+
return;
|
|
185
|
+
// An overlay floats over the frame; no region holds it, and the cells right
|
|
186
|
+
// of its column are all it can reach.
|
|
187
|
+
const width = Math.max(0, options.maxWidth - at.x);
|
|
188
|
+
const overlay = widget.renderOverlay({ ...options, maxWidth: width, height: undefined });
|
|
189
|
+
if (overlay === null)
|
|
190
|
+
return;
|
|
191
|
+
const rows = Segment.anchorLines(Segment.splitLines(Segment.cropLines(overlay, width)), widget, at.rows);
|
|
192
|
+
paintLines(lines, rows, at.x, at.y + at.rows);
|
|
193
|
+
}
|
|
194
|
+
// Paint `rows` into `lines` with its top-left cell at (x, y), growing `lines`
|
|
195
|
+
// as needed; each painted cell replaces the one beneath it.
|
|
196
|
+
function paintLines(lines, rows, x, y) {
|
|
197
|
+
while (lines.length < y + rows.length)
|
|
198
|
+
lines.push([]);
|
|
199
|
+
rows.forEach((source, i) => {
|
|
200
|
+
const row = lines[y + i];
|
|
201
|
+
// Padded to `x`, so a row that ends short of the overlay still reaches it.
|
|
202
|
+
const gap = Math.max(0, x - Segment.getLineLength(row));
|
|
203
|
+
const [before = [], , after = []] = Segment.divide([...row, new Segment(" ".repeat(gap))], [x, x + Segment.getLineLength(source)]);
|
|
204
|
+
lines[y + i] = [...before, ...source, ...after];
|
|
205
|
+
});
|
|
206
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WidgetBase — shared MobX observable foundation for interactive widgets.
|
|
3
|
+
* [LAW:one-type-per-behavior] All widgets share the same base; differences
|
|
4
|
+
* are in configuration and state, not in infrastructure.
|
|
5
|
+
*
|
|
6
|
+
* Reactive state is declared with TC39 decorators, which MobX 7 requires on
|
|
7
|
+
* `accessor` members. Each decorator registers its own member, so there is no
|
|
8
|
+
* makeObservable call and subclasses add observables the same way.
|
|
9
|
+
*/
|
|
10
|
+
import { Segment } from "../core/segment.js";
|
|
11
|
+
import { type Style } from "../core/style.js";
|
|
12
|
+
import type { RenderOptions } from "../core/protocol.js";
|
|
13
|
+
import type { InteractiveWidget, KeyEvent, WidgetMouseEvent, WidgetFocusEvent } from "./types.js";
|
|
14
|
+
import type { Unsubscribe } from "../core/subscription.js";
|
|
15
|
+
export declare abstract class WidgetBase implements InteractiveWidget {
|
|
16
|
+
abstract readonly id: string;
|
|
17
|
+
abstract readonly focusable: boolean;
|
|
18
|
+
accessor focused: boolean;
|
|
19
|
+
accessor hovered: boolean;
|
|
20
|
+
accessor active: boolean;
|
|
21
|
+
accessor disabled: boolean;
|
|
22
|
+
private readonly changeHandlers;
|
|
23
|
+
private readonly submitHandlers;
|
|
24
|
+
abstract handleKey(event: KeyEvent): void;
|
|
25
|
+
handleMouse(_event: WidgetMouseEvent): void;
|
|
26
|
+
handleFocus(event: WidgetFocusEvent): void;
|
|
27
|
+
focus(): void;
|
|
28
|
+
blur(): void;
|
|
29
|
+
setDisabled(value: boolean): void;
|
|
30
|
+
setHovered(value: boolean): void;
|
|
31
|
+
onChange(handler: (widget: InteractiveWidget) => void): Unsubscribe;
|
|
32
|
+
onSubmit(handler: (widget: InteractiveWidget) => void): Unsubscribe;
|
|
33
|
+
protected emitChange(): void;
|
|
34
|
+
protected emitSubmit(): void;
|
|
35
|
+
render(options: RenderOptions): Iterable<Segment>;
|
|
36
|
+
protected get ground(): Style;
|
|
37
|
+
protected abstract draw(options: RenderOptions): Iterable<Segment>;
|
|
38
|
+
abstract measure(options: RenderOptions): {
|
|
39
|
+
minimum: number;
|
|
40
|
+
maximum: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WidgetBase — shared MobX observable foundation for interactive widgets.
|
|
3
|
+
* [LAW:one-type-per-behavior] All widgets share the same base; differences
|
|
4
|
+
* are in configuration and state, not in infrastructure.
|
|
5
|
+
*
|
|
6
|
+
* Reactive state is declared with TC39 decorators, which MobX 7 requires on
|
|
7
|
+
* `accessor` members. Each decorator registers its own member, so there is no
|
|
8
|
+
* makeObservable call and subclasses add observables the same way.
|
|
9
|
+
*/
|
|
10
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
11
|
+
var useValue = arguments.length > 2;
|
|
12
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
13
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
14
|
+
}
|
|
15
|
+
return useValue ? value : void 0;
|
|
16
|
+
};
|
|
17
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
18
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
19
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
20
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
21
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
22
|
+
var _, done = false;
|
|
23
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
24
|
+
var context = {};
|
|
25
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
26
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
27
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
28
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
29
|
+
if (kind === "accessor") {
|
|
30
|
+
if (result === void 0) continue;
|
|
31
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
32
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
33
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
34
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
35
|
+
}
|
|
36
|
+
else if (_ = accept(result)) {
|
|
37
|
+
if (kind === "field") initializers.unshift(_);
|
|
38
|
+
else descriptor[key] = _;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
42
|
+
done = true;
|
|
43
|
+
};
|
|
44
|
+
import { observable, action } from "mobx";
|
|
45
|
+
import { Segment } from "../core/segment.js";
|
|
46
|
+
import { NULL_STYLE } from "../core/style.js";
|
|
47
|
+
let WidgetBase = (() => {
|
|
48
|
+
let _instanceExtraInitializers = [];
|
|
49
|
+
let _focused_decorators;
|
|
50
|
+
let _focused_initializers = [];
|
|
51
|
+
let _focused_extraInitializers = [];
|
|
52
|
+
let _hovered_decorators;
|
|
53
|
+
let _hovered_initializers = [];
|
|
54
|
+
let _hovered_extraInitializers = [];
|
|
55
|
+
let _active_decorators;
|
|
56
|
+
let _active_initializers = [];
|
|
57
|
+
let _active_extraInitializers = [];
|
|
58
|
+
let _disabled_decorators;
|
|
59
|
+
let _disabled_initializers = [];
|
|
60
|
+
let _disabled_extraInitializers = [];
|
|
61
|
+
let _handleFocus_decorators;
|
|
62
|
+
let _focus_decorators;
|
|
63
|
+
let _blur_decorators;
|
|
64
|
+
let _setDisabled_decorators;
|
|
65
|
+
let _setHovered_decorators;
|
|
66
|
+
return class WidgetBase {
|
|
67
|
+
static {
|
|
68
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
69
|
+
_focused_decorators = [observable];
|
|
70
|
+
_hovered_decorators = [observable];
|
|
71
|
+
_active_decorators = [observable];
|
|
72
|
+
_disabled_decorators = [observable];
|
|
73
|
+
_handleFocus_decorators = [action];
|
|
74
|
+
_focus_decorators = [action];
|
|
75
|
+
_blur_decorators = [action];
|
|
76
|
+
_setDisabled_decorators = [action];
|
|
77
|
+
_setHovered_decorators = [action];
|
|
78
|
+
__esDecorate(this, null, _focused_decorators, { kind: "accessor", name: "focused", static: false, private: false, access: { has: obj => "focused" in obj, get: obj => obj.focused, set: (obj, value) => { obj.focused = value; } }, metadata: _metadata }, _focused_initializers, _focused_extraInitializers);
|
|
79
|
+
__esDecorate(this, null, _hovered_decorators, { kind: "accessor", name: "hovered", static: false, private: false, access: { has: obj => "hovered" in obj, get: obj => obj.hovered, set: (obj, value) => { obj.hovered = value; } }, metadata: _metadata }, _hovered_initializers, _hovered_extraInitializers);
|
|
80
|
+
__esDecorate(this, null, _active_decorators, { kind: "accessor", name: "active", static: false, private: false, access: { has: obj => "active" in obj, get: obj => obj.active, set: (obj, value) => { obj.active = value; } }, metadata: _metadata }, _active_initializers, _active_extraInitializers);
|
|
81
|
+
__esDecorate(this, null, _disabled_decorators, { kind: "accessor", name: "disabled", static: false, private: false, access: { has: obj => "disabled" in obj, get: obj => obj.disabled, set: (obj, value) => { obj.disabled = value; } }, metadata: _metadata }, _disabled_initializers, _disabled_extraInitializers);
|
|
82
|
+
__esDecorate(this, null, _handleFocus_decorators, { kind: "method", name: "handleFocus", static: false, private: false, access: { has: obj => "handleFocus" in obj, get: obj => obj.handleFocus }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
83
|
+
__esDecorate(this, null, _focus_decorators, { kind: "method", name: "focus", static: false, private: false, access: { has: obj => "focus" in obj, get: obj => obj.focus }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
84
|
+
__esDecorate(this, null, _blur_decorators, { kind: "method", name: "blur", static: false, private: false, access: { has: obj => "blur" in obj, get: obj => obj.blur }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
85
|
+
__esDecorate(this, null, _setDisabled_decorators, { kind: "method", name: "setDisabled", static: false, private: false, access: { has: obj => "setDisabled" in obj, get: obj => obj.setDisabled }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
86
|
+
__esDecorate(this, null, _setHovered_decorators, { kind: "method", name: "setHovered", static: false, private: false, access: { has: obj => "setHovered" in obj, get: obj => obj.setHovered }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
87
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
88
|
+
}
|
|
89
|
+
#focused_accessor_storage = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _focused_initializers, false));
|
|
90
|
+
get focused() { return this.#focused_accessor_storage; }
|
|
91
|
+
set focused(value) { this.#focused_accessor_storage = value; }
|
|
92
|
+
#hovered_accessor_storage = (__runInitializers(this, _focused_extraInitializers), __runInitializers(this, _hovered_initializers, false));
|
|
93
|
+
get hovered() { return this.#hovered_accessor_storage; }
|
|
94
|
+
set hovered(value) { this.#hovered_accessor_storage = value; }
|
|
95
|
+
#active_accessor_storage = (__runInitializers(this, _hovered_extraInitializers), __runInitializers(this, _active_initializers, false));
|
|
96
|
+
get active() { return this.#active_accessor_storage; }
|
|
97
|
+
set active(value) { this.#active_accessor_storage = value; }
|
|
98
|
+
#disabled_accessor_storage = (__runInitializers(this, _active_extraInitializers), __runInitializers(this, _disabled_initializers, false));
|
|
99
|
+
get disabled() { return this.#disabled_accessor_storage; }
|
|
100
|
+
set disabled(value) { this.#disabled_accessor_storage = value; }
|
|
101
|
+
changeHandlers = (__runInitializers(this, _disabled_extraInitializers), new Set());
|
|
102
|
+
submitHandlers = new Set();
|
|
103
|
+
handleMouse(_event) { }
|
|
104
|
+
handleFocus(event) {
|
|
105
|
+
this.focused = event.type === "focus";
|
|
106
|
+
}
|
|
107
|
+
// --- Programmatic control ---
|
|
108
|
+
// [LAW:single-enforcer] focus()/blur() route through handleFocus so any
|
|
109
|
+
// subclass override (e.g. Dropdown collapsing its overlay on blur)
|
|
110
|
+
// participates in the same transition path the event router uses. There
|
|
111
|
+
// is no second code path that flips `focused` directly from the outside.
|
|
112
|
+
focus() {
|
|
113
|
+
this.handleFocus({ type: "focus" });
|
|
114
|
+
}
|
|
115
|
+
blur() {
|
|
116
|
+
this.handleFocus({ type: "blur" });
|
|
117
|
+
}
|
|
118
|
+
setDisabled(value) {
|
|
119
|
+
this.disabled = value;
|
|
120
|
+
}
|
|
121
|
+
// [LAW:single-enforcer] One canonical setter for hover state lives on
|
|
122
|
+
// the base. EventRouter calls it when the pointer moves onto or off this
|
|
123
|
+
// widget; widgets that need to react to hover transitions override
|
|
124
|
+
// handleMouse and read the synthesized `mouse_move`.
|
|
125
|
+
setHovered(value) {
|
|
126
|
+
this.hovered = value;
|
|
127
|
+
}
|
|
128
|
+
// --- Subscriptions ---
|
|
129
|
+
onChange(handler) {
|
|
130
|
+
this.changeHandlers.add(handler);
|
|
131
|
+
return () => this.changeHandlers.delete(handler);
|
|
132
|
+
}
|
|
133
|
+
onSubmit(handler) {
|
|
134
|
+
this.submitHandlers.add(handler);
|
|
135
|
+
return () => this.submitHandlers.delete(handler);
|
|
136
|
+
}
|
|
137
|
+
emitChange() {
|
|
138
|
+
for (const handler of this.changeHandlers) {
|
|
139
|
+
handler(this);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
emitSubmit() {
|
|
143
|
+
for (const handler of this.submitHandlers) {
|
|
144
|
+
handler(this);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// --- Renderable + Measurable ---
|
|
148
|
+
// [LAW:single-enforcer] The one place a widget's cells get their anchor, so
|
|
149
|
+
// a hit on any cell names this widget and the cell's place in its output
|
|
150
|
+
// (`widgetAt`). Subclasses draw; none stamps itself. A `RichText` admits no
|
|
151
|
+
// anchor, so stamping has to follow every text layout `draw` does.
|
|
152
|
+
// Every row is padded to the widest, so the widget owns its whole rectangle
|
|
153
|
+
// and a click past the end of a short row still reaches it; every row ends
|
|
154
|
+
// in a newline, so a blank last row survives the next `splitLines`. The
|
|
155
|
+
// padding stands on `ground`, whatever the widget draws its own cells on.
|
|
156
|
+
// Reporting itself before it draws is what puts a widget ahead of the
|
|
157
|
+
// widgets nested in it in document order — the order focus traverses.
|
|
158
|
+
render(options) {
|
|
159
|
+
options.onDraw?.(this);
|
|
160
|
+
const drawn = Segment.splitLines(this.draw(options));
|
|
161
|
+
const [width] = Segment.getShape(drawn);
|
|
162
|
+
const lines = Segment.anchorLines(drawn.map((line) => Segment.adjustLineLength(line, width, this.ground)), this);
|
|
163
|
+
return lines.flatMap((line) => [...line, Segment.line()]);
|
|
164
|
+
}
|
|
165
|
+
// What a padded row stands on. None by default: the padding shows whatever
|
|
166
|
+
// the widget is drawn over, as the cells of content it did not draw do.
|
|
167
|
+
get ground() {
|
|
168
|
+
return NULL_STYLE;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
})();
|
|
172
|
+
export { WidgetBase };
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptctl/rich-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Rich text and beautiful formatting in the terminal — a TypeScript port of Python's Rich",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/brandon-fryslie/rich-js"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"sideEffects": false,
|
|
7
11
|
"main": "dist/index.js",
|
|
@@ -53,7 +57,7 @@
|
|
|
53
57
|
"THIRD-PARTY-NOTICES"
|
|
54
58
|
],
|
|
55
59
|
"scripts": {
|
|
56
|
-
"prepublishOnly": "node scripts/verify-release-tag.mjs",
|
|
60
|
+
"prepublishOnly": "node scripts/verify-release-tag.mjs && node scripts/verify-entry-points.mjs",
|
|
57
61
|
"build": "npm run clean && tsc",
|
|
58
62
|
"test": "vitest run",
|
|
59
63
|
"test:watch": "vitest",
|
|
@@ -66,6 +70,7 @@
|
|
|
66
70
|
"demo:build": "tsc -p tsconfig.demo.json",
|
|
67
71
|
"sessions": "tsc -p tsconfig.demo.json && node dist-demo/examples/claude-sessions/index.js",
|
|
68
72
|
"strip": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-strip/index.js",
|
|
73
|
+
"effects-feel": "tsc -p tsconfig.demo.json && node dist-demo/examples/effects-feel/index.js",
|
|
69
74
|
"viewport": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-viewport/index.js",
|
|
70
75
|
"markup-plugins": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-markup-plugins/index.js",
|
|
71
76
|
"demo-inputs": "tsc -p tsconfig.demo.json && node dist-demo/examples/rich-config/index.js",
|
|
@@ -96,16 +101,25 @@
|
|
|
96
101
|
"node": ">=20"
|
|
97
102
|
},
|
|
98
103
|
"devDependencies": {
|
|
104
|
+
"@codemirror/commands": "^6.11.1",
|
|
105
|
+
"@codemirror/lang-javascript": "^6.2.5",
|
|
106
|
+
"@codemirror/language": "^6.12.4",
|
|
107
|
+
"@codemirror/state": "^6.7.6",
|
|
108
|
+
"@codemirror/view": "^6.43.13",
|
|
109
|
+
"@lezer/highlight": "^1.2.5",
|
|
99
110
|
"@playwright/test": "^1.63.0",
|
|
100
111
|
"@promptctl/go-template-js": "^0.10.0",
|
|
101
112
|
"@types/markdown-it": "^14.2.0",
|
|
102
113
|
"@types/node": "^26.5.1",
|
|
103
114
|
"@types/semver": "^7.8.0",
|
|
115
|
+
"@xterm/headless": "^6.0.0",
|
|
104
116
|
"markdown-it": "^14.3.2",
|
|
105
117
|
"markdown-it-container": "^4.0.0",
|
|
106
118
|
"mobx": "^7.0.3",
|
|
119
|
+
"node-inspect-extracted": "^3.3.3",
|
|
107
120
|
"semver": "^7.8.5",
|
|
108
121
|
"strip-ansi": "^7.2.0",
|
|
122
|
+
"sucrase": "^3.35.1",
|
|
109
123
|
"typescript": "^6.0.3",
|
|
110
124
|
"vite": "^8.3.0",
|
|
111
125
|
"vitepress": "^1.6.4",
|