@promptctl/rich-js 0.17.0 → 0.18.1
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 +13 -26
- 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 +0 -1
- package/dist/core/box.js +6 -18
- package/dist/core/cells.d.ts +16 -5
- package/dist/core/cells.js +31 -10
- package/dist/core/color.d.ts +4 -8
- package/dist/core/color.js +28 -19
- package/dist/core/console.d.ts +13 -4
- package/dist/core/console.js +91 -36
- package/dist/core/export-html.d.ts +40 -5
- package/dist/core/export-html.js +111 -17
- package/dist/core/markup.d.ts +27 -5
- package/dist/core/markup.js +282 -170
- package/dist/core/measure.d.ts +1 -1
- package/dist/core/measure.js +2 -1
- package/dist/core/place.d.ts +30 -0
- package/dist/core/place.js +51 -0
- package/dist/core/pretty.d.ts +3 -0
- package/dist/core/pretty.js +5 -1
- package/dist/core/protocol.d.ts +136 -6
- package/dist/core/protocol.js +64 -2
- package/dist/core/render.d.ts +2 -2
- package/dist/core/render.js +2 -2
- package/dist/core/segment.d.ts +23 -4
- package/dist/core/segment.js +75 -16
- package/dist/core/spinnerData.d.ts +2 -0
- package/dist/core/spinnerData.js +3 -1
- package/dist/core/strip.d.ts +3 -7
- package/dist/core/strip.js +32 -35
- package/dist/core/style.d.ts +25 -0
- package/dist/core/style.js +61 -89
- package/dist/core/text.d.ts +16 -15
- package/dist/core/text.js +62 -76
- package/dist/host/app.d.ts +121 -0
- package/dist/host/app.js +255 -0
- package/dist/host/host-environment.d.ts +26 -0
- package/dist/host/host-environment.js +44 -0
- package/dist/host/index.d.ts +4 -2
- package/dist/host/index.js +2 -1
- package/dist/host/terminal-host.d.ts +43 -7
- package/dist/host/terminal-host.js +57 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +4 -1
- package/dist/node/terminal-host.d.ts +31 -2
- package/dist/node/terminal-host.js +69 -2
- package/dist/node/traceback.d.ts +16 -6
- package/dist/node/traceback.js +30 -8
- package/dist/renderables/align.d.ts +2 -2
- package/dist/renderables/align.js +6 -19
- package/dist/renderables/columns.js +4 -20
- package/dist/renderables/embed.d.ts +50 -0
- package/dist/renderables/embed.js +71 -0
- package/dist/renderables/group.js +3 -1
- package/dist/renderables/layout.js +31 -34
- package/dist/renderables/live.d.ts +6 -3
- package/dist/renderables/live.js +89 -45
- package/dist/renderables/markdown.d.ts +1 -1
- package/dist/renderables/markdown.js +6 -4
- package/dist/renderables/padding.js +3 -2
- package/dist/renderables/panel.js +26 -41
- package/dist/renderables/progress.js +5 -8
- package/dist/renderables/progressBar.js +6 -5
- package/dist/renderables/rule.d.ts +3 -2
- package/dist/renderables/rule.js +9 -9
- package/dist/renderables/spinner.d.ts +5 -3
- package/dist/renderables/spinner.js +14 -8
- package/dist/renderables/syntax.d.ts +1 -1
- package/dist/renderables/syntax.js +2 -1
- package/dist/renderables/table.d.ts +26 -14
- package/dist/renderables/table.js +239 -141
- package/dist/renderables/traceback.js +8 -9
- package/dist/renderables/tree.d.ts +17 -3
- package/dist/renderables/tree.js +62 -71
- package/dist/renderables/viewport.d.ts +114 -0
- package/dist/renderables/viewport.js +198 -0
- package/dist/template-bindings/color-funcs.js +11 -13
- package/dist/template-bindings/index.js +4 -2
- package/dist/template-bindings/palette-funcs.js +6 -16
- package/dist/template-bindings/style-funcs.js +4 -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.js +19 -0
- package/dist/themes/data/atom-one-light.js +19 -0
- package/dist/themes/data/catppuccin-frappe.js +19 -0
- package/dist/themes/data/catppuccin-latte.js +19 -0
- package/dist/themes/data/catppuccin-macchiato.js +19 -0
- package/dist/themes/data/catppuccin-mocha.js +19 -0
- package/dist/themes/data/cyberpunk.js +2 -0
- package/dist/themes/data/default.js +2 -0
- package/dist/themes/data/dracula.js +19 -0
- package/dist/themes/data/flexoki.js +19 -0
- package/dist/themes/data/gruvbox.js +19 -0
- package/dist/themes/data/monokai.js +19 -0
- package/dist/themes/data/nord.js +19 -0
- package/dist/themes/data/rose-pine-dawn.js +19 -0
- package/dist/themes/data/rose-pine-moon.js +19 -0
- package/dist/themes/data/rose-pine.js +19 -0
- package/dist/themes/data/solarized-dark.js +19 -0
- package/dist/themes/data/solarized-light.js +19 -0
- package/dist/themes/data/svg-export.js +19 -0
- package/dist/themes/data/textual-ansi.js +2 -0
- package/dist/themes/data/textual-dark.js +2 -0
- package/dist/themes/data/textual-light.js +2 -0
- package/dist/themes/data/tokyo-night.js +19 -0
- package/dist/themes/data/types.d.ts +16 -0
- package/dist/themes/data/types.js +10 -1
- package/dist/themes/registry.d.ts +3 -2
- package/dist/themes/registry.js +13 -1
- package/dist/themes/terminalThemes.d.ts +2 -7
- package/dist/themes/terminalThemes.js +4 -9
- package/dist/widgets/button.d.ts +1 -1
- package/dist/widgets/button.js +4 -2
- package/dist/widgets/checkbox.d.ts +2 -2
- package/dist/widgets/checkbox.js +5 -3
- package/dist/widgets/dropdown.d.ts +2 -2
- package/dist/widgets/dropdown.js +13 -16
- package/dist/widgets/event-router.d.ts +21 -28
- package/dist/widgets/event-router.js +80 -73
- package/dist/widgets/focus-manager.d.ts +25 -6
- package/dist/widgets/focus-manager.js +63 -65
- package/dist/widgets/hit.d.ts +64 -0
- package/dist/widgets/hit.js +113 -0
- package/dist/widgets/index.d.ts +5 -7
- package/dist/widgets/index.js +3 -4
- package/dist/widgets/slider.d.ts +2 -2
- package/dist/widgets/slider.js +7 -7
- package/dist/widgets/static-item.d.ts +9 -15
- package/dist/widgets/static-item.js +9 -15
- package/dist/widgets/text-input.d.ts +2 -2
- package/dist/widgets/text-input.js +17 -14
- package/dist/widgets/toggle.d.ts +1 -1
- package/dist/widgets/toggle.js +3 -2
- package/dist/widgets/types.d.ts +14 -35
- package/dist/widgets/types.js +0 -1
- package/dist/widgets/widget-app.d.ts +63 -0
- package/dist/widgets/widget-app.js +199 -0
- package/dist/widgets/widget-base.d.ts +4 -6
- package/dist/widgets/widget-base.js +22 -30
- package/package.json +12 -4
- package/dist/host/host-stream.d.ts +0 -30
- package/dist/host/host-stream.js +0 -35
- package/dist/widgets/screen.d.ts +0 -98
- package/dist/widgets/screen.js +0 -530
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* FocusManager —
|
|
2
|
+
* FocusManager — which widget has focus, over the widgets on the frame.
|
|
3
3
|
* [LAW:one-source-of-truth] single authority for which widget has focus.
|
|
4
4
|
* [LAW:dataflow-not-control-flow] focus transitions are observable state;
|
|
5
5
|
* widgets react to focus/blur events, the manager never skips dispatch.
|
|
@@ -7,20 +7,39 @@
|
|
|
7
7
|
import type { InteractiveWidget, FocusManager, KeyEvent } from "./types.js";
|
|
8
8
|
import type { Unsubscribe } from "../core/subscription.js";
|
|
9
9
|
export declare class DefaultFocusManager implements FocusManager {
|
|
10
|
-
|
|
10
|
+
private readonly onScreen;
|
|
11
11
|
accessor currentWidget: InteractiveWidget | null;
|
|
12
12
|
private readonly changeHandlers;
|
|
13
|
+
/**
|
|
14
|
+
* `onScreen` is the widgets on the frame on screen, in document order.
|
|
15
|
+
*
|
|
16
|
+
* [LAW:one-source-of-truth] It is asked every time rather than kept: a list
|
|
17
|
+
* of the widgets held here would be a second picture of the frame, and
|
|
18
|
+
* wrong from the next paint.
|
|
19
|
+
*/
|
|
20
|
+
constructor(onScreen: () => readonly InteractiveWidget[]);
|
|
13
21
|
get current(): InteractiveWidget | null;
|
|
14
22
|
get widgets(): readonly InteractiveWidget[];
|
|
15
|
-
register(widget: InteractiveWidget): void;
|
|
16
|
-
unregister(widget: InteractiveWidget): void;
|
|
17
23
|
next(): void;
|
|
18
24
|
prev(): void;
|
|
19
25
|
focus(widget: InteractiveWidget): void;
|
|
20
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Put focus where the frame just painted lets it rest: it stays on a widget
|
|
28
|
+
* the view drew (`drawn`) that can take it, and otherwise moves to the first
|
|
29
|
+
* one on screen that can, or to none.
|
|
30
|
+
*
|
|
31
|
+
* Drawn, not on screen: a widget cropped for the moment — the terminal
|
|
32
|
+
* shrank, a viewport scrolled, an overlay covers it — is still in the view,
|
|
33
|
+
* and keeps focus and whatever focus holds, like a half-typed filter.
|
|
34
|
+
*
|
|
35
|
+
* [LAW:single-enforcer] The one place focus follows the frame — onto the
|
|
36
|
+
* first widget when the app starts, off a widget that stopped being drawn
|
|
37
|
+
* or was disabled.
|
|
38
|
+
*/
|
|
39
|
+
settle(drawn: readonly InteractiveWidget[]): void;
|
|
21
40
|
onChange(handler: (current: InteractiveWidget | null) => void): Unsubscribe;
|
|
22
41
|
handleKey(event: KeyEvent): void;
|
|
23
|
-
private
|
|
42
|
+
private clear;
|
|
24
43
|
private setFocus;
|
|
25
44
|
private emitChange;
|
|
26
45
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* FocusManager —
|
|
2
|
+
* FocusManager — which widget has focus, over the widgets on the frame.
|
|
3
3
|
* [LAW:one-source-of-truth] single authority for which widget has focus.
|
|
4
4
|
* [LAW:dataflow-not-control-flow] focus transitions are observable state;
|
|
5
5
|
* widgets react to focus/blur events, the manager never skips dispatch.
|
|
@@ -38,89 +38,60 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
38
38
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
39
39
|
done = true;
|
|
40
40
|
};
|
|
41
|
-
import { action, observableRef
|
|
41
|
+
import { action, observableRef } from "mobx";
|
|
42
42
|
let DefaultFocusManager = (() => {
|
|
43
43
|
let _instanceExtraInitializers = [];
|
|
44
|
-
let _widgetList_decorators;
|
|
45
|
-
let _widgetList_initializers = [];
|
|
46
|
-
let _widgetList_extraInitializers = [];
|
|
47
44
|
let _currentWidget_decorators;
|
|
48
45
|
let _currentWidget_initializers = [];
|
|
49
46
|
let _currentWidget_extraInitializers = [];
|
|
50
|
-
let _register_decorators;
|
|
51
|
-
let _unregister_decorators;
|
|
52
47
|
let _next_decorators;
|
|
53
48
|
let _prev_decorators;
|
|
54
49
|
let _focus_decorators;
|
|
55
|
-
let
|
|
50
|
+
let _settle_decorators;
|
|
51
|
+
let _clear_decorators;
|
|
56
52
|
let _setFocus_decorators;
|
|
57
53
|
return class DefaultFocusManager {
|
|
58
54
|
static {
|
|
59
55
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
60
|
-
_widgetList_decorators = [observableShallow];
|
|
61
56
|
_currentWidget_decorators = [observableRef];
|
|
62
|
-
_register_decorators = [action];
|
|
63
|
-
_unregister_decorators = [action];
|
|
64
57
|
_next_decorators = [action];
|
|
65
58
|
_prev_decorators = [action];
|
|
66
59
|
_focus_decorators = [action];
|
|
67
|
-
|
|
60
|
+
_settle_decorators = [action];
|
|
61
|
+
_clear_decorators = [action];
|
|
68
62
|
_setFocus_decorators = [action];
|
|
69
|
-
__esDecorate(this, null, _widgetList_decorators, { kind: "accessor", name: "widgetList", static: false, private: false, access: { has: obj => "widgetList" in obj, get: obj => obj.widgetList, set: (obj, value) => { obj.widgetList = value; } }, metadata: _metadata }, _widgetList_initializers, _widgetList_extraInitializers);
|
|
70
63
|
__esDecorate(this, null, _currentWidget_decorators, { kind: "accessor", name: "currentWidget", static: false, private: false, access: { has: obj => "currentWidget" in obj, get: obj => obj.currentWidget, set: (obj, value) => { obj.currentWidget = value; } }, metadata: _metadata }, _currentWidget_initializers, _currentWidget_extraInitializers);
|
|
71
|
-
__esDecorate(this, null, _register_decorators, { kind: "method", name: "register", static: false, private: false, access: { has: obj => "register" in obj, get: obj => obj.register }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
|
-
__esDecorate(this, null, _unregister_decorators, { kind: "method", name: "unregister", static: false, private: false, access: { has: obj => "unregister" in obj, get: obj => obj.unregister }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
73
64
|
__esDecorate(this, null, _next_decorators, { kind: "method", name: "next", static: false, private: false, access: { has: obj => "next" in obj, get: obj => obj.next }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
74
65
|
__esDecorate(this, null, _prev_decorators, { kind: "method", name: "prev", static: false, private: false, access: { has: obj => "prev" in obj, get: obj => obj.prev }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
66
|
__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);
|
|
76
|
-
__esDecorate(this, null,
|
|
67
|
+
__esDecorate(this, null, _settle_decorators, { kind: "method", name: "settle", static: false, private: false, access: { has: obj => "settle" in obj, get: obj => obj.settle }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
68
|
+
__esDecorate(this, null, _clear_decorators, { kind: "method", name: "clear", static: false, private: false, access: { has: obj => "clear" in obj, get: obj => obj.clear }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
77
69
|
__esDecorate(this, null, _setFocus_decorators, { kind: "method", name: "setFocus", static: false, private: false, access: { has: obj => "setFocus" in obj, get: obj => obj.setFocus }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
78
70
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
79
71
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
set widgetList(value) { this.#widgetList_accessor_storage = value; }
|
|
83
|
-
#currentWidget_accessor_storage = (__runInitializers(this, _widgetList_extraInitializers), __runInitializers(this, _currentWidget_initializers, null));
|
|
72
|
+
onScreen = __runInitializers(this, _instanceExtraInitializers);
|
|
73
|
+
#currentWidget_accessor_storage = __runInitializers(this, _currentWidget_initializers, null);
|
|
84
74
|
get currentWidget() { return this.#currentWidget_accessor_storage; }
|
|
85
75
|
set currentWidget(value) { this.#currentWidget_accessor_storage = value; }
|
|
86
76
|
changeHandlers = (__runInitializers(this, _currentWidget_extraInitializers), new Set());
|
|
77
|
+
/**
|
|
78
|
+
* `onScreen` is the widgets on the frame on screen, in document order.
|
|
79
|
+
*
|
|
80
|
+
* [LAW:one-source-of-truth] It is asked every time rather than kept: a list
|
|
81
|
+
* of the widgets held here would be a second picture of the frame, and
|
|
82
|
+
* wrong from the next paint.
|
|
83
|
+
*/
|
|
84
|
+
constructor(onScreen) {
|
|
85
|
+
this.onScreen = onScreen;
|
|
86
|
+
}
|
|
87
87
|
get current() {
|
|
88
88
|
return this.currentWidget;
|
|
89
89
|
}
|
|
90
90
|
get widgets() {
|
|
91
|
-
return this.
|
|
92
|
-
}
|
|
93
|
-
register(widget) {
|
|
94
|
-
// [LAW:one-source-of-truth] register is idempotent — the widgetList is
|
|
95
|
-
// a set in spirit, not a multiset. Without this guard, double-registration
|
|
96
|
-
// would duplicate the widget so next/prev cycle through it twice and
|
|
97
|
-
// unregister only removes one copy.
|
|
98
|
-
if (this.widgetList.includes(widget))
|
|
99
|
-
return;
|
|
100
|
-
this.widgetList = [...this.widgetList, widget];
|
|
101
|
-
if (!this.currentWidget && widget.focusable && !widget.disabled) {
|
|
102
|
-
this.setFocus(widget);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
unregister(widget) {
|
|
106
|
-
const idx = this.widgetList.indexOf(widget);
|
|
107
|
-
if (idx === -1)
|
|
108
|
-
return;
|
|
109
|
-
this.widgetList = this.widgetList.filter((w) => w !== widget);
|
|
110
|
-
if (this.currentWidget === widget) {
|
|
111
|
-
// [LAW:single-enforcer] WidgetBase.focus()/blur() already route through
|
|
112
|
-
// handleFocus, so calling both would double-dispatch and trigger any
|
|
113
|
-
// subclass side effect (e.g. Dropdown clearing filter/overlay) twice.
|
|
114
|
-
widget.blur();
|
|
115
|
-
const next = this.widgetList.find((w) => w.focusable && !w.disabled) ?? null;
|
|
116
|
-
if (next)
|
|
117
|
-
next.focus();
|
|
118
|
-
this.currentWidget = next;
|
|
119
|
-
this.emitChange();
|
|
120
|
-
}
|
|
91
|
+
return this.onScreen().filter(takesFocus);
|
|
121
92
|
}
|
|
122
93
|
next() {
|
|
123
|
-
const focusable = this.
|
|
94
|
+
const focusable = this.widgets;
|
|
124
95
|
if (focusable.length === 0)
|
|
125
96
|
return;
|
|
126
97
|
const currentIdx = this.currentWidget ? focusable.indexOf(this.currentWidget) : -1;
|
|
@@ -128,28 +99,42 @@ let DefaultFocusManager = (() => {
|
|
|
128
99
|
this.setFocus(focusable[nextIdx]);
|
|
129
100
|
}
|
|
130
101
|
prev() {
|
|
131
|
-
const focusable = this.
|
|
102
|
+
const focusable = this.widgets;
|
|
132
103
|
if (focusable.length === 0)
|
|
133
104
|
return;
|
|
134
105
|
const currentIdx = this.currentWidget ? focusable.indexOf(this.currentWidget) : -1;
|
|
135
106
|
const prevIdx = currentIdx <= 0 ? focusable.length - 1 : currentIdx - 1;
|
|
136
107
|
this.setFocus(focusable[prevIdx]);
|
|
137
108
|
}
|
|
109
|
+
// A widget not on screen yet can take focus: an app that is about to show
|
|
110
|
+
// it focuses it first, and the frame that shows it keeps it (`settle`).
|
|
138
111
|
focus(widget) {
|
|
139
|
-
if (!widget
|
|
140
|
-
return;
|
|
141
|
-
if (!this.widgetList.includes(widget))
|
|
112
|
+
if (!takesFocus(widget))
|
|
142
113
|
return;
|
|
143
114
|
this.setFocus(widget);
|
|
144
115
|
}
|
|
145
|
-
|
|
146
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Put focus where the frame just painted lets it rest: it stays on a widget
|
|
118
|
+
* the view drew (`drawn`) that can take it, and otherwise moves to the first
|
|
119
|
+
* one on screen that can, or to none.
|
|
120
|
+
*
|
|
121
|
+
* Drawn, not on screen: a widget cropped for the moment — the terminal
|
|
122
|
+
* shrank, a viewport scrolled, an overlay covers it — is still in the view,
|
|
123
|
+
* and keeps focus and whatever focus holds, like a half-typed filter.
|
|
124
|
+
*
|
|
125
|
+
* [LAW:single-enforcer] The one place focus follows the frame — onto the
|
|
126
|
+
* first widget when the app starts, off a widget that stopped being drawn
|
|
127
|
+
* or was disabled.
|
|
128
|
+
*/
|
|
129
|
+
settle(drawn) {
|
|
130
|
+
const current = this.currentWidget;
|
|
131
|
+
if (current && takesFocus(current) && drawn.includes(current))
|
|
147
132
|
return;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
133
|
+
const first = this.widgets[0];
|
|
134
|
+
if (first)
|
|
135
|
+
this.setFocus(first);
|
|
136
|
+
else
|
|
137
|
+
this.clear();
|
|
153
138
|
}
|
|
154
139
|
onChange(handler) {
|
|
155
140
|
this.changeHandlers.add(handler);
|
|
@@ -169,14 +154,24 @@ let DefaultFocusManager = (() => {
|
|
|
169
154
|
event.stop();
|
|
170
155
|
}
|
|
171
156
|
// --- Private ---
|
|
172
|
-
|
|
173
|
-
|
|
157
|
+
// No focus is where `settle` leaves an app with nothing to focus, and only
|
|
158
|
+
// there: anywhere else the next frame would put focus back on the first
|
|
159
|
+
// widget, so there is no public way in.
|
|
160
|
+
clear() {
|
|
161
|
+
if (!this.currentWidget)
|
|
162
|
+
return;
|
|
163
|
+
// [LAW:single-enforcer] WidgetBase.focus()/blur() route through
|
|
164
|
+
// handleFocus; calling it here as well would dispatch twice and run any
|
|
165
|
+
// subclass side effect (e.g. Dropdown clearing its filter) twice.
|
|
166
|
+
this.currentWidget.blur();
|
|
167
|
+
this.currentWidget = null;
|
|
168
|
+
this.emitChange();
|
|
174
169
|
}
|
|
175
170
|
setFocus(widget) {
|
|
176
171
|
if (this.currentWidget === widget)
|
|
177
172
|
return;
|
|
178
173
|
// [LAW:single-enforcer] focus()/blur() already dispatch handleFocus —
|
|
179
|
-
// see
|
|
174
|
+
// see clear() for the rationale.
|
|
180
175
|
if (this.currentWidget)
|
|
181
176
|
this.currentWidget.blur();
|
|
182
177
|
this.currentWidget = widget;
|
|
@@ -191,3 +186,6 @@ let DefaultFocusManager = (() => {
|
|
|
191
186
|
};
|
|
192
187
|
})();
|
|
193
188
|
export { DefaultFocusManager };
|
|
189
|
+
function takesFocus(widget) {
|
|
190
|
+
return widget.focusable && !widget.disabled;
|
|
191
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* widgetAt — which widget drew a cell of a painted frame, and where in its
|
|
3
|
+
* own output that cell sits; scrollTargetAt — what the wheel over that cell
|
|
4
|
+
* moves.
|
|
5
|
+
*
|
|
6
|
+
* A widget stamps every cell it draws (`WidgetBase.render`), and a
|
|
7
|
+
* `Scrollable` such as `Viewport` every cell it shows, around its content's
|
|
8
|
+
* stamps. The stamp rides
|
|
9
|
+
* through every container on the cell's `Style`, and the frame a screen paints
|
|
10
|
+
* is therefore its own hit map: nothing lays widgets out a second time to
|
|
11
|
+
* answer "what is under the pointer", so the answer cannot disagree with what
|
|
12
|
+
* the user sees. Z-order is paint order — whoever painted a cell last owns it.
|
|
13
|
+
*/
|
|
14
|
+
import { Segment } from "../core/segment.js";
|
|
15
|
+
import type { Scrollable } from "../core/protocol.js";
|
|
16
|
+
import type { InteractiveWidget } from "./types.js";
|
|
17
|
+
/** A pointer position resolved to the widget that drew it. */
|
|
18
|
+
export interface WidgetHit {
|
|
19
|
+
readonly widget: InteractiveWidget;
|
|
20
|
+
/** Column of the cell in the widget's own output. */
|
|
21
|
+
readonly col: number;
|
|
22
|
+
/** Row of the cell in the widget's own output. */
|
|
23
|
+
readonly row: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The innermost widget that drew the cell at column `x` of row `y` of
|
|
27
|
+
* `frame`, or `undefined` when no widget drew it. A widget nested inside
|
|
28
|
+
* another widget's output wins over the one containing it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function widgetAt(frame: readonly (readonly Segment[])[], x: number, y: number): WidgetHit | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* The innermost `Scrollable` that drew the cell at column `x` of row `y` of
|
|
33
|
+
* `frame` and that `scrollBy(lines)` would move, or `undefined` when none
|
|
34
|
+
* did. A widget drawn inside it does not hide it: the widget's anchor is
|
|
35
|
+
* nested in its. One that cannot move that way — a viewport whose content
|
|
36
|
+
* fits, or that is at that end — passes the wheel to the one around it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scrollTargetAt(frame: readonly (readonly Segment[])[], x: number, y: number, lines: number): Scrollable | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Where `widget`'s output starts in `frame` — the cell its own row 0, column
|
|
41
|
+
* 0 falls on — read off the first cell it drew, or `undefined` when it drew
|
|
42
|
+
* none.
|
|
43
|
+
*/
|
|
44
|
+
export declare function originOf(frame: readonly (readonly Segment[])[], widget: InteractiveWidget): {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
} | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Whether `widget` drew the cell at column `x` of row `y` of `frame`, itself
|
|
50
|
+
* or through a widget nested in its output.
|
|
51
|
+
*/
|
|
52
|
+
export declare function drew(frame: readonly (readonly Segment[])[], widget: InteractiveWidget, x: number, y: number): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Where `widget`'s footprint sits in `frame`: the cell its row 0, column 0
|
|
55
|
+
* falls on, and how many of its rows the frame shows down to its lowest — or
|
|
56
|
+
* `undefined` when the frame does not show its row 0.
|
|
57
|
+
*/
|
|
58
|
+
export declare function footprintOf(frame: readonly (readonly Segment[])[], widget: InteractiveWidget): {
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
rows: number;
|
|
62
|
+
} | undefined;
|
|
63
|
+
/** Every owner that drew a cell of `frame`, at every depth of nesting. */
|
|
64
|
+
export declare function ownersOn(frame: readonly (readonly Segment[])[]): Set<object>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* widgetAt — which widget drew a cell of a painted frame, and where in its
|
|
3
|
+
* own output that cell sits; scrollTargetAt — what the wheel over that cell
|
|
4
|
+
* moves.
|
|
5
|
+
*
|
|
6
|
+
* A widget stamps every cell it draws (`WidgetBase.render`), and a
|
|
7
|
+
* `Scrollable` such as `Viewport` every cell it shows, around its content's
|
|
8
|
+
* stamps. The stamp rides
|
|
9
|
+
* through every container on the cell's `Style`, and the frame a screen paints
|
|
10
|
+
* is therefore its own hit map: nothing lays widgets out a second time to
|
|
11
|
+
* answer "what is under the pointer", so the answer cannot disagree with what
|
|
12
|
+
* the user sees. Z-order is paint order — whoever painted a cell last owns it.
|
|
13
|
+
*/
|
|
14
|
+
import { Segment } from "../core/segment.js";
|
|
15
|
+
import { isScrollable } from "../core/protocol.js";
|
|
16
|
+
import { WidgetBase } from "./widget-base.js";
|
|
17
|
+
/**
|
|
18
|
+
* The innermost widget that drew the cell at column `x` of row `y` of
|
|
19
|
+
* `frame`, or `undefined` when no widget drew it. A widget nested inside
|
|
20
|
+
* another widget's output wins over the one containing it.
|
|
21
|
+
*/
|
|
22
|
+
export function widgetAt(frame, x, y) {
|
|
23
|
+
const hit = innermost(frame, x, y, (owner) => (owner instanceof WidgetBase ? owner : undefined));
|
|
24
|
+
return hit && { widget: hit.owner, col: hit.col, row: hit.row };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The innermost `Scrollable` that drew the cell at column `x` of row `y` of
|
|
28
|
+
* `frame` and that `scrollBy(lines)` would move, or `undefined` when none
|
|
29
|
+
* did. A widget drawn inside it does not hide it: the widget's anchor is
|
|
30
|
+
* nested in its. One that cannot move that way — a viewport whose content
|
|
31
|
+
* fits, or that is at that end — passes the wheel to the one around it.
|
|
32
|
+
*/
|
|
33
|
+
export function scrollTargetAt(frame, x, y, lines) {
|
|
34
|
+
return innermost(frame, x, y, (owner) => isScrollable(owner) && owner.canScrollBy(lines) ? owner : undefined)?.owner;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The innermost owner of the cell at column `x` of row `y` of `frame` that
|
|
38
|
+
* `accept` turns into a `T`, with the cell's place in that owner's output.
|
|
39
|
+
*
|
|
40
|
+
* [LAW:parse-dont-validate] An anchor's owner is any object; `accept` is
|
|
41
|
+
* where it becomes the kind of owner the caller asked for, or nothing.
|
|
42
|
+
*/
|
|
43
|
+
function innermost(frame, x, y, accept) {
|
|
44
|
+
let found;
|
|
45
|
+
for (let a = Segment.anchorAt(frame, x, y); a; a = a.inner) {
|
|
46
|
+
const owner = accept(a.owner);
|
|
47
|
+
if (owner)
|
|
48
|
+
found = { owner, col: a.col, row: a.row };
|
|
49
|
+
}
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Where `widget`'s output starts in `frame` — the cell its own row 0, column
|
|
54
|
+
* 0 falls on — read off the first cell it drew, or `undefined` when it drew
|
|
55
|
+
* none.
|
|
56
|
+
*/
|
|
57
|
+
export function originOf(frame, widget) {
|
|
58
|
+
for (let y = 0; y < frame.length; y++) {
|
|
59
|
+
let start = 0;
|
|
60
|
+
for (const segment of frame[y]) {
|
|
61
|
+
for (let a = segment.style?.anchor; a; a = a.inner) {
|
|
62
|
+
if (a.owner === widget)
|
|
63
|
+
return { x: start - a.col, y: y - a.row };
|
|
64
|
+
}
|
|
65
|
+
start += segment.cellLength;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether `widget` drew the cell at column `x` of row `y` of `frame`, itself
|
|
72
|
+
* or through a widget nested in its output.
|
|
73
|
+
*/
|
|
74
|
+
export function drew(frame, widget, x, y) {
|
|
75
|
+
for (let a = Segment.anchorAt(frame, x, y); a; a = a.inner)
|
|
76
|
+
if (a.owner === widget)
|
|
77
|
+
return true;
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Where `widget`'s footprint sits in `frame`: the cell its row 0, column 0
|
|
82
|
+
* falls on, and how many of its rows the frame shows down to its lowest — or
|
|
83
|
+
* `undefined` when the frame does not show its row 0.
|
|
84
|
+
*/
|
|
85
|
+
export function footprintOf(frame, widget) {
|
|
86
|
+
let origin;
|
|
87
|
+
let rows = 0;
|
|
88
|
+
for (let y = 0; y < frame.length; y++) {
|
|
89
|
+
let start = 0;
|
|
90
|
+
for (const segment of frame[y]) {
|
|
91
|
+
for (let a = segment.style?.anchor; a; a = a.inner) {
|
|
92
|
+
if (a.owner !== widget)
|
|
93
|
+
continue;
|
|
94
|
+
if (a.row === 0 && !origin)
|
|
95
|
+
origin = { x: start - a.col, y };
|
|
96
|
+
rows = Math.max(rows, a.row + 1);
|
|
97
|
+
}
|
|
98
|
+
start += segment.cellLength;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return origin && { ...origin, rows };
|
|
102
|
+
}
|
|
103
|
+
/** Every owner that drew a cell of `frame`, at every depth of nesting. */
|
|
104
|
+
export function ownersOn(frame) {
|
|
105
|
+
const owners = new Set();
|
|
106
|
+
for (const line of frame) {
|
|
107
|
+
for (const segment of line) {
|
|
108
|
+
for (let a = segment.style?.anchor; a; a = a.inner)
|
|
109
|
+
owners.add(a.owner);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return owners;
|
|
113
|
+
}
|
package/dist/widgets/index.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export type { KeyEventInit, KeyHandlerPriority, KeyHandlerOptions, WidgetMouseEvent, WidgetFocusEvent,
|
|
2
|
-
export {
|
|
1
|
+
export type { KeyEventInit, KeyHandlerPriority, KeyHandlerOptions, ScreenMouseEvent, WidgetMouseEvent, WidgetFocusEvent, InteractiveWidget, OverlayRenderable, FocusManager, } from "./types.js";
|
|
2
|
+
export { hasOverlay, KeyEvent } from "./types.js";
|
|
3
3
|
export { StaticItem } from "./static-item.js";
|
|
4
4
|
export type { StaticItemOptions } from "./static-item.js";
|
|
5
5
|
export { WidgetBase } from "./widget-base.js";
|
|
6
|
-
export {
|
|
7
|
-
export { DefaultScreen } from "./screen.js";
|
|
8
|
-
export type { ScreenOptions, ColorSystemSpec } from "./screen.js";
|
|
6
|
+
export { WidgetApp } from "./widget-app.js";
|
|
9
7
|
export { Button } from "./button.js";
|
|
10
8
|
export type { ButtonVariant, ButtonOptions } from "./button.js";
|
|
11
9
|
export { Checkbox } from "./checkbox.js";
|
|
@@ -18,5 +16,5 @@ export { Dropdown } from "./dropdown.js";
|
|
|
18
16
|
export type { DropdownOptions } from "./dropdown.js";
|
|
19
17
|
export { Slider } from "./slider.js";
|
|
20
18
|
export type { SliderOptions } from "./slider.js";
|
|
21
|
-
export {
|
|
22
|
-
export type {
|
|
19
|
+
export { widgetAt } from "./hit.js";
|
|
20
|
+
export type { WidgetHit } from "./hit.js";
|
package/dist/widgets/index.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { hasOverlay, KeyEvent } from "./types.js";
|
|
2
2
|
export { StaticItem } from "./static-item.js";
|
|
3
3
|
export { WidgetBase } from "./widget-base.js";
|
|
4
|
-
export {
|
|
5
|
-
export { DefaultScreen } from "./screen.js";
|
|
4
|
+
export { WidgetApp } from "./widget-app.js";
|
|
6
5
|
export { Button } from "./button.js";
|
|
7
6
|
export { Checkbox } from "./checkbox.js";
|
|
8
7
|
export { Toggle } from "./toggle.js";
|
|
9
8
|
export { TextInput, charGreedyWrap } from "./text-input.js";
|
|
10
9
|
export { Dropdown } from "./dropdown.js";
|
|
11
10
|
export { Slider } from "./slider.js";
|
|
12
|
-
export {
|
|
11
|
+
export { widgetAt } from "./hit.js";
|
|
13
12
|
// The terminal seam is not re-exported here. It lives on the `host` subpath,
|
|
14
13
|
// and a second path to `TerminalHost` would be the two-clocks problem the
|
|
15
14
|
// subpath split exists to remove.
|
package/dist/widgets/slider.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* disabled — dim
|
|
19
19
|
*/
|
|
20
20
|
import { Segment } from "../core/segment.js";
|
|
21
|
-
import type
|
|
21
|
+
import { type RenderOptions } from "../core/protocol.js";
|
|
22
22
|
import type { TerminalTheme } from "../core/color.js";
|
|
23
23
|
import { WidgetBase } from "./widget-base.js";
|
|
24
24
|
import type { KeyEvent, WidgetMouseEvent } from "./types.js";
|
|
@@ -48,7 +48,7 @@ export declare class Slider extends WidgetBase {
|
|
|
48
48
|
handleMouse(event: WidgetMouseEvent): void;
|
|
49
49
|
private setValue;
|
|
50
50
|
private setValueFromMouseX;
|
|
51
|
-
|
|
51
|
+
protected draw(options: RenderOptions): Iterable<Segment>;
|
|
52
52
|
measure(_options: RenderOptions): {
|
|
53
53
|
minimum: number;
|
|
54
54
|
maximum: number;
|
package/dist/widgets/slider.js
CHANGED
|
@@ -56,6 +56,7 @@ import { Segment } from "../core/segment.js";
|
|
|
56
56
|
import { Style } from "../core/style.js";
|
|
57
57
|
import { ColorSpec } from "../core/color.js";
|
|
58
58
|
import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
|
|
59
|
+
import { drawable } from "../core/protocol.js";
|
|
59
60
|
import { WidgetBase } from "./widget-base.js";
|
|
60
61
|
const DEFAULT_WIDTH = 20;
|
|
61
62
|
let Slider = (() => {
|
|
@@ -206,20 +207,19 @@ let Slider = (() => {
|
|
|
206
207
|
this.value = snapped;
|
|
207
208
|
this.emitChange();
|
|
208
209
|
}
|
|
210
|
+
// `x` is a column of this slider's own track, and runs outside 0..width
|
|
211
|
+
// when a drag carries the pointer past either end.
|
|
209
212
|
setValueFromMouseX(x) {
|
|
210
|
-
const b = this.bounds;
|
|
211
|
-
if (!b)
|
|
212
|
-
return;
|
|
213
213
|
const cellsAvailable = Math.max(1, this.width - 1);
|
|
214
|
-
const clamped = Math.max(0, Math.min(this.width - 1, x
|
|
214
|
+
const clamped = Math.max(0, Math.min(this.width - 1, x));
|
|
215
215
|
const fraction = clamped / cellsAvailable;
|
|
216
216
|
const next = this.min + fraction * (this.max - this.min);
|
|
217
217
|
this.setValue(next);
|
|
218
218
|
}
|
|
219
219
|
// --- Rendering ---
|
|
220
|
-
|
|
221
|
-
const trackChar = options
|
|
222
|
-
const markerChar = options
|
|
220
|
+
draw(options) {
|
|
221
|
+
const trackChar = drawable(options, "─", "-");
|
|
222
|
+
const markerChar = drawable(options, "●", "*");
|
|
223
223
|
const range = this.max - this.min;
|
|
224
224
|
const fraction = range === 0 ? 0 : (this.value - this.min) / range;
|
|
225
225
|
const markerIdx = Math.round(fraction * (this.width - 1));
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* StaticItem — a
|
|
3
|
-
*
|
|
4
|
-
* real widgets.
|
|
2
|
+
* StaticItem — a renderable on the widget surface: it takes no focus and
|
|
3
|
+
* ignores keys, and it stamps its cells like any widget.
|
|
5
4
|
*
|
|
6
|
-
* [LAW:one-type-per-behavior]
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* same type. The differences (no focus, no event handling) are configured
|
|
10
|
-
* via `focusable: false` plus the no-op handlers WidgetBase already
|
|
11
|
-
* provides — there is no second type, no "static vs interactive" branch in
|
|
12
|
-
* Screen's render loop.
|
|
5
|
+
* [LAW:one-type-per-behavior] Display-only content is a `WidgetBase` with
|
|
6
|
+
* `focusable: false` and the no-op handlers the base already provides, not a
|
|
7
|
+
* second type.
|
|
13
8
|
*
|
|
14
|
-
* The wrapped renderable can be a function that re-evaluates each frame
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* every time.
|
|
9
|
+
* The wrapped renderable can be a function that re-evaluates each frame — it
|
|
10
|
+
* reads MobX observables, and the app repaints when they change — or a plain
|
|
11
|
+
* renderable.
|
|
18
12
|
*/
|
|
19
13
|
import { Segment } from "../core/segment.js";
|
|
20
14
|
import type { Measurable, Renderable, RenderOptions } from "../core/protocol.js";
|
|
@@ -32,7 +26,7 @@ export declare class StaticItem extends WidgetBase {
|
|
|
32
26
|
private readonly measureFn;
|
|
33
27
|
constructor(options: StaticItemOptions);
|
|
34
28
|
handleKey(_event: KeyEvent): void;
|
|
35
|
-
|
|
29
|
+
protected draw(options: RenderOptions): Iterable<Segment>;
|
|
36
30
|
measure(options: RenderOptions): {
|
|
37
31
|
minimum: number;
|
|
38
32
|
maximum: number;
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* StaticItem — a
|
|
3
|
-
*
|
|
4
|
-
* real widgets.
|
|
2
|
+
* StaticItem — a renderable on the widget surface: it takes no focus and
|
|
3
|
+
* ignores keys, and it stamps its cells like any widget.
|
|
5
4
|
*
|
|
6
|
-
* [LAW:one-type-per-behavior]
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* same type. The differences (no focus, no event handling) are configured
|
|
10
|
-
* via `focusable: false` plus the no-op handlers WidgetBase already
|
|
11
|
-
* provides — there is no second type, no "static vs interactive" branch in
|
|
12
|
-
* Screen's render loop.
|
|
5
|
+
* [LAW:one-type-per-behavior] Display-only content is a `WidgetBase` with
|
|
6
|
+
* `focusable: false` and the no-op handlers the base already provides, not a
|
|
7
|
+
* second type.
|
|
13
8
|
*
|
|
14
|
-
* The wrapped renderable can be a function that re-evaluates each frame
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* every time.
|
|
9
|
+
* The wrapped renderable can be a function that re-evaluates each frame — it
|
|
10
|
+
* reads MobX observables, and the app repaints when they change — or a plain
|
|
11
|
+
* renderable.
|
|
18
12
|
*/
|
|
19
13
|
import { Segment } from "../core/segment.js";
|
|
20
14
|
import { WidgetBase } from "./widget-base.js";
|
|
@@ -36,7 +30,7 @@ export class StaticItem extends WidgetBase {
|
|
|
36
30
|
// Static items don't respond to keys. WidgetBase.handleMouse / handleFocus
|
|
37
31
|
// are already no-ops, so we only need to satisfy the abstract handleKey.
|
|
38
32
|
handleKey(_event) { }
|
|
39
|
-
|
|
33
|
+
draw(options) {
|
|
40
34
|
return this.renderFn(options);
|
|
41
35
|
}
|
|
42
36
|
measure(options) {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
import { Segment } from "../core/segment.js";
|
|
59
59
|
import { Style } from "../core/style.js";
|
|
60
60
|
import { type CellCol, type CodePoint } from "../core/cells.js";
|
|
61
|
-
import type
|
|
61
|
+
import { type RenderOptions } from "../core/protocol.js";
|
|
62
62
|
import type { TerminalTheme } from "../core/color.js";
|
|
63
63
|
import { WidgetBase } from "./widget-base.js";
|
|
64
64
|
import type { KeyEvent, WidgetMouseEvent } from "./types.js";
|
|
@@ -247,7 +247,7 @@ export declare class TextInput extends WidgetBase {
|
|
|
247
247
|
private _lineStart;
|
|
248
248
|
private _lineEnd;
|
|
249
249
|
private _insertText;
|
|
250
|
-
|
|
250
|
+
protected draw(options: RenderOptions): Iterable<Segment>;
|
|
251
251
|
private _renderSingleLine;
|
|
252
252
|
private _renderMultiline;
|
|
253
253
|
private _emitRowContent;
|