@mdxui/terminal 2.0.0

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 (191) hide show
  1. package/README.md +571 -0
  2. package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
  3. package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
  4. package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
  5. package/dist/chunk-3EFDH7PK.js +5235 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-3X5IR6WE.js +884 -0
  8. package/dist/chunk-4FV5ZDCE.js +5236 -0
  9. package/dist/chunk-4OVMSF2J.js +243 -0
  10. package/dist/chunk-63FEETIS.js +4048 -0
  11. package/dist/chunk-B43KP7XJ.js +884 -0
  12. package/dist/chunk-BMTJXWUV.js +655 -0
  13. package/dist/chunk-C3SVH4N7.js +882 -0
  14. package/dist/chunk-EVWR7Y47.js +874 -0
  15. package/dist/chunk-F6A5VWUC.js +1285 -0
  16. package/dist/chunk-FD7KW7GE.js +882 -0
  17. package/dist/chunk-GBQ6UD6I.js +655 -0
  18. package/dist/chunk-GMDD3M6U.js +5227 -0
  19. package/dist/chunk-JBHRXOXM.js +1058 -0
  20. package/dist/chunk-JFOO3EYO.js +1182 -0
  21. package/dist/chunk-JQ5H3WXL.js +1291 -0
  22. package/dist/chunk-JQD5NASE.js +234 -0
  23. package/dist/chunk-KRHJP5R7.js +592 -0
  24. package/dist/chunk-KWF6WVJE.js +962 -0
  25. package/dist/chunk-LHYQVN3H.js +1038 -0
  26. package/dist/chunk-M3TLQLGC.js +1032 -0
  27. package/dist/chunk-MVW4Q5OP.js +240 -0
  28. package/dist/chunk-NXCZSWLU.js +1294 -0
  29. package/dist/chunk-O25TNRO6.js +607 -0
  30. package/dist/chunk-PNECDA2I.js +884 -0
  31. package/dist/chunk-QIHWRLJR.js +962 -0
  32. package/dist/chunk-QW5YMQ7K.js +882 -0
  33. package/dist/chunk-R5U7XKVJ.js +16 -0
  34. package/dist/chunk-RP2MVQLR.js +962 -0
  35. package/dist/chunk-TP6RXGXA.js +1087 -0
  36. package/dist/chunk-TQQSTITZ.js +655 -0
  37. package/dist/chunk-X24GWXQV.js +1281 -0
  38. package/dist/components/index.d.ts +802 -0
  39. package/dist/components/index.js +149 -0
  40. package/dist/data/index.d.ts +2554 -0
  41. package/dist/data/index.js +51 -0
  42. package/dist/forms/index.d.ts +1596 -0
  43. package/dist/forms/index.js +464 -0
  44. package/dist/index-CQRFZntR.d.ts +867 -0
  45. package/dist/index.d.ts +579 -0
  46. package/dist/index.js +786 -0
  47. package/dist/interactive-D0JkWosD.d.ts +217 -0
  48. package/dist/keyboard/index.d.ts +2 -0
  49. package/dist/keyboard/index.js +43 -0
  50. package/dist/renderers/index.d.ts +546 -0
  51. package/dist/renderers/index.js +2157 -0
  52. package/dist/storybook/index.d.ts +396 -0
  53. package/dist/storybook/index.js +641 -0
  54. package/dist/theme/index.d.ts +1339 -0
  55. package/dist/theme/index.js +123 -0
  56. package/dist/types-Bxu5PAgA.d.ts +710 -0
  57. package/dist/types-CIlop5Ji.d.ts +701 -0
  58. package/dist/types-Ca8p_p5X.d.ts +710 -0
  59. package/package.json +90 -0
  60. package/src/__tests__/components/data/card.test.ts +458 -0
  61. package/src/__tests__/components/data/list.test.ts +473 -0
  62. package/src/__tests__/components/data/metrics.test.ts +541 -0
  63. package/src/__tests__/components/data/table.test.ts +448 -0
  64. package/src/__tests__/components/input/field.test.ts +555 -0
  65. package/src/__tests__/components/input/form.test.ts +870 -0
  66. package/src/__tests__/components/input/search.test.ts +1238 -0
  67. package/src/__tests__/components/input/select.test.ts +658 -0
  68. package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
  69. package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
  70. package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
  71. package/src/__tests__/components/navigation/tabs.test.ts +995 -0
  72. package/src/__tests__/components.test.tsx +1197 -0
  73. package/src/__tests__/core/compiler.test.ts +986 -0
  74. package/src/__tests__/core/parser.test.ts +785 -0
  75. package/src/__tests__/core/tier-switcher.test.ts +1103 -0
  76. package/src/__tests__/core/types.test.ts +1398 -0
  77. package/src/__tests__/data/collections.test.ts +1337 -0
  78. package/src/__tests__/data/db.test.ts +1265 -0
  79. package/src/__tests__/data/reactive.test.ts +1010 -0
  80. package/src/__tests__/data/sync.test.ts +1614 -0
  81. package/src/__tests__/errors.test.ts +660 -0
  82. package/src/__tests__/forms/integration.test.ts +444 -0
  83. package/src/__tests__/integration.test.ts +905 -0
  84. package/src/__tests__/keyboard.test.ts +1791 -0
  85. package/src/__tests__/renderer.test.ts +489 -0
  86. package/src/__tests__/renderers/ansi-css.test.ts +948 -0
  87. package/src/__tests__/renderers/ansi.test.ts +1366 -0
  88. package/src/__tests__/renderers/ascii.test.ts +1360 -0
  89. package/src/__tests__/renderers/interactive.test.ts +2353 -0
  90. package/src/__tests__/renderers/markdown.test.ts +1483 -0
  91. package/src/__tests__/renderers/text.test.ts +1369 -0
  92. package/src/__tests__/renderers/unicode.test.ts +1307 -0
  93. package/src/__tests__/theme.test.ts +639 -0
  94. package/src/__tests__/utils/assertions.ts +685 -0
  95. package/src/__tests__/utils/index.ts +115 -0
  96. package/src/__tests__/utils/test-renderer.ts +381 -0
  97. package/src/__tests__/utils/utils.test.ts +560 -0
  98. package/src/components/containers/card.ts +56 -0
  99. package/src/components/containers/dialog.ts +53 -0
  100. package/src/components/containers/index.ts +9 -0
  101. package/src/components/containers/panel.ts +59 -0
  102. package/src/components/feedback/badge.ts +40 -0
  103. package/src/components/feedback/index.ts +8 -0
  104. package/src/components/feedback/spinner.ts +23 -0
  105. package/src/components/helpers.ts +81 -0
  106. package/src/components/index.ts +153 -0
  107. package/src/components/layout/breadcrumb.ts +31 -0
  108. package/src/components/layout/index.ts +10 -0
  109. package/src/components/layout/list.ts +29 -0
  110. package/src/components/layout/sidebar.ts +79 -0
  111. package/src/components/layout/table.ts +62 -0
  112. package/src/components/primitives/box.ts +95 -0
  113. package/src/components/primitives/button.ts +54 -0
  114. package/src/components/primitives/index.ts +11 -0
  115. package/src/components/primitives/input.ts +88 -0
  116. package/src/components/primitives/select.ts +97 -0
  117. package/src/components/primitives/text.ts +60 -0
  118. package/src/components/render.ts +155 -0
  119. package/src/components/templates/app.ts +43 -0
  120. package/src/components/templates/index.ts +8 -0
  121. package/src/components/templates/site.ts +54 -0
  122. package/src/components/types.ts +777 -0
  123. package/src/core/compiler.ts +718 -0
  124. package/src/core/parser.ts +127 -0
  125. package/src/core/tier-switcher.ts +607 -0
  126. package/src/core/types.ts +672 -0
  127. package/src/data/collection.ts +316 -0
  128. package/src/data/collections.ts +50 -0
  129. package/src/data/context.tsx +174 -0
  130. package/src/data/db.ts +127 -0
  131. package/src/data/hooks.ts +532 -0
  132. package/src/data/index.ts +138 -0
  133. package/src/data/reactive.ts +1225 -0
  134. package/src/data/saas-collections.ts +375 -0
  135. package/src/data/sync.ts +1213 -0
  136. package/src/data/types.ts +660 -0
  137. package/src/forms/converters.ts +512 -0
  138. package/src/forms/index.ts +133 -0
  139. package/src/forms/schemas.ts +403 -0
  140. package/src/forms/types.ts +476 -0
  141. package/src/index.ts +542 -0
  142. package/src/keyboard/focus.ts +748 -0
  143. package/src/keyboard/index.ts +96 -0
  144. package/src/keyboard/integration.ts +371 -0
  145. package/src/keyboard/manager.ts +377 -0
  146. package/src/keyboard/presets.ts +90 -0
  147. package/src/renderers/ansi-css.ts +576 -0
  148. package/src/renderers/ansi.ts +802 -0
  149. package/src/renderers/ascii.ts +680 -0
  150. package/src/renderers/breadcrumb.ts +480 -0
  151. package/src/renderers/command-palette.ts +802 -0
  152. package/src/renderers/components/field.ts +210 -0
  153. package/src/renderers/components/form.ts +327 -0
  154. package/src/renderers/components/index.ts +21 -0
  155. package/src/renderers/components/search.ts +449 -0
  156. package/src/renderers/components/select.ts +222 -0
  157. package/src/renderers/index.ts +101 -0
  158. package/src/renderers/interactive/component-handlers.ts +622 -0
  159. package/src/renderers/interactive/cursor-manager.ts +147 -0
  160. package/src/renderers/interactive/focus-manager.ts +279 -0
  161. package/src/renderers/interactive/index.ts +661 -0
  162. package/src/renderers/interactive/input-handler.ts +164 -0
  163. package/src/renderers/interactive/keyboard-handler.ts +212 -0
  164. package/src/renderers/interactive/mouse-handler.ts +167 -0
  165. package/src/renderers/interactive/state-manager.ts +109 -0
  166. package/src/renderers/interactive/types.ts +338 -0
  167. package/src/renderers/interactive-string.ts +299 -0
  168. package/src/renderers/interactive.ts +59 -0
  169. package/src/renderers/markdown.ts +950 -0
  170. package/src/renderers/sidebar.ts +549 -0
  171. package/src/renderers/tabs.ts +682 -0
  172. package/src/renderers/text.ts +791 -0
  173. package/src/renderers/unicode.ts +917 -0
  174. package/src/renderers/utils.ts +942 -0
  175. package/src/router/adapters.ts +383 -0
  176. package/src/router/types.ts +140 -0
  177. package/src/router/utils.ts +452 -0
  178. package/src/schemas.ts +205 -0
  179. package/src/storybook/index.ts +91 -0
  180. package/src/storybook/interactive-decorator.tsx +659 -0
  181. package/src/storybook/keyboard-simulator.ts +501 -0
  182. package/src/theme/ansi-codes.ts +80 -0
  183. package/src/theme/box-drawing.ts +132 -0
  184. package/src/theme/color-convert.ts +254 -0
  185. package/src/theme/color-support.ts +321 -0
  186. package/src/theme/index.ts +134 -0
  187. package/src/theme/strip-ansi.ts +50 -0
  188. package/src/theme/tailwind-map.ts +469 -0
  189. package/src/theme/text-styles.ts +206 -0
  190. package/src/theme/theme-system.ts +568 -0
  191. package/src/types.ts +103 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @mdxui/terminal Keyboard Navigation
