@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.
Files changed (39) hide show
  1. package/README.md +126 -13
  2. package/index.ts +11 -5
  3. package/package.json +2 -2
  4. package/src/api/history.ts +451 -0
  5. package/src/api/mount.ts +66 -31
  6. package/src/engine/arrays/core.ts +13 -21
  7. package/src/engine/arrays/dimensions.ts +22 -32
  8. package/src/engine/arrays/index.ts +88 -86
  9. package/src/engine/arrays/interaction.ts +34 -48
  10. package/src/engine/arrays/layout.ts +67 -92
  11. package/src/engine/arrays/spacing.ts +37 -52
  12. package/src/engine/arrays/text.ts +23 -31
  13. package/src/engine/arrays/visual.ts +56 -75
  14. package/src/engine/inheritance.ts +18 -18
  15. package/src/engine/registry.ts +15 -0
  16. package/src/pipeline/frameBuffer.ts +26 -26
  17. package/src/pipeline/layout/index.ts +2 -2
  18. package/src/pipeline/layout/titan-engine.ts +112 -84
  19. package/src/primitives/animation.ts +194 -0
  20. package/src/primitives/box.ts +74 -86
  21. package/src/primitives/each.ts +87 -0
  22. package/src/primitives/index.ts +7 -0
  23. package/src/primitives/scope.ts +215 -0
  24. package/src/primitives/show.ts +77 -0
  25. package/src/primitives/text.ts +63 -59
  26. package/src/primitives/types.ts +1 -1
  27. package/src/primitives/when.ts +102 -0
  28. package/src/renderer/append-region.ts +159 -0
  29. package/src/renderer/index.ts +4 -2
  30. package/src/renderer/output.ts +11 -34
  31. package/src/state/focus.ts +16 -5
  32. package/src/state/global-keys.ts +184 -0
  33. package/src/state/index.ts +44 -8
  34. package/src/state/input.ts +534 -0
  35. package/src/state/keyboard.ts +98 -674
  36. package/src/state/mouse.ts +163 -340
  37. package/src/state/scroll.ts +7 -9
  38. package/src/types/index.ts +23 -2
  39. 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
+ }
@@ -11,9 +11,45 @@
11
11
  * - cursor: Cursor visibility, shape, position
12
12
  */
13
13
 
14
- // Keyboard is primary for focus navigation
15
- export * from './keyboard'
16
- export * from './mouse'
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
- // Convenient namespace exports
40
- export { keyboard } from './keyboard'
41
- export { mouse } from './mouse'
42
- export { scroll } from './scroll'
43
- export { cursor } from './cursor'
75
+ // Global keys - all shortcuts wired together
76
+ export { globalKeys } from './global-keys'
77
+
78
+ // Input - stdin ownership
79
+ export { input } from './input'