@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.
- package/README.md +141 -0
- package/index.ts +45 -0
- package/package.json +59 -0
- package/src/api/index.ts +7 -0
- package/src/api/mount.ts +230 -0
- package/src/engine/arrays/core.ts +60 -0
- package/src/engine/arrays/dimensions.ts +68 -0
- package/src/engine/arrays/index.ts +166 -0
- package/src/engine/arrays/interaction.ts +112 -0
- package/src/engine/arrays/layout.ts +175 -0
- package/src/engine/arrays/spacing.ts +100 -0
- package/src/engine/arrays/text.ts +55 -0
- package/src/engine/arrays/visual.ts +140 -0
- package/src/engine/index.ts +25 -0
- package/src/engine/inheritance.ts +138 -0
- package/src/engine/registry.ts +180 -0
- package/src/pipeline/frameBuffer.ts +473 -0
- package/src/pipeline/layout/index.ts +105 -0
- package/src/pipeline/layout/titan-engine.ts +798 -0
- package/src/pipeline/layout/types.ts +194 -0
- package/src/pipeline/layout/utils/hierarchy.ts +202 -0
- package/src/pipeline/layout/utils/math.ts +134 -0
- package/src/pipeline/layout/utils/text-measure.ts +160 -0
- package/src/pipeline/layout.ts +30 -0
- package/src/primitives/box.ts +312 -0
- package/src/primitives/index.ts +12 -0
- package/src/primitives/text.ts +199 -0
- package/src/primitives/types.ts +222 -0
- package/src/primitives/utils.ts +37 -0
- package/src/renderer/ansi.ts +625 -0
- package/src/renderer/buffer.ts +667 -0
- package/src/renderer/index.ts +40 -0
- package/src/renderer/input.ts +518 -0
- package/src/renderer/output.ts +451 -0
- package/src/state/cursor.ts +176 -0
- package/src/state/focus.ts +241 -0
- package/src/state/index.ts +43 -0
- package/src/state/keyboard.ts +771 -0
- package/src/state/mouse.ts +524 -0
- package/src/state/scroll.ts +341 -0
- package/src/state/theme.ts +687 -0
- package/src/types/color.ts +401 -0
- package/src/types/index.ts +316 -0
- package/src/utils/text.ts +471 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Framework - Parallel Arrays
|
|
3
|
+
*
|
|
4
|
+
* All component state lives in these parallel arrays.
|
|
5
|
+
* Each array index corresponds to one component.
|
|
6
|
+
*
|
|
7
|
+
* Components write directly to these arrays.
|
|
8
|
+
* Deriveds read from these arrays and RETURN computed values.
|
|
9
|
+
*
|
|
10
|
+
* Array categories:
|
|
11
|
+
* - core: Component type, parent, visibility
|
|
12
|
+
* - dimensions: Width, height, min/max constraints
|
|
13
|
+
* - spacing: Margin, padding, gap
|
|
14
|
+
* - layout: Flexbox properties, positioning
|
|
15
|
+
* - visual: Colors, borders, opacity
|
|
16
|
+
* - text: Text content and styling
|
|
17
|
+
* - interaction: Scroll, focus, mouse state
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export * as core from './core'
|
|
21
|
+
export * as dimensions from './dimensions'
|
|
22
|
+
export * as spacing from './spacing'
|
|
23
|
+
export * as layout from './layout'
|
|
24
|
+
export * as visual from './visual'
|
|
25
|
+
export * as text from './text'
|
|
26
|
+
export * as interaction from './interaction'
|
|
27
|
+
|
|
28
|
+
import { disconnectBinding } from '@rlabs-inc/signals'
|
|
29
|
+
import * as core from './core'
|
|
30
|
+
import * as dimensions from './dimensions'
|
|
31
|
+
import * as spacing from './spacing'
|
|
32
|
+
import * as layout from './layout'
|
|
33
|
+
import * as visual from './visual'
|
|
34
|
+
import * as text from './text'
|
|
35
|
+
import * as interaction from './interaction'
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ensure all arrays have capacity for the given index.
|
|
39
|
+
* Called by registry when allocating.
|
|
40
|
+
*/
|
|
41
|
+
export function ensureAllCapacity(index: number): void {
|
|
42
|
+
core.ensureCapacity(index)
|
|
43
|
+
dimensions.ensureCapacity(index)
|
|
44
|
+
spacing.ensureCapacity(index)
|
|
45
|
+
layout.ensureCapacity(index)
|
|
46
|
+
visual.ensureCapacity(index)
|
|
47
|
+
text.ensureCapacity(index)
|
|
48
|
+
interaction.ensureCapacity(index)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Clear all array values at an index.
|
|
53
|
+
* Called by registry when releasing.
|
|
54
|
+
*/
|
|
55
|
+
export function clearAllAtIndex(index: number): void {
|
|
56
|
+
core.clearAtIndex(index)
|
|
57
|
+
dimensions.clearAtIndex(index)
|
|
58
|
+
spacing.clearAtIndex(index)
|
|
59
|
+
layout.clearAtIndex(index)
|
|
60
|
+
visual.clearAtIndex(index)
|
|
61
|
+
text.clearAtIndex(index)
|
|
62
|
+
interaction.clearAtIndex(index)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Disconnect all bindings in an array before truncating */
|
|
66
|
+
function disconnectArray(arr: unknown[]): void {
|
|
67
|
+
for (let i = 0; i < arr.length; i++) {
|
|
68
|
+
disconnectBinding(arr[i] as any)
|
|
69
|
+
}
|
|
70
|
+
arr.length = 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Reset all parallel arrays to release memory.
|
|
75
|
+
* Called automatically when all components are destroyed (allocatedIndices.size === 0).
|
|
76
|
+
* This is the "reset on zero" cleanup - no manual API needed!
|
|
77
|
+
*
|
|
78
|
+
* IMPORTANT: Disconnects all bindings before truncating to break circular refs.
|
|
79
|
+
*/
|
|
80
|
+
export function resetAllArrays(): void {
|
|
81
|
+
// Core arrays
|
|
82
|
+
core.componentType.length = 0
|
|
83
|
+
disconnectArray(core.parentIndex)
|
|
84
|
+
disconnectArray(core.visible)
|
|
85
|
+
disconnectArray(core.componentId)
|
|
86
|
+
|
|
87
|
+
// Dimension arrays
|
|
88
|
+
disconnectArray(dimensions.width)
|
|
89
|
+
disconnectArray(dimensions.height)
|
|
90
|
+
disconnectArray(dimensions.minWidth)
|
|
91
|
+
disconnectArray(dimensions.minHeight)
|
|
92
|
+
disconnectArray(dimensions.maxWidth)
|
|
93
|
+
disconnectArray(dimensions.maxHeight)
|
|
94
|
+
|
|
95
|
+
// Spacing arrays
|
|
96
|
+
disconnectArray(spacing.marginTop)
|
|
97
|
+
disconnectArray(spacing.marginRight)
|
|
98
|
+
disconnectArray(spacing.marginBottom)
|
|
99
|
+
disconnectArray(spacing.marginLeft)
|
|
100
|
+
disconnectArray(spacing.paddingTop)
|
|
101
|
+
disconnectArray(spacing.paddingRight)
|
|
102
|
+
disconnectArray(spacing.paddingBottom)
|
|
103
|
+
disconnectArray(spacing.paddingLeft)
|
|
104
|
+
disconnectArray(spacing.gap)
|
|
105
|
+
disconnectArray(spacing.rowGap)
|
|
106
|
+
disconnectArray(spacing.columnGap)
|
|
107
|
+
|
|
108
|
+
// Layout arrays
|
|
109
|
+
disconnectArray(layout.flexDirection)
|
|
110
|
+
disconnectArray(layout.flexWrap)
|
|
111
|
+
disconnectArray(layout.justifyContent)
|
|
112
|
+
disconnectArray(layout.alignItems)
|
|
113
|
+
disconnectArray(layout.alignContent)
|
|
114
|
+
disconnectArray(layout.flexGrow)
|
|
115
|
+
disconnectArray(layout.flexShrink)
|
|
116
|
+
disconnectArray(layout.flexBasis)
|
|
117
|
+
disconnectArray(layout.alignSelf)
|
|
118
|
+
disconnectArray(layout.order)
|
|
119
|
+
disconnectArray(layout.position)
|
|
120
|
+
disconnectArray(layout.top)
|
|
121
|
+
disconnectArray(layout.right)
|
|
122
|
+
disconnectArray(layout.bottom)
|
|
123
|
+
disconnectArray(layout.left)
|
|
124
|
+
disconnectArray(layout.borderTop)
|
|
125
|
+
disconnectArray(layout.borderRight)
|
|
126
|
+
disconnectArray(layout.borderBottom)
|
|
127
|
+
disconnectArray(layout.borderLeft)
|
|
128
|
+
disconnectArray(layout.zIndex)
|
|
129
|
+
disconnectArray(layout.overflow)
|
|
130
|
+
|
|
131
|
+
// Visual arrays
|
|
132
|
+
disconnectArray(visual.fgColor)
|
|
133
|
+
disconnectArray(visual.bgColor)
|
|
134
|
+
disconnectArray(visual.opacity)
|
|
135
|
+
disconnectArray(visual.borderStyle)
|
|
136
|
+
disconnectArray(visual.borderColor)
|
|
137
|
+
disconnectArray(visual.borderTop)
|
|
138
|
+
disconnectArray(visual.borderRight)
|
|
139
|
+
disconnectArray(visual.borderBottom)
|
|
140
|
+
disconnectArray(visual.borderLeft)
|
|
141
|
+
disconnectArray(visual.borderColorTop)
|
|
142
|
+
disconnectArray(visual.borderColorRight)
|
|
143
|
+
disconnectArray(visual.borderColorBottom)
|
|
144
|
+
disconnectArray(visual.borderColorLeft)
|
|
145
|
+
disconnectArray(visual.showFocusRing)
|
|
146
|
+
disconnectArray(visual.focusRingColor)
|
|
147
|
+
|
|
148
|
+
// Text arrays
|
|
149
|
+
disconnectArray(text.textContent)
|
|
150
|
+
disconnectArray(text.textAttrs)
|
|
151
|
+
disconnectArray(text.textAlign)
|
|
152
|
+
disconnectArray(text.textWrap)
|
|
153
|
+
disconnectArray(text.ellipsis)
|
|
154
|
+
|
|
155
|
+
// Interaction arrays
|
|
156
|
+
disconnectArray(interaction.scrollOffsetX)
|
|
157
|
+
disconnectArray(interaction.scrollOffsetY)
|
|
158
|
+
disconnectArray(interaction.focusable)
|
|
159
|
+
disconnectArray(interaction.tabIndex)
|
|
160
|
+
disconnectArray(interaction.hovered)
|
|
161
|
+
disconnectArray(interaction.pressed)
|
|
162
|
+
disconnectArray(interaction.mouseEnabled)
|
|
163
|
+
disconnectArray(interaction.cursorPosition)
|
|
164
|
+
disconnectArray(interaction.selectionStart)
|
|
165
|
+
disconnectArray(interaction.selectionEnd)
|
|
166
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Framework - Interaction Arrays
|
|
3
|
+
*
|
|
4
|
+
* State for scroll, focus, and mouse interactions.
|
|
5
|
+
*
|
|
6
|
+
* CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
|
|
7
|
+
* state() proxies snapshot getter values, breaking reactivity.
|
|
8
|
+
*
|
|
9
|
+
* Exception: focusedIndex is a single signal, not an array.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: scrollable/maxScrollX/maxScrollY are COMPUTED by TITAN layout engine
|
|
12
|
+
* and live in ComputedLayout, not here. Only scroll OFFSETS (user state) are here.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { signal, bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// SCROLL STATE (user-controlled position only)
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
/** Current vertical scroll offset (user state) */
|
|
22
|
+
export const scrollOffsetY: Binding<number>[] = []
|
|
23
|
+
|
|
24
|
+
/** Current horizontal scroll offset (user state) */
|
|
25
|
+
export const scrollOffsetX: Binding<number>[] = []
|
|
26
|
+
|
|
27
|
+
// NOTE: scrollable, maxScrollX, maxScrollY are COMPUTED values from TITAN.
|
|
28
|
+
// Read them from layoutDerived.value, not from interaction arrays.
|
|
29
|
+
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// FOCUS STATE
|
|
32
|
+
// =============================================================================
|
|
33
|
+
|
|
34
|
+
/** Can this component receive focus */
|
|
35
|
+
export const focusable: Binding<number>[] = [] // 0=no, 1=yes
|
|
36
|
+
|
|
37
|
+
/** Tab order for focus navigation (-1 = not in tab order) */
|
|
38
|
+
export const tabIndex: Binding<number>[] = []
|
|
39
|
+
|
|
40
|
+
/** Currently focused component index (-1 = none) - SINGLE SIGNAL, not array */
|
|
41
|
+
export const focusedIndex = signal(-1)
|
|
42
|
+
|
|
43
|
+
// =============================================================================
|
|
44
|
+
// MOUSE STATE
|
|
45
|
+
// =============================================================================
|
|
46
|
+
|
|
47
|
+
/** Is mouse currently hovering over this component */
|
|
48
|
+
export const hovered: Binding<number>[] = [] // 0=no, 1=yes
|
|
49
|
+
|
|
50
|
+
/** Is mouse button currently pressed on this component */
|
|
51
|
+
export const pressed: Binding<number>[] = [] // 0=no, 1=yes
|
|
52
|
+
|
|
53
|
+
/** Is mouse events enabled for this component */
|
|
54
|
+
export const mouseEnabled: Binding<number>[] = [] // 0=no, 1=yes
|
|
55
|
+
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// CURSOR STATE (for input components)
|
|
58
|
+
// =============================================================================
|
|
59
|
+
|
|
60
|
+
/** Cursor position in text content */
|
|
61
|
+
export const cursorPosition: Binding<number>[] = []
|
|
62
|
+
|
|
63
|
+
/** Selection start position (-1 = no selection) */
|
|
64
|
+
export const selectionStart: Binding<number>[] = []
|
|
65
|
+
|
|
66
|
+
/** Selection end position */
|
|
67
|
+
export const selectionEnd: Binding<number>[] = []
|
|
68
|
+
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// CAPACITY MANAGEMENT
|
|
71
|
+
// =============================================================================
|
|
72
|
+
|
|
73
|
+
/** LAZY BINDING: Push undefined, primitives create bindings for used props only */
|
|
74
|
+
export function ensureCapacity(index: number): void {
|
|
75
|
+
while (scrollOffsetY.length <= index) {
|
|
76
|
+
scrollOffsetY.push(undefined as any)
|
|
77
|
+
scrollOffsetX.push(undefined as any)
|
|
78
|
+
focusable.push(undefined as any)
|
|
79
|
+
tabIndex.push(undefined as any)
|
|
80
|
+
hovered.push(undefined as any)
|
|
81
|
+
pressed.push(undefined as any)
|
|
82
|
+
mouseEnabled.push(undefined as any)
|
|
83
|
+
cursorPosition.push(undefined as any)
|
|
84
|
+
selectionStart.push(undefined as any)
|
|
85
|
+
selectionEnd.push(undefined as any)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function clearAtIndex(index: number): void {
|
|
90
|
+
if (index < scrollOffsetY.length) {
|
|
91
|
+
disconnectBinding(scrollOffsetY[index])
|
|
92
|
+
disconnectBinding(scrollOffsetX[index])
|
|
93
|
+
disconnectBinding(focusable[index])
|
|
94
|
+
disconnectBinding(tabIndex[index])
|
|
95
|
+
disconnectBinding(hovered[index])
|
|
96
|
+
disconnectBinding(pressed[index])
|
|
97
|
+
disconnectBinding(mouseEnabled[index])
|
|
98
|
+
disconnectBinding(cursorPosition[index])
|
|
99
|
+
disconnectBinding(selectionStart[index])
|
|
100
|
+
disconnectBinding(selectionEnd[index])
|
|
101
|
+
scrollOffsetY[index] = undefined as any
|
|
102
|
+
scrollOffsetX[index] = undefined as any
|
|
103
|
+
focusable[index] = undefined as any
|
|
104
|
+
tabIndex[index] = undefined as any
|
|
105
|
+
hovered[index] = undefined as any
|
|
106
|
+
pressed[index] = undefined as any
|
|
107
|
+
mouseEnabled[index] = undefined as any
|
|
108
|
+
cursorPosition[index] = undefined as any
|
|
109
|
+
selectionStart[index] = undefined as any
|
|
110
|
+
selectionEnd[index] = undefined as any
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Framework - Layout Arrays
|
|
3
|
+
*
|
|
4
|
+
* Flexbox properties, positioning, and stacking.
|
|
5
|
+
* Uses numeric enums for compact storage.
|
|
6
|
+
*
|
|
7
|
+
* CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
|
|
8
|
+
* state() proxies snapshot getter values, breaking reactivity.
|
|
9
|
+
*
|
|
10
|
+
* Flex direction: 0=column, 1=row, 2=column-reverse, 3=row-reverse
|
|
11
|
+
* Flex wrap: 0=nowrap, 1=wrap, 2=wrap-reverse
|
|
12
|
+
* Justify: 0=flex-start, 1=center, 2=flex-end, 3=space-between, 4=space-around, 5=space-evenly
|
|
13
|
+
* Align: 0=stretch, 1=flex-start, 2=center, 3=flex-end, 4=baseline
|
|
14
|
+
* Position: 0=relative, 1=absolute
|
|
15
|
+
* Overflow: 0=visible, 1=hidden, 2=scroll
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// FLEX CONTAINER PROPERTIES
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/** Main axis direction: 0=column, 1=row, 2=column-reverse, 3=row-reverse */
|
|
25
|
+
export const flexDirection: Binding<number>[] = []
|
|
26
|
+
|
|
27
|
+
/** Wrap behavior: 0=nowrap, 1=wrap, 2=wrap-reverse */
|
|
28
|
+
export const flexWrap: Binding<number>[] = []
|
|
29
|
+
|
|
30
|
+
/** Main axis alignment: 0=flex-start, 1=center, 2=flex-end, 3=space-between, 4=space-around, 5=space-evenly */
|
|
31
|
+
export const justifyContent: Binding<number>[] = []
|
|
32
|
+
|
|
33
|
+
/** Cross axis alignment for items: 0=stretch, 1=flex-start, 2=center, 3=flex-end, 4=baseline */
|
|
34
|
+
export const alignItems: Binding<number>[] = []
|
|
35
|
+
|
|
36
|
+
/** Cross axis alignment for wrapped lines: 0=stretch, 1=flex-start, 2=center, 3=flex-end */
|
|
37
|
+
export const alignContent: Binding<number>[] = []
|
|
38
|
+
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// FLEX ITEM PROPERTIES
|
|
41
|
+
// =============================================================================
|
|
42
|
+
|
|
43
|
+
/** Grow factor - how much item grows to fill space (0 = don't grow) */
|
|
44
|
+
export const flexGrow: Binding<number>[] = []
|
|
45
|
+
|
|
46
|
+
/** Shrink factor - how much item shrinks when overflow (1 = default, 0 = don't shrink) */
|
|
47
|
+
export const flexShrink: Binding<number>[] = []
|
|
48
|
+
|
|
49
|
+
/** Initial size before growing/shrinking (0 = use width/height) */
|
|
50
|
+
export const flexBasis: Binding<number>[] = []
|
|
51
|
+
|
|
52
|
+
/** Override alignItems for this item: 0=auto, 1=stretch, 2=flex-start, 3=center, 4=flex-end */
|
|
53
|
+
export const alignSelf: Binding<number>[] = []
|
|
54
|
+
|
|
55
|
+
/** Visual order override (not used yet) */
|
|
56
|
+
export const order: Binding<number>[] = []
|
|
57
|
+
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// POSITIONING
|
|
60
|
+
// =============================================================================
|
|
61
|
+
|
|
62
|
+
/** Position mode: 0=relative (in flow), 1=absolute (out of flow) */
|
|
63
|
+
export const position: Binding<number>[] = []
|
|
64
|
+
|
|
65
|
+
/** Offset from top of positioned ancestor (for absolute) or normal position (for relative) */
|
|
66
|
+
export const top: Binding<number>[] = []
|
|
67
|
+
|
|
68
|
+
/** Offset from right of positioned ancestor */
|
|
69
|
+
export const right: Binding<number>[] = []
|
|
70
|
+
|
|
71
|
+
/** Offset from bottom of positioned ancestor */
|
|
72
|
+
export const bottom: Binding<number>[] = []
|
|
73
|
+
|
|
74
|
+
/** Offset from left of positioned ancestor */
|
|
75
|
+
export const left: Binding<number>[] = []
|
|
76
|
+
|
|
77
|
+
// =============================================================================
|
|
78
|
+
// BORDER (for layout calculations)
|
|
79
|
+
// =============================================================================
|
|
80
|
+
|
|
81
|
+
/** Has top border? 0=no, 1+=yes (takes 1 cell) */
|
|
82
|
+
export const borderTop: Binding<number>[] = []
|
|
83
|
+
|
|
84
|
+
/** Has right border? 0=no, 1+=yes (takes 1 cell) */
|
|
85
|
+
export const borderRight: Binding<number>[] = []
|
|
86
|
+
|
|
87
|
+
/** Has bottom border? 0=no, 1+=yes (takes 1 cell) */
|
|
88
|
+
export const borderBottom: Binding<number>[] = []
|
|
89
|
+
|
|
90
|
+
/** Has left border? 0=no, 1+=yes (takes 1 cell) */
|
|
91
|
+
export const borderLeft: Binding<number>[] = []
|
|
92
|
+
|
|
93
|
+
// =============================================================================
|
|
94
|
+
// STACKING & OVERFLOW
|
|
95
|
+
// =============================================================================
|
|
96
|
+
|
|
97
|
+
/** Z-index for overlapping components (higher = on top) */
|
|
98
|
+
export const zIndex: Binding<number>[] = []
|
|
99
|
+
|
|
100
|
+
/** Overflow handling: 0=visible, 1=hidden, 2=scroll, 3=auto */
|
|
101
|
+
export const overflow: Binding<number>[] = []
|
|
102
|
+
|
|
103
|
+
/** LAZY BINDING: Push undefined, primitives create bindings for used props only */
|
|
104
|
+
export function ensureCapacity(index: number): void {
|
|
105
|
+
while (flexDirection.length <= index) {
|
|
106
|
+
flexDirection.push(undefined as any)
|
|
107
|
+
flexWrap.push(undefined as any)
|
|
108
|
+
justifyContent.push(undefined as any)
|
|
109
|
+
alignItems.push(undefined as any)
|
|
110
|
+
alignContent.push(undefined as any)
|
|
111
|
+
flexGrow.push(undefined as any)
|
|
112
|
+
flexShrink.push(undefined as any)
|
|
113
|
+
flexBasis.push(undefined as any)
|
|
114
|
+
alignSelf.push(undefined as any)
|
|
115
|
+
order.push(undefined as any)
|
|
116
|
+
position.push(undefined as any)
|
|
117
|
+
top.push(undefined as any)
|
|
118
|
+
right.push(undefined as any)
|
|
119
|
+
bottom.push(undefined as any)
|
|
120
|
+
left.push(undefined as any)
|
|
121
|
+
borderTop.push(undefined as any)
|
|
122
|
+
borderRight.push(undefined as any)
|
|
123
|
+
borderBottom.push(undefined as any)
|
|
124
|
+
borderLeft.push(undefined as any)
|
|
125
|
+
zIndex.push(undefined as any)
|
|
126
|
+
overflow.push(undefined as any)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function clearAtIndex(index: number): void {
|
|
131
|
+
if (index < flexDirection.length) {
|
|
132
|
+
disconnectBinding(flexDirection[index])
|
|
133
|
+
disconnectBinding(flexWrap[index])
|
|
134
|
+
disconnectBinding(justifyContent[index])
|
|
135
|
+
disconnectBinding(alignItems[index])
|
|
136
|
+
disconnectBinding(alignContent[index])
|
|
137
|
+
disconnectBinding(flexGrow[index])
|
|
138
|
+
disconnectBinding(flexShrink[index])
|
|
139
|
+
disconnectBinding(flexBasis[index])
|
|
140
|
+
disconnectBinding(alignSelf[index])
|
|
141
|
+
disconnectBinding(order[index])
|
|
142
|
+
disconnectBinding(position[index])
|
|
143
|
+
disconnectBinding(top[index])
|
|
144
|
+
disconnectBinding(right[index])
|
|
145
|
+
disconnectBinding(bottom[index])
|
|
146
|
+
disconnectBinding(left[index])
|
|
147
|
+
disconnectBinding(borderTop[index])
|
|
148
|
+
disconnectBinding(borderRight[index])
|
|
149
|
+
disconnectBinding(borderBottom[index])
|
|
150
|
+
disconnectBinding(borderLeft[index])
|
|
151
|
+
disconnectBinding(zIndex[index])
|
|
152
|
+
disconnectBinding(overflow[index])
|
|
153
|
+
flexDirection[index] = undefined as any
|
|
154
|
+
flexWrap[index] = undefined as any
|
|
155
|
+
justifyContent[index] = undefined as any
|
|
156
|
+
alignItems[index] = undefined as any
|
|
157
|
+
alignContent[index] = undefined as any
|
|
158
|
+
flexGrow[index] = undefined as any
|
|
159
|
+
flexShrink[index] = undefined as any
|
|
160
|
+
flexBasis[index] = undefined as any
|
|
161
|
+
alignSelf[index] = undefined as any
|
|
162
|
+
order[index] = undefined as any
|
|
163
|
+
position[index] = undefined as any
|
|
164
|
+
top[index] = undefined as any
|
|
165
|
+
right[index] = undefined as any
|
|
166
|
+
bottom[index] = undefined as any
|
|
167
|
+
left[index] = undefined as any
|
|
168
|
+
borderTop[index] = undefined as any
|
|
169
|
+
borderRight[index] = undefined as any
|
|
170
|
+
borderBottom[index] = undefined as any
|
|
171
|
+
borderLeft[index] = undefined as any
|
|
172
|
+
zIndex[index] = undefined as any
|
|
173
|
+
overflow[index] = undefined as any
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Framework - Spacing Arrays
|
|
3
|
+
*
|
|
4
|
+
* Margin, padding, and gap values.
|
|
5
|
+
* All values are in terminal cells (integers).
|
|
6
|
+
*
|
|
7
|
+
* CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
|
|
8
|
+
* state() proxies snapshot getter values, breaking reactivity.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// MARGIN - Space outside the component (offsets position in parent)
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
/** Top margin - adds space above the component (in cells) */
|
|
18
|
+
export const marginTop: Binding<number>[] = []
|
|
19
|
+
|
|
20
|
+
/** Right margin - adds space to the right of the component (in cells) */
|
|
21
|
+
export const marginRight: Binding<number>[] = []
|
|
22
|
+
|
|
23
|
+
/** Bottom margin - adds space below the component (in cells) */
|
|
24
|
+
export const marginBottom: Binding<number>[] = []
|
|
25
|
+
|
|
26
|
+
/** Left margin - adds space to the left of the component (in cells) */
|
|
27
|
+
export const marginLeft: Binding<number>[] = []
|
|
28
|
+
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// PADDING - Space inside the component (reduces content area)
|
|
31
|
+
// =============================================================================
|
|
32
|
+
|
|
33
|
+
/** Top padding - pushes content down from top edge (in cells) */
|
|
34
|
+
export const paddingTop: Binding<number>[] = []
|
|
35
|
+
|
|
36
|
+
/** Right padding - pushes content left from right edge (in cells) */
|
|
37
|
+
export const paddingRight: Binding<number>[] = []
|
|
38
|
+
|
|
39
|
+
/** Bottom padding - pushes content up from bottom edge (in cells) */
|
|
40
|
+
export const paddingBottom: Binding<number>[] = []
|
|
41
|
+
|
|
42
|
+
/** Left padding - pushes content right from left edge (in cells) */
|
|
43
|
+
export const paddingLeft: Binding<number>[] = []
|
|
44
|
+
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// GAP - Space between flex items (CSS gap property)
|
|
47
|
+
// =============================================================================
|
|
48
|
+
|
|
49
|
+
/** Gap between flex items in both directions (in cells) */
|
|
50
|
+
export const gap: Binding<number>[] = []
|
|
51
|
+
|
|
52
|
+
/** Row gap - vertical space between wrapped lines (in cells) */
|
|
53
|
+
export const rowGap: Binding<number>[] = []
|
|
54
|
+
|
|
55
|
+
/** Column gap - horizontal space between items in a row (in cells) */
|
|
56
|
+
export const columnGap: Binding<number>[] = []
|
|
57
|
+
|
|
58
|
+
/** LAZY BINDING: Push undefined, primitives create bindings for used props only */
|
|
59
|
+
export function ensureCapacity(index: number): void {
|
|
60
|
+
while (marginTop.length <= index) {
|
|
61
|
+
marginTop.push(undefined as any)
|
|
62
|
+
marginRight.push(undefined as any)
|
|
63
|
+
marginBottom.push(undefined as any)
|
|
64
|
+
marginLeft.push(undefined as any)
|
|
65
|
+
paddingTop.push(undefined as any)
|
|
66
|
+
paddingRight.push(undefined as any)
|
|
67
|
+
paddingBottom.push(undefined as any)
|
|
68
|
+
paddingLeft.push(undefined as any)
|
|
69
|
+
gap.push(undefined as any)
|
|
70
|
+
rowGap.push(undefined as any)
|
|
71
|
+
columnGap.push(undefined as any)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function clearAtIndex(index: number): void {
|
|
76
|
+
if (index < marginTop.length) {
|
|
77
|
+
disconnectBinding(marginTop[index])
|
|
78
|
+
disconnectBinding(marginRight[index])
|
|
79
|
+
disconnectBinding(marginBottom[index])
|
|
80
|
+
disconnectBinding(marginLeft[index])
|
|
81
|
+
disconnectBinding(paddingTop[index])
|
|
82
|
+
disconnectBinding(paddingRight[index])
|
|
83
|
+
disconnectBinding(paddingBottom[index])
|
|
84
|
+
disconnectBinding(paddingLeft[index])
|
|
85
|
+
disconnectBinding(gap[index])
|
|
86
|
+
disconnectBinding(rowGap[index])
|
|
87
|
+
disconnectBinding(columnGap[index])
|
|
88
|
+
marginTop[index] = undefined as any
|
|
89
|
+
marginRight[index] = undefined as any
|
|
90
|
+
marginBottom[index] = undefined as any
|
|
91
|
+
marginLeft[index] = undefined as any
|
|
92
|
+
paddingTop[index] = undefined as any
|
|
93
|
+
paddingRight[index] = undefined as any
|
|
94
|
+
paddingBottom[index] = undefined as any
|
|
95
|
+
paddingLeft[index] = undefined as any
|
|
96
|
+
gap[index] = undefined as any
|
|
97
|
+
rowGap[index] = undefined as any
|
|
98
|
+
columnGap[index] = undefined as any
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Framework - Text Arrays
|
|
3
|
+
*
|
|
4
|
+
* Text content and styling for text/input components.
|
|
5
|
+
*
|
|
6
|
+
* CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
|
|
7
|
+
* state() proxies snapshot getter values, breaking reactivity.
|
|
8
|
+
*
|
|
9
|
+
* Flow: user signal → bind() → Binding stored → unwrap() reads .value → dependency!
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
|
|
13
|
+
import type { CellAttrs } from '../../types'
|
|
14
|
+
import { Attr } from '../../types'
|
|
15
|
+
|
|
16
|
+
// Text content - Regular array to preserve binding getters
|
|
17
|
+
export const textContent: Binding<string>[] = []
|
|
18
|
+
|
|
19
|
+
// Text styling (CellAttrs bitfield)
|
|
20
|
+
export const textAttrs: Binding<CellAttrs>[] = []
|
|
21
|
+
|
|
22
|
+
// Text alignment: 0=left, 1=center, 2=right
|
|
23
|
+
export const textAlign: Binding<number>[] = []
|
|
24
|
+
|
|
25
|
+
// Text wrapping: 0=nowrap, 1=wrap, 2=truncate
|
|
26
|
+
export const textWrap: Binding<number>[] = []
|
|
27
|
+
|
|
28
|
+
// Ellipsis for truncated text
|
|
29
|
+
export const ellipsis: Binding<string>[] = []
|
|
30
|
+
|
|
31
|
+
/** LAZY BINDING: Push undefined, primitives create bindings for used props only */
|
|
32
|
+
export function ensureCapacity(index: number): void {
|
|
33
|
+
while (textContent.length <= index) {
|
|
34
|
+
textContent.push(undefined as any)
|
|
35
|
+
textAttrs.push(undefined as any)
|
|
36
|
+
textAlign.push(undefined as any)
|
|
37
|
+
textWrap.push(undefined as any)
|
|
38
|
+
ellipsis.push(undefined as any)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function clearAtIndex(index: number): void {
|
|
43
|
+
if (index < textContent.length) {
|
|
44
|
+
disconnectBinding(textContent[index])
|
|
45
|
+
disconnectBinding(textAttrs[index])
|
|
46
|
+
disconnectBinding(textAlign[index])
|
|
47
|
+
disconnectBinding(textWrap[index])
|
|
48
|
+
disconnectBinding(ellipsis[index])
|
|
49
|
+
textContent[index] = undefined as any
|
|
50
|
+
textAttrs[index] = undefined as any
|
|
51
|
+
textAlign[index] = undefined as any
|
|
52
|
+
textWrap[index] = undefined as any
|
|
53
|
+
ellipsis[index] = undefined as any
|
|
54
|
+
}
|
|
55
|
+
}
|