@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
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* Colors, borders, and visual styling.
|
|
5
5
|
* Colors stored as RGBA objects for alpha blending support.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* state() proxies snapshot getter values, breaking reactivity.
|
|
7
|
+
* Uses slotArray for stable reactive cells that NEVER get replaced.
|
|
9
8
|
*
|
|
10
9
|
* Border styles:
|
|
11
10
|
* 0 = none
|
|
@@ -23,118 +22,100 @@
|
|
|
23
22
|
* Per-side borders can have independent styles.
|
|
24
23
|
*/
|
|
25
24
|
|
|
26
|
-
import {
|
|
25
|
+
import { slotArray, type SlotArray } from '@rlabs-inc/signals'
|
|
27
26
|
import type { RGBA } from '../../types'
|
|
28
27
|
|
|
29
28
|
// =============================================================================
|
|
30
|
-
//
|
|
29
|
+
// DEFAULT VALUES
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
const DEFAULT_FOCUS_COLOR: RGBA = { r: 100, g: 149, b: 237, a: 255 } // cornflowerblue
|
|
33
|
+
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// COLORS - SlotArrays for stable reactive cells
|
|
31
36
|
// =============================================================================
|
|
32
37
|
|
|
33
38
|
/** Foreground color (text) - null means inherit from parent */
|
|
34
|
-
export const fgColor:
|
|
39
|
+
export const fgColor: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
35
40
|
|
|
36
41
|
/** Background color - null means transparent/inherit */
|
|
37
|
-
export const bgColor:
|
|
42
|
+
export const bgColor: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
38
43
|
|
|
39
44
|
/** Opacity 0-1 (1 = fully opaque) */
|
|
40
|
-
export const opacity:
|
|
45
|
+
export const opacity: SlotArray<number> = slotArray<number>(1)
|
|
41
46
|
|
|
42
47
|
// =============================================================================
|
|
43
48
|
// BORDERS - Per-side independent styles
|
|
44
49
|
// =============================================================================
|
|
45
50
|
|
|
46
51
|
/** Default border style for all sides (0-10) */
|
|
47
|
-
export const borderStyle:
|
|
52
|
+
export const borderStyle: SlotArray<number> = slotArray<number>(0)
|
|
48
53
|
|
|
49
54
|
/** Default border color for all sides - null means use foreground */
|
|
50
|
-
export const borderColor:
|
|
55
|
+
export const borderColor: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
51
56
|
|
|
52
57
|
/** Top border style (0=none or inherit from borderStyle, 1-10=specific style) */
|
|
53
|
-
export const borderTop:
|
|
58
|
+
export const borderTop: SlotArray<number> = slotArray<number>(0)
|
|
54
59
|
|
|
55
60
|
/** Right border style */
|
|
56
|
-
export const borderRight:
|
|
61
|
+
export const borderRight: SlotArray<number> = slotArray<number>(0)
|
|
57
62
|
|
|
58
63
|
/** Bottom border style */
|
|
59
|
-
export const borderBottom:
|
|
64
|
+
export const borderBottom: SlotArray<number> = slotArray<number>(0)
|
|
60
65
|
|
|
61
66
|
/** Left border style */
|
|
62
|
-
export const borderLeft:
|
|
67
|
+
export const borderLeft: SlotArray<number> = slotArray<number>(0)
|
|
63
68
|
|
|
64
69
|
/** Per-side border colors - null means use borderColor or foreground */
|
|
65
|
-
export const borderColorTop:
|
|
66
|
-
export const borderColorRight:
|
|
67
|
-
export const borderColorBottom:
|
|
68
|
-
export const borderColorLeft:
|
|
70
|
+
export const borderColorTop: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
71
|
+
export const borderColorRight: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
72
|
+
export const borderColorBottom: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
73
|
+
export const borderColorLeft: SlotArray<RGBA | null> = slotArray<RGBA | null>(null)
|
|
69
74
|
|
|
70
75
|
// =============================================================================
|
|
71
76
|
// FOCUS RING
|
|
72
77
|
// =============================================================================
|
|
73
78
|
|
|
74
79
|
/** Show focus ring when focused (1=yes, 0=no) */
|
|
75
|
-
export const showFocusRing:
|
|
80
|
+
export const showFocusRing: SlotArray<number> = slotArray<number>(0)
|
|
76
81
|
|
|
77
82
|
/** Focus ring color */
|
|
78
|
-
export const focusRingColor:
|
|
79
|
-
|
|
80
|
-
// =============================================================================
|
|
81
|
-
// DEFAULT VALUES
|
|
82
|
-
// =============================================================================
|
|
83
|
-
|
|
84
|
-
const DEFAULT_FOCUS_COLOR: RGBA = { r: 100, g: 149, b: 237, a: 255 } // cornflowerblue
|
|
83
|
+
export const focusRingColor: SlotArray<RGBA> = slotArray<RGBA>(DEFAULT_FOCUS_COLOR)
|
|
85
84
|
|
|
86
|
-
/**
|
|
85
|
+
/** Ensure capacity for all visual arrays */
|
|
87
86
|
export function ensureCapacity(index: number): void {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
focusRingColor.push(undefined as any)
|
|
104
|
-
}
|
|
87
|
+
fgColor.ensureCapacity(index)
|
|
88
|
+
bgColor.ensureCapacity(index)
|
|
89
|
+
opacity.ensureCapacity(index)
|
|
90
|
+
borderStyle.ensureCapacity(index)
|
|
91
|
+
borderColor.ensureCapacity(index)
|
|
92
|
+
borderTop.ensureCapacity(index)
|
|
93
|
+
borderRight.ensureCapacity(index)
|
|
94
|
+
borderBottom.ensureCapacity(index)
|
|
95
|
+
borderLeft.ensureCapacity(index)
|
|
96
|
+
borderColorTop.ensureCapacity(index)
|
|
97
|
+
borderColorRight.ensureCapacity(index)
|
|
98
|
+
borderColorBottom.ensureCapacity(index)
|
|
99
|
+
borderColorLeft.ensureCapacity(index)
|
|
100
|
+
showFocusRing.ensureCapacity(index)
|
|
101
|
+
focusRingColor.ensureCapacity(index)
|
|
105
102
|
}
|
|
106
103
|
|
|
104
|
+
/** Clear slot at index (reset to default) */
|
|
107
105
|
export function clearAtIndex(index: number): void {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
disconnectBinding(focusRingColor[index])
|
|
124
|
-
fgColor[index] = undefined as any
|
|
125
|
-
bgColor[index] = undefined as any
|
|
126
|
-
opacity[index] = undefined as any
|
|
127
|
-
borderStyle[index] = undefined as any
|
|
128
|
-
borderColor[index] = undefined as any
|
|
129
|
-
borderTop[index] = undefined as any
|
|
130
|
-
borderRight[index] = undefined as any
|
|
131
|
-
borderBottom[index] = undefined as any
|
|
132
|
-
borderLeft[index] = undefined as any
|
|
133
|
-
borderColorTop[index] = undefined as any
|
|
134
|
-
borderColorRight[index] = undefined as any
|
|
135
|
-
borderColorBottom[index] = undefined as any
|
|
136
|
-
borderColorLeft[index] = undefined as any
|
|
137
|
-
showFocusRing[index] = undefined as any
|
|
138
|
-
focusRingColor[index] = undefined as any
|
|
139
|
-
}
|
|
106
|
+
fgColor.clear(index)
|
|
107
|
+
bgColor.clear(index)
|
|
108
|
+
opacity.clear(index)
|
|
109
|
+
borderStyle.clear(index)
|
|
110
|
+
borderColor.clear(index)
|
|
111
|
+
borderTop.clear(index)
|
|
112
|
+
borderRight.clear(index)
|
|
113
|
+
borderBottom.clear(index)
|
|
114
|
+
borderLeft.clear(index)
|
|
115
|
+
borderColorTop.clear(index)
|
|
116
|
+
borderColorRight.clear(index)
|
|
117
|
+
borderColorBottom.clear(index)
|
|
118
|
+
borderColorLeft.clear(index)
|
|
119
|
+
showFocusRing.clear(index)
|
|
120
|
+
focusRingColor.clear(index)
|
|
140
121
|
}
|
|
@@ -19,9 +19,9 @@ export function getInheritedFg(index: number): RGBA {
|
|
|
19
19
|
let current: number = index
|
|
20
20
|
|
|
21
21
|
while (current >= 0) {
|
|
22
|
-
const fg =
|
|
22
|
+
const fg = visual.fgColor[current]
|
|
23
23
|
if (fg !== null && fg !== undefined) return fg
|
|
24
|
-
const parent =
|
|
24
|
+
const parent = core.parentIndex[current]
|
|
25
25
|
if (parent === undefined || parent < 0) break
|
|
26
26
|
current = parent
|
|
27
27
|
}
|
|
@@ -37,9 +37,9 @@ export function getInheritedBg(index: number): RGBA {
|
|
|
37
37
|
let current: number = index
|
|
38
38
|
|
|
39
39
|
while (current >= 0) {
|
|
40
|
-
const bg =
|
|
40
|
+
const bg = visual.bgColor[current]
|
|
41
41
|
if (bg !== null && bg !== undefined) return bg
|
|
42
|
-
const parent =
|
|
42
|
+
const parent = core.parentIndex[current]
|
|
43
43
|
if (parent === undefined || parent < 0) break
|
|
44
44
|
current = parent
|
|
45
45
|
}
|
|
@@ -59,11 +59,11 @@ export function getInheritedBorderColor(index: number, side: 'top' | 'right' | '
|
|
|
59
59
|
left: visual.borderColorLeft,
|
|
60
60
|
}[side]
|
|
61
61
|
|
|
62
|
-
const color =
|
|
62
|
+
const color = colorArray[index]
|
|
63
63
|
if (color !== null && color !== undefined) return color
|
|
64
64
|
|
|
65
65
|
// Try unified border color
|
|
66
|
-
const unifiedColor =
|
|
66
|
+
const unifiedColor = visual.borderColor[index]
|
|
67
67
|
if (unifiedColor !== null && unifiedColor !== undefined) return unifiedColor
|
|
68
68
|
|
|
69
69
|
// Fall back to foreground color
|
|
@@ -80,14 +80,14 @@ export function getBorderColors(index: number): {
|
|
|
80
80
|
left: RGBA
|
|
81
81
|
} {
|
|
82
82
|
const fg = getInheritedFg(index)
|
|
83
|
-
const unified =
|
|
83
|
+
const unified = visual.borderColor[index]
|
|
84
84
|
const fallback = unified ?? fg
|
|
85
85
|
|
|
86
86
|
return {
|
|
87
|
-
top:
|
|
88
|
-
right:
|
|
89
|
-
bottom:
|
|
90
|
-
left:
|
|
87
|
+
top: visual.borderColorTop[index] ?? fallback,
|
|
88
|
+
right: visual.borderColorRight[index] ?? fallback,
|
|
89
|
+
bottom: visual.borderColorBottom[index] ?? fallback,
|
|
90
|
+
left: visual.borderColorLeft[index] ?? fallback,
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -101,13 +101,13 @@ export function getBorderStyles(index: number): {
|
|
|
101
101
|
bottom: number
|
|
102
102
|
left: number
|
|
103
103
|
} {
|
|
104
|
-
const unified =
|
|
104
|
+
const unified = visual.borderStyle[index] || 0
|
|
105
105
|
|
|
106
106
|
return {
|
|
107
|
-
top:
|
|
108
|
-
right:
|
|
109
|
-
bottom:
|
|
110
|
-
left:
|
|
107
|
+
top: visual.borderTop[index] || unified,
|
|
108
|
+
right: visual.borderRight[index] || unified,
|
|
109
|
+
bottom: visual.borderBottom[index] || unified,
|
|
110
|
+
left: visual.borderLeft[index] || unified,
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -127,11 +127,11 @@ export function getEffectiveOpacity(index: number): number {
|
|
|
127
127
|
let current: number | undefined = index
|
|
128
128
|
|
|
129
129
|
while (current !== undefined && current >= 0) {
|
|
130
|
-
const nodeOpacity =
|
|
130
|
+
const nodeOpacity = visual.opacity[current]
|
|
131
131
|
if (nodeOpacity !== undefined && nodeOpacity !== 1) {
|
|
132
132
|
opacity *= nodeOpacity
|
|
133
133
|
}
|
|
134
|
-
current =
|
|
134
|
+
current = core.parentIndex[current]
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
return opacity
|
package/src/engine/registry.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { ReactiveSet } from '@rlabs-inc/signals'
|
|
14
14
|
import { ensureAllCapacity, clearAllAtIndex, resetAllArrays } from './arrays'
|
|
15
|
+
import { parentIndex as parentIndexArray } from './arrays/core'
|
|
15
16
|
import { resetTitanArrays } from '../pipeline/layout/titan-engine'
|
|
16
17
|
|
|
17
18
|
// =============================================================================
|
|
@@ -101,6 +102,7 @@ export function allocateIndex(id?: string): number {
|
|
|
101
102
|
|
|
102
103
|
/**
|
|
103
104
|
* Release an index back to the pool.
|
|
105
|
+
* Also recursively releases all children!
|
|
104
106
|
*
|
|
105
107
|
* @param index - The index to release.
|
|
106
108
|
*/
|
|
@@ -108,6 +110,19 @@ export function releaseIndex(index: number): void {
|
|
|
108
110
|
const id = indexToId.get(index)
|
|
109
111
|
if (id === undefined) return
|
|
110
112
|
|
|
113
|
+
// FIRST: Find and release all children (recursive!)
|
|
114
|
+
// We collect children first to avoid modifying while iterating
|
|
115
|
+
const children: number[] = []
|
|
116
|
+
for (const childIndex of allocatedIndices) {
|
|
117
|
+
if (parentIndexArray[childIndex] === index) {
|
|
118
|
+
children.push(childIndex)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Release children recursively
|
|
122
|
+
for (const childIndex of children) {
|
|
123
|
+
releaseIndex(childIndex)
|
|
124
|
+
}
|
|
125
|
+
|
|
111
126
|
// Clean up mappings
|
|
112
127
|
idToIndex.delete(id)
|
|
113
128
|
indexToId.delete(index)
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* HitGrid updates are returned as data to be applied by the render effect.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { derived,
|
|
19
|
+
import { derived, neverEquals } from '@rlabs-inc/signals'
|
|
20
20
|
import type { FrameBuffer, RGBA } from '../types'
|
|
21
21
|
import { ComponentType } from '../types'
|
|
22
22
|
import { Colors, TERMINAL_DEFAULT, rgbaBlend, rgbaLerp } from '../types/color'
|
|
@@ -46,10 +46,10 @@ import {
|
|
|
46
46
|
// Import arrays
|
|
47
47
|
import * as core from '../engine/arrays/core'
|
|
48
48
|
import * as visual from '../engine/arrays/visual'
|
|
49
|
-
import *
|
|
50
|
-
import *
|
|
51
|
-
import *
|
|
52
|
-
import *
|
|
49
|
+
import *as text from '../engine/arrays/text'
|
|
50
|
+
import *as spacing from '../engine/arrays/spacing'
|
|
51
|
+
import *as layout from '../engine/arrays/layout'
|
|
52
|
+
import *as interaction from '../engine/arrays/interaction'
|
|
53
53
|
|
|
54
54
|
// Import layout derived
|
|
55
55
|
import { layoutDerived, terminalWidth, terminalHeight, renderMode } from './layout'
|
|
@@ -119,10 +119,10 @@ export const frameBufferDerived = derived((): FrameBufferResult => {
|
|
|
119
119
|
|
|
120
120
|
for (const i of indices) {
|
|
121
121
|
if (core.componentType[i] === ComponentType.NONE) continue
|
|
122
|
-
const vis =
|
|
122
|
+
const vis = core.visible[i]
|
|
123
123
|
if (vis === 0 || vis === false) continue
|
|
124
124
|
|
|
125
|
-
const parent =
|
|
125
|
+
const parent = core.parentIndex[i] ?? -1
|
|
126
126
|
if (parent === -1) {
|
|
127
127
|
rootIndices.push(i)
|
|
128
128
|
} else {
|
|
@@ -136,11 +136,11 @@ export const frameBufferDerived = derived((): FrameBufferResult => {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// Sort roots by zIndex
|
|
139
|
-
rootIndices.sort((a, b) => (
|
|
139
|
+
rootIndices.sort((a, b) => (layout.zIndex[a] || 0) - (layout.zIndex[b] || 0))
|
|
140
140
|
|
|
141
141
|
// Sort children by zIndex
|
|
142
142
|
for (const children of childMap.values()) {
|
|
143
|
-
children.sort((a, b) => (
|
|
143
|
+
children.sort((a, b) => (layout.zIndex[a] || 0) - (layout.zIndex[b] || 0))
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// Render tree recursively
|
|
@@ -180,7 +180,7 @@ function renderComponent(
|
|
|
180
180
|
parentScrollX: number
|
|
181
181
|
): void {
|
|
182
182
|
// Skip invisible/invalid components
|
|
183
|
-
const vis =
|
|
183
|
+
const vis = core.visible[index]
|
|
184
184
|
if (vis === 0 || vis === false) return
|
|
185
185
|
if (core.componentType[index] === ComponentType.NONE) return
|
|
186
186
|
|
|
@@ -239,10 +239,10 @@ function renderComponent(
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
// Calculate content area (inside borders and padding)
|
|
242
|
-
const padTop = (
|
|
243
|
-
const padRight = (
|
|
244
|
-
const padBottom = (
|
|
245
|
-
const padLeft = (
|
|
242
|
+
const padTop = (spacing.paddingTop[index] || 0) + (hasAnyBorder && borderStyles.top > 0 ? 1 : 0)
|
|
243
|
+
const padRight = (spacing.paddingRight[index] || 0) + (hasAnyBorder && borderStyles.right > 0 ? 1 : 0)
|
|
244
|
+
const padBottom = (spacing.paddingBottom[index] || 0) + (hasAnyBorder && borderStyles.bottom > 0 ? 1 : 0)
|
|
245
|
+
const padLeft = (spacing.paddingLeft[index] || 0) + (hasAnyBorder && borderStyles.left > 0 ? 1 : 0)
|
|
246
246
|
|
|
247
247
|
const contentX = x + padLeft
|
|
248
248
|
const contentY = y + padTop
|
|
@@ -292,8 +292,8 @@ function renderComponent(
|
|
|
292
292
|
|
|
293
293
|
// Get this component's scroll offset (scrollable comes from layout, offset from interaction)
|
|
294
294
|
const isScrollable = (computedLayout.scrollable[index] ?? 0) === 1
|
|
295
|
-
const scrollY = isScrollable ? (
|
|
296
|
-
const scrollX = isScrollable ? (
|
|
295
|
+
const scrollY = isScrollable ? (interaction.scrollOffsetY[index] || 0) : 0
|
|
296
|
+
const scrollX = isScrollable ? (interaction.scrollOffsetX[index] || 0) : 0
|
|
297
297
|
|
|
298
298
|
// Accumulated scroll for children
|
|
299
299
|
const childScrollY = parentScrollY + scrollY
|
|
@@ -331,13 +331,13 @@ function renderText(
|
|
|
331
331
|
fg: RGBA,
|
|
332
332
|
clip: ClipRect
|
|
333
333
|
): void {
|
|
334
|
+
// Read through slotArray proxy - same pattern as color reads in inheritance.ts
|
|
334
335
|
const rawValue = text.textContent[index]
|
|
335
|
-
const
|
|
336
|
-
const content = unwrapped == null ? '' : String(unwrapped)
|
|
336
|
+
const content = rawValue == null ? '' : String(rawValue)
|
|
337
337
|
if (!content) return
|
|
338
338
|
|
|
339
|
-
const attrs =
|
|
340
|
-
const align =
|
|
339
|
+
const attrs = text.textAttrs[index] || 0
|
|
340
|
+
const align = text.textAlign[index] || 0
|
|
341
341
|
|
|
342
342
|
// Word wrap the text
|
|
343
343
|
const lines = wrapText(content, w)
|
|
@@ -376,9 +376,9 @@ function renderInput(
|
|
|
376
376
|
fg: RGBA,
|
|
377
377
|
clip: ClipRect
|
|
378
378
|
): void {
|
|
379
|
-
const content =
|
|
380
|
-
const attrs =
|
|
381
|
-
const cursorPos =
|
|
379
|
+
const content = text.textContent[index] || '' // SlotArray auto-unwraps
|
|
380
|
+
const attrs = text.textAttrs[index] || 0
|
|
381
|
+
const cursorPos = interaction.cursorPosition[index] || 0
|
|
382
382
|
|
|
383
383
|
if (w <= 0) return
|
|
384
384
|
|
|
@@ -427,7 +427,7 @@ function renderProgress(
|
|
|
427
427
|
fg: RGBA,
|
|
428
428
|
clip?: ClipRect
|
|
429
429
|
): void {
|
|
430
|
-
const valueStr =
|
|
430
|
+
const valueStr = text.textContent[index] || '0' // SlotArray auto-unwraps
|
|
431
431
|
const progress = Math.max(0, Math.min(1, parseFloat(valueStr) || 0))
|
|
432
432
|
const filled = Math.round(progress * w)
|
|
433
433
|
|
|
@@ -458,8 +458,8 @@ function renderSelect(
|
|
|
458
458
|
fg: RGBA,
|
|
459
459
|
clip: ClipRect
|
|
460
460
|
): void {
|
|
461
|
-
const content =
|
|
462
|
-
const attrs =
|
|
461
|
+
const content = text.textContent[index] || '' // SlotArray auto-unwraps
|
|
462
|
+
const attrs = text.textAttrs[index] || 0
|
|
463
463
|
|
|
464
464
|
// For now, just show current selection
|
|
465
465
|
const displayText = truncateText(content, w - 2) // Leave room for dropdown indicator
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - Return plain output arrays
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { derived, signal } from '@rlabs-inc/signals'
|
|
18
|
+
import { derived, signal, neverEquals } from '@rlabs-inc/signals'
|
|
19
19
|
import { getAllocatedIndices } from '../../engine/registry'
|
|
20
20
|
import { computeLayoutTitan, resetTitanArrays } from './titan-engine'
|
|
21
21
|
import type { ComputedLayout } from './types'
|
|
@@ -87,7 +87,7 @@ export const layoutDerived = derived((): ComputedLayout => {
|
|
|
87
87
|
// TITAN ENGINE: Read arrays, compute, return.
|
|
88
88
|
// Reactivity tracks dependencies as we read - no manual tracking needed.
|
|
89
89
|
return computeLayoutTitan(tw, th, indices, constrainHeight)
|
|
90
|
-
})
|
|
90
|
+
}, { equals: neverEquals })
|
|
91
91
|
|
|
92
92
|
// =============================================================================
|
|
93
93
|
// EXPORTS
|