@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.
Files changed (38) hide show
  1. package/README.md +126 -13
  2. package/index.ts +11 -5
  3. package/package.json +2 -2
  4. package/src/api/mount.ts +42 -27
  5. package/src/engine/arrays/core.ts +13 -21
  6. package/src/engine/arrays/dimensions.ts +22 -32
  7. package/src/engine/arrays/index.ts +88 -86
  8. package/src/engine/arrays/interaction.ts +34 -48
  9. package/src/engine/arrays/layout.ts +67 -92
  10. package/src/engine/arrays/spacing.ts +37 -52
  11. package/src/engine/arrays/text.ts +23 -31
  12. package/src/engine/arrays/visual.ts +56 -75
  13. package/src/engine/inheritance.ts +18 -18
  14. package/src/engine/registry.ts +15 -0
  15. package/src/pipeline/frameBuffer.ts +26 -26
  16. package/src/pipeline/layout/index.ts +2 -2
  17. package/src/pipeline/layout/titan-engine.ts +112 -84
  18. package/src/primitives/animation.ts +194 -0
  19. package/src/primitives/box.ts +74 -86
  20. package/src/primitives/each.ts +87 -0
  21. package/src/primitives/index.ts +7 -0
  22. package/src/primitives/scope.ts +215 -0
  23. package/src/primitives/show.ts +77 -0
  24. package/src/primitives/text.ts +63 -59
  25. package/src/primitives/types.ts +1 -1
  26. package/src/primitives/when.ts +102 -0
  27. package/src/renderer/append-region.ts +303 -0
  28. package/src/renderer/index.ts +4 -2
  29. package/src/renderer/output.ts +11 -34
  30. package/src/state/focus.ts +16 -5
  31. package/src/state/global-keys.ts +184 -0
  32. package/src/state/index.ts +44 -8
  33. package/src/state/input.ts +534 -0
  34. package/src/state/keyboard.ts +98 -674
  35. package/src/state/mouse.ts +163 -340
  36. package/src/state/scroll.ts +7 -9
  37. package/src/types/index.ts +6 -0
  38. package/src/renderer/input.ts +0 -518
package/README.md CHANGED
@@ -57,17 +57,6 @@ setInterval(() => count.value++, 1000)
57
57
  - Bindings for prop connections
58
58
  - Zero reconciliation - reactivity IS the update mechanism
59
59
 
60
- ### Svelte-like .tui Files (Compiler)
61
- ```html
62
- <script>
63
- const name = signal('World')
64
- </script>
65
-
66
- <box width={40} height={3}>
67
- <text content={`Hello, ${name.value}!`} />
68
- </box>
69
- ```
70
-
71
60
  ### State Modules
72
61
  - **keyboard** - Key events, shortcuts, input buffering
73
62
  - **mouse** - Click, hover, drag, wheel events
@@ -110,6 +99,8 @@ bun run examples/tests/03-layout-flex.ts
110
99
 
111
100
  ## Primitives
112
101
 
102
+ ### UI Primitives
103
+
113
104
  | Primitive | Status | Description |
114
105
  |-----------|--------|-------------|
115
106
  | `box` | Complete | Container with flexbox layout |
@@ -118,13 +109,135 @@ bun run examples/tests/03-layout-flex.ts
118
109
  | `select` | Planned | Dropdown selection |
119
110
  | `progress` | Planned | Progress bar |
120
111
 
