@rlabs-inc/tui 0.1.0 → 0.2.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 +126 -13
- package/index.ts +11 -5
- package/package.json +2 -2
- package/src/api/history.ts +451 -0
- package/src/api/mount.ts +66 -31
- package/src/engine/arrays/core.ts +13 -21
- package/src/engine/arrays/dimensions.ts +22 -32
- package/src/engine/arrays/index.ts +88 -86
- package/src/engine/arrays/interaction.ts +34 -48
- package/src/engine/arrays/layout.ts +67 -92
- package/src/engine/arrays/spacing.ts +37 -52
- package/src/engine/arrays/text.ts +23 -31
- package/src/engine/arrays/visual.ts +56 -75
- package/src/engine/inheritance.ts +18 -18
- package/src/engine/registry.ts +15 -0
- package/src/pipeline/frameBuffer.ts +26 -26
- package/src/pipeline/layout/index.ts +2 -2
- package/src/pipeline/layout/titan-engine.ts +112 -84
- package/src/primitives/animation.ts +194 -0
- package/src/primitives/box.ts +74 -86
- package/src/primitives/each.ts +87 -0
- package/src/primitives/index.ts +7 -0
- package/src/primitives/scope.ts +215 -0
- package/src/primitives/show.ts +77 -0
- package/src/primitives/text.ts +63 -59
- package/src/primitives/types.ts +1 -1
- package/src/primitives/when.ts +102 -0
- package/src/renderer/append-region.ts +159 -0
- package/src/renderer/index.ts +4 -2
- package/src/renderer/output.ts +11 -34
- package/src/state/focus.ts +16 -5
- package/src/state/global-keys.ts +184 -0
- package/src/state/index.ts +44 -8
- package/src/state/input.ts +534 -0
- package/src/state/keyboard.ts +98 -674
- package/src/state/mouse.ts +163 -340
- package/src/state/scroll.ts +7 -9
- package/src/types/index.ts +23 -2
- package/src/renderer/input.ts +0 -518
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Framework - Global Keys Module
|
|
3
|
+
*
|
|
4
|
+
* Central location for ALL global keyboard and mouse shortcuts.
|
|
5
|
+
* This is the ONLY place where global input behaviors are defined.
|
|
6
|
+
*
|
|
7
|
+
* Wires together: input, keyboard, mouse, focus, scroll
|
|
8
|
+
*
|
|
9
|
+
* Global behaviors:
|
|
10
|
+
* - Ctrl+C: Exit application
|
|
11
|
+
* - Tab/Shift+Tab: Focus navigation
|
|
12
|
+
* - Arrow keys: Scroll focused scrollable
|
|
13
|
+
* - Page Up/Down: Large scroll jumps
|
|
14
|
+
* - Home/End: Scroll to start/end
|
|
15
|
+
* - Mouse wheel: Scroll hovered or focused
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { input } from './input'
|
|
19
|
+
import { keyboard, dispatch as dispatchKeyboard, dispatchFocused } from './keyboard'
|
|
20
|
+
import { mouse, dispatch as dispatchMouse } from './mouse'
|
|
21
|
+
import { focusNext, focusPrevious, focusedIndex } from './focus'
|
|
22
|
+
import { handleArrowScroll, handlePageScroll, handleHomeEnd, handleWheelScroll } from './scroll'
|
|
23
|
+
import type { KeyboardEvent } from './keyboard'
|
|
24
|
+
import type { MouseEvent } from './mouse'
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// STATE
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
let initialized = false
|
|
31
|
+
let cleanupCallback: (() => void) | null = null
|
|
32
|
+
let exitOnCtrlC = true
|
|
33
|
+
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// EVENT HANDLERS
|
|
36
|
+
// =============================================================================
|
|
37
|
+
|
|
38
|
+
function handleKeyboardEvent(event: KeyboardEvent): void {
|
|
39
|
+
try {
|
|
40
|
+
// Only process press events for most shortcuts
|
|
41
|
+
const isPress = event.state === 'press'
|
|
42
|
+
|
|
43
|
+
// Ctrl+C - exit (always, even on release for safety)
|
|
44
|
+
if (exitOnCtrlC && event.key === 'c' && event.modifiers.ctrl) {
|
|
45
|
+
cleanup()
|
|
46
|
+
process.exit(0)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Skip non-press events for other shortcuts
|
|
50
|
+
if (!isPress) {
|
|
51
|
+
// Still dispatch to handlers (they may want release events)
|
|
52
|
+
dispatchFocused(focusedIndex.value, event)
|
|
53
|
+
dispatchKeyboard(event)
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Tab - focus navigation
|
|
58
|
+
if (event.key === 'Tab' && !event.modifiers.ctrl && !event.modifiers.alt) {
|
|
59
|
+
if (event.modifiers.shift) {
|
|
60
|
+
focusPrevious()
|
|
61
|
+
} else {
|
|
62
|
+
focusNext()
|
|
63
|
+
}
|
|
64
|
+
return // Tab is always consumed
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Dispatch to focused component handlers first
|
|
68
|
+
if (dispatchFocused(focusedIndex.value, event)) {
|
|
69
|
+
return // Consumed by focused handler
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Dispatch to user handlers (keyboard.onKey)
|
|
73
|
+
// If user returns true, they handled it - skip framework defaults
|
|
74
|
+
if (dispatchKeyboard(event)) {
|
|
75
|
+
return // User handled it
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// =========================================================================
|
|
79
|
+
// FRAMEWORK DEFAULTS - only run if user didn't handle
|
|
80
|
+
// =========================================================================
|
|
81
|
+
|
|
82
|
+
// Arrow keys - scroll focused scrollable
|
|
83
|
+
if (event.key === 'ArrowUp' && handleArrowScroll('up')) return
|
|
84
|
+
if (event.key === 'ArrowDown' && handleArrowScroll('down')) return
|
|
85
|
+
if (event.key === 'ArrowLeft' && handleArrowScroll('left')) return
|
|
86
|
+
if (event.key === 'ArrowRight' && handleArrowScroll('right')) return
|
|
87
|
+
|
|
88
|
+
// Page Up/Down
|
|
89
|
+
if (event.key === 'PageUp' && handlePageScroll('up')) return
|
|
90
|
+
if (event.key === 'PageDown' && handlePageScroll('down')) return
|
|
91
|
+
|
|
92
|
+
// Home/End (without Ctrl - Ctrl+Home/End could be used for something else)
|
|
93
|
+
if (event.key === 'Home' && !event.modifiers.ctrl && handleHomeEnd('home')) return
|
|
94
|
+
if (event.key === 'End' && !event.modifiers.ctrl && handleHomeEnd('end')) return
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error('[TUI] Keyboard handler error:', err)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function handleMouseEvent(event: MouseEvent): void {
|
|
101
|
+
// Mouse wheel - scroll hovered or focused
|
|
102
|
+
if (event.action === 'scroll' && event.scroll) {
|
|
103
|
+
handleWheelScroll(event.x, event.y, event.scroll.direction)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Dispatch to mouse module for hover/click handling
|
|
107
|
+
dispatchMouse(event)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// =============================================================================
|
|
111
|
+
// PUBLIC API
|
|
112
|
+
// =============================================================================
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Initialize the global input system.
|
|
116
|
+
* Sets up stdin handling and wires all global shortcuts.
|
|
117
|
+
*/
|
|
118
|
+
export function initialize(options?: {
|
|
119
|
+
onCleanup?: () => void
|
|
120
|
+
exitOnCtrlC?: boolean
|
|
121
|
+
enableMouse?: boolean
|
|
122
|
+
}): void {
|
|
123
|
+
if (initialized) return
|
|
124
|
+
|
|
125
|
+
initialized = true
|
|
126
|
+
cleanupCallback = options?.onCleanup ?? null
|
|
127
|
+
exitOnCtrlC = options?.exitOnCtrlC ?? true
|
|
128
|
+
|
|
129
|
+
// Initialize input system with our handlers
|
|
130
|
+
input.initialize(handleKeyboardEvent, handleMouseEvent)
|
|
131
|
+
|
|
132
|
+
// Enable mouse tracking if requested
|
|
133
|
+
if (options?.enableMouse !== false) {
|
|
134
|
+
mouse.enableTracking()
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Clean up the global input system.
|
|
140
|
+
*/
|
|
141
|
+
export function cleanup(): void {
|
|
142
|
+
if (!initialized) return
|
|
143
|
+
|
|
144
|
+
initialized = false
|
|
145
|
+
|
|
146
|
+
// Clean up all modules
|
|
147
|
+
mouse.cleanup()
|
|
148
|
+
keyboard.cleanup()
|
|
149
|
+
input.cleanup()
|
|
150
|
+
|
|
151
|
+
// Show cursor
|
|
152
|
+
process.stdout.write('\x1b[?25h')
|
|
153
|
+
|
|
154
|
+
// Call user cleanup
|
|
155
|
+
if (cleanupCallback) {
|
|
156
|
+
cleanupCallback()
|
|
157
|
+
cleanupCallback = null
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Set whether Ctrl+C exits the application.
|
|
163
|
+
*/
|
|
164
|
+
export function setExitOnCtrlC(enabled: boolean): void {
|
|
165
|
+
exitOnCtrlC = enabled
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if global keys system is initialized.
|
|
170
|
+
*/
|
|
171
|
+
export function isInitialized(): boolean {
|
|
172
|
+
return initialized
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// =============================================================================
|
|
176
|
+
// EXPORT
|
|
177
|
+
// =============================================================================
|
|
178
|
+
|
|
179
|
+
export const globalKeys = {
|
|
180
|
+
initialize,
|
|
181
|
+
cleanup,
|
|
182
|
+
setExitOnCtrlC,
|
|
183
|
+
isInitialized,
|
|
184
|
+
}
|
package/src/state/index.ts
CHANGED
|
@@ -11,9 +11,45 @@
|
|
|
11
11
|
* - cursor: Cursor visibility, shape, position
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
// Keyboard
|
|
15
|
-
export
|
|
16
|
-
|
|
14
|
+
// Keyboard - explicit exports to avoid conflicts
|
|
15
|
+
export {
|
|
16
|
+
keyboard,
|
|
17
|
+
lastKey,
|
|
18
|
+
lastEvent,
|
|
19
|
+
on,
|
|
20
|
+
onKey,
|
|
21
|
+
onFocused,
|
|
22
|
+
cleanupIndex,
|
|
23
|
+
dispatch as dispatchKeyboard,
|
|
24
|
+
dispatchFocused,
|
|
25
|
+
cleanup as cleanupKeyboard,
|
|
26
|
+
} from './keyboard'
|
|
27
|
+
export type { Modifiers, KeyState, KeyboardEvent, KeyHandler } from './keyboard'
|
|
28
|
+
|
|
29
|
+
// Mouse - explicit exports to avoid conflicts
|
|
30
|
+
export {
|
|
31
|
+
mouse,
|
|
32
|
+
hitGrid,
|
|
33
|
+
lastMouseEvent,
|
|
34
|
+
mouseX,
|
|
35
|
+
mouseY,
|
|
36
|
+
isMouseDown,
|
|
37
|
+
HitGrid,
|
|
38
|
+
MouseButton,
|
|
39
|
+
onMouseDown,
|
|
40
|
+
onMouseUp,
|
|
41
|
+
onClick,
|
|
42
|
+
onScroll,
|
|
43
|
+
onComponent,
|
|
44
|
+
resize,
|
|
45
|
+
clearHitGrid,
|
|
46
|
+
enableTracking,
|
|
47
|
+
disableTracking,
|
|
48
|
+
isTrackingEnabled,
|
|
49
|
+
dispatch as dispatchMouse,
|
|
50
|
+
cleanup as cleanupMouse,
|
|
51
|
+
} from './mouse'
|
|
52
|
+
export type { MouseAction, ScrollInfo, MouseEvent, MouseHandlers, MouseHandler } from './mouse'
|
|
17
53
|
// Focus exports - exclude duplicates that are in keyboard
|
|
18
54
|
export {
|
|
19
55
|
focusedIndex,
|
|
@@ -36,8 +72,8 @@ export {
|
|
|
36
72
|
export * from './scroll'
|
|
37
73
|
export * from './cursor'
|
|
38
74
|
|
|
39
|
-
//
|
|
40
|
-
export {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export {
|
|
75
|
+
// Global keys - all shortcuts wired together
|
|
76
|
+
export { globalKeys } from './global-keys'
|
|
77
|
+
|
|
78
|
+
// Input - stdin ownership
|
|
79
|
+
export { input } from './input'
|