pi-libtui 0.3.2
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/LICENSE +21 -0
- package/README.md +205 -0
- package/package.json +55 -0
- package/src/activity-presentation.ts +75 -0
- package/src/appearance.ts +632 -0
- package/src/background-surface.ts +125 -0
- package/src/color/palette.ts +186 -0
- package/src/color/preview.ts +157 -0
- package/src/color/resolver.ts +75 -0
- package/src/color/theme.ts +531 -0
- package/src/component-stack.ts +247 -0
- package/src/content/markdown-code.ts +60 -0
- package/src/content/terminal-text.ts +135 -0
- package/src/content/text.ts +105 -0
- package/src/controls/action-panel.ts +420 -0
- package/src/controls/dialog-button-bar.ts +287 -0
- package/src/controls/multi-select.ts +467 -0
- package/src/controls/picker-panel.ts +447 -0
- package/src/controls/screen-icon-actions.ts +134 -0
- package/src/controls/searchable-select.ts +394 -0
- package/src/controls/select-box.ts +224 -0
- package/src/controls/selectable-list.ts +433 -0
- package/src/controls/selection-action-bar.ts +382 -0
- package/src/controls/semantic-input.ts +23 -0
- package/src/controls/tab.ts +189 -0
- package/src/cursor.ts +288 -0
- package/src/decoration/editor-pills.ts +156 -0
- package/src/decoration/glyphs.ts +326 -0
- package/src/decoration/pointer-interaction.ts +123 -0
- package/src/decoration/powerline-pill.ts +104 -0
- package/src/decoration/status.ts +185 -0
- package/src/decoration/transient-pill.ts +176 -0
- package/src/diff/index.ts +21 -0
- package/src/diff/model.ts +76 -0
- package/src/diff/parse.ts +310 -0
- package/src/diff/pierre-syntax.ts +86 -0
- package/src/diff/render.ts +504 -0
- package/src/diff/structured.ts +264 -0
- package/src/diff/view.ts +75 -0
- package/src/editor/chrome.ts +341 -0
- package/src/editor/composition.ts +53 -0
- package/src/editor/layout.ts +81 -0
- package/src/editor/presentation.ts +35 -0
- package/src/editor/protocol.ts +188 -0
- package/src/editor.ts +41 -0
- package/src/extension.ts +30 -0
- package/src/folding.ts +192 -0
- package/src/host/cursor-bridge.ts +210 -0
- package/src/host/editor-bridge.ts +148 -0
- package/src/host/extension-host.ts +231 -0
- package/src/host/mouse-bridge.ts +212 -0
- package/src/host/mouse-dispatch.ts +400 -0
- package/src/host/mouse-input.ts +85 -0
- package/src/host/mouse-targets.ts +238 -0
- package/src/host/pi-layout-adapter.ts +298 -0
- package/src/host/pi-native-selection.ts +227 -0
- package/src/host/split-pane-bridge.ts +720 -0
- package/src/host/transcript-bridge.ts +236 -0
- package/src/index.ts +334 -0
- package/src/line-layout.ts +6 -0
- package/src/motion.ts +1409 -0
- package/src/mouse/events.ts +55 -0
- package/src/mouse/fullscreen-layout.ts +260 -0
- package/src/mouse/registry.ts +348 -0
- package/src/mouse.ts +37 -0
- package/src/native-binary.ts +189 -0
- package/src/overlay/anchored.ts +91 -0
- package/src/overlay/detail-card.ts +158 -0
- package/src/overlay/dialog.ts +266 -0
- package/src/overlay/floating.ts +106 -0
- package/src/overlay/framed-editor.ts +236 -0
- package/src/overlay/fullscreen.ts +170 -0
- package/src/overlay/hover-tooltip.ts +109 -0
- package/src/overlay/modal-mount.ts +118 -0
- package/src/panels.ts +207 -0
- package/src/render-cache.ts +80 -0
- package/src/render-epoch.ts +48 -0
- package/src/request-animation.ts +160 -0
- package/src/scrollbar.ts +27 -0
- package/src/selection.ts +148 -0
- package/src/split-pane.ts +232 -0
- package/src/status-presentation.ts +411 -0
- package/src/stream.ts +543 -0
- package/src/syntax.ts +316 -0
- package/src/terminal/bridge-client.ts +243 -0
- package/src/terminal/embedding.ts +6 -0
- package/src/terminal/output.ts +151 -0
- package/src/terminal/projection.ts +362 -0
- package/src/terminal/pty-host.ts +263 -0
- package/src/terminal/pty-pane.ts +308 -0
- package/src/terminal-colors.ts +334 -0
- package/src/tool/action.ts +170 -0
- package/src/tool/activity.ts +500 -0
- package/src/tool/call-preview.ts +44 -0
- package/src/tool/disclosure-action.ts +89 -0
- package/src/tool/expanded-region-viewport.ts +103 -0
- package/src/tool/index.ts +26 -0
- package/src/tool/output.ts +271 -0
- package/src/tool/transcript.ts +150 -0
- package/src/tool/view-region.ts +581 -0
- package/test/appearance.test.ts +183 -0
- package/test/bounded-stream.test.ts +150 -0
- package/test/bridge-client.test.ts +42 -0
- package/test/color-preview.test.ts +23 -0
- package/test/component-stack.test.ts +206 -0
- package/test/components.test.ts +1165 -0
- package/test/content-text.test.ts +21 -0
- package/test/cursor-bridge.test.ts +151 -0
- package/test/cursor.test.ts +147 -0
- package/test/editor-chrome.test.ts +119 -0
- package/test/editor-layout.test.ts +30 -0
- package/test/editor-registry.test.ts +203 -0
- package/test/editor-theme.test.ts +33 -0
- package/test/editor-token-pill.test.ts +72 -0
- package/test/floating-overlay.test.ts +57 -0
- package/test/folding.test.ts +147 -0
- package/test/harmonious-theme.test.ts +23 -0
- package/test/interaction.test.ts +104 -0
- package/test/key-icons.test.ts +62 -0
- package/test/markdown-code.test.ts +24 -0
- package/test/modal-overlay-mount.test.ts +94 -0
- package/test/motion.test.ts +649 -0
- package/test/mouse/bridge.test.ts +1302 -0
- package/test/mouse/extension.test.ts +205 -0
- package/test/mouse/registry.test.ts +140 -0
- package/test/mouse/screen-decoration.test.ts +167 -0
- package/test/native-binary.test.ts +52 -0
- package/test/palette.test.ts +32 -0
- package/test/panels.test.ts +74 -0
- package/test/pointer-components.test.ts +151 -0
- package/test/progress.test.ts +54 -0
- package/test/pty-host.test.ts +126 -0
- package/test/pty-pane.test.ts +62 -0
- package/test/public-surface.test.ts +75 -0
- package/test/render-cache.test.ts +25 -0
- package/test/render-epoch.test.ts +119 -0
- package/test/request-animation.test.ts +193 -0
- package/test/screen-icon-actions.test.ts +69 -0
- package/test/select-box.test.ts +86 -0
- package/test/select-pointer.test.ts +369 -0
- package/test/selectable-list.test.ts +258 -0
- package/test/selection/registry.test.ts +44 -0
- package/test/selection-action-bar.test.ts +204 -0
- package/test/split-pane-bridge.test.ts +944 -0
- package/test/split-pane.test.ts +134 -0
- package/test/status-presentation.test.ts +39 -0
- package/test/syntax.test.ts +59 -0
- package/test/terminal-colors.test.ts +115 -0
- package/test/terminal-embedding.test.ts +12 -0
- package/test/terminal-projection.test.ts +312 -0
- package/test/theme-colors.test.ts +175 -0
- package/test/tool-output.test.ts +160 -0
- package/test/tool-transcript.test.ts +1190 -0
- package/test/unified-diff.test.ts +691 -0
- package/themes/harmonious.json +61 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const TEXT_INTERACTION_TARGET = Symbol.for("pi-libtui/mouse/text-interaction-target/v1");
|
|
2
|
+
|
|
3
|
+
/** Pointer lifecycle events emitted by the shared mouse bridge. */
|
|
4
|
+
export type TuiMouseEventType = "press" | "release" | "drag" | "wheel" | "enter" | "move" | "leave";
|
|
5
|
+
|
|
6
|
+
/** A normalized pointer event delivered to a component or registered overlay region. */
|
|
7
|
+
export interface TuiMouseEvent {
|
|
8
|
+
/** Pointer lifecycle phase. */
|
|
9
|
+
type: TuiMouseEventType;
|
|
10
|
+
/** Zero-based row relative to the receiving component or region. */
|
|
11
|
+
row: number;
|
|
12
|
+
/** Zero-based column relative to the receiving component or region. */
|
|
13
|
+
col: number;
|
|
14
|
+
/** Zero-based row in the complete terminal screen. */
|
|
15
|
+
screenRow: number;
|
|
16
|
+
/** Zero-based column in the complete terminal screen. */
|
|
17
|
+
screenCol: number;
|
|
18
|
+
/** Pressed or released button: primary, middle, or secondary. Undefined for hover and wheel events. */
|
|
19
|
+
button: 0 | 1 | 2 | undefined;
|
|
20
|
+
/** Wheel direction, or undefined for non-wheel events. */
|
|
21
|
+
wheel: -1 | 1 | undefined;
|
|
22
|
+
/** Whether Shift was held when the terminal emitted the event. */
|
|
23
|
+
shift: boolean;
|
|
24
|
+
/** Whether Alt was held when the terminal emitted the event. */
|
|
25
|
+
alt: boolean;
|
|
26
|
+
/** Whether Control was held when the terminal emitted the event. */
|
|
27
|
+
ctrl: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Opt-in interaction implemented by controls embedded in selectable transcript text.
|
|
32
|
+
*
|
|
33
|
+
* The mouse bridge lets native selection observe pointer presses first, activates
|
|
34
|
+
* the target only when the gesture ends as a click, and offers keyboard input
|
|
35
|
+
* only after higher-priority viewport handlers such as copy mode decline it.
|
|
36
|
+
*/
|
|
37
|
+
export interface TextInteractionTarget {
|
|
38
|
+
readonly [TEXT_INTERACTION_TARGET]: true;
|
|
39
|
+
/** Synchronize visible keyboard focus owned by the shared viewport bridge. */
|
|
40
|
+
setViewportFocus(focused: boolean): void;
|
|
41
|
+
/** Handle one unclaimed viewport key. Return true only when it was consumed. */
|
|
42
|
+
handleViewportInput(data: string): boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A terminal-cell rectangle in zero-based screen coordinates. */
|
|
46
|
+
export interface MouseRect {
|
|
47
|
+
/** Leftmost screen column. */
|
|
48
|
+
x: number;
|
|
49
|
+
/** Topmost screen row. */
|
|
50
|
+
y: number;
|
|
51
|
+
/** Width in terminal cells. */
|
|
52
|
+
width: number;
|
|
53
|
+
/** Height in terminal rows. */
|
|
54
|
+
height: number;
|
|
55
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { SelectionPoint } from "../selection.ts";
|
|
2
|
+
import type { MouseRect } from "./events.ts";
|
|
3
|
+
import type { ViewportRect } from "./registry.ts";
|
|
4
|
+
|
|
5
|
+
/** Versioned host capability for Pi's validated fullscreen transcript layout. */
|
|
6
|
+
export const FULLSCREEN_LAYOUT_CAPABILITY_KEY = Symbol.for("pi-libtui/fullscreen-layout/v1");
|
|
7
|
+
export const FULLSCREEN_LAYOUT_PROTOCOL = "pi-libtui/fullscreen-layout/v1" as const;
|
|
8
|
+
|
|
9
|
+
/** The private scroll-view shape exposed through the fullscreen layout capability. */
|
|
10
|
+
export interface LayoutScrollViewReference {
|
|
11
|
+
readonly scrollTop: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The primary transcript scroll view with viewport and scrolling controls. */
|
|
15
|
+
export interface LayoutScrollView extends LayoutScrollViewReference {
|
|
16
|
+
readonly viewportHeight: number;
|
|
17
|
+
scrollTo(row: number, options?: { disableFollow?: boolean }): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A validated component layout box supplied by the Pi host bridge. */
|
|
21
|
+
export interface LayoutBox {
|
|
22
|
+
readonly component: object;
|
|
23
|
+
readonly rect: MouseRect;
|
|
24
|
+
readonly clip: MouseRect;
|
|
25
|
+
readonly children: readonly LayoutBox[];
|
|
26
|
+
readonly scrollView?: LayoutScrollViewReference;
|
|
27
|
+
readonly scrollContentLines?: readonly string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A validated Pi layout frame. The primary scroll view is absent on non-transcript layouts. */
|
|
31
|
+
export interface LayoutFrame {
|
|
32
|
+
readonly root: LayoutBox;
|
|
33
|
+
readonly primaryScrollView?: LayoutScrollView;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A selection point tied to the scroll view that owns its logical row. */
|
|
37
|
+
export interface LayoutSelectionPoint extends SelectionPoint {
|
|
38
|
+
readonly scrollView: LayoutScrollView;
|
|
39
|
+
readonly boundary?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Current validated fullscreen transcript geometry and selection mapping. */
|
|
43
|
+
export interface FullscreenLayout {
|
|
44
|
+
readonly frame: LayoutFrame;
|
|
45
|
+
readonly primaryBox: LayoutBox;
|
|
46
|
+
readonly primaryScrollView: LayoutScrollView;
|
|
47
|
+
readonly lines: readonly string[];
|
|
48
|
+
readonly viewport: ViewportRect;
|
|
49
|
+
readonly selectionAnchor: LayoutSelectionPoint | undefined;
|
|
50
|
+
readonly selectionFocus: LayoutSelectionPoint | undefined;
|
|
51
|
+
setSelection(anchor: LayoutSelectionPoint | undefined, focus: LayoutSelectionPoint | undefined): void;
|
|
52
|
+
point(point: SelectionPoint, boundary?: boolean): LayoutSelectionPoint;
|
|
53
|
+
screenPoint(point: SelectionPoint): SelectionPoint;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** UI-free provider for current Pi fullscreen layout state. */
|
|
57
|
+
export interface FullscreenLayoutCapability {
|
|
58
|
+
readonly protocol: typeof FULLSCREEN_LAYOUT_PROTOCOL;
|
|
59
|
+
readonly version: 1;
|
|
60
|
+
resolve(renderer: object): FullscreenLayout | undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// type-boundary: Symbol.for can contain a capability from another extension realm; this validator narrows its public shape.
|
|
64
|
+
type UntrustedLayoutValue = unknown;
|
|
65
|
+
|
|
66
|
+
function isRecord(value: UntrustedLayoutValue): value is Record<PropertyKey, UntrustedLayoutValue> {
|
|
67
|
+
return value !== null && typeof value === "object";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isFiniteNumber(value: UntrustedLayoutValue): value is number {
|
|
71
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isLayoutRect(value: UntrustedLayoutValue): value is MouseRect {
|
|
75
|
+
if (!isRecord(value)) return false;
|
|
76
|
+
return (
|
|
77
|
+
isFiniteNumber(value.x) &&
|
|
78
|
+
isFiniteNumber(value.y) &&
|
|
79
|
+
isFiniteNumber(value.width) &&
|
|
80
|
+
isFiniteNumber(value.height) &&
|
|
81
|
+
value.width >= 0 &&
|
|
82
|
+
value.height >= 0
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isLayoutScrollView(value: UntrustedLayoutValue): value is LayoutScrollView {
|
|
87
|
+
if (!isRecord(value)) return false;
|
|
88
|
+
return (
|
|
89
|
+
isFiniteNumber(value.scrollTop) &&
|
|
90
|
+
isFiniteNumber(value.viewportHeight) &&
|
|
91
|
+
value.viewportHeight >= 0 &&
|
|
92
|
+
typeof value.scrollTo === "function"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isLayoutScrollViewReference(value: UntrustedLayoutValue): value is LayoutScrollViewReference {
|
|
97
|
+
return isRecord(value) && isFiniteNumber(value.scrollTop);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isLayoutBox(value: UntrustedLayoutValue, ancestors = new Set<object>()): value is LayoutBox {
|
|
101
|
+
if (!isRecord(value) || ancestors.has(value)) return false;
|
|
102
|
+
if (
|
|
103
|
+
!isRecord(value.component) ||
|
|
104
|
+
!isLayoutRect(value.rect) ||
|
|
105
|
+
!isLayoutRect(value.clip) ||
|
|
106
|
+
!Array.isArray(value.children)
|
|
107
|
+
)
|
|
108
|
+
return false;
|
|
109
|
+
if (value.scrollView !== undefined && !isLayoutScrollViewReference(value.scrollView)) return false;
|
|
110
|
+
if (
|
|
111
|
+
value.scrollContentLines !== undefined &&
|
|
112
|
+
(!Array.isArray(value.scrollContentLines) || !value.scrollContentLines.every((line) => typeof line === "string"))
|
|
113
|
+
)
|
|
114
|
+
return false;
|
|
115
|
+
ancestors.add(value);
|
|
116
|
+
const valid = value.children.every((child) => isLayoutBox(child, ancestors));
|
|
117
|
+
ancestors.delete(value);
|
|
118
|
+
return valid;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isLayoutFrame(value: UntrustedLayoutValue): value is LayoutFrame {
|
|
122
|
+
if (!isRecord(value) || !isLayoutBox(value.root)) return false;
|
|
123
|
+
return value.primaryScrollView === undefined || isLayoutScrollView(value.primaryScrollView);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isLayoutSelectionPoint(value: UntrustedLayoutValue): value is LayoutSelectionPoint {
|
|
127
|
+
if (!isRecord(value)) return false;
|
|
128
|
+
return isFiniteNumber(value.row) && isFiniteNumber(value.col) && isLayoutScrollView(value.scrollView);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function containsLayoutBox(
|
|
132
|
+
root: LayoutBox,
|
|
133
|
+
target: UntrustedLayoutValue,
|
|
134
|
+
ancestors = new Set<object>(),
|
|
135
|
+
): target is LayoutBox {
|
|
136
|
+
if (root === target) return true;
|
|
137
|
+
if (ancestors.has(root)) return false;
|
|
138
|
+
ancestors.add(root);
|
|
139
|
+
try {
|
|
140
|
+
return root.children.some((child) => containsLayoutBox(child, target, ancestors));
|
|
141
|
+
} finally {
|
|
142
|
+
ancestors.delete(root);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function isFullscreenLayout(value: UntrustedLayoutValue): value is FullscreenLayout {
|
|
147
|
+
try {
|
|
148
|
+
if (!isRecord(value) || !isLayoutFrame(value.frame) || !containsLayoutBox(value.frame.root, value.primaryBox))
|
|
149
|
+
return false;
|
|
150
|
+
const viewport = value.viewport as UntrustedLayoutValue;
|
|
151
|
+
if (
|
|
152
|
+
!isLayoutScrollView(value.primaryScrollView) ||
|
|
153
|
+
value.frame.primaryScrollView !== value.primaryScrollView ||
|
|
154
|
+
value.primaryBox.scrollView !== value.primaryScrollView ||
|
|
155
|
+
!Array.isArray(value.lines) ||
|
|
156
|
+
!value.lines.every((line) => typeof line === "string") ||
|
|
157
|
+
value.primaryBox.scrollContentLines !== value.lines ||
|
|
158
|
+
!isLayoutRect(viewport) ||
|
|
159
|
+
!isFiniteNumber(isRecord(viewport) ? viewport.scrollTop : undefined) ||
|
|
160
|
+
(isRecord(viewport) ? viewport.scrollTop : undefined) !== value.primaryScrollView.scrollTop ||
|
|
161
|
+
typeof value.setSelection !== "function" ||
|
|
162
|
+
typeof value.point !== "function" ||
|
|
163
|
+
typeof value.screenPoint !== "function"
|
|
164
|
+
)
|
|
165
|
+
return false;
|
|
166
|
+
const expectedX = Math.max(value.primaryBox.rect.x, value.primaryBox.clip.x);
|
|
167
|
+
const expectedY = Math.max(value.primaryBox.rect.y, value.primaryBox.clip.y);
|
|
168
|
+
const expectedWidth = Math.max(
|
|
169
|
+
0,
|
|
170
|
+
Math.min(
|
|
171
|
+
value.primaryBox.rect.x + value.primaryBox.rect.width,
|
|
172
|
+
value.primaryBox.clip.x + value.primaryBox.clip.width,
|
|
173
|
+
) - expectedX,
|
|
174
|
+
);
|
|
175
|
+
const expectedHeight = Math.max(
|
|
176
|
+
0,
|
|
177
|
+
Math.min(
|
|
178
|
+
value.primaryBox.rect.y + value.primaryBox.rect.height,
|
|
179
|
+
value.primaryBox.clip.y + value.primaryBox.clip.height,
|
|
180
|
+
) - expectedY,
|
|
181
|
+
);
|
|
182
|
+
if (
|
|
183
|
+
expectedWidth <= 0 ||
|
|
184
|
+
expectedHeight <= 0 ||
|
|
185
|
+
viewport.x !== expectedX ||
|
|
186
|
+
viewport.y !== expectedY ||
|
|
187
|
+
viewport.width !== expectedWidth ||
|
|
188
|
+
viewport.height !== expectedHeight
|
|
189
|
+
)
|
|
190
|
+
return false;
|
|
191
|
+
if (value.selectionAnchor !== undefined && !isLayoutSelectionPoint(value.selectionAnchor)) return false;
|
|
192
|
+
if (value.selectionFocus !== undefined && !isLayoutSelectionPoint(value.selectionFocus)) return false;
|
|
193
|
+
return (
|
|
194
|
+
(value.selectionAnchor === undefined || value.selectionAnchor.scrollView === value.primaryScrollView) &&
|
|
195
|
+
(value.selectionFocus === undefined || value.selectionFocus.scrollView === value.primaryScrollView)
|
|
196
|
+
);
|
|
197
|
+
} catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function isFullscreenLayoutCapability(value: UntrustedLayoutValue): value is FullscreenLayoutCapability {
|
|
203
|
+
try {
|
|
204
|
+
if (!isRecord(value)) return false;
|
|
205
|
+
const candidate = value as Partial<FullscreenLayoutCapability>;
|
|
206
|
+
return (
|
|
207
|
+
candidate.protocol === FULLSCREEN_LAYOUT_PROTOCOL &&
|
|
208
|
+
candidate.version === 1 &&
|
|
209
|
+
typeof candidate.resolve === "function"
|
|
210
|
+
);
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Read the optional host-provided fullscreen layout capability without creating one. */
|
|
217
|
+
export function getFullscreenLayoutCapability(
|
|
218
|
+
scope: typeof globalThis = globalThis,
|
|
219
|
+
): FullscreenLayoutCapability | undefined {
|
|
220
|
+
try {
|
|
221
|
+
const slots = scope as Record<PropertyKey, UntrustedLayoutValue>;
|
|
222
|
+
const candidate = slots[FULLSCREEN_LAYOUT_CAPABILITY_KEY];
|
|
223
|
+
return isFullscreenLayoutCapability(candidate) ? candidate : undefined;
|
|
224
|
+
} catch {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Publish the host-owned fullscreen layout provider and return an identity-safe disposer. */
|
|
230
|
+
export function publishFullscreenLayoutCapability(
|
|
231
|
+
capability: FullscreenLayoutCapability,
|
|
232
|
+
scope: typeof globalThis = globalThis,
|
|
233
|
+
): () => void {
|
|
234
|
+
const slots = scope as Record<PropertyKey, UntrustedLayoutValue>;
|
|
235
|
+
const existing = slots[FULLSCREEN_LAYOUT_CAPABILITY_KEY];
|
|
236
|
+
if (isFullscreenLayoutCapability(existing) && existing !== capability) return () => {};
|
|
237
|
+
if (existing !== capability) slots[FULLSCREEN_LAYOUT_CAPABILITY_KEY] = capability;
|
|
238
|
+
let disposed = false;
|
|
239
|
+
return () => {
|
|
240
|
+
if (disposed) return;
|
|
241
|
+
disposed = true;
|
|
242
|
+
if (slots[FULLSCREEN_LAYOUT_CAPABILITY_KEY] === capability)
|
|
243
|
+
Reflect.deleteProperty(slots, FULLSCREEN_LAYOUT_CAPABILITY_KEY);
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Resolve current fullscreen geometry, tolerating an absent, incompatible, or malformed provider. */
|
|
248
|
+
export function resolveFullscreenLayout(
|
|
249
|
+
renderer: object,
|
|
250
|
+
scope: typeof globalThis = globalThis,
|
|
251
|
+
): FullscreenLayout | undefined {
|
|
252
|
+
const capability = getFullscreenLayoutCapability(scope);
|
|
253
|
+
if (!capability) return undefined;
|
|
254
|
+
try {
|
|
255
|
+
const layout = capability.resolve(renderer);
|
|
256
|
+
return isFullscreenLayout(layout) ? layout : undefined;
|
|
257
|
+
} catch {
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import type { NativeSelectionGeometry } from "../selection.ts";
|
|
2
|
+
import type { MouseRect, TuiMouseEvent } from "./events.ts";
|
|
3
|
+
|
|
4
|
+
export const MOUSE_REGISTRY_KEY = Symbol.for("pi-libtui/mouse/registry/v1");
|
|
5
|
+
export const MOUSE_PROTOCOL = "pi-libtui/mouse/registry/v1" as const;
|
|
6
|
+
|
|
7
|
+
/** The visible primary viewport and its logical vertical scroll offset. */
|
|
8
|
+
export interface ViewportRect extends MouseRect {
|
|
9
|
+
/** Zero-based logical row at the top of the viewport. */
|
|
10
|
+
scrollTop: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Terminal and renderer state supplied to screen decorators. */
|
|
14
|
+
export interface ScreenDecorationContext {
|
|
15
|
+
/** Current terminal width in cells. */
|
|
16
|
+
width: number;
|
|
17
|
+
/** Current terminal height in rows. */
|
|
18
|
+
height: number;
|
|
19
|
+
/** Whether Pi currently has a visible overlay. */
|
|
20
|
+
hasOverlay: boolean;
|
|
21
|
+
/** Native Pi selection is already painted into the screen. */
|
|
22
|
+
selectionActive?: boolean;
|
|
23
|
+
/** Current native selection geometry recomputed from Pi's latest layout. */
|
|
24
|
+
selection?: NativeSelectionGeometry;
|
|
25
|
+
/** Visible primary viewport when Pi exposes its layout and scroll state. */
|
|
26
|
+
viewport?: ViewportRect;
|
|
27
|
+
/** Current logical transcript rows before viewport clipping, when Pi exposes its fullscreen layout. */
|
|
28
|
+
transcriptLines?: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A viewport input transformation, optionally consuming the resulting input. */
|
|
32
|
+
export type ViewportInputHandlerResult = { consume?: boolean; data?: string } | undefined;
|
|
33
|
+
/** Returns whether native copy handling should be deferred for the current selection release. */
|
|
34
|
+
export type NativeCopyDeferrer = () => boolean;
|
|
35
|
+
|
|
36
|
+
/** Options for one ordered viewport input handler. */
|
|
37
|
+
export interface ViewportInputHandlerRegistration {
|
|
38
|
+
/** Stable diagnostic identity for the handler. Registrations are removed by object identity. */
|
|
39
|
+
id: string;
|
|
40
|
+
/** Dispatch priority. Higher values run first; equal priorities run newest first. Defaults to zero. */
|
|
41
|
+
priority?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Inspects or transforms viewport input.
|
|
44
|
+
*
|
|
45
|
+
* @param data Current input after transformations from earlier handlers.
|
|
46
|
+
* @returns A replacement `data`, a `consume` decision, both, or undefined to pass through unchanged.
|
|
47
|
+
*/
|
|
48
|
+
handle(data: string): ViewportInputHandlerResult;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Options for one ordered fullscreen screen decorator. */
|
|
52
|
+
export interface ScreenDecoratorRegistration {
|
|
53
|
+
/** Stable diagnostic identity for the decorator. Registrations are removed by object identity. */
|
|
54
|
+
id: string;
|
|
55
|
+
/** Dispatch priority. Higher values run first; equal priorities run newest first. Defaults to zero. */
|
|
56
|
+
priority?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Decorates the current rendered screen.
|
|
59
|
+
*
|
|
60
|
+
* @param screen Current screen after earlier decorators have run.
|
|
61
|
+
* @param context Current terminal, overlay, selection, and viewport state.
|
|
62
|
+
* @returns The complete screen to pass to the next decorator.
|
|
63
|
+
*/
|
|
64
|
+
decorate(screen: string[], context: ScreenDecorationContext): string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Options for a screen-positioned pointer target outside Pi's component layout. */
|
|
68
|
+
export interface OverlayMouseRegion {
|
|
69
|
+
/** Stable diagnostic identity for the region. Registrations are removed by object identity. */
|
|
70
|
+
id: string;
|
|
71
|
+
/** Higher priorities are hit first. Equal priorities use latest registration first. */
|
|
72
|
+
priority?: number;
|
|
73
|
+
/** Resolve on every event because Pi can move or resize an overlay. */
|
|
74
|
+
getRect(): MouseRect | undefined;
|
|
75
|
+
/**
|
|
76
|
+
* Handles an event whose `row` and `col` are relative to the latest resolved rectangle.
|
|
77
|
+
*
|
|
78
|
+
* @param event Normalized pointer event for this region.
|
|
79
|
+
* @returns `true` when the region handled the event and dispatch should stop.
|
|
80
|
+
*/
|
|
81
|
+
onMouse(event: TuiMouseEvent): boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Versioned cross-extension registry for pointer, input, copy, and screen-decoration hooks. */
|
|
85
|
+
export interface MouseRegistry {
|
|
86
|
+
/** Protocol identity used to validate a registry shared through `globalThis`. */
|
|
87
|
+
readonly protocol: typeof MOUSE_PROTOCOL;
|
|
88
|
+
/** Public registry API version. */
|
|
89
|
+
readonly version: 1;
|
|
90
|
+
/**
|
|
91
|
+
* Registers a dynamic screen-positioned pointer target.
|
|
92
|
+
*
|
|
93
|
+
* @param region Region definition to register.
|
|
94
|
+
* @returns An idempotent function that removes this exact registration object.
|
|
95
|
+
*/
|
|
96
|
+
registerOverlayRegion(region: OverlayMouseRegion): () => void;
|
|
97
|
+
/**
|
|
98
|
+
* Registers an ordered viewport input handler.
|
|
99
|
+
*
|
|
100
|
+
* @param handler Handler definition to register.
|
|
101
|
+
* @returns An idempotent function that removes this exact registration object.
|
|
102
|
+
*/
|
|
103
|
+
registerViewportInputHandler(handler: ViewportInputHandlerRegistration): () => void;
|
|
104
|
+
/**
|
|
105
|
+
* Runs viewport input through registered handlers until one consumes it.
|
|
106
|
+
* Handler failures are ignored so optional extensions cannot break input.
|
|
107
|
+
*
|
|
108
|
+
* @param data Raw viewport input to dispatch.
|
|
109
|
+
* @returns The final transformed data and whether a handler consumed it.
|
|
110
|
+
*/
|
|
111
|
+
dispatchViewportInput(data: string): { data: string; consumed: boolean };
|
|
112
|
+
/**
|
|
113
|
+
* Registers a predicate that can defer Pi's native copy handling.
|
|
114
|
+
*
|
|
115
|
+
* @param deferrer Predicate evaluated during native selection release.
|
|
116
|
+
* @returns An idempotent function that removes this exact predicate.
|
|
117
|
+
*/
|
|
118
|
+
registerNativeCopyDeferrer(deferrer: NativeCopyDeferrer): () => void;
|
|
119
|
+
/**
|
|
120
|
+
* Evaluates registered copy deferrers until one returns true.
|
|
121
|
+
* Predicate failures are ignored.
|
|
122
|
+
*
|
|
123
|
+
* @returns `true` when native copy should be deferred; otherwise `false`.
|
|
124
|
+
*/
|
|
125
|
+
shouldDeferNativeCopy(): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Registers an ordered fullscreen screen decorator.
|
|
128
|
+
*
|
|
129
|
+
* @param decorator Decorator definition to register.
|
|
130
|
+
* @returns An idempotent function that removes this exact registration object.
|
|
131
|
+
*/
|
|
132
|
+
registerScreenDecorator(decorator: ScreenDecoratorRegistration): () => void;
|
|
133
|
+
/**
|
|
134
|
+
* Composes registered decorators over a rendered screen.
|
|
135
|
+
* Throwing decorators and invalid non-string-array results are ignored.
|
|
136
|
+
*
|
|
137
|
+
* @param screen Initial rendered screen.
|
|
138
|
+
* @param context Current terminal and renderer state.
|
|
139
|
+
* @returns The final valid decorated screen, or the last valid screen when a decorator fails.
|
|
140
|
+
*/
|
|
141
|
+
dispatchScreenDecorators(screen: string[], context: ScreenDecorationContext): string[];
|
|
142
|
+
/** Ask the active fullscreen bridge to keep the primary viewport anchored for the next resize. */
|
|
143
|
+
preserveViewportOnResize?(): void;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Shared registration storage used by the mouse bridge for direct overlay dispatch. */
|
|
147
|
+
export interface MouseRegistryState {
|
|
148
|
+
/** Registered overlay regions in registration order. */
|
|
149
|
+
readonly regions: OverlayMouseRegion[];
|
|
150
|
+
/** Registered viewport input handlers in registration order. */
|
|
151
|
+
readonly viewportInputHandlers: ViewportInputHandlerRegistration[];
|
|
152
|
+
/** Registered native-copy predicates in registration order. */
|
|
153
|
+
readonly nativeCopyDeferrers: NativeCopyDeferrer[];
|
|
154
|
+
/** Registered screen decorators in registration order. */
|
|
155
|
+
readonly screenDecorators: ScreenDecoratorRegistration[];
|
|
156
|
+
viewportPreserver?: () => void;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const STATE_KEY = Symbol.for("pi-libtui/mouse/registry-state/v1");
|
|
160
|
+
const states = new WeakMap<MouseRegistry, MouseRegistryState>();
|
|
161
|
+
|
|
162
|
+
// type-boundary: Symbol.for capabilities can be populated by another extension realm; this validator narrows the public methods.
|
|
163
|
+
type UntrustedRegistryValue = unknown;
|
|
164
|
+
|
|
165
|
+
function isRegistry(value: UntrustedRegistryValue): value is MouseRegistry {
|
|
166
|
+
if (!value || typeof value !== "object") return false;
|
|
167
|
+
const candidate = value as Partial<MouseRegistry>;
|
|
168
|
+
return (
|
|
169
|
+
candidate.protocol === MOUSE_PROTOCOL &&
|
|
170
|
+
candidate.version === 1 &&
|
|
171
|
+
typeof candidate.registerOverlayRegion === "function" &&
|
|
172
|
+
typeof candidate.registerViewportInputHandler === "function" &&
|
|
173
|
+
typeof candidate.dispatchViewportInput === "function" &&
|
|
174
|
+
typeof candidate.registerNativeCopyDeferrer === "function" &&
|
|
175
|
+
typeof candidate.shouldDeferNativeCopy === "function" &&
|
|
176
|
+
typeof candidate.registerScreenDecorator === "function" &&
|
|
177
|
+
typeof candidate.dispatchScreenDecorators === "function"
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function stateFor(registry: MouseRegistry): MouseRegistryState {
|
|
182
|
+
const existing = states.get(registry);
|
|
183
|
+
if (existing) return existing;
|
|
184
|
+
const shared = Reflect.get(registry as object, STATE_KEY) as UntrustedRegistryValue;
|
|
185
|
+
if (isState(shared)) {
|
|
186
|
+
states.set(registry, shared);
|
|
187
|
+
return shared;
|
|
188
|
+
}
|
|
189
|
+
const state: MouseRegistryState = {
|
|
190
|
+
regions: [],
|
|
191
|
+
viewportInputHandlers: [],
|
|
192
|
+
nativeCopyDeferrers: [],
|
|
193
|
+
screenDecorators: [],
|
|
194
|
+
};
|
|
195
|
+
Object.defineProperty(registry, STATE_KEY, {
|
|
196
|
+
value: state,
|
|
197
|
+
enumerable: false,
|
|
198
|
+
configurable: false,
|
|
199
|
+
});
|
|
200
|
+
states.set(registry, state);
|
|
201
|
+
return state;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function isState(value: UntrustedRegistryValue): value is MouseRegistryState {
|
|
205
|
+
if (!value || typeof value !== "object") return false;
|
|
206
|
+
const candidate = value as Partial<MouseRegistryState>;
|
|
207
|
+
return (
|
|
208
|
+
Array.isArray(candidate.regions) &&
|
|
209
|
+
Array.isArray(candidate.viewportInputHandlers) &&
|
|
210
|
+
Array.isArray(candidate.nativeCopyDeferrers) &&
|
|
211
|
+
Array.isArray(candidate.screenDecorators)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Returns the shared registration state associated with a mouse registry.
|
|
217
|
+
*
|
|
218
|
+
* @param registry Registry whose state should be resolved.
|
|
219
|
+
* @returns The stable state object shared across compatible extension realms.
|
|
220
|
+
*/
|
|
221
|
+
export function getMouseRegistryState(registry: MouseRegistry): MouseRegistryState {
|
|
222
|
+
return stateFor(registry);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function removeIdentity<T>(values: T[], value: T): void {
|
|
226
|
+
const index = values.indexOf(value);
|
|
227
|
+
if (index >= 0) values.splice(index, 1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function prioritized<T extends { priority?: number }>(values: readonly T[]): T[] {
|
|
231
|
+
return values
|
|
232
|
+
.map((value, order) => ({ value, order }))
|
|
233
|
+
.sort((left, right) => (right.value.priority ?? 0) - (left.value.priority ?? 0) || right.order - left.order)
|
|
234
|
+
.map(({ value }) => value);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Resolves or creates the process-wide version-one mouse registry.
|
|
239
|
+
*
|
|
240
|
+
* Compatible registries already published under {@link MOUSE_REGISTRY_KEY} are reused so
|
|
241
|
+
* independently loaded extensions share registrations without runtime dependencies.
|
|
242
|
+
*
|
|
243
|
+
* @param scope Global object that owns the registry capability. Defaults to `globalThis`.
|
|
244
|
+
* @returns The existing compatible registry, or a newly installed version-one registry.
|
|
245
|
+
*/
|
|
246
|
+
export function ensureMouseRegistry(scope: typeof globalThis = globalThis): MouseRegistry {
|
|
247
|
+
const slots = scope as Record<PropertyKey, UntrustedRegistryValue>;
|
|
248
|
+
const existing = slots[MOUSE_REGISTRY_KEY];
|
|
249
|
+
if (isRegistry(existing)) {
|
|
250
|
+
stateFor(existing);
|
|
251
|
+
return existing;
|
|
252
|
+
}
|
|
253
|
+
const registry: MouseRegistry = {
|
|
254
|
+
protocol: MOUSE_PROTOCOL,
|
|
255
|
+
version: 1,
|
|
256
|
+
registerOverlayRegion(region) {
|
|
257
|
+
const state = stateFor(registry);
|
|
258
|
+
state.regions.push(region);
|
|
259
|
+
return () => removeIdentity(state.regions, region);
|
|
260
|
+
},
|
|
261
|
+
registerViewportInputHandler(handler) {
|
|
262
|
+
const state = stateFor(registry);
|
|
263
|
+
state.viewportInputHandlers.push(handler);
|
|
264
|
+
return () => removeIdentity(state.viewportInputHandlers, handler);
|
|
265
|
+
},
|
|
266
|
+
dispatchViewportInput(data) {
|
|
267
|
+
let current = data;
|
|
268
|
+
for (const handler of prioritized(stateFor(registry).viewportInputHandlers)) {
|
|
269
|
+
let result: ViewportInputHandlerResult;
|
|
270
|
+
try {
|
|
271
|
+
result = handler.handle(current);
|
|
272
|
+
} catch {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (typeof result?.data === "string") current = result.data;
|
|
276
|
+
if (result?.consume === true) return { data: current, consumed: true };
|
|
277
|
+
}
|
|
278
|
+
return { data: current, consumed: false };
|
|
279
|
+
},
|
|
280
|
+
registerNativeCopyDeferrer(deferrer) {
|
|
281
|
+
const state = stateFor(registry);
|
|
282
|
+
state.nativeCopyDeferrers.push(deferrer);
|
|
283
|
+
return () => removeIdentity(state.nativeCopyDeferrers, deferrer);
|
|
284
|
+
},
|
|
285
|
+
shouldDeferNativeCopy() {
|
|
286
|
+
for (const deferrer of [...stateFor(registry).nativeCopyDeferrers]) {
|
|
287
|
+
try {
|
|
288
|
+
if (deferrer()) return true;
|
|
289
|
+
} catch {
|
|
290
|
+
// Optional predicates must not affect native copy behavior.
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
},
|
|
295
|
+
registerScreenDecorator(decorator) {
|
|
296
|
+
const state = stateFor(registry);
|
|
297
|
+
state.screenDecorators.push(decorator);
|
|
298
|
+
return () => removeIdentity(state.screenDecorators, decorator);
|
|
299
|
+
},
|
|
300
|
+
dispatchScreenDecorators(screen, context) {
|
|
301
|
+
let current = screen;
|
|
302
|
+
for (const decorator of prioritized(stateFor(registry).screenDecorators)) {
|
|
303
|
+
try {
|
|
304
|
+
const decorated = decorator.decorate(current, context);
|
|
305
|
+
if (Array.isArray(decorated) && decorated.every((line) => typeof line === "string")) current = decorated;
|
|
306
|
+
} catch {
|
|
307
|
+
// Optional decorators must not break fullscreen rendering.
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return current;
|
|
311
|
+
},
|
|
312
|
+
preserveViewportOnResize() {
|
|
313
|
+
try {
|
|
314
|
+
stateFor(registry).viewportPreserver?.();
|
|
315
|
+
} catch {
|
|
316
|
+
// The fullscreen bridge is optional and must not break tool interaction.
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
stateFor(registry);
|
|
321
|
+
slots[MOUSE_REGISTRY_KEY] = registry;
|
|
322
|
+
return registry;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** Preserve the host transcript viewport across an expansion resize when a bridge is installed. */
|
|
326
|
+
export function preserveViewportOnResize(scope: typeof globalThis = globalThis): void {
|
|
327
|
+
ensureMouseRegistry(scope).preserveViewportOnResize?.();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Options for blocking transcript selection behind a modal pointer surface. */
|
|
331
|
+
export interface ModalPointerShieldOptions {
|
|
332
|
+
/** Stable diagnostic identity for the registered region. */
|
|
333
|
+
id: string;
|
|
334
|
+
/** Current screen bounds covered by the modal surface. */
|
|
335
|
+
getRect(): MouseRect | undefined;
|
|
336
|
+
/** Hit priority below the modal's own region; defaults to 9999. */
|
|
337
|
+
priority?: number;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Consume pointer gestures behind a modal overlay before Pi can begin transcript selection. */
|
|
341
|
+
export function registerModalPointerShield(registry: MouseRegistry, options: ModalPointerShieldOptions): () => void {
|
|
342
|
+
return registry.registerOverlayRegion({
|
|
343
|
+
id: options.id,
|
|
344
|
+
priority: options.priority ?? 9_999,
|
|
345
|
+
getRect: options.getRect,
|
|
346
|
+
onMouse: (event) => event.type === "press" || event.type === "drag" || event.type === "release",
|
|
347
|
+
});
|
|
348
|
+
}
|
package/src/mouse.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type MouseRect,
|
|
3
|
+
TEXT_INTERACTION_TARGET,
|
|
4
|
+
type TextInteractionTarget,
|
|
5
|
+
type TuiMouseEvent,
|
|
6
|
+
type TuiMouseEventType,
|
|
7
|
+
} from "./mouse/events.ts";
|
|
8
|
+
export {
|
|
9
|
+
FULLSCREEN_LAYOUT_CAPABILITY_KEY,
|
|
10
|
+
FULLSCREEN_LAYOUT_PROTOCOL,
|
|
11
|
+
type FullscreenLayout,
|
|
12
|
+
type FullscreenLayoutCapability,
|
|
13
|
+
getFullscreenLayoutCapability,
|
|
14
|
+
type LayoutBox,
|
|
15
|
+
type LayoutFrame,
|
|
16
|
+
type LayoutScrollView,
|
|
17
|
+
type LayoutScrollViewReference,
|
|
18
|
+
type LayoutSelectionPoint,
|
|
19
|
+
publishFullscreenLayoutCapability,
|
|
20
|
+
resolveFullscreenLayout,
|
|
21
|
+
} from "./mouse/fullscreen-layout.ts";
|
|
22
|
+
export {
|
|
23
|
+
ensureMouseRegistry,
|
|
24
|
+
MOUSE_PROTOCOL,
|
|
25
|
+
MOUSE_REGISTRY_KEY,
|
|
26
|
+
type ModalPointerShieldOptions,
|
|
27
|
+
type MouseRegistry,
|
|
28
|
+
type NativeCopyDeferrer,
|
|
29
|
+
type OverlayMouseRegion,
|
|
30
|
+
preserveViewportOnResize,
|
|
31
|
+
registerModalPointerShield,
|
|
32
|
+
type ScreenDecorationContext,
|
|
33
|
+
type ScreenDecoratorRegistration,
|
|
34
|
+
type ViewportInputHandlerRegistration,
|
|
35
|
+
type ViewportInputHandlerResult,
|
|
36
|
+
type ViewportRect,
|
|
37
|
+
} from "./mouse/registry.ts";
|