112
+ ### Template Primitives
113
+
114
+ Reactive control flow for dynamic UIs - no manual effects needed!
115
+
116
+ | Primitive | Purpose | Description |
117
+ |-----------|---------|-------------|
118
+ | `each()` | Lists | Reactive list rendering with keyed reconciliation |
119
+ | `show()` | Conditionals | Show/hide components based on condition |
120
+ | `when()` | Async | Suspense-like pending/success/error states |
121
+
122
+ #### `each()` - Reactive Lists
123
+
124
+ Renders a list of components that automatically updates when the array changes.
125
+
126
+ ```typescript
127
+ import { each, box, text, signal } from '@rlabs-inc/tui'
128
+
129
+ const todos = signal([
130
+ { id: '1', text: 'Learn TUI', done: false },
131
+ { id: '2', text: 'Build app', done: false },
132
+ ])
133
+
134
+ box({
135
+ children: () => {
136
+ each(
137
+ () => todos.value, // Reactive array getter
138
+ (todo) => box({ // Render function per item
139
+ id: `todo-${todo.id}`, // Stable ID for reconciliation
140
+ children: () => {
141
+ text({ content: () => todo.text }) // Props can be reactive too!
142
+ }
143
+ }),
144
+ { key: (todo) => todo.id } // Key function for efficient updates
145
+ )
146
+ }
147
+ })
148
+
149
+ // Add item - UI updates automatically
150
+ todos.value = [...todos.value, { id: '3', text: 'Deploy', done: false }]
151
+
152
+ // Remove item - component is cleaned up automatically
153
+ todos.value = todos.value.filter(t => t.id !== '1')
154
+ ```
155
+
156
+ #### `show()` - Conditional Rendering
157
+
158
+ Shows or hides components based on a reactive condition.
159
+
160
+ ```typescript
161
+ import { show, box, text, signal } from '@rlabs-inc/tui'
162
+
163
+ const isLoggedIn = signal(false)
164
+
165
+ box({
166
+ children: () => {
167
+ show(
168
+ () => isLoggedIn.value, // Condition getter
169
+ () => box({ // Render when true
170
+ children: () => {
171
+ text({ content: 'Welcome back!' })
172
+ }
173
+ }),
174
+ () => text({ content: 'Please log in' }) // Optional: render when false
175
+ )
176
+ }
177
+ })
178
+
179
+ // Toggle - UI switches automatically
180
+ isLoggedIn.value = true
181
+ ```
182
+
183
+ #### `when()` - Async/Suspense
184
+
185
+ Handles async operations with loading, success, and error states.
186
+
187
+ ```typescript
188
+ import { when, box, text, signal } from '@rlabs-inc/tui'
189
+
190
+ const userId = signal('123')
191
+
192
+ // Fetch function that returns a promise
193
+ const fetchUser = (id: string) =>
194
+ fetch(`/api/users/${id}`).then(r => r.json())
195
+
196
+ box({
197
+ children: () => {
198
+ when(
199
+ () => fetchUser(userId.value), // Promise getter (re-runs on userId change)
200
+ {
201
+ pending: () => text({ content: 'Loading...' }),
202
+ then: (user) => box({
203
+ children: () => {
204
+ text({ content: `Name: ${user.name}` })
205
+ text({ content: `Email: ${user.email}` })
206
+ }
207
+ }),
208
+ catch: (error) => text({
209
+ content: `Error: ${error.message}`,
210
+ fg: 'red'
211
+ })
212
+ }
213
+ )
214
+ }
215
+ })
216
+
217
+ // Change userId - triggers new fetch, shows loading, then result
218
+ userId.value = '456'
219
+ ```
220
+
221
+ ### How Template Primitives Work
222
+
223
+ All template primitives follow the same elegant pattern:
224
+
225
+ 1. **Capture parent context** at creation time
226
+ 2. **Initial render synchronously** (correct parent hierarchy)
227
+ 3. **Internal effect** tracks reactive dependencies
228
+ 4. **Reconcile on change** (create new, cleanup removed)
229
+
230
+ This means:
231
+ - User code stays clean - no manual effects
232
+ - Props inside templates are fully reactive
233
+ - Cleanup is automatic
234
+ - Performance is optimal (only affected components update)
235
+
121
236
  ## Test Coverage
122
237
 
123
- - **130 tests** passing
124
238
  - TITAN layout engine: 48 tests
125
239
  - Parallel arrays: 17 tests
126
240
  - Focus manager: 29 tests
127
- - Compiler: 36 tests (unit + integration)
128
241
 
129
242
  ## Requirements
130
243
 
package/index.ts CHANGED
@@ -9,21 +9,24 @@
9
9
  export { mount } from './src/api'
10
10
 
11
11
  // Primitives - UI building blocks
12
- export { box, text } from './src/primitives'
13
- export type { BoxProps, TextProps, Cleanup } from './src/primitives'
12
+ export { box, text, each, show, when, scoped, onCleanup, useAnimation, AnimationFrames } from './src/primitives'
13
+ export type { BoxProps, TextProps, Cleanup, AnimationOptions } from './src/primitives'
14
14
 
15
15
  // State modules - Input handling
