@ebowwa/coder 0.7.64 → 0.7.66
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/dist/index.js +36233 -32
- package/dist/interfaces/ui/terminal/cli/index.js +34318 -158
- package/dist/interfaces/ui/terminal/native/README.md +53 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
- package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/interfaces/ui/terminal/native/index.js +43 -0
- package/dist/interfaces/ui/terminal/native/index.node +0 -0
- package/dist/interfaces/ui/terminal/native/package.json +34 -0
- package/dist/native/README.md +53 -0
- package/dist/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/native/claude_code_native.dylib +0 -0
- package/dist/native/index.d.ts +0 -480
- package/dist/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.js +43 -1625
- package/dist/native/index.node +0 -0
- package/dist/native/package.json +34 -0
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +33 -19
- package/package.json +3 -2
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
- package/packages/src/core/agent-loop/compaction.ts +6 -2
- package/packages/src/core/agent-loop/index.ts +2 -0
- package/packages/src/core/agent-loop/loop-state.ts +1 -1
- package/packages/src/core/agent-loop/turn-executor.ts +4 -0
- package/packages/src/core/agent-loop/types.ts +4 -0
- package/packages/src/core/api-client-impl.ts +377 -176
- package/packages/src/core/cognitive-security/hooks.ts +2 -1
- package/packages/src/core/config/todo +7 -0
- package/packages/src/core/context/__tests__/integration.test.ts +334 -0
- package/packages/src/core/context/compaction.ts +170 -0
- package/packages/src/core/context/constants.ts +58 -0
- package/packages/src/core/context/extraction.ts +85 -0
- package/packages/src/core/context/index.ts +66 -0
- package/packages/src/core/context/summarization.ts +251 -0
- package/packages/src/core/context/token-estimation.ts +98 -0
- package/packages/src/core/context/types.ts +59 -0
- package/packages/src/core/models.ts +81 -4
- package/packages/src/core/normalizers/todo +5 -1
- package/packages/src/core/providers/README.md +230 -0
- package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
- package/packages/src/core/providers/index.ts +419 -0
- package/packages/src/core/providers/types.ts +132 -0
- package/packages/src/core/retry.ts +10 -0
- package/packages/src/ecosystem/tools/index.ts +174 -0
- package/packages/src/index.ts +23 -2
- package/packages/src/interfaces/ui/index.ts +17 -20
- package/packages/src/interfaces/ui/spinner.ts +2 -2
- package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
- package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
- package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
- package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
- package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
- package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
- package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +402 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
- package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
- package/packages/src/native/index.ts +404 -27
- package/packages/src/native/tui_v2_types.ts +39 -0
- package/packages/src/teammates/coordination.test.ts +279 -0
- package/packages/src/teammates/coordination.ts +646 -0
- package/packages/src/teammates/index.ts +95 -25
- package/packages/src/teammates/integration.test.ts +272 -0
- package/packages/src/teammates/runner.test.ts +235 -0
- package/packages/src/teammates/runner.ts +750 -0
- package/packages/src/teammates/schemas.ts +673 -0
- package/packages/src/types/index.ts +1 -0
- package/packages/src/core/context-compaction.ts +0 -578
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
- package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
- package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
- package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +0 -239
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Handler - Non-React Implementation
|
|
3
|
+
*
|
|
4
|
+
* Centralized keyboard input management extracted from v1 TUI patterns.
|
|
5
|
+
* This is a pure TypeScript class without React dependency.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Priority-based handler registration
|
|
9
|
+
* - Focus system for exclusive input
|
|
10
|
+
* - Block/unblock for loading states
|
|
11
|
+
* - Native key event support
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { InputManager, InputHandler, InputHandlerOptions, NativeKeyEvent } from "./types.js";
|
|
15
|
+
import type { InputEvent } from "../../../../../native/index.js";
|
|
16
|
+
import { InputPriority } from "./types.js";
|
|
17
|
+
|
|
18
|
+
// Re-export for convenience
|
|
19
|
+
export type { InputEvent } from "../../../../../native/index.js";
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// INPUT EVENT CONVERSION
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Convert native InputEvent to NativeKeyEvent format
|
|
27
|
+
*
|
|
28
|
+
* The native module returns InputEvent with:
|
|
29
|
+
* - eventType: "key" | "resize" | "none"
|
|
30
|
+
* - key?: string
|
|
31
|
+
* - modifiers?: string (comma-separated like "ctrl,shift")
|
|
32
|
+
*
|
|
33
|
+
* Input handlers expect NativeKeyEvent with:
|
|
34
|
+
* - code: string
|
|
35
|
+
* - ctrl: boolean
|
|
36
|
+
* - alt: boolean
|
|
37
|
+
* - shift: boolean
|
|
38
|
+
* - is_special: boolean
|
|
39
|
+
* - kind: "press" | "release" | "repeat"
|
|
40
|
+
*/
|
|
41
|
+
export function inputEventToNativeKeyEvent(event: InputEvent): NativeKeyEvent | null {
|
|
42
|
+
// Only convert key events
|
|
43
|
+
if (event.eventType !== "key" || !event.key) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse modifiers string
|
|
48
|
+
const modifiers = (event.modifiers ?? "").toLowerCase();
|
|
49
|
+
const hasCtrl = modifiers.includes("ctrl") || modifiers.includes("control");
|
|
50
|
+
const hasAlt = modifiers.includes("alt") || modifiers.includes("meta");
|
|
51
|
+
const hasShift = modifiers.includes("shift");
|
|
52
|
+
|
|
53
|
+
// Detect special keys
|
|
54
|
+
const specialKeys = new Set([
|
|
55
|
+
"return", "enter", "escape", "tab", "backspace", "delete",
|
|
56
|
+
"up", "down", "left", "right",
|
|
57
|
+
"pageup", "pagedown", "home", "end",
|
|
58
|
+
"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12"
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const keyLower = event.key.toLowerCase();
|
|
62
|
+
const isSpecial = specialKeys.has(keyLower) || hasCtrl || hasAlt;
|
|
63
|
+
|
|
64
|
+
// Normalize key names
|
|
65
|
+
let code = event.key;
|
|
66
|
+
if (keyLower === "return" || keyLower === "enter") code = "Enter";
|
|
67
|
+
else if (keyLower === "escape") code = "Escape";
|
|
68
|
+
else if (keyLower === " ") code = "Space";
|
|
69
|
+
else if (keyLower === "backspace") code = "Backspace";
|
|
70
|
+
else if (keyLower === "delete") code = "Delete";
|
|
71
|
+
else if (keyLower === "tab") code = "Tab";
|
|
72
|
+
else if (keyLower === "up") code = "Up";
|
|
73
|
+
else if (keyLower === "down") code = "Down";
|
|
74
|
+
else if (keyLower === "left") code = "Left";
|
|
75
|
+
else if (keyLower === "right") code = "Right";
|
|
76
|
+
else if (keyLower === "home") code = "Home";
|
|
77
|
+
else if (keyLower === "end") code = "End";
|
|
78
|
+
else if (keyLower === "pageup") code = "PageUp";
|
|
79
|
+
else if (keyLower === "pagedown") code = "PageDown";
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
code,
|
|
83
|
+
is_special: isSpecial,
|
|
84
|
+
ctrl: hasCtrl,
|
|
85
|
+
alt: hasAlt,
|
|
86
|
+
shift: hasShift,
|
|
87
|
+
kind: "press", // Default to press since native doesn't provide this
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================
|
|
92
|
+
// KEY EVENT DETECTION UTILITIES
|
|
93
|
+
// ============================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Static utilities for detecting key events
|
|
97
|
+
* Pattern from v1 TUI's KeyEvents class
|
|
98
|
+
*
|
|
99
|
+
* Note: Native module's NativeKeyEvent has * - code: string (key code/char)
|
|
100
|
+
* - is_special: boolean
|
|
101
|
+
* - ctrl: boolean
|
|
102
|
+
* - kind: "press" | "release" | "repeat"
|
|
103
|
+
*/
|
|
104
|
+
export const KeyEvents = {
|
|
105
|
+
/** Check if event is Enter key */
|
|
106
|
+
isEnter(event: NativeKeyEvent): boolean {
|
|
107
|
+
return event.code === "return" || event.code === "Enter";
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/** Check if event is Escape key */
|
|
111
|
+
isEscape(event: NativeKeyEvent): boolean {
|
|
112
|
+
return event.code === "escape" || event.code === "Escape";
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/** Check if event is Tab key */
|
|
116
|
+
isTab(event: NativeKeyEvent): boolean {
|
|
117
|
+
return event.code === "tab" || event.code === "Tab";
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/** Check if event is Backspace key */
|
|
121
|
+
isBackspace(event: NativeKeyEvent): boolean {
|
|
122
|
+
return event.code === "backspace" || event.code === "Backspace" || event.code === "DEL";
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/** Check if event is Delete key */
|
|
126
|
+
isDelete(event: NativeKeyEvent): boolean {
|
|
127
|
+
return event.code === "delete" || event.code === "Delete" || event.code === "DEL";
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/** Check if event is Up arrow */
|
|
131
|
+
isUp(event: NativeKeyEvent): boolean {
|
|
132
|
+
return event.code === "up" || event.code === "Up";
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/** Check if event is Down arrow */
|
|
136
|
+
isDown(event: NativeKeyEvent): boolean {
|
|
137
|
+
return event.code === "down" || event.code === "Down";
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
/** Check if event is Left arrow */
|
|
141
|
+
isLeft(event: NativeKeyEvent): boolean {
|
|
142
|
+
return event.code === "left" || event.code === "Left";
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/** Check if event is Right arrow */
|
|
146
|
+
isRight(event: NativeKeyEvent): boolean {
|
|
147
|
+
return event.code === "right" || event.code === "Right";
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/** Check if event is Page Up */
|
|
151
|
+
isPageUp(event: NativeKeyEvent): boolean {
|
|
152
|
+
return event.code === "pageup" || event.code === "PageUp";
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
/** Check if event is Page Down */
|
|
156
|
+
isPageDown(event: NativeKeyEvent): boolean {
|
|
157
|
+
return event.code === "pagedown" || event.code === "PageDown";
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/** Check if event is Home key */
|
|
161
|
+
isHome(event: NativeKeyEvent): boolean {
|
|
162
|
+
return event.code === "home" || event.code === "Home";
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/** Check if event is End key */
|
|
166
|
+
isEnd(event: NativeKeyEvent): boolean {
|
|
167
|
+
return event.code === "end" || event.code === "End";
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/** Check if event is Ctrl+C */
|
|
171
|
+
isCtrlC(event: NativeKeyEvent): boolean {
|
|
172
|
+
return event.ctrl === true && (event.code === "c" || event.code === "C");
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
/** Check if event is Ctrl+D */
|
|
176
|
+
isCtrlD(event: NativeKeyEvent): boolean {
|
|
177
|
+
return event.ctrl === true && (event.code === "d" || event.code === "D");
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
/** Check if event is Ctrl+A */
|
|
181
|
+
isCtrlA(event: NativeKeyEvent): boolean {
|
|
182
|
+
return event.ctrl === true && (event.code === "a" || event.code === "A");
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/** Check if event is Ctrl+E */
|
|
186
|
+
isCtrlE(event: NativeKeyEvent): boolean {
|
|
187
|
+
return event.ctrl === true && (event.code === "e" || event.code === "E");
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/** Check if event is Shift+Up */
|
|
191
|
+
isShiftUp(event: NativeKeyEvent): boolean {
|
|
192
|
+
// Note: Native module doesn't have shift modifier in NativeKeyEvent
|
|
193
|
+
// For now, check if up arrow (without ctrl check since we can't detect shift)
|
|
194
|
+
return event.code === "up" || event.code === "Up";
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
/** Check if event is Shift+Down */
|
|
198
|
+
isShiftDown(event: NativeKeyEvent): boolean {
|
|
199
|
+
// Note: Similar to Shift+Up
|
|
200
|
+
return event.code === "down" || event.code === "Down";
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
/** Check if event is Space key */
|
|
204
|
+
isSpace(event: NativeKeyEvent): boolean {
|
|
205
|
+
return event.code === " " || event.code === "Space";
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
/** Check if event is a printable character */
|
|
209
|
+
isPrintable(event: NativeKeyEvent): boolean {
|
|
210
|
+
if (event.is_special) return false;
|
|
211
|
+
const code = event.code;
|
|
212
|
+
// Space is printable (normalized to "Space" but should work in text input)
|
|
213
|
+
if (code === " " || code === "Space") return true;
|
|
214
|
+
// Single character that is not a control character
|
|
215
|
+
return code.length === 1 && !event.ctrl;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
/** Get the character from the event */
|
|
219
|
+
getChar(event: NativeKeyEvent): string {
|
|
220
|
+
// Handle normalized space
|
|
221
|
+
if (event.code === "Space") return " ";
|
|
222
|
+
return event.code;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// ============================================
|
|
227
|
+
// INPUT MANAGER CLASS
|
|
228
|
+
// ============================================
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Non-React input manager implementation
|
|
232
|
+
*
|
|
233
|
+
* Usage:
|
|
234
|
+
* ```ts
|
|
235
|
+
* const input = new InputManagerImpl();
|
|
236
|
+
*
|
|
237
|
+
* // Register a handler
|
|
238
|
+
* const unregister = input.register({
|
|
239
|
+
* id: "main-input",
|
|
240
|
+
* priority: InputPriority.INPUT,
|
|
241
|
+
* handler: (event) => {
|
|
242
|
+
* if (KeyEvents.isEnter(event)) {
|
|
243
|
+
* console.log("Enter pressed");
|
|
244
|
+
* return true; // consume
|
|
245
|
+
* }
|
|
246
|
+
* return false; // pass through
|
|
247
|
+
* },
|
|
248
|
+
* });
|
|
249
|
+
*
|
|
250
|
+
* // Dispatch events
|
|
251
|
+
* input.dispatch(nativeKeyEvent);
|
|
252
|
+
*
|
|
253
|
+
* // Cleanup
|
|
254
|
+
* unregister();
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
export class InputManagerImpl implements InputManager {
|
|
258
|
+
private _handlers: Map<string, InputHandlerOptions> = new Map();
|
|
259
|
+
private _focusedId: string | null = null;
|
|
260
|
+
private _isBlocked = false;
|
|
261
|
+
private _listeners: Set<() => void> = new Set();
|
|
262
|
+
|
|
263
|
+
/** Get currently focused handler ID */
|
|
264
|
+
get focusedId(): string | null {
|
|
265
|
+
return this._focusedId;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Whether input is currently blocked */
|
|
269
|
+
get isBlocked(): boolean {
|
|
270
|
+
return this._isBlocked;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Register an input handler
|
|
275
|
+
* Returns unregister function
|
|
276
|
+
*/
|
|
277
|
+
register(options: InputHandlerOptions): () => void {
|
|
278
|
+
const { id } = options;
|
|
279
|
+
this._handlers.set(id, options);
|
|
280
|
+
|
|
281
|
+
// If no focus set, focus this handler
|
|
282
|
+
if (!this._focusedId) {
|
|
283
|
+
this._focusedId = id;
|
|
284
|
+
this._notify();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Return unregister function
|
|
288
|
+
return () => {
|
|
289
|
+
this._handlers.delete(id);
|
|
290
|
+
if (this._focusedId === id) {
|
|
291
|
+
// Focus next available handler (highest priority)
|
|
292
|
+
const remaining = Array.from(this._handlers.values())
|
|
293
|
+
.filter((h) => h.isActive !== false)
|
|
294
|
+
.sort((a, b) => (b.priority ?? 1) - (a.priority ?? 1));
|
|
295
|
+
this._focusedId = remaining[0]?.id ?? null;
|
|
296
|
+
this._notify();
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Set focus to a specific handler
|
|
303
|
+
*/
|
|
304
|
+
focus(handlerId: string): void {
|
|
305
|
+
if (this._handlers.has(handlerId)) {
|
|
306
|
+
this._focusedId = handlerId;
|
|
307
|
+
this._notify();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Dispatch a key event to handlers
|
|
313
|
+
* Returns true if consumed, false if not
|
|
314
|
+
*/
|
|
315
|
+
dispatch(event: NativeKeyEvent): boolean {
|
|
316
|
+
if (this._isBlocked) return false;
|
|
317
|
+
|
|
318
|
+
// Get all active handlers sorted by priority (highest first)
|
|
319
|
+
const activeHandlers = Array.from(this._handlers.values())
|
|
320
|
+
.filter((h) => h.isActive !== false)
|
|
321
|
+
.sort((a, b) => (b.priority ?? 1) - (a.priority ?? 1));
|
|
322
|
+
|
|
323
|
+
// First, try the focused handler
|
|
324
|
+
if (this._focusedId) {
|
|
325
|
+
const focused = this._handlers.get(this._focusedId);
|
|
326
|
+
if (focused?.isActive !== false && focused?.handler) {
|
|
327
|
+
const consumed = focused.handler(event);
|
|
328
|
+
if (consumed) return true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Then try other handlers by priority
|
|
333
|
+
for (const h of activeHandlers) {
|
|
334
|
+
if (h.id === this._focusedId) continue; // Already tried
|
|
335
|
+
const consumed = h.handler(event);
|
|
336
|
+
if (consumed) return true;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Block/unblock input (for loading states)
|
|
344
|
+
*/
|
|
345
|
+
setBlocked(blocked: boolean): void {
|
|
346
|
+
this._isBlocked = blocked;
|
|
347
|
+
this._notify();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Subscribe to focus/blocked state changes
|
|
352
|
+
*/
|
|
353
|
+
subscribe(listener: () => void): () => void {
|
|
354
|
+
this._listeners.add(listener);
|
|
355
|
+
return () => {
|
|
356
|
+
this._listeners.delete(listener);
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Notify all listeners of a change
|
|
362
|
+
*/
|
|
363
|
+
private _notify(): void {
|
|
364
|
+
for (const listener of this._listeners) {
|
|
365
|
+
try {
|
|
366
|
+
listener();
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.error("InputManager listener error:", err);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ============================================
|
|
375
|
+
// SINGLETON (optional convenience)
|
|
376
|
+
// ============================================
|
|
377
|
+
|
|
378
|
+
let globalInputManager: InputManagerImpl | null = null;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get or create the global input manager
|
|
382
|
+
*/
|
|
383
|
+
export function getInputManager(): InputManagerImpl {
|
|
384
|
+
if (!globalInputManager) {
|
|
385
|
+
globalInputManager = new InputManagerImpl();
|
|
386
|
+
}
|
|
387
|
+
return globalInputManager;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Reset the global input manager (for testing)
|
|
392
|
+
*/
|
|
393
|
+
export function resetInputManager(): void {
|
|
394
|
+
globalInputManager = null;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// ============================================
|
|
398
|
+
// EXPORTS
|
|
399
|
+
// ============================================
|
|
400
|
+
|
|
401
|
+
export { InputPriority };
|
|
402
|
+
export type { InputManager, InputHandler, InputHandlerOptions, NativeKeyEvent } from "./types.js";
|