@promptctl/rich-js 0.24.0 → 0.25.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 +7 -0
- package/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +243 -0
- package/dist/core/animation.js +375 -0
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +40 -24
- package/dist/core/cells.js +151 -66
- package/dist/core/clock.d.ts +2 -2
- package/dist/core/clock.js +6 -4
- package/dist/core/color.d.ts +27 -10
- package/dist/core/color.js +21 -29
- package/dist/core/console.d.ts +28 -17
- package/dist/core/console.js +48 -43
- package/dist/core/easing.d.ts +140 -10
- package/dist/core/easing.js +328 -50
- package/dist/core/emoji.d.ts +1 -1
- package/dist/core/emoji.js +1 -2
- package/dist/core/export-html.js +2 -1
- package/dist/core/export-lines.js +3 -3
- package/dist/core/highlighter.d.ts +9 -8
- package/dist/core/highlighter.js +3 -2
- package/dist/core/json.js +3 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +3 -3
- package/dist/core/memo.d.ts +11 -2
- package/dist/core/memo.js +16 -0
- package/dist/core/oklch.js +17 -6
- package/dist/core/osc8.d.ts +1 -1
- package/dist/core/osc8.js +4 -5
- package/dist/core/paint.d.ts +2 -1
- package/dist/core/paint.js +4 -3
- package/dist/core/pattern.d.ts +21 -0
- package/dist/core/pattern.js +21 -0
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +11 -12
- package/dist/core/protocol.d.ts +136 -21
- package/dist/core/protocol.js +203 -16
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +27 -6
- package/dist/core/segment.d.ts +31 -6
- package/dist/core/segment.js +68 -53
- package/dist/core/spinnerData.d.ts +1 -1
- package/dist/core/state.d.ts +53 -0
- package/dist/core/state.js +82 -0
- package/dist/core/strip.d.ts +11 -13
- package/dist/core/strip.js +126 -113
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +74 -34
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +133 -133
- package/dist/core/timeline.d.ts +109 -0
- package/dist/core/timeline.js +180 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +71 -0
- package/dist/core/view.d.ts +92 -0
- package/dist/core/view.js +83 -0
- package/dist/host/app.d.ts +49 -21
- package/dist/host/app.js +72 -38
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +30 -15
- package/dist/index.js +20 -6
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +1 -1
- package/dist/renderables/columns.js +3 -3
- package/dist/renderables/constrain.js +2 -1
- package/dist/renderables/effect.d.ts +155 -92
- package/dist/renderables/effect.js +203 -136
- package/dist/renderables/effects.d.ts +137 -24
- package/dist/renderables/effects.js +276 -113
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +18 -9
- package/dist/renderables/flexStrip.js +2 -1
- package/dist/renderables/frames.d.ts +45 -43
- package/dist/renderables/frames.js +57 -45
- package/dist/renderables/glyph-effect.d.ts +158 -0
- package/dist/renderables/glyph-effect.js +237 -0
- package/dist/renderables/glyph-effects.d.ts +147 -0
- package/dist/renderables/glyph-effects.js +283 -0
- package/dist/renderables/group.d.ts +1 -1
- package/dist/renderables/group.js +2 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +10 -9
- package/dist/renderables/layout.js +42 -35
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +2 -2
- package/dist/renderables/list-view.js +11 -8
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +89 -42
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1427 -0
- package/dist/renderables/markdown-url.d.ts +19 -0
- package/dist/renderables/markdown-url.js +314 -0
- package/dist/renderables/markdown.d.ts +5 -7
- package/dist/renderables/markdown.js +356 -683
- package/dist/renderables/padding.d.ts +7 -20
- package/dist/renderables/padding.js +29 -50
- package/dist/renderables/panel.d.ts +30 -32
- package/dist/renderables/panel.js +65 -109
- package/dist/renderables/progress.d.ts +83 -32
- package/dist/renderables/progress.js +186 -90
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +33 -10
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +25 -0
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +57 -24
- package/dist/renderables/spinner.js +101 -49
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +5 -3
- package/dist/renderables/table.d.ts +48 -46
- package/dist/renderables/table.js +170 -208
- package/dist/renderables/traceback.d.ts +3 -3
- package/dist/renderables/traceback.js +12 -10
- package/dist/renderables/tree.d.ts +28 -25
- package/dist/renderables/tree.js +108 -58
- package/dist/renderables/viewport.d.ts +77 -38
- package/dist/renderables/viewport.js +156 -72
- package/dist/template-bindings/animation-funcs.d.ts +78 -9
- package/dist/template-bindings/animation-funcs.js +132 -14
- package/dist/template-bindings/index.d.ts +5 -5
- package/dist/template-bindings/index.js +7 -6
- package/dist/themes/ramp.d.ts +62 -4
- package/dist/themes/ramp.js +82 -4
- package/dist/themes/registry.js +5 -7
- package/dist/widgets/button.js +3 -2
- package/dist/widgets/checkbox.js +4 -3
- package/dist/widgets/dropdown.d.ts +3 -1
- package/dist/widgets/dropdown.js +20 -15
- package/dist/widgets/event-router.js +4 -2
- package/dist/widgets/focus-manager.d.ts +23 -14
- package/dist/widgets/focus-manager.js +34 -33
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -1
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/observable.d.ts +23 -0
- package/dist/widgets/observable.js +43 -0
- package/dist/widgets/slider.d.ts +1 -1
- package/dist/widgets/slider.js +12 -3
- package/dist/widgets/static-item.d.ts +1 -1
- package/dist/widgets/static-item.js +6 -7
- package/dist/widgets/text-input.d.ts +53 -47
- package/dist/widgets/text-input.js +456 -399
- package/dist/widgets/themed-widget.js +2 -1
- package/dist/widgets/toggle.js +4 -3
- package/dist/widgets/types.d.ts +10 -4
- package/dist/widgets/types.js +8 -2
- package/dist/widgets/widget-app.d.ts +32 -6
- package/dist/widgets/widget-app.js +74 -27
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -21
- package/package.json +9 -5
|
@@ -41,7 +41,8 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
41
41
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
42
42
|
done = true;
|
|
43
43
|
};
|
|
44
|
-
import {
|
|
44
|
+
import { action } from "mobx";
|
|
45
|
+
import { observableRef } from "./observable.js";
|
|
45
46
|
import { Style } from "../core/style.js";
|
|
46
47
|
import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
|
|
47
48
|
import { WidgetBase } from "./widget-base.js";
|
package/dist/widgets/toggle.js
CHANGED
|
@@ -44,7 +44,8 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
44
44
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
45
45
|
done = true;
|
|
46
46
|
};
|
|
47
|
-
import {
|
|
47
|
+
import { action } from "mobx";
|
|
48
|
+
import { observableRef } from "./observable.js";
|
|
48
49
|
import { Segment } from "../core/segment.js";
|
|
49
50
|
import { Style } from "../core/style.js";
|
|
50
51
|
import { cellLen } from "../core/cells.js";
|
|
@@ -79,8 +80,8 @@ let Toggle = (() => {
|
|
|
79
80
|
return class Toggle extends _classSuper {
|
|
80
81
|
static {
|
|
81
82
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
82
|
-
_label_decorators = [
|
|
83
|
-
_on_decorators = [
|
|
83
|
+
_label_decorators = [observableRef];
|
|
84
|
+
_on_decorators = [observableRef];
|
|
84
85
|
_variant_decorators = [observableRef];
|
|
85
86
|
_handleKey_decorators = [action];
|
|
86
87
|
_handleMouse_decorators = [action];
|
package/dist/widgets/types.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
9
9
|
import type { Segment } from "../core/segment.js";
|
|
10
|
+
import type { Anchor } from "../core/anchor.js";
|
|
10
11
|
import type { Unsubscribe } from "../core/subscription.js";
|
|
11
12
|
import type { KeyPress, ScreenMouseEvent } from "../host/input.js";
|
|
12
13
|
export declare class KeyEvent implements KeyPress {
|
|
@@ -15,8 +16,9 @@ export declare class KeyEvent implements KeyPress {
|
|
|
15
16
|
readonly shift: boolean;
|
|
16
17
|
readonly ctrl: boolean;
|
|
17
18
|
readonly meta: boolean;
|
|
19
|
+
readonly time: number;
|
|
18
20
|
private _stopped;
|
|
19
|
-
constructor(init: KeyPress);
|
|
21
|
+
constructor(init: KeyPress, time: number);
|
|
20
22
|
get stopped(): boolean;
|
|
21
23
|
stop(): void;
|
|
22
24
|
}
|
|
@@ -28,9 +30,12 @@ export type WidgetMouseEvent = Extract<ScreenMouseEvent, {
|
|
|
28
30
|
type: "mouse_down" | "mouse_up" | "mouse_move";
|
|
29
31
|
}> & {
|
|
30
32
|
readonly over: boolean;
|
|
33
|
+
readonly cell: Anchor | undefined;
|
|
34
|
+
readonly time: number;
|
|
31
35
|
};
|
|
32
36
|
export interface WidgetFocusEvent {
|
|
33
|
-
type: "focus" | "blur";
|
|
37
|
+
readonly type: "focus" | "blur";
|
|
38
|
+
readonly time: number;
|
|
34
39
|
}
|
|
35
40
|
export interface InteractiveWidget extends Renderable, Measurable {
|
|
36
41
|
readonly id: string;
|
|
@@ -42,8 +47,8 @@ export interface InteractiveWidget extends Renderable, Measurable {
|
|
|
42
47
|
handleKey(event: KeyEvent): void;
|
|
43
48
|
handleMouse(event: WidgetMouseEvent): void;
|
|
44
49
|
handleFocus(event: WidgetFocusEvent): void;
|
|
45
|
-
focus(): void;
|
|
46
|
-
blur(): void;
|
|
50
|
+
focus(time: number): void;
|
|
51
|
+
blur(time: number): void;
|
|
47
52
|
setDisabled(value: boolean): void;
|
|
48
53
|
setHovered(value: boolean): void;
|
|
49
54
|
onChange(handler: (widget: InteractiveWidget) => void): Unsubscribe;
|
|
@@ -66,5 +71,6 @@ export interface FocusManager {
|
|
|
66
71
|
export interface FrameSource {
|
|
67
72
|
readonly focusManager: FocusManager;
|
|
68
73
|
readonly frame: readonly (readonly Segment[])[];
|
|
74
|
+
readonly frameTime: number;
|
|
69
75
|
refresh(): void;
|
|
70
76
|
}
|
package/dist/widgets/types.js
CHANGED
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
* terminal escape sequences, or their host environment.
|
|
7
7
|
*/
|
|
8
8
|
// --- Event types ---
|
|
9
|
+
// Every event a widget hears carries `time`: when it was heard, on the clock
|
|
10
|
+
// the frame's cells are read on (`RenderOptions.frame`), so a widget
|
|
11
|
+
// keeping the moment of its last interaction — a caret's blink restarting —
|
|
12
|
+
// compares it with the frame it draws and never reads a clock.
|
|
9
13
|
// [LAW:types-are-the-program] KeyEvent is a `KeyPress` the app heard, plus
|
|
10
|
-
// the say over who else hears it. It carries a single mutable signal
|
|
14
|
+
// when it was heard and the say over who else hears it. It carries a single mutable signal
|
|
11
15
|
// (`stopped`) that participants in the dispatch chain set by calling
|
|
12
16
|
// `stop()`. Treating it as an interface would force every handler to
|
|
13
17
|
// shuttle a return value upward and the router to interpret it — the
|
|
@@ -19,13 +23,15 @@ export class KeyEvent {
|
|
|
19
23
|
shift;
|
|
20
24
|
ctrl;
|
|
21
25
|
meta;
|
|
26
|
+
time;
|
|
22
27
|
_stopped = false;
|
|
23
|
-
constructor(init) {
|
|
28
|
+
constructor(init, time) {
|
|
24
29
|
this.key = init.key;
|
|
25
30
|
this.character = init.character;
|
|
26
31
|
this.shift = init.shift;
|
|
27
32
|
this.ctrl = init.ctrl;
|
|
28
33
|
this.meta = init.meta;
|
|
34
|
+
this.time = time;
|
|
29
35
|
}
|
|
30
36
|
get stopped() { return this._stopped; }
|
|
31
37
|
// Claim this key. Halts further chain dispatch — no high/normal handler
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* `Table` cell — and nothing lays it out a second time. Everything added here
|
|
8
8
|
* reads the frame the view drew:
|
|
9
9
|
*
|
|
10
|
-
* - Focus moves in document order: the order
|
|
11
|
-
* (`RenderOptions.
|
|
10
|
+
* - Focus moves in document order: the order the view reports its widgets in
|
|
11
|
+
* (`RenderOptions.onPresent`), among those with a cell on the frame on screen.
|
|
12
|
+
* A widget its container hands no column is reported held, not drawn: it is
|
|
13
|
+
* in the view, so it keeps focus, and it has no cell, so Tab passes it.
|
|
12
14
|
* Not reading order — in a `Layout` split into columns, reading order
|
|
13
15
|
* zigzags between the columns row by row.
|
|
14
16
|
* - Overlays are painted over the view's frame, in document order, directly
|
|
@@ -24,10 +26,14 @@
|
|
|
24
26
|
* hear the input.
|
|
25
27
|
*/
|
|
26
28
|
import { Segment } from "../core/segment.js";
|
|
29
|
+
import type { FrameHost, Presence } from "../core/protocol.js";
|
|
27
30
|
import type { Unsubscribe } from "../core/subscription.js";
|
|
31
|
+
import type { Clock, FrameRate } from "../core/clock.js";
|
|
32
|
+
import type { Timeline } from "../core/timeline.js";
|
|
28
33
|
import { type AppOptions, type AppPhase, type Wheel } from "../host/app.js";
|
|
29
34
|
import type { FocusManager, FrameSource, KeyEvent, KeyHandlerOptions } from "./types.js";
|
|
30
35
|
import type { AppMouseEvent } from "../host/app.js";
|
|
36
|
+
import { WidgetBase } from "./widget-base.js";
|
|
31
37
|
export interface WidgetAppOptions extends AppOptions {
|
|
32
38
|
/**
|
|
33
39
|
* Whether a left-button press focuses the widget under it, before it hears the
|
|
@@ -35,17 +41,39 @@ export interface WidgetAppOptions extends AppOptions {
|
|
|
35
41
|
*/
|
|
36
42
|
readonly focusOnClick?: boolean;
|
|
37
43
|
}
|
|
38
|
-
|
|
44
|
+
/** A widget the view reported for a frame, and whether it was drawn or held. */
|
|
45
|
+
export interface WidgetPresence {
|
|
46
|
+
readonly widget: WidgetBase;
|
|
47
|
+
readonly presence: Presence;
|
|
48
|
+
}
|
|
49
|
+
export declare class WidgetApp implements FrameSource, FrameHost {
|
|
39
50
|
readonly focusManager: FocusManager;
|
|
40
51
|
private readonly focus;
|
|
41
52
|
private readonly app;
|
|
42
53
|
private readonly router;
|
|
43
54
|
private readonly reaction;
|
|
44
|
-
private
|
|
55
|
+
private _present;
|
|
45
56
|
constructor(options: WidgetAppOptions);
|
|
46
57
|
get phase(): AppPhase;
|
|
58
|
+
/** The clock the app reads the time from: where the host time a `Timeline` operation takes is read. */
|
|
59
|
+
get clock(): Clock;
|
|
60
|
+
/** The map from the clock to the time the view is drawn for; setting it paints the frame for the new time, as `App.timeline` does. */
|
|
61
|
+
get timeline(): Timeline;
|
|
62
|
+
set timeline(timeline: Timeline);
|
|
63
|
+
/** How often the app paints while what it shows moves; setting it goes on at the new rate from the next frame, as `App.rate` does. */
|
|
64
|
+
get rate(): FrameRate;
|
|
65
|
+
set rate(rate: FrameRate);
|
|
66
|
+
/**
|
|
67
|
+
* The widgets the view reported for the frame on screen, in document order,
|
|
68
|
+
* each drawn or held (`RenderOptions.onPresent`). A held widget is one its
|
|
69
|
+
* container handed no column: in the view, with no cell on the frame.
|
|
70
|
+
*/
|
|
71
|
+
get present(): readonly WidgetPresence[];
|
|
72
|
+
private get widgets();
|
|
47
73
|
/** The rows on screen now, as painted: what a pointer event lands on. */
|
|
48
74
|
get frame(): readonly (readonly Segment[])[];
|
|
75
|
+
/** The time a frame drawn now is drawn for, as `App.frameTime` reads it. */
|
|
76
|
+
get frameTime(): number;
|
|
49
77
|
/**
|
|
50
78
|
* Take the terminal, paint the first frame, and route input to its widgets
|
|
51
79
|
* until the app stops — `App.run`, with the input.
|
|
@@ -60,8 +88,6 @@ export declare class WidgetApp implements FrameSource {
|
|
|
60
88
|
*/
|
|
61
89
|
refresh(): void;
|
|
62
90
|
suspend(): Promise<void>;
|
|
63
|
-
/** Paint at the app's rate until the returned function is called, as `App.animate` does. */
|
|
64
|
-
animate(): Unsubscribe;
|
|
65
91
|
/**
|
|
66
92
|
* Hear every key the app does not quit on. A `high` handler hears it before the focused widget, a
|
|
67
93
|
* `normal` one after it — and after Tab has moved focus — and a handler
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* `Table` cell — and nothing lays it out a second time. Everything added here
|
|
8
8
|
* reads the frame the view drew:
|
|
9
9
|
*
|
|
10
|
-
* - Focus moves in document order: the order
|
|
11
|
-
* (`RenderOptions.
|
|
10
|
+
* - Focus moves in document order: the order the view reports its widgets in
|
|
11
|
+
* (`RenderOptions.onPresent`), among those with a cell on the frame on screen.
|
|
12
|
+
* A widget its container hands no column is reported held, not drawn: it is
|
|
13
|
+
* in the view, so it keeps focus, and it has no cell, so Tab passes it.
|
|
12
14
|
* Not reading order — in a `Layout` split into columns, reading order
|
|
13
15
|
* zigzags between the columns row by row.
|
|
14
16
|
* - Overlays are painted over the view's frame, in document order, directly
|
|
@@ -25,6 +27,8 @@
|
|
|
25
27
|
*/
|
|
26
28
|
import { Reaction } from "mobx";
|
|
27
29
|
import { Segment } from "../core/segment.js";
|
|
30
|
+
import { render } from "../core/protocol.js";
|
|
31
|
+
import { hostView } from "../core/view.js";
|
|
28
32
|
import { App } from "../host/app.js";
|
|
29
33
|
import { DefaultFocusManager } from "./focus-manager.js";
|
|
30
34
|
import { EventRouter } from "./event-router.js";
|
|
@@ -38,18 +42,30 @@ export class WidgetApp {
|
|
|
38
42
|
router;
|
|
39
43
|
// Paints the next frame when anything the last one read changes.
|
|
40
44
|
reaction;
|
|
41
|
-
|
|
42
|
-
drawn = [];
|
|
45
|
+
_present = [];
|
|
43
46
|
constructor(options) {
|
|
44
|
-
const {
|
|
47
|
+
const { focusOnClick = true } = options;
|
|
48
|
+
const view = hostView(options.view);
|
|
45
49
|
this.reaction = new Reaction("WidgetApp", () => this.app.refresh());
|
|
50
|
+
// The view is drawn inside `compose`, where what it reads is tracked —
|
|
51
|
+
// a function view asked there too — and when it changes is its own to
|
|
52
|
+
// say, as it would be to the App.
|
|
53
|
+
// Its frames are its own, drawn for its `frameTime`: what draws in them
|
|
54
|
+
// is told this app is the host, not the App it paints through, so
|
|
55
|
+
// `drawnBy: widgetApp` names the host its frames carry.
|
|
46
56
|
this.app = new App({
|
|
47
57
|
...options,
|
|
48
|
-
view:
|
|
58
|
+
view: {
|
|
59
|
+
render: (renderOptions) => this.compose(view, { ...renderOptions, frame: renderOptions.frame && { time: renderOptions.frame.time, host: this } }),
|
|
60
|
+
},
|
|
49
61
|
});
|
|
50
|
-
this.focus = new DefaultFocusManager(
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
this.focus = new DefaultFocusManager({
|
|
63
|
+
present: () => this.widgets,
|
|
64
|
+
onScreen: () => {
|
|
65
|
+
const owners = ownersOn(this.app.frame);
|
|
66
|
+
return this.widgets.filter((widget) => owners.has(widget));
|
|
67
|
+
},
|
|
68
|
+
frameTime: () => this.frameTime,
|
|
53
69
|
});
|
|
54
70
|
this.focusManager = this.focus;
|
|
55
71
|
this.router = new EventRouter({ source: this, focusOnClick });
|
|
@@ -59,7 +75,7 @@ export class WidgetApp {
|
|
|
59
75
|
this.app.onMouse((event) => this.router.mouse(event));
|
|
60
76
|
this.app.onWheel((wheel) => this.router.wheel(wheel));
|
|
61
77
|
this.app.onPaint(() => {
|
|
62
|
-
this.focus.settle(
|
|
78
|
+
this.focus.settle();
|
|
63
79
|
this.router.framePainted();
|
|
64
80
|
});
|
|
65
81
|
// [LAW:single-enforcer] `settle` judges every focus move, so every move
|
|
@@ -69,10 +85,43 @@ export class WidgetApp {
|
|
|
69
85
|
get phase() {
|
|
70
86
|
return this.app.phase;
|
|
71
87
|
}
|
|
88
|
+
/** The clock the app reads the time from: where the host time a `Timeline` operation takes is read. */
|
|
89
|
+
get clock() {
|
|
90
|
+
return this.app.clock;
|
|
91
|
+
}
|
|
92
|
+
/** The map from the clock to the time the view is drawn for; setting it paints the frame for the new time, as `App.timeline` does. */
|
|
93
|
+
get timeline() {
|
|
94
|
+
return this.app.timeline;
|
|
95
|
+
}
|
|
96
|
+
set timeline(timeline) {
|
|
97
|
+
this.app.timeline = timeline;
|
|
98
|
+
}
|
|
99
|
+
/** How often the app paints while what it shows moves; setting it goes on at the new rate from the next frame, as `App.rate` does. */
|
|
100
|
+
get rate() {
|
|
101
|
+
return this.app.rate;
|
|
102
|
+
}
|
|
103
|
+
set rate(rate) {
|
|
104
|
+
this.app.rate = rate;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The widgets the view reported for the frame on screen, in document order,
|
|
108
|
+
* each drawn or held (`RenderOptions.onPresent`). A held widget is one its
|
|
109
|
+
* container handed no column: in the view, with no cell on the frame.
|
|
110
|
+
*/
|
|
111
|
+
get present() {
|
|
112
|
+
return this._present;
|
|
113
|
+
}
|
|
114
|
+
get widgets() {
|
|
115
|
+
return this._present.map(({ widget }) => widget);
|
|
116
|
+
}
|
|
72
117
|
/** The rows on screen now, as painted: what a pointer event lands on. */
|
|
73
118
|
get frame() {
|
|
74
119
|
return this.app.frame;
|
|
75
120
|
}
|
|
121
|
+
/** The time a frame drawn now is drawn for, as `App.frameTime` reads it. */
|
|
122
|
+
get frameTime() {
|
|
123
|
+
return this.app.frameTime;
|
|
124
|
+
}
|
|
76
125
|
/**
|
|
77
126
|
* Take the terminal, paint the first frame, and route input to its widgets
|
|
78
127
|
* until the app stops — `App.run`, with the input.
|
|
@@ -106,10 +155,6 @@ export class WidgetApp {
|
|
|
106
155
|
suspend() {
|
|
107
156
|
return this.app.suspend();
|
|
108
157
|
}
|
|
109
|
-
/** Paint at the app's rate until the returned function is called, as `App.animate` does. */
|
|
110
|
-
animate() {
|
|
111
|
-
return this.app.animate();
|
|
112
|
-
}
|
|
113
158
|
/**
|
|
114
159
|
* Hear every key the app does not quit on. A `high` handler hears it before the focused widget, a
|
|
115
160
|
* `normal` one after it — and after Tab has moved focus — and a handler
|
|
@@ -131,26 +176,28 @@ export class WidgetApp {
|
|
|
131
176
|
return this.app.onWheel(handler);
|
|
132
177
|
}
|
|
133
178
|
compose(view, options) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
179
|
+
// Each widget once, where it was first reported; drawn anywhere, it is drawn.
|
|
180
|
+
const present = new Map();
|
|
181
|
+
// [LAW:parse-dont-validate] What the view reports is any renderable; only
|
|
182
|
+
// a `WidgetBase` is a widget, so this is where a renderable becomes one.
|
|
183
|
+
const onPresent = (renderable, presence) => {
|
|
184
|
+
if (renderable instanceof WidgetBase)
|
|
185
|
+
present.set(renderable, present.get(renderable) === "drawn" ? "drawn" : presence);
|
|
140
186
|
};
|
|
141
187
|
// The view, its render and the overlays are all tracked, so an observable
|
|
142
188
|
// any of them reads — a label, `focused`, the state that picks what the
|
|
143
189
|
// view shows — paints the next frame when it changes.
|
|
144
|
-
// An overlay reports what it draws too: a widget in it is
|
|
145
|
-
// and visiting `
|
|
146
|
-
|
|
190
|
+
// An overlay reports what it draws too: a widget in it is in the view,
|
|
191
|
+
// and visiting `present` in report order paints its overlay in turn. A
|
|
192
|
+
// held widget has no footprint on the frame, and so paints none.
|
|
193
|
+
const drawing = { ...options, onPresent };
|
|
147
194
|
const lines = tracked(this.reaction, () => {
|
|
148
|
-
const base = Segment.splitLines(
|
|
149
|
-
for (const widget of
|
|
150
|
-
paintOverlay(base, widget,
|
|
195
|
+
const base = Segment.splitLines(render(view, drawing));
|
|
196
|
+
for (const widget of present.keys())
|
|
197
|
+
paintOverlay(base, widget, drawing);
|
|
151
198
|
return base;
|
|
152
199
|
});
|
|
153
|
-
this.
|
|
200
|
+
this._present = [...present].map(([widget, presence]) => ({ widget, presence }));
|
|
154
201
|
return lines.flatMap((line) => [...line, Segment.line()]);
|
|
155
202
|
}
|
|
156
203
|
}
|
|
@@ -24,8 +24,8 @@ export declare abstract class WidgetBase implements InteractiveWidget {
|
|
|
24
24
|
abstract handleKey(event: KeyEvent): void;
|
|
25
25
|
handleMouse(_event: WidgetMouseEvent): void;
|
|
26
26
|
handleFocus(event: WidgetFocusEvent): void;
|
|
27
|
-
focus(): void;
|
|
28
|
-
blur(): void;
|
|
27
|
+
focus(time: number): void;
|
|
28
|
+
blur(time: number): void;
|
|
29
29
|
setDisabled(value: boolean): void;
|
|
30
30
|
setHovered(value: boolean): void;
|
|
31
31
|
onChange(handler: (widget: InteractiveWidget) => void): Unsubscribe;
|
|
@@ -41,9 +41,11 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
41
41
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
42
42
|
done = true;
|
|
43
43
|
};
|
|
44
|
-
import {
|
|
44
|
+
import { action } from "mobx";
|
|
45
|
+
import { observableRef } from "./observable.js";
|
|
45
46
|
import { Segment } from "../core/segment.js";
|
|
46
47
|
import { NULL_STYLE } from "../core/style.js";
|
|
48
|
+
import { State } from "../core/state.js";
|
|
47
49
|
let WidgetBase = (() => {
|
|
48
50
|
let _instanceExtraInitializers = [];
|
|
49
51
|
let _focused_decorators;
|
|
@@ -66,10 +68,10 @@ let WidgetBase = (() => {
|
|
|
66
68
|
return class WidgetBase {
|
|
67
69
|
static {
|
|
68
70
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
69
|
-
_focused_decorators = [
|
|
70
|
-
_hovered_decorators = [
|
|
71
|
-
_active_decorators = [
|
|
72
|
-
_disabled_decorators = [
|
|
71
|
+
_focused_decorators = [observableRef];
|
|
72
|
+
_hovered_decorators = [observableRef];
|
|
73
|
+
_active_decorators = [observableRef];
|
|
74
|
+
_disabled_decorators = [observableRef];
|
|
73
75
|
_handleFocus_decorators = [action];
|
|
74
76
|
_focus_decorators = [action];
|
|
75
77
|
_blur_decorators = [action];
|
|
@@ -98,8 +100,9 @@ let WidgetBase = (() => {
|
|
|
98
100
|
#disabled_accessor_storage = (__runInitializers(this, _active_extraInitializers), __runInitializers(this, _disabled_initializers, false));
|
|
99
101
|
get disabled() { return this.#disabled_accessor_storage; }
|
|
100
102
|
set disabled(value) { this.#disabled_accessor_storage = value; }
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
// Subscribed and let go between frames; each set is replaced whole.
|
|
104
|
+
changeHandlers = (__runInitializers(this, _disabled_extraInitializers), new State(new Set()));
|
|
105
|
+
submitHandlers = new State(new Set());
|
|
103
106
|
handleMouse(_event) { }
|
|
104
107
|
handleFocus(event) {
|
|
105
108
|
this.focused = event.type === "focus";
|
|
@@ -109,11 +112,11 @@ let WidgetBase = (() => {
|
|
|
109
112
|
// subclass override (e.g. Dropdown collapsing its overlay on blur)
|
|
110
113
|
// participates in the same transition path the event router uses. There
|
|
111
114
|
// is no second code path that flips `focused` directly from the outside.
|
|
112
|
-
focus() {
|
|
113
|
-
this.handleFocus({ type: "focus" });
|
|
115
|
+
focus(time) {
|
|
116
|
+
this.handleFocus({ type: "focus", time });
|
|
114
117
|
}
|
|
115
|
-
blur() {
|
|
116
|
-
this.handleFocus({ type: "blur" });
|
|
118
|
+
blur(time) {
|
|
119
|
+
this.handleFocus({ type: "blur", time });
|
|
117
120
|
}
|
|
118
121
|
setDisabled(value) {
|
|
119
122
|
this.disabled = value;
|
|
@@ -127,36 +130,32 @@ let WidgetBase = (() => {
|
|
|
127
130
|
}
|
|
128
131
|
// --- Subscriptions ---
|
|
129
132
|
onChange(handler) {
|
|
130
|
-
this.changeHandlers
|
|
131
|
-
return () => this.changeHandlers.delete(handler);
|
|
133
|
+
return subscribe(this.changeHandlers, handler);
|
|
132
134
|
}
|
|
133
135
|
onSubmit(handler) {
|
|
134
|
-
this.submitHandlers
|
|
135
|
-
return () => this.submitHandlers.delete(handler);
|
|
136
|
+
return subscribe(this.submitHandlers, handler);
|
|
136
137
|
}
|
|
137
138
|
emitChange() {
|
|
138
|
-
for (const handler of this.changeHandlers) {
|
|
139
|
+
for (const handler of this.changeHandlers.get()) {
|
|
139
140
|
handler(this);
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
emitSubmit() {
|
|
143
|
-
for (const handler of this.submitHandlers) {
|
|
144
|
+
for (const handler of this.submitHandlers.get()) {
|
|
144
145
|
handler(this);
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
// --- Renderable + Measurable ---
|
|
148
149
|
// [LAW:single-enforcer] The one place a widget's cells get their anchor, so
|
|
149
150
|
// a hit on any cell names this widget and the cell's place in its output
|
|
150
|
-
// (`widgetAt`). Subclasses draw; none stamps itself
|
|
151
|
+
// (`widgetAt`). Subclasses draw; none stamps itself — what a draw stamps
|
|
152
|
+
// inside this stamp is its own (`WidgetMouseEvent.cell`). A `RichText` admits no
|
|
151
153
|
// anchor, so stamping has to follow every text layout `draw` does.
|
|
152
154
|
// Every row is padded to the widest, so the widget owns its whole rectangle
|
|
153
155
|
// and a click past the end of a short row still reaches it; every row ends
|
|
154
156
|
// in a newline, so a blank last row survives the next `splitLines`. The
|
|
155
157
|
// 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
158
|
render(options) {
|
|
159
|
-
options.onDraw?.(this);
|
|
160
159
|
const drawn = Segment.splitLines(this.draw(options));
|
|
161
160
|
const [width] = Segment.getShape(drawn);
|
|
162
161
|
const lines = Segment.anchorLines(drawn.map((line) => Segment.adjustLineLength(line, width, this.ground)), this);
|
|
@@ -170,3 +169,12 @@ let WidgetBase = (() => {
|
|
|
170
169
|
};
|
|
171
170
|
})();
|
|
172
171
|
export { WidgetBase };
|
|
172
|
+
/** `handler` added to `handlers` until the returned call takes it out. */
|
|
173
|
+
function subscribe(handlers, handler) {
|
|
174
|
+
handlers.set(new Set(handlers.get()).add(handler));
|
|
175
|
+
return () => {
|
|
176
|
+
const left = new Set(handlers.get());
|
|
177
|
+
left.delete(handler);
|
|
178
|
+
handlers.set(left);
|
|
179
|
+
};
|
|
180
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptctl/rich-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Rich text and beautiful formatting in the terminal — a TypeScript port of Python's Rich",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -74,6 +74,13 @@
|
|
|
74
74
|
"sessions": "node scripts/build-demos.ts && node dist-demo/examples/claude-sessions/node.js",
|
|
75
75
|
"strip": "node scripts/build-demos.ts && node dist-demo/examples/rich-strip/main.js",
|
|
76
76
|
"effects-feel": "node scripts/build-demos.ts && node dist-demo/examples/effects-feel/index.js",
|
|
77
|
+
"glyph-effects": "node scripts/build-demos.ts && node dist-demo/examples/glyph-effects/index.js",
|
|
78
|
+
"attribute-effects": "node scripts/build-demos.ts && node dist-demo/examples/attribute-effects/index.js",
|
|
79
|
+
"gradient-effects": "node scripts/build-demos.ts && node dist-demo/examples/gradient-effects/index.js",
|
|
80
|
+
"transitions": "node scripts/build-demos.ts && node dist-demo/examples/transitions/index.js",
|
|
81
|
+
"lifecycle": "node scripts/build-demos.ts && node dist-demo/examples/lifecycle/index.js",
|
|
82
|
+
"animation-playground": "node scripts/build-demos.ts && node dist-demo/examples/animation-playground/main.js",
|
|
83
|
+
"scroll": "node scripts/build-demos.ts && node dist-demo/examples/scroll/index.js",
|
|
77
84
|
"viewport": "node scripts/build-demos.ts && node dist-demo/examples/rich-viewport/main.js",
|
|
78
85
|
"markup-plugins": "node scripts/build-demos.ts && node dist-demo/examples/rich-markup-plugins/main.js",
|
|
79
86
|
"demo-inputs": "node scripts/build-demos.ts && node dist-demo/examples/rich-config/main.js",
|
|
@@ -101,7 +108,7 @@
|
|
|
101
108
|
"devEngines": {
|
|
102
109
|
"runtime": {
|
|
103
110
|
"name": "node",
|
|
104
|
-
"version": "^22.
|
|
111
|
+
"version": "^22.19.0 || >=23.9.0",
|
|
105
112
|
"onFail": "error"
|
|
106
113
|
}
|
|
107
114
|
},
|
|
@@ -133,9 +140,6 @@
|
|
|
133
140
|
"vitepress": "^1.6.4",
|
|
134
141
|
"vitest": "^5.0.0"
|
|
135
142
|
},
|
|
136
|
-
"dependencies": {
|
|
137
|
-
"string-width": "^8.2.2"
|
|
138
|
-
},
|
|
139
143
|
"peerDependencies": {
|
|
140
144
|
"@promptctl/go-template-js": "^0.10.0",
|
|
141
145
|
"mobx": "^7.0.3"
|