16
- export { keyboard } from './src/state/keyboard'
16
+ export { keyboard, lastKey, lastEvent } from './src/state/keyboard'
17
+ export { mouse, hitGrid, lastMouseEvent, mouseX, mouseY, isMouseDown } from './src/state/mouse'
17
18
  export { focusManager, focusedIndex } from './src/state/focus'
18
19
  export { scroll } from './src/state/scroll'
19
- export { mouse, hitGrid } from './src/state/mouse'
20
+ export { globalKeys } from './src/state/global-keys'
21
+ export { cursor } from './src/state/cursor'
20
22
 
21
23
  // Types
22
24
  export * from './src/types'
23
25
  export * from './src/types/color'
24
26
 
25
27
  // Signals re-export for convenience
26
- export { signal, state, derived, effect, bind, signals } from '@rlabs-inc/signals'
28
+ export { signal, state, derived, effect, bind, signals, batch, reactiveProps } from '@rlabs-inc/signals'
29
+ export type { PropInput, PropsInput, ReactiveProps } from '@rlabs-inc/signals'
27
30
 
28
31
  // Theme
29
32
  export {
@@ -43,3 +46,6 @@ export * from './src/renderer'
43
46
 
44
47
  // Engine (advanced)
45
48
  export * from './src/engine'
49
+
50
+ // Layout (advanced - for debugging)
51
+ export { layoutDerived } from './src/pipeline/layout'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlabs-inc/tui",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "The Terminal UI Framework for TypeScript/Bun - Blazing-fast, fine-grained reactive terminal UI with complete flexbox layout",
5
5
  "module": "index.ts",
6
6
  "main": "index.ts",
@@ -54,6 +54,6 @@
54
54
  "typescript": "^5.0.0"
55
55
  },
56
56
  "dependencies": {
57
- "@rlabs-inc/signals": "^1.6.0"
57
+ "@rlabs-inc/signals": "^1.8.2"
58
58
  }
59
59
  }
package/src/api/mount.ts CHANGED
@@ -28,12 +28,13 @@ import {
28
28
  DiffRenderer,
29
29
  InlineRenderer,
30
30
  } from '../renderer/output'
31
+ import { AppendRegionRenderer } from '../renderer/append-region'
31
32
  import * as ansi from '../renderer/ansi'
32
33
  import { frameBufferDerived } from '../pipeline/frameBuffer'
33
34
  import { layoutDerived, terminalWidth, terminalHeight, updateTerminalSize, renderMode } from '../pipeline/layout'
34
35
  import { resetRegistry } from '../engine/registry'
35
- import { hitGrid, clearHitGrid } from '../state/mouse'
36
- import { keyboard } from '../state/keyboard'
36
+ import { hitGrid, clearHitGrid, mouse } from '../state/mouse'
37
+ import { globalKeys } from '../state/global-keys'
37
38
 
38
39
  // =============================================================================
39
40
  // MOUNT
@@ -54,6 +55,7 @@ export async function mount(
54
55
  mode = 'fullscreen',
55
56
  mouse = true,
56
57
  kittyKeyboard = true,
58
+ getStaticHeight,
57
59
  } = options
58
60
 
59
61
  // Set render mode signal BEFORE creating components
