@rlabs-inc/tui 0.1.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 (44) hide show
  1. package/README.md +141 -0
  2. package/index.ts +45 -0
  3. package/package.json +59 -0
  4. package/src/api/index.ts +7 -0
  5. package/src/api/mount.ts +230 -0
  6. package/src/engine/arrays/core.ts +60 -0
  7. package/src/engine/arrays/dimensions.ts +68 -0
  8. package/src/engine/arrays/index.ts +166 -0
  9. package/src/engine/arrays/interaction.ts +112 -0
  10. package/src/engine/arrays/layout.ts +175 -0
  11. package/src/engine/arrays/spacing.ts +100 -0
  12. package/src/engine/arrays/text.ts +55 -0
  13. package/src/engine/arrays/visual.ts +140 -0
  14. package/src/engine/index.ts +25 -0
  15. package/src/engine/inheritance.ts +138 -0
  16. package/src/engine/registry.ts +180 -0
  17. package/src/pipeline/frameBuffer.ts +473 -0
  18. package/src/pipeline/layout/index.ts +105 -0
  19. package/src/pipeline/layout/titan-engine.ts +798 -0
  20. package/src/pipeline/layout/types.ts +194 -0
  21. package/src/pipeline/layout/utils/hierarchy.ts +202 -0
  22. package/src/pipeline/layout/utils/math.ts +134 -0
  23. package/src/pipeline/layout/utils/text-measure.ts +160 -0
  24. package/src/pipeline/layout.ts +30 -0
  25. package/src/primitives/box.ts +312 -0
  26. package/src/primitives/index.ts +12 -0
  27. package/src/primitives/text.ts +199 -0
  28. package/src/primitives/types.ts +222 -0
  29. package/src/primitives/utils.ts +37 -0
  30. package/src/renderer/ansi.ts +625 -0
  31. package/src/renderer/buffer.ts +667 -0
  32. package/src/renderer/index.ts +40 -0
  33. package/src/renderer/input.ts +518 -0
  34. package/src/renderer/output.ts +451 -0
  35. package/src/state/cursor.ts +176 -0
  36. package/src/state/focus.ts +241 -0
  37. package/src/state/index.ts +43 -0
  38. package/src/state/keyboard.ts +771 -0
  39. package/src/state/mouse.ts +524 -0
  40. package/src/state/scroll.ts +341 -0
  41. package/src/state/theme.ts +687 -0
  42. package/src/types/color.ts +401 -0
  43. package/src/types/index.ts +316 -0
  44. package/src/utils/text.ts +471 -0
