@rlabs-inc/tui 0.1.0 → 0.2.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 +126 -13
- package/index.ts +11 -5
- package/package.json +2 -2
- package/src/api/mount.ts +42 -27
- 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 +303 -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 +6 -0
- package/src/renderer/input.ts +0 -518
|
@@ -4,97 +4,82 @@
|
|
|
4
4
|
* Margin, padding, and gap values.
|
|
5
5
|
* All values are in terminal cells (integers).
|
|
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
|
|
|
11
|
-
import {
|
|
10
|
+
import { slotArray, type SlotArray } from '@rlabs-inc/signals'
|
|
12
11
|
|
|
13
12
|
// =============================================================================
|
|
14
13
|
// MARGIN - Space outside the component (offsets position in parent)
|
|
15
14
|
// =============================================================================
|
|
16
15
|
|
|
17
16
|
/** Top margin - adds space above the component (in cells) */
|
|
18
|
-
export const marginTop:
|
|
17
|
+
export const marginTop: SlotArray<number> = slotArray<number>(0)
|
|
19
18
|
|
|
20
19
|
/** Right margin - adds space to the right of the component (in cells) */
|
|
21
|
-
export const marginRight:
|
|
20
|
+
export const marginRight: SlotArray<number> = slotArray<number>(0)
|
|
22
21
|
|
|
23
22
|
/** Bottom margin - adds space below the component (in cells) */
|
|
24
|
-
export const marginBottom:
|
|
23
|
+
export const marginBottom: SlotArray<number> = slotArray<number>(0)
|
|
25
24
|
|
|
26
25
|
/** Left margin - adds space to the left of the component (in cells) */
|
|
27
|
-
export const marginLeft:
|
|
26
|
+
export const marginLeft: SlotArray<number> = slotArray<number>(0)
|
|
28
27
|
|
|
29
28
|
// =============================================================================
|
|
30
29
|
// PADDING - Space inside the component (reduces content area)
|
|
31
30
|
// =============================================================================
|
|
32
31
|
|
|
33
32
|
/** Top padding - pushes content down from top edge (in cells) */
|
|
34
|
-
export const paddingTop:
|
|
33
|
+
export const paddingTop: SlotArray<number> = slotArray<number>(0)
|
|
35
34
|
|
|
36
35
|
/** Right padding - pushes content left from right edge (in cells) */
|
|
37
|
-
export const paddingRight:
|
|
36
|
+
export const paddingRight: SlotArray<number> = slotArray<number>(0)
|
|
38
37
|
|
|
39
38
|
/** Bottom padding - pushes content up from bottom edge (in cells) */
|
|
40
|
-
export const paddingBottom:
|
|
39
|
+
export const paddingBottom: SlotArray<number> = slotArray<number>(0)
|
|
41
40
|
|
|
42
41
|
/** Left padding - pushes content right from left edge (in cells) */
|
|
43
|
-
export const paddingLeft:
|
|
42
|
+
export const paddingLeft: SlotArray<number> = slotArray<number>(0)
|
|
44
43
|
|
|
45
44
|
// =============================================================================
|
|
46
45
|
// GAP - Space between flex items (CSS gap property)
|
|
47
46
|
// =============================================================================
|
|
48
47
|
|
|
49
48
|
/** Gap between flex items in both directions (in cells) */
|
|
50
|
-
export const gap:
|
|
49
|
+
export const gap: SlotArray<number> = slotArray<number>(0)
|
|
51
50
|
|
|
52
51
|
/** Row gap - vertical space between wrapped lines (in cells) */
|
|
53
|
-
export const rowGap:
|
|
52
|
+
export const rowGap: SlotArray<number> = slotArray<number>(0)
|
|
54
53
|
|
|
55
54
|
/** Column gap - horizontal space between items in a row (in cells) */
|
|
56
|
-
export const columnGap:
|
|
55
|
+
export const columnGap: SlotArray<number> = slotArray<number>(0)
|
|
57
56
|
|
|
58
|
-
/**
|
|
57
|
+
/** Ensure capacity for all spacing arrays */
|
|
59
58
|
export function ensureCapacity(index: number): void {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
columnGap.push(undefined as any)
|
|
72
|
-
}
|
|
59
|
+
marginTop.ensureCapacity(index)
|
|
60
|
+
marginRight.ensureCapacity(index)
|
|
61
|
+
marginBottom.ensureCapacity(index)
|
|
62
|
+
marginLeft.ensureCapacity(index)
|
|
63
|
+
paddingTop.ensureCapacity(index)
|
|
64
|
+
paddingRight.ensureCapacity(index)
|
|
65
|
+
paddingBottom.ensureCapacity(index)
|
|
66
|
+
paddingLeft.ensureCapacity(index)
|
|
67
|
+
gap.ensureCapacity(index)
|
|
68
|
+
rowGap.ensureCapacity(index)
|
|
69
|
+
columnGap.ensureCapacity(index)
|
|
73
70
|
}
|
|
74
71
|
|
|
72
|
+
/** Clear slot at index (reset to default) */
|
|
75
73
|
export function clearAtIndex(index: number): void {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
}
|
|
74
|
+
marginTop.clear(index)
|
|
75
|
+
marginRight.clear(index)
|
|
76
|
+
marginBottom.clear(index)
|
|
77
|
+
marginLeft.clear(index)
|
|
78
|
+
paddingTop.clear(index)
|
|
79
|
+
paddingRight.clear(index)
|
|
80
|
+
paddingBottom.clear(index)
|
|
81
|
+
paddingLeft.clear(index)
|
|
82
|
+
gap.clear(index)
|
|
83
|
+
rowGap.clear(index)
|
|
84
|
+
columnGap.clear(index)
|
|
100
85
|
}
|
|
@@ -3,53 +3,45 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Text content and styling for text/input components.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Uses slotArray for stable reactive cells that NEVER get replaced.
|
|
7
|
+
* This fixes the bind() tracking bug where deriveds miss updates
|
|
8
|
+
* when binding objects are replaced.
|
|
8
9
|
*
|
|
9
|
-
* Flow: user signal →
|
|
10
|
+
* Flow: user signal → setSource() → Slot tracks → arr[i] reads value → dependency!
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
|
-
import {
|
|
13
|
+
import { slotArray, type SlotArray } from '@rlabs-inc/signals'
|
|
13
14
|
import type { CellAttrs } from '../../types'
|
|
14
|
-
import { Attr } from '../../types'
|
|
15
15
|
|
|
16
|
-
// Text content -
|
|
17
|
-
export const textContent:
|
|
16
|
+
// Text content - SlotArray auto-tracks and auto-unwraps
|
|
17
|
+
export const textContent: SlotArray<string> = slotArray<string>('')
|
|
18
18
|
|
|
19
19
|
// Text styling (CellAttrs bitfield)
|
|
20
|
-
export const textAttrs:
|
|
20
|
+
export const textAttrs: SlotArray<CellAttrs> = slotArray<CellAttrs>(0)
|
|
21
21
|
|
|
22
22
|
// Text alignment: 0=left, 1=center, 2=right
|
|
23
|
-
export const textAlign:
|
|
23
|
+
export const textAlign: SlotArray<number> = slotArray<number>(0)
|
|
24
24
|
|
|
25
25
|
// Text wrapping: 0=nowrap, 1=wrap, 2=truncate
|
|
26
|
-
export const textWrap:
|
|
26
|
+
export const textWrap: SlotArray<number> = slotArray<number>(1) // wrap by default
|
|
27
27
|
|
|
28
28
|
// Ellipsis for truncated text
|
|
29
|
-
export const ellipsis:
|
|
29
|
+
export const ellipsis: SlotArray<string> = slotArray<string>('...')
|
|
30
30
|
|
|
31
|
-
/**
|
|
31
|
+
/** Ensure capacity for all text arrays */
|
|
32
32
|
export function ensureCapacity(index: number): void {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
ellipsis.push(undefined as any)
|
|
39
|
-
}
|
|
33
|
+
textContent.ensureCapacity(index)
|
|
34
|
+
textAttrs.ensureCapacity(index)
|
|
35
|
+
textAlign.ensureCapacity(index)
|
|
36
|
+
textWrap.ensureCapacity(index)
|
|
37
|
+
ellipsis.ensureCapacity(index)
|
|
40
38
|
}
|
|
41
39
|
|
|
40
|
+
/** Clear slot at index (reset to default) */
|
|
42
41
|
export function clearAtIndex(index: number): void {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
}
|
|
42
|
+
textContent.clear(index)
|
|
43
|
+
textAttrs.clear(index)
|
|
44
|
+
textAlign.clear(index)
|
|
45
|
+
textWrap.clear(index)
|
|
46
|
+
ellipsis.clear(index)
|
|
55
47
|
}
|
|
@@ -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)
|