@@ -63,9 +65,10 @@ export async function mount(
63
65
  // Create renderer based on mode
64
66
  // Fullscreen uses DiffRenderer (absolute positioning)
65
67
  // Inline uses InlineRenderer (eraseLines + sequential write)
66
- // Append uses DiffRenderer for now (TODO: may need AppendRenderer)
68
+ // Append uses AppendRegionRenderer (two-region: static + reactive)
67
69
  const diffRenderer = new DiffRenderer()
68
70
  const inlineRenderer = new InlineRenderer()
71
+ const appendRegionRenderer = new AppendRegionRenderer()
69
72
 
70
73
  // Mode-specific state
71
74
  let previousHeight = 0 // For append mode: track last rendered height
@@ -87,9 +90,7 @@ export async function mount(
87
90
 
88
91
  setupSequence.push(ansi.hideCursor)
89
92
 
90
- if (mouse) {
91
- setupSequence.push(ansi.enableMouse)
92
- }
93
+ // Mouse tracking is handled by globalKeys.initialize() via mouse.enableTracking()
93
94
 
94
95
  if (kittyKeyboard) {
95
96
  setupSequence.push(ansi.enableKittyKeyboard)
@@ -101,9 +102,8 @@ export async function mount(
101
102
  // Write setup sequence
102
103
  process.stdout.write(setupSequence.join(''))
103
104
 
104
- // Initialize keyboard module (handles stdin, raw mode, input parsing)
105
- // Pass cleanup callback for proper shutdown on Ctrl+C
106
- keyboard.initialize()
105
+ // Initialize global input system (stdin, keyboard, mouse, shortcuts)
106
+ globalKeys.initialize({ enableMouse: mouse })
107
107
 
108
108
  // Handle resize
109
109
  const handleResize = () => {
@@ -127,20 +127,29 @@ export async function mount(
127
127
  // Create the component tree
128
128
  root()
129
129
 
130
+ // Global error handlers for debugging
131
+ process.on('uncaughtException', (err) => {
132
+ console.error('[TUI] Uncaught exception:', err)
133
+ })
134
+ process.on('unhandledRejection', (err) => {
135
+ console.error('[TUI] Unhandled rejection:', err)
136
+ })
137
+
130
138
  // THE ONE RENDER EFFECT
131
139
  // This is where the magic happens - reactive rendering!
132
140
  // Side effects (HitGrid) are applied HERE, not in the derived.
133
141
  let stopEffect: (() => void) | null = null
134
142
 
135
143
  stopEffect = effect(() => {
144
+ try {
136
145
  const start = Bun.nanoseconds()
137
146
 
138
147
  // Time layout separately (reading .value triggers computation if needed)
139
- // const layoutStart = Bun.nanoseconds()
140
- // const _layout = layoutDerived.value
141
- // const layoutNs = Bun.nanoseconds() - layoutStart
148
+ const layoutStart = Bun.nanoseconds()
149
+ const computedLayout = layoutDerived.value
150
+ const layoutNs = Bun.nanoseconds() - layoutStart
142
151
 
143
- // Time buffer computation
152
+ // Time buffer computation (layout is cached, so this is just framebuffer)
144
153
  const bufferStart = Bun.nanoseconds()
145
154
  const { buffer, hitRegions, terminalSize } = frameBufferDerived.value
146
155
  const bufferNs = Bun.nanoseconds() - bufferStart
@@ -161,7 +170,13 @@ export async function mount(
161
170
  diffRenderer.render(buffer)
162
171
  } else if (mode === 'inline') {
163
172
  inlineRenderer.render(buffer)
173
+ } else if (mode === 'append') {
174
+ // Append mode: use two-region renderer
175
+ // staticHeight determined by getStaticHeight callback or defaults to 0
176
+ const staticHeight = getStaticHeight ? getStaticHeight() : 0
177
+ appendRegionRenderer.render(buffer, { staticHeight })
164
178
  } else {
179
+ // Fallback to inline for unknown modes
165
180
  inlineRenderer.render(buffer)
166
181
  }
167
182
  const renderNs = Bun.nanoseconds() - renderStart
@@ -169,12 +184,14 @@ export async function mount(
169
184
  const totalNs = Bun.nanoseconds() - start
170
185
 
171
186
  // Show all timing stats in window title
172
- // const layoutMs = layoutNs / 1_000_000
187
+ const layoutMs = layoutNs / 1_000_000
173
188
  const bufferMs = bufferNs / 1_000_000
174
189
  const renderMs = renderNs / 1_000_000
175
190
  const totalMs = totalNs / 1_000_000
176
- // process.stdout.write(`\x1b]0;TUI | ${mode} | layout: ${layoutMs.toFixed(3)}ms | buffer: ${bufferMs.toFixed(3)}ms | render: ${renderMs.toFixed(3)}ms | total: ${totalMs.toFixed(3)}ms\x07`)
177
- process.stdout.write(`\x1b]0;TUI | ${mode} | buffer: ${bufferMs.toFixed(3)}ms | render: ${renderMs.toFixed(3)}ms | total: ${totalMs.toFixed(3)}ms\x07`)
191
+ process.stdout.write(`\x1b]0;TUI | h:${computedLayout.contentHeight} | layout: ${layoutMs.toFixed(3)}ms | buffer: ${bufferMs.toFixed(3)}ms | render: ${renderMs.toFixed(3)}ms | total: ${totalMs.toFixed(3)}ms\x07`)
192
+ } catch (err) {
193
+ console.error('[TUI] Render effect error:', err)
194
+ }
178
195
  })
179
196
 
180
197
  // Cleanup function
@@ -185,8 +202,13 @@ export async function mount(
185
202
  stopEffect = null
186
203
  }
187
204
 
188
- // Cleanup keyboard module (removes stdin listener, restores terminal)
189
- keyboard.cleanup()
205
+ // Cleanup global input system
206
+ globalKeys.cleanup()
207
+
208
+ // Cleanup append region renderer if used
209
+ if (mode === 'append') {
210
+ appendRegionRenderer.cleanup()
211
+ }
190
212
 
191
213
  // Remove resize listener
192
214
  process.stdout.removeListener('resize', handleResize)
@@ -201,11 +223,7 @@ export async function mount(
201
223
  restoreSequence.push(ansi.disableKittyKeyboard)
202
224
  }
203
225
 
204
- if (mouse) {
205
- restoreSequence.push(ansi.disableMouse)
206
- }
207
-
208
- restoreSequence.push(ansi.showCursor)
226
+ // Mouse disable and cursor show handled by globalKeys.cleanup()
209
227
  restoreSequence.push(ansi.reset)
210
228
 
211
229
  if (mode === 'fullscreen') {
@@ -218,10 +236,7 @@ export async function mount(
218
236
 
219
237
  process.stdout.write(restoreSequence.join(''))
220
238
 
221
- // Disable raw mode
222
- if (process.stdin.isTTY) {
223
- process.stdin.setRawMode(false)
224
- }
239
+ // Raw mode disabled by globalKeys.cleanup() -> input.cleanup()
225
240
 
226
241
  // Reset registry for clean slate
227
242
  resetRegistry()
@@ -8,53 +8,45 @@
8
8
  *
9
9
  * NOTE: Focus state (focusable, tabIndex) is in interaction.ts
10
10
  *
11
- * CRITICAL: Arrays storing Bindings must be regular arrays (NOT state!)
12
- * state() proxies snapshot getter values, breaking reactivity.
13
- * componentType is the exception - it stores values directly, not bindings.
11
+ * Uses slotArray for stable reactive cells that NEVER get replaced.
12
+ * componentType is the exception - it stores values directly, not reactively.
14
13
  */
15
14
 
16
- import { bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
15
+ import { slotArray, type SlotArray } from '@rlabs-inc/signals'
17
16
  import { ComponentType } from '../../types'
18
17
  import type { ComponentTypeValue } from '../../types'
19
18
 
20
- /** Component type (box, text, input, etc.) - stores values directly */
19
+ /** Component type (box, text, input, etc.) - stores values directly (not reactive) */
21
20
  export const componentType: ComponentTypeValue[] = []
22
21
 
23
22
  /** Parent component index (-1 for root) */
24
- export const parentIndex: Binding<number>[] = []
23
+ export const parentIndex: SlotArray<number> = slotArray<number>(-1)
25
24
 
26
25
  /** Is component visible (0/false = hidden, 1/true = visible) */
27
- export const visible: Binding<number | boolean>[] = []
26
+ export const visible: SlotArray<number | boolean> = slotArray<number | boolean>(1)
28
27
 
29
28
  /** Component ID (for debugging and lookups) */
30
- export const componentId: Binding<string>[] = []
29
+ export const componentId: SlotArray<string> = slotArray<string>('')
31
30
 
32
31
  /**
33
32
  * Ensure array has capacity for the given index.
34
33
  * Called by registry when allocating.
35
- *
36
- * LAZY BINDING: We push undefined here, not bindings.
37
- * Primitives create bindings only for props they actually use.
38
- * This reduces memory from ~70 bindings/component to ~5-10.
39
34
  */
40
35
  export function ensureCapacity(index: number): void {
41
36
  while (componentType.length <= index) {
42
37
  componentType.push(ComponentType.NONE)
43
- parentIndex.push(undefined as any)
44
- visible.push(undefined as any)
45
- componentId.push(undefined as any)
46
38
  }
39
+ parentIndex.ensureCapacity(index)
40
+ visible.ensureCapacity(index)
41
+ componentId.ensureCapacity(index)
47
42
  }
48
43
 
49
44
  /** Clear values at index (called when releasing) */
50
45
  export function clearAtIndex(index: number): void {
51
46
  if (index < componentType.length) {
52
47
  componentType[index] = ComponentType.NONE
53
- disconnectBinding(parentIndex[index])
54
- disconnectBinding(visible[index])
55
- disconnectBinding(componentId[index])
56
- parentIndex[index] = undefined as any
57
- visible[index] = undefined as any
58
- componentId[index] = undefined as any
59
48
  }
49
+ parentIndex.clear(index)
50
+ visible.clear(index)
51
+ componentId.clear(index)
60
52
  }
@@ -5,8 +5,7 @@
5
5
  * These are INPUT values that components write.
6
6
  * The layout derived READS these and RETURNS computed positions.
7
7
  *
8
- * CRITICAL: Use regular arrays (NOT state!) to preserve binding getters.
9
- * state() proxies snapshot getter values, breaking reactivity.
8
+ * Uses slotArray for stable reactive cells that NEVER get replaced.
10
9
  *
11
10
  * Supports both absolute and percentage dimensions:
12
11
  * - number: Absolute value in cells (e.g., 50)
@@ -17,52 +16,43 @@
17
16
  * Note: 0 means "auto" for width/height, "no constraint" for min/max.
18
17
  */
19
18
 
20
- import { bind, disconnectBinding, type Binding } from '@rlabs-inc/signals'
19
+ import { slotArray, type SlotArray } from '@rlabs-inc/signals'
21
20
  import type { Dimension } from '../../types'
22
21
 
23
22
  /** Requested width (0 = auto, '100%' = full parent width) */
24
- export const width: Binding<Dimension>[] = []
23
+ export const width: SlotArray<Dimension> = slotArray<Dimension>(0)
25
24
 
26
25
  /** Requested height (0 = auto, '100%' = full parent height) */
27
- export const height: Binding<Dimension>[] = []
26
+ export const height: SlotArray<Dimension> = slotArray<Dimension>(0)
28
27
 
29
28
  /** Minimum width constraint */
30
- export const minWidth: Binding<Dimension>[] = []
29
+ export const minWidth: SlotArray<Dimension> = slotArray<Dimension>(0)
31
30
 
32
31
  /** Minimum height constraint */
33
- export const minHeight: Binding<Dimension>[] = []
32
+ export const minHeight: SlotArray<Dimension> = slotArray<Dimension>(0)
34
33
 
35
34
  /** Maximum width constraint (0 = no max) */
36
- export const maxWidth: Binding<Dimension>[] = []
35
+ export const maxWidth: SlotArray<Dimension> = slotArray<Dimension>(0)
37
36
 
38
37
  /** Maximum height constraint (0 = no max) */
39
- export const maxHeight: Binding<Dimension>[] = []
38
+ export const maxHeight: SlotArray<Dimension> = slotArray<Dimension>(0)
40
39
 
41
- /** LAZY BINDING: Push undefined, primitives create bindings for used props only */
40
+ /** Ensure capacity for all dimension arrays */
42
41
  export function ensureCapacity(index: number): void {
43
- while (width.length <= index) {
44
- width.push(undefined as any)
45
- height.push(undefined as any)
46
- minWidth.push(undefined as any)
47
- minHeight.push(undefined as any)
48
- maxWidth.push(undefined as any)
49
- maxHeight.push(undefined as any)
50
- }
42
+ width.ensureCapacity(index)
43
+ height.ensureCapacity(index)
44
+ minWidth.ensureCapacity(index)
45
+ minHeight.ensureCapacity(index)
46
+ maxWidth.ensureCapacity(index)
47
+ maxHeight.ensureCapacity(index)
51
48
  }
52
49
 
50
+ /** Clear slot at index (reset to default) */
53
51
  export function clearAtIndex(index: number): void {
54
- if (index < width.length) {
55
- disconnectBinding(width[index])
56
- disconnectBinding(height[index])
57
- disconnectBinding(minWidth[index])
58
- disconnectBinding(minHeight[index])
59
- disconnectBinding(maxWidth[index])
60
- disconnectBinding(maxHeight[index])
61
- width[index] = undefined as any
62
- height[index] = undefined as any
63
- minWidth[index] = undefined as any
64
- minHeight[index] = undefined as any
65
- maxWidth[index] = undefined as any
66
- maxHeight[index] = undefined as any
67
- }
52
+ width.clear(index)
53
+ height.clear(index)
54
+ minWidth.clear(index)
55
+ minHeight.clear(index)
56
+ maxWidth.clear(index)
57
+ maxHeight.clear(index)
68
58
  }