@@ -0,0 +1,316 @@
1
+ /**
2
+ * TUI Framework - Core Type Definitions
3
+ *
4
+ * The foundation everything builds on. These types define
5
+ * what the blind renderer understands and what flows through
6
+ * the reactive pipeline.
7
+ */
8
+
9
+ // =============================================================================
10
+ // Color
11
+ // =============================================================================
12
+
13
+ /**
14
+ * RGBA color with 8-bit channels (0-255)
15
+ *
16
+ * Using integers for exact comparison - no floating point epsilon needed.
17
+ * Alpha 255 = fully opaque, 0 = fully transparent.
18
+ */
19
+ export interface RGBA {
20
+ r: number
21
+ g: number
22
+ b: number
23
+ a: number
24
+ }
25
+
26
+ // =============================================================================
27
+ // Dimension - Supports absolute and percentage values
28
+ // =============================================================================
29
+
30
+ /**
31
+ * A dimension value that can be absolute (number) or percentage (string).
32
+ *
33
+ * - number: Absolute value in terminal cells (e.g., 50 = 50 chars)
34
+ * - string: Percentage of parent (e.g., '50%' = half of parent)
35
+ * - 0 or '0': Auto-size based on content
36
+ *
37
+ * Examples:
38
+ * width: 50 // 50 characters
39
+ * width: '100%' // Full parent width
40
+ * width: '50%' // Half of parent width
41
+ * height: 0 // Auto-height based on content
42
+ */
43
+ export type Dimension = number | `${number}%`
44
+
45
+ /**
46
+ * Parsed dimension for internal use.
47
+ * TITAN resolves these against parent computed sizes.
48
+ */
49
+ export interface ParsedDimension {
50
+ value: number
51
+ isPercent: boolean
52
+ }
53
+
54
+ /**
55
+ * Parse a Dimension into value and percent flag.
56
+ * Used by primitives when binding dimensions.
57
+ */
58
+ export function parseDimension(dim: Dimension | undefined | null): ParsedDimension {
59
+ if (dim === undefined || dim === null) {
60
+ return { value: 0, isPercent: false }
61
+ }
62
+ if (typeof dim === 'number') {
63
+ return { value: dim, isPercent: false }
64
+ }
65
+ // String like '50%'
66
+ const num = parseFloat(dim)
67
+ return { value: isNaN(num) ? 0 : num, isPercent: true }
68
+ }
69
+
70
+ // =============================================================================
71
+ // Cell Attributes (bitfield)
72
+ // =============================================================================
73
+
74
+ /**
75
+ * Text attributes as a bitfield for efficient storage and comparison.
76
+ * Combine with bitwise OR: Attr.BOLD | Attr.ITALIC
77
+ */
78
+ export const Attr = {
79
+ NONE: 0,
80
+ BOLD: 1 << 0,
81
+ DIM: 1 << 1,
82
+ ITALIC: 1 << 2,
83
+ UNDERLINE: 1 << 3,
84
+ BLINK: 1 << 4,
85
+ INVERSE: 1 << 5,
86
+ HIDDEN: 1 << 6,
87
+ STRIKETHROUGH: 1 << 7,
88
+ } as const
89
+
90
+ export type CellAttrs = number
91
+
92
+ // =============================================================================
93
+ // Cell - The atomic unit of terminal rendering
94
+ // =============================================================================
95
+
96
+ /**
97
+ * A single terminal cell.
98
+ *
99
+ * This is what the renderer deals with. Nothing more complex.
100
+ * The entire pipeline computes these, the renderer outputs them.
101
+ */
102
+ export interface Cell {
103
+ /** Unicode codepoint (32 for space) */
104
+ char: number
105
+ /** Foreground color */
106
+ fg: RGBA
107
+ /** Background color */
108
+ bg: RGBA
109
+ /** Attribute flags (bold, italic, etc.) */
110
+ attrs: CellAttrs
111
+ }
112
+
113
+ // =============================================================================
114
+ // Cursor
115
+ // =============================================================================
116
+
117
+ export type CursorShape = 'block' | 'underline' | 'bar'
118
+
119
+ export interface Cursor {
120
+ x: number
121
+ y: number
122
+ shape: CursorShape
123
+ visible: boolean
124
+ blinking: boolean
125
+ }
126
+
127
+ // =============================================================================
128
+ // FrameBuffer - What the renderer receives
129
+ // =============================================================================
130
+
131
+ /**
132
+ * A 2D buffer of cells representing one frame.
133
+ *
134
+ * Stored as [y][x] for cache-friendly row access during rendering.
135
+ * This is what layoutDerived and frameBufferDerived ultimately produce.
136
+ */
137
+ export interface FrameBuffer {
138
+ readonly width: number
139
+ readonly height: number
140
+ readonly cells: Cell[][]
141
+ }
142
+
143
+ // =============================================================================
144
+ // Input Events - What the terminal reports
145
+ // =============================================================================
146
+
147
+ export interface Modifiers {
148
+ ctrl: boolean
149
+ alt: boolean
150
+ shift: boolean
151
+ meta: boolean
152
+ }
153
+
154
+ export type KeyState = 'press' | 'release' | 'repeat'
155
+
156
+ export interface KeyEvent {
157
+ /** The key character or name (e.g., 'a', 'Enter', 'ArrowUp') */
158
+ key: string
159
+ /** Modifier keys held during the event */
160
+ modifiers: Modifiers
161
+ /** Key state (press/release/repeat) */
162
+ state: KeyState
163
+ }
164
+
165
+ export type MouseButton = 'left' | 'middle' | 'right' | 'none'
166
+ export type MouseAction = 'down' | 'up' | 'move' | 'scroll'
167
+
168
+ export interface MouseEvent {
169
+ /** Cell X position (0-indexed) */
170
+ x: number
171
+ /** Cell Y position (0-indexed) */
172
+ y: number
173
+ /** Which button */
174
+ button: MouseButton
175
+ /** Event type */
176
+ action: MouseAction
177
+ /** Scroll direction (-1 up, 1 down) when action is 'scroll' */
178
+ scrollDelta?: number
179
+ /** Modifier keys */
180
+ modifiers: Modifiers
181
+ }
182
+
183
+ export interface ResizeEvent {
184
+ width: number
185
+ height: number
186
+ }
187
+
188
+ export interface FocusEvent {
189
+ focused: boolean
190
+ }
191
+
192
+ // =============================================================================
193
+ // Component Types - For parallel arrays
194
+ // =============================================================================
195
+
196
+ /**
197
+ * Component types for the parallel arrays pattern.
198
+ * Each component at index i has componentType[i] set to one of these.
199
+ */
200
+ export const ComponentType = {
201
+ NONE: 0,
202
+ BOX: 1,
203
+ TEXT: 2,
204
+ INPUT: 3,
205
+ SELECT: 4,
206
+ PROGRESS: 5,
207
+ CANVAS: 6,
208
+ } as const
209
+
210
+ export type ComponentTypeValue = (typeof ComponentType)[keyof typeof ComponentType]
211
+
212
+ // =============================================================================
213
+ // Border Styles
214
+ // =============================================================================
215
+
216
+ /**
217
+ * Border style constants - use numbers in arrays, consistent with ComponentType.
218
+ * All 10 standard terminal border styles.
219
+ */
220
+ export const BorderStyle = {
221
+ NONE: 0,
222
+ SINGLE: 1, // ─ │ ┌ ┐ └ ┘
223
+ DOUBLE: 2, // ═ ║ ╔ ╗ ╚ ╝
224
+ ROUNDED: 3, // ─ │ ╭ ╮ ╰ ╯
225
+ BOLD: 4, // ━ ┃ ┏ ┓ ┗ ┛
226
+ DASHED: 5, // ┄ ┆ ┌ ┐ └ ┘
227
+ DOTTED: 6, // · · · · · ·
228
+ ASCII: 7, // - | + + + +
229
+ BLOCK: 8, // █ █ █ █ █ █
230
+ DOUBLE_HORZ: 9, // ═ │ ╒ ╕ ╘ ╛ (double horizontal, single vertical)
231
+ DOUBLE_VERT: 10, // ─ ║ ╓ ╖ ╙ ╜ (single horizontal, double vertical)
232
+ } as const
233
+
234
+ export type BorderStyleValue = (typeof BorderStyle)[keyof typeof BorderStyle]
235
+
236
+ /**
237
+ * Border characters for each style.
238
+ * Order: [0]=horizontal, [1]=vertical, [2]=topLeft, [3]=topRight, [4]=bottomRight, [5]=bottomLeft
239
+ * Access as BorderChars[BorderStyle.SINGLE][0] for horizontal char
240
+ */
241
+ export const BorderChars: Record<number, readonly [string, string, string, string, string, string]> = {
242
+ [BorderStyle.SINGLE]: ['─', '│', '┌', '┐', '┘', '└'],
243
+ [BorderStyle.DOUBLE]: ['═', '║', '╔', '╗', '╝', '╚'],
244
+ [BorderStyle.ROUNDED]: ['─', '│', '╭', '╮', '╯', '╰'],
245
+ [BorderStyle.BOLD]: ['━', '┃', '┏', '┓', '┛', '┗'],
246
+ [BorderStyle.DASHED]: ['┄', '┆', '┌', '┐', '┘', '└'],
247
+ [BorderStyle.DOTTED]: ['·', '·', '·', '·', '·', '·'],
248
+ [BorderStyle.ASCII]: ['-', '|', '+', '+', '+', '+'],
249
+ [BorderStyle.BLOCK]: ['█', '█', '█', '█', '█', '█'],
250
+ [BorderStyle.DOUBLE_HORZ]: ['═', '│', '╒', '╕', '╛', '╘'],
251
+ [BorderStyle.DOUBLE_VERT]: ['─', '║', '╓', '╖', '╜', '╙'],
252
+ }
253
+
254
+ // =============================================================================
255
+ // Scroll State
256
+ // =============================================================================
257
+
258
+ export interface ScrollState {
259
+ x: number
260
+ y: number
261
+ maxX: number
262
+ maxY: number
263
+ }
264
+
265
+ // =============================================================================
266
+ // Mount Options
267
+ // =============================================================================
268
+
269
+ export type RenderMode = 'fullscreen' | 'inline' | 'append'
270
+
271
+ export interface MountOptions {
272
+ /**
273
+ * Render mode:
274
+ * - 'fullscreen': Alternate screen buffer, full terminal control
275
+ * - 'inline': Renders inline, save/restore cursor, updates in place
276
+ * - 'append': Content flows down, still reactive (can update previous content)
277
+ */
278
+ mode?: RenderMode
279
+ /** Enable mouse tracking (default: true) */
280
+ mouse?: boolean
281
+ /** Enable Kitty keyboard protocol if available (default: true) */
282
+ kittyKeyboard?: boolean
283
+ /** Initial cursor configuration */
284
+ cursor?: Partial<Cursor>
285
+ }
286
+
287
+ // =============================================================================
288
+ // Renderer Interface
289
+ // =============================================================================
290
+
291
+ /**
292
+ * The blind renderer interface.
293
+ * Knows only about cells, not components.
294
+ */
295
+ export interface Renderer {
296
+ readonly width: number
297
+ readonly height: number
298
+
299
+ /** Render a frame buffer to the terminal */
300
+ render(buffer: FrameBuffer): void
301
+
302
+ /** Cursor control */
303
+ setCursor(x: number, y: number): void
304
+ setCursorVisible(visible: boolean): void
305
+ setCursorShape(shape: CursorShape): void
306
+
307
+ /** Event handlers - return unsubscribe function */
308
+ onKey(handler: (event: KeyEvent) => void): () => void
309
+ onMouse(handler: (event: MouseEvent) => void): () => void
310
+ onResize(handler: (event: ResizeEvent) => void): () => void
311
+ onFocus(handler: (event: FocusEvent) => void): () => void
312
+
313
+ /** Lifecycle */
314
+ start(options?: MountOptions): Promise<void>
315
+ stop(): Promise<void>
316
+ }