3
+ *
4
+ * Keyboard binding manager and React hooks for terminal keyboard navigation.
5
+ * Supports Vim-style bindings, modifier keys, and key sequences.
6
+ *
7
+ * This module provides:
8
+ * - Keyboard manager for handling key bindings and sequences
9
+ * - React hooks for keyboard input, focus management, and navigation
10
+ * - Preset bindings for Vim-style and arrow key navigation
11
+ * - Focus provider for managing focus across components
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { createKeyboardManager, VIM_BINDINGS, useFocus } from '@mdxui/terminal'
16
+ *
17
+ * // Create a keyboard manager with Vim bindings
18
+ * const keyboard = createKeyboardManager({
19
+ * bindings: VIM_BINDINGS,
20
+ * onAction: (action) => console.log('Action:', action)
21
+ * })
22
+ *
23
+ * // Handle key presses
24
+ * keyboard.handleKey('j') // triggers 'move-down'
25
+ * keyboard.handleKey('g')
26
+ * keyboard.handleKey('g') // triggers 'move-first' (sequence: gg)
27
+ * ```
28
+ *
29
+ * @packageDocumentation
30
+ */
31
+
32
+ // Manager exports
33
+ export {
34
+ // Types
35
+ type KeyModifiers,
36
+ type KeyboardAction,
37
+ type KeyBindings,
38
+ type ActionContext,
39
+ type ActionHandler,
40
+ type KeyboardManagerOptions,
41
+ type KeyboardManager,
42
+ // Constants
43
+ KEY,
44
+ // Functions
45
+ matchKey,
46
+ createKeyboardManager,
47
+ } from './manager'
48
+
49
+ // Preset exports
50
+ export {
51
+ VIM_BINDINGS,
52
+ ARROW_BINDINGS,
53
+ COMMON_BINDINGS,
54
+ EMACS_BINDINGS,
55
+ } from './presets'
56
+
57
+ // Focus exports
58
+ export {
59
+ // Types
60
+ type UseKeyboardOptions,
61
+ type UseKeyboardResult,
62
+ type UseFocusOptions,
63
+ type FocusableElement,
64
+ type FocusManagerState,
65
+ type UseNavigableListOptions,
66
+ type UseNavigableListResult,
67
+ type UseNavigableGridOptions,
68
+ type UseNavigableGridResult,
69
+ type FocusProviderProps,
70
+ // Context
71
+ FocusContext,
72
+ // Hooks
73
+ useKeyboard,
74
+ useFocus,
75
+ useFocusManager,
76
+ useNavigableList,
77
+ useNavigableGrid,
78
+ // Components
79
+ FocusProvider,
80
+ } from './focus'
81
+
82
+ // Integration exports
83
+ export {
84
+ // Types
85
+ type NormalizedKey,
86
+ type ReadlineKey,
87
+ type AttachKeyboardOptions,
88
+ type DetachKeyboard,
89
+ type OpenTUIKeyEvent,
90
+ // Functions
91
+ normalizeReadlineKey,
92
+ keyToBindingString,
93
+ attachKeyboardManager,
94
+ normalizeOpenTUIKey,
95
+ createOpenTUIKeyHandler,
96
+ } from './integration'
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Terminal Input Integration
3
+ *
4
+ * Integration helpers for connecting keyboard managers to terminal input sources.
5
+ * Supports Node.js readline and OpenTUI key events.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import type { KeyboardManager, ActionHandler } from './manager'
11
+
12
+ // ============================================================================
13
+ // Normalized Key Types
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Normalized key event structure for keyboard handling.
18
+ *
19
+ * This is the standardized format used internally by the keyboard manager.
20
+ * Input from various sources (readline, OpenTUI) is normalized to this format.
21
+ */
22
+ export interface NormalizedKey {
23
+ /** The key name (e.g., 'a', 'enter', 'up', 'escape') */
24
+ name: string
25
+ /** Whether Ctrl was held */
26
+ ctrl: boolean
27
+ /** Whether Alt/Option was held */
28
+ alt: boolean
29
+ /** Whether Shift was held */
30
+ shift: boolean
31
+ /** Whether Meta/Cmd was held */
32
+ meta: boolean
33
+ /** Raw character sequence if available */
34
+ sequence?: string
35
+ }
36
+
37
+ /**
38
+ * Readline key event structure from Node.js readline module.
39
+ * @see https://nodejs.org/api/readline.html#event-keypress
40
+ */
41
+ export interface ReadlineKey {
42
+ /** Key name */
43
+ name?: string
44
+ /** Whether Ctrl was held */
45
+ ctrl?: boolean
46
+ /** Whether Meta/Cmd was held */
47
+ meta?: boolean
48
+ /** Whether Shift was held */
49
+ shift?: boolean
50
+ /** Raw sequence */
51
+ sequence?: string
52
+ }
53
+
54
+ // ============================================================================
55
+ // Readline Integration
56
+ // ============================================================================
57
+
58
+ /**
59
+ * Normalizes a readline key event to our standard NormalizedKey format.
60
+ *
61
+ * @param str - The character string from the keypress event
62
+ * @param key - The readline key object
63
+ * @returns Normalized key event
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * import { emitKeypressEvents } from 'readline'
68
+ *
69
+ * emitKeypressEvents(process.stdin)
70
+ * process.stdin.on('keypress', (str, key) => {
71
+ * const normalized = normalizeReadlineKey(str, key)
72
+ * console.log(normalized) // { name: 'a', ctrl: false, ... }
73
+ * })
74
+ * ```
75
+ */
76
+ export function normalizeReadlineKey(str: string | undefined, key: ReadlineKey | undefined): NormalizedKey {
77
+ // Handle special characters by their sequence
78
+ if (!key && str) {
79
+ // Single character input
80
+ return {
81
+ name: str,
82
+ ctrl: false,
83
+ alt: false,
84
+ shift: str !== str.toLowerCase() && str === str.toUpperCase(),
85
+ meta: false,
86
+ sequence: str,
87
+ }
88
+ }
89
+
90
+ // Map readline key names to our standard names
91
+ const nameMap: Record<string, string> = {
92
+ return: 'enter',
93
+ escape: 'escape',
94
+ up: 'up',
95
+ down: 'down',
96
+ left: 'left',
97
+ right: 'right',
98
+ tab: 'tab',
99
+ backspace: 'backspace',
100
+ delete: 'delete',
101
+ space: 'space',
102
+ }
103
+
104
+ const keyName = key?.name ?? str ?? ''
105
+ const normalizedName = nameMap[keyName.toLowerCase()] ?? keyName.toLowerCase()
106
+
107
+ return {
108
+ name: normalizedName,
109
+ ctrl: key?.ctrl ?? false,
110
+ alt: false, // readline doesn't distinguish alt reliably
111
+ shift: key?.shift ?? false,
112
+ meta: key?.meta ?? false,
113
+ sequence: key?.sequence ?? str,
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Converts a NormalizedKey to a string format for binding lookup.
119
+ *
120
+ * Produces strings like:
121
+ * - 'a' (plain key)
122
+ * - 'ctrl+c' (with ctrl modifier)
123
+ * - 'shift+tab' (with shift modifier)
124
+ * - 'ctrl+shift+s' (combined modifiers)
125
+ *
126
+ * @param key - The normalized key event
127
+ * @returns String representation for binding lookup
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const key: NormalizedKey = { name: 'c', ctrl: true, alt: false, shift: false, meta: false }
132
+ * console.log(keyToBindingString(key)) // 'ctrl+c'
133
+ * ```
134
+ */
135
+ export function keyToBindingString(key: NormalizedKey): string {
136
+ const modifiers: string[] = []
137
+
138
+ if (key.ctrl) modifiers.push('ctrl')
139
+ if (key.alt) modifiers.push('alt')
140
+ if (key.shift) modifiers.push('shift')
141
+ if (key.meta) modifiers.push('meta')
142
+
143
+ if (modifiers.length > 0) {
144
+ return `${modifiers.join('+')}+${key.name}`
145
+ }
146
+
147
+ return key.name
148
+ }
149
+
150
+ /**
151
+ * Configuration for attaching a keyboard manager to terminal input.
152
+ */
153
+ export interface AttachKeyboardOptions {
154
+ /** Custom input stream (defaults to process.stdin) */
155
+ input?: NodeJS.ReadStream
156
+ /** Whether to exit on Ctrl+C (defaults to true) */
157
+ exitOnCtrlC?: boolean
158
+ }
159
+
160
+ /**
161
+ * Cleanup function returned by attachKeyboardManager.
162
+ * Call this to detach the keyboard manager from input.
163
+ */
164
+ export type DetachKeyboard = () => void
165
+
166
+ /**
167
+ * Attaches a keyboard manager to terminal stdin for receiving key events.
168
+ *
169
+ * This function sets up raw mode on stdin and processes keypress events,
170
+ * normalizing them and passing them to the keyboard manager.
171
+ *
172
+ * **Important:** This function requires a TTY stdin. It will throw if
173
+ * stdin is not a TTY (e.g., when input is piped).
174
+ *
175
+ * @param manager - The keyboard manager to receive key events
176
+ * @param options - Configuration options
177
+ * @returns A cleanup function to detach the keyboard manager
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * import { createKeyboardManager, attachKeyboardManager, VIM_BINDINGS } from '@mdxui/terminal'
182
+ *
183
+ * const manager = createKeyboardManager({
184
+ * bindings: VIM_BINDINGS,
185
+ * onAction: (action) => console.log('Action:', action),
186
+ * })
187
+ *
188
+ * const detach = attachKeyboardManager(manager)
189
+ *
190
+ * // Later, to clean up:
191
+ * detach()
192
+ * ```
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * // With custom options
197
+ * const detach = attachKeyboardManager(manager, {
198
+ * exitOnCtrlC: false, // Don't exit on Ctrl+C, let manager handle it
199
+ * })
200
+ * ```
201
+ */
202
+ export function attachKeyboardManager(
203
+ manager: KeyboardManager,
204
+ options: AttachKeyboardOptions = {}
205
+ ): DetachKeyboard {
206
+ const { input = process.stdin, exitOnCtrlC = true } = options
207
+
208
+ // Check if we have a TTY
209
+ if (!input.isTTY) {
210
+ throw new Error('attachKeyboardManager requires a TTY stdin. Cannot attach to non-TTY input.')
211
+ }
212
+
213
+ // Dynamic import readline to emit keypress events
214
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
215
+ const readline = require('readline')
216
+ readline.emitKeypressEvents(input)
217
+
218
+ // Store original raw mode state
219
+ const wasRaw = input.isRaw
220
+
221
+ // Enable raw mode to receive individual keypresses
222
+ input.setRawMode(true)
223
+
224
+ // Keypress handler
225
+ const handleKeypress = (str: string | undefined, key: ReadlineKey | undefined) => {
226
+ // Handle Ctrl+C exit if enabled
227
+ if (exitOnCtrlC && key?.ctrl && key?.name === 'c') {
228
+ // Restore terminal state before exiting
229
+ input.setRawMode(wasRaw ?? false)
230
+ process.exit(0)
231
+ }
232
+
233
+ // Normalize the key event
234
+ const normalized = normalizeReadlineKey(str, key)
235
+
236
+ // Convert to binding string format
237
+ const bindingKey = keyToBindingString(normalized)
238
+
239
+ // Pass to manager
240
+ manager.handleKey(bindingKey)
241
+ }
242
+
243
+ // Attach the handler
244
+ input.on('keypress', handleKeypress)
245
+
246
+ // Resume stdin (required when raw mode is enabled)
247
+ input.resume()
248
+
249
+ // Return cleanup function
250
+ return () => {
251
+ input.off('keypress', handleKeypress)
252
+ input.setRawMode(wasRaw ?? false)
253
+ // Note: We don't pause stdin as other handlers may still need it
254
+ }
255
+ }
256
+
257
+ // ============================================================================
258
+ // OpenTUI Integration
259
+ // ============================================================================
260
+
261
+ /**
262
+ * OpenTUI key event structure.
263
+ * Matches the KeyEvent interface from @opentui/core.
264
+ */
265
+ export interface OpenTUIKeyEvent {
266
+ /** Key name (e.g., 'a', 'enter', 'escape') */
267
+ name: string
268
+ /** Ctrl modifier */
269
+ ctrl: boolean
270
+ /** Meta/Cmd modifier */
271
+ meta: boolean
272
+ /** Shift modifier */
273
+ shift: boolean
274
+ /** Option/Alt modifier */
275
+ option: boolean
276
+ /** Raw sequence */
277
+ sequence: string
278
+ /** Event type: 'press', 'repeat', or 'release' */
279
+ eventType?: 'press' | 'repeat' | 'release'
280
+ /** Whether this is a repeated key */
281
+ repeated?: boolean
282
+ }
283
+
284
+ /**
285
+ * Normalizes an OpenTUI KeyEvent to our standard NormalizedKey format.
286
+ *
287
+ * @param event - The OpenTUI KeyEvent
288
+ * @returns Normalized key event
289
+ *
290
+ * @example
291
+ * ```typescript
292
+ * import { useKeyboard } from '@opentui/react'
293
+ *
294
+ * useKeyboard((event) => {
295
+ * const normalized = normalizeOpenTUIKey(event)
296
+ * manager.handleKey(keyToBindingString(normalized))
297
+ * })
298
+ * ```
299
+ */
300
+ export function normalizeOpenTUIKey(event: OpenTUIKeyEvent): NormalizedKey {
301
+ // Map OpenTUI key names to our standard names
302
+ const nameMap: Record<string, string> = {
303
+ return: 'enter',
304
+ escape: 'escape',
305
+ up: 'up',
306
+ down: 'down',
307
+ left: 'left',
308
+ right: 'right',
309
+ tab: 'tab',
310
+ backspace: 'backspace',
311
+ delete: 'delete',
312
+ space: 'space',
313
+ }
314
+
315
+ const normalizedName = nameMap[event.name.toLowerCase()] ?? event.name.toLowerCase()
316
+
317
+ return {
318
+ name: normalizedName,
319
+ ctrl: event.ctrl,
320
+ alt: event.option, // OpenTUI uses 'option' for Alt
321
+ shift: event.shift,
322
+ meta: event.meta,
323
+ sequence: event.sequence,
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Creates a keyboard event handler for use with OpenTUI's useKeyboard hook.
329
+ *
330
+ * This function returns a handler that can be passed directly to OpenTUI's
331
+ * useKeyboard hook to connect it to a KeyboardManager.
332
+ *
333
+ * @param manager - The keyboard manager to receive key events
334
+ * @returns A handler function for OpenTUI's useKeyboard hook
335
+ *
336
+ * @example
337
+ * ```tsx
338
+ * import { useKeyboard } from '@opentui/react'
339
+ * import { createKeyboardManager, createOpenTUIKeyHandler, VIM_BINDINGS } from '@mdxui/terminal'
340
+ *
341
+ * function MyComponent() {
342
+ * const manager = useMemo(() => createKeyboardManager({
343
+ * bindings: VIM_BINDINGS,
344
+ * onAction: (action) => console.log('Action:', action),
345
+ * }), [])
346
+ *
347
+ * const handler = useMemo(() => createOpenTUIKeyHandler(manager), [manager])
348
+ *
349
+ * useKeyboard(handler)
350
+ *
351
+ * return <div>...</div>
352
+ * }
353
+ * ```
354
+ */
355
+ export function createOpenTUIKeyHandler(manager: KeyboardManager): (event: OpenTUIKeyEvent) => void {
356
+ return (event: OpenTUIKeyEvent) => {
357
+ // Skip release events - only handle press/repeat
358
+ if (event.eventType === 'release') {
359
+ return
360
+ }
361
+
362
+ // Normalize the key event
363
+ const normalized = normalizeOpenTUIKey(event)
364
+
365
+ // Convert to binding string format
366
+ const bindingKey = keyToBindingString(normalized)
367
+
368
+ // Pass to manager
369
+ manager.handleKey(bindingKey)
370
+ }
371
+ }