@rlabs-inc/tui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +141 -0
  2. package/index.ts +45 -0
  3. package/package.json +59 -0
  4. package/src/api/index.ts +7 -0
  5. package/src/api/mount.ts +230 -0
  6. package/src/engine/arrays/core.ts +60 -0
  7. package/src/engine/arrays/dimensions.ts +68 -0
  8. package/src/engine/arrays/index.ts +166 -0
  9. package/src/engine/arrays/interaction.ts +112 -0
  10. package/src/engine/arrays/layout.ts +175 -0
  11. package/src/engine/arrays/spacing.ts +100 -0
  12. package/src/engine/arrays/text.ts +55 -0
  13. package/src/engine/arrays/visual.ts +140 -0
  14. package/src/engine/index.ts +25 -0
  15. package/src/engine/inheritance.ts +138 -0
  16. package/src/engine/registry.ts +180 -0
  17. package/src/pipeline/frameBuffer.ts +473 -0
  18. package/src/pipeline/layout/index.ts +105 -0
  19. package/src/pipeline/layout/titan-engine.ts +798 -0
  20. package/src/pipeline/layout/types.ts +194 -0
  21. package/src/pipeline/layout/utils/hierarchy.ts +202 -0
  22. package/src/pipeline/layout/utils/math.ts +134 -0
  23. package/src/pipeline/layout/utils/text-measure.ts +160 -0
  24. package/src/pipeline/layout.ts +30 -0
  25. package/src/primitives/box.ts +312 -0
  26. package/src/primitives/index.ts +12 -0
  27. package/src/primitives/text.ts +199 -0
  28. package/src/primitives/types.ts +222 -0
  29. package/src/primitives/utils.ts +37 -0
  30. package/src/renderer/ansi.ts +625 -0
  31. package/src/renderer/buffer.ts +667 -0
  32. package/src/renderer/index.ts +40 -0
  33. package/src/renderer/input.ts +518 -0
  34. package/src/renderer/output.ts +451 -0
  35. package/src/state/cursor.ts +176 -0
  36. package/src/state/focus.ts +241 -0
  37. package/src/state/index.ts +43 -0
  38. package/src/state/keyboard.ts +771 -0
  39. package/src/state/mouse.ts +524 -0
  40. package/src/state/scroll.ts +341 -0
  41. package/src/state/theme.ts +687 -0
  42. package/src/types/color.ts +401 -0
  43. package/src/types/index.ts +316 -0
  44. package/src/utils/text.ts +471 -0
@@ -0,0 +1,473 @@
1
+ /**
2
+ * TUI Framework - Production FrameBuffer Derived
3
+ *
4
+ * Transforms layout and component arrays into a renderable FrameBuffer.
5
+ *
6
+ * Production features:
7
+ * - ClipRect-based clipping with intersection
8
+ * - Scroll offset accumulation through parent chain
9
+ * - Per-side border rendering (10 styles)
10
+ * - Color inheritance (walk up parent tree)
11
+ * - Opacity blending
12
+ * - zIndex sorting
13
+ * - Text wrapping and truncation
14
+ *
15
+ * This is a DERIVED - pure computation, returns data without side effects.
16
+ * HitGrid updates are returned as data to be applied by the render effect.
17
+ */
18
+
19
+ import { derived, unwrap } from '@rlabs-inc/signals'
20
+ import type { FrameBuffer, RGBA } from '../types'
21
+ import { ComponentType } from '../types'
22
+ import { Colors, TERMINAL_DEFAULT, rgbaBlend, rgbaLerp } from '../types/color'
23
+ import {
24
+ createBuffer,
25
+ fillRect,
26
+ drawBorder,
27
+ drawText,
28
+ drawTextCentered,
29
+ drawTextRight,
30
+ createClipRect,
31
+ intersectClipRects,
32
+ type ClipRect,
33
+ type BorderConfig,
34
+ } from '../renderer/buffer'
35
+ import { getAllocatedIndices } from '../engine/registry'
36
+ import { wrapText, truncateText } from '../utils/text'
37
+ import {
38
+ getInheritedFg,
39
+ getInheritedBg,
40
+ getBorderColors,
41
+ getBorderStyles,
42
+ hasBorder,
43
+ getEffectiveOpacity,
44
+ } from '../engine/inheritance'
45
+
46
+ // Import arrays
47
+ import * as core from '../engine/arrays/core'
48
+ import * as visual from '../engine/arrays/visual'
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
+
54
+ // Import layout derived
55
+ import { layoutDerived, terminalWidth, terminalHeight, renderMode } from './layout'
56
+
57
+ // =============================================================================
58
+ // HIT REGION TYPE - returned as data, applied by render effect
59
+ // =============================================================================
60
+
61
+ export interface HitRegion {
62
+ x: number
63
+ y: number
64
+ width: number
65
+ height: number
66
+ componentIndex: number
67
+ }
68
+
69
+ export interface FrameBufferResult {
70
+ buffer: FrameBuffer
71
+ hitRegions: HitRegion[]
72
+ terminalSize: { width: number; height: number }
73
+ }
74
+
75
+ // =============================================================================
76
+ // FRAME BUFFER DERIVED
77
+ // =============================================================================
78
+
79
+ /**
80
+ * Production frameBuffer derived.
81
+ *
82
+ * Reads from:
83
+ * - layoutDerived (computed positions)
84
+ * - All visual, text, spacing, interaction arrays
85
+ * - terminalWidth, terminalHeight
86
+ *
87
+ * Returns:
88
+ * - FrameBufferResult containing buffer, hitRegions, and terminal size
89
+ * - hitRegions should be applied to HitGrid by the render effect (no side effects here!)
90
+ */
91
+ export const frameBufferDerived = derived((): FrameBufferResult => {
92
+ const computedLayout = layoutDerived.value
93
+ const tw = terminalWidth.value
94
+ const th = terminalHeight.value
95
+ const mode = renderMode.value
96
+
97
+ // Collect hit regions as DATA - no side effects!
98
+ const hitRegions: HitRegion[] = []
99
+
100
+ // Buffer sizing depends on render mode:
101
+ // - Fullscreen: terminal dimensions (fixed viewport)
102
+ // - Inline/Append: terminal width × content height (content determines size)
103
+ const bufferWidth = tw
104
+ const bufferHeight = mode === 'fullscreen'
105
+ ? th
106
+ : Math.max(1, computedLayout.contentHeight) // Use content bounds for inline/append
107
+
108
+ // Create fresh buffer with terminal default background
109
+ const buffer = createBuffer(bufferWidth, bufferHeight, TERMINAL_DEFAULT)
110
+
111
+ const indices = getAllocatedIndices()
112
+ if (indices.size === 0) {
113
+ return { buffer, hitRegions, terminalSize: { width: bufferWidth, height: bufferHeight } }
114
+ }
115
+
116
+ // Find root components and build child index map
117
+ const rootIndices: number[] = []
118
+ const childMap = new Map<number, number[]>()
119
+
120
+ for (const i of indices) {
121
+ if (core.componentType[i] === ComponentType.NONE) continue
122
+ const vis = unwrap(core.visible[i])
123
+ if (vis === 0 || vis === false) continue
124
+
125
+ const parent = unwrap(core.parentIndex[i]) ?? -1
126
+ if (parent === -1) {
127
+ rootIndices.push(i)
128
+ } else {
129
+ const children = childMap.get(parent)
130
+ if (children) {
131
+ children.push(i)
132
+ } else {
133
+ childMap.set(parent, [i])
134
+ }
135
+ }
136
+ }
137
+
138
+ // Sort roots by zIndex
139
+ rootIndices.sort((a, b) => (unwrap(layout.zIndex[a]) || 0) - (unwrap(layout.zIndex[b]) || 0))
140
+
141
+ // Sort children by zIndex
142
+ for (const children of childMap.values()) {
143
+ children.sort((a, b) => (unwrap(layout.zIndex[a]) || 0) - (unwrap(layout.zIndex[b]) || 0))
144
+ }
145
+
146
+ // Render tree recursively
147
+ for (const rootIdx of rootIndices) {
148
+ renderComponent(
149
+ buffer,
150
+ rootIdx,
151
+ computedLayout,
152
+ childMap,
153
+ hitRegions, // Pass hitRegions array to collect data
154
+ undefined, // No parent clip for roots
155
+ 0, // No parent scroll Y
156
+ 0 // No parent scroll X
157
+ )
158
+ }
159
+
160
+ return { buffer, hitRegions, terminalSize: { width: bufferWidth, height: bufferHeight } }
161
+ })
162
+
163
+ // =============================================================================
164
+ // RECURSIVE COMPONENT RENDERER
165
+ // =============================================================================
166
+
167
+ /**
168
+ * Render a component and its children recursively.
169
+ * Handles clipping, scrolling, and proper z-ordering.
170
+ * Collects hit regions as data (no side effects).
171
+ */
172
+ function renderComponent(
173
+ buffer: FrameBuffer,
174
+ index: number,
175
+ computedLayout: { x: number[]; y: number[]; width: number[]; height: number[]; scrollable: number[] },
176
+ childMap: Map<number, number[]>,
177
+ hitRegions: HitRegion[],
178
+ parentClip: ClipRect | undefined,
179
+ parentScrollY: number,
180
+ parentScrollX: number
181
+ ): void {
182
+ // Skip invisible/invalid components
183
+ const vis = unwrap(core.visible[index])
184
+ if (vis === 0 || vis === false) return
185
+ if (core.componentType[index] === ComponentType.NONE) return
186
+
187
+ // Apply parent's scroll offset to this component's position
188
+ const x = Math.floor((computedLayout.x[index] || 0) - parentScrollX)
189
+ const y = Math.floor((computedLayout.y[index] || 0) - parentScrollY)
190
+ const w = Math.floor(computedLayout.width[index] || 0)
191
+ const h = Math.floor(computedLayout.height[index] || 0)
192
+
193
+ if (w <= 0 || h <= 0) return
194
+
195
+ // Create component bounds
196
+ const componentBounds = createClipRect(x, y, w, h)
197
+
198
+ // If parent is clipping, check if this component is visible
199
+ if (parentClip) {
200
+ const intersection = intersectClipRects(componentBounds, parentClip)
201
+ if (!intersection) return // Completely clipped out
202
+ }
203
+
204
+ // Get effective colors (with inheritance)
205
+ const fg = getInheritedFg(index)
206
+ const bg = getInheritedBg(index)
207
+ const opacity = getEffectiveOpacity(index)
208
+
209
+ // Apply opacity to colors
210
+ const effectiveFg = opacity < 1 ? { ...fg, a: Math.round(fg.a * opacity) } : fg
211
+ const effectiveBg = opacity < 1 ? { ...bg, a: Math.round(bg.a * opacity) } : bg
212
+
213
+ // Fill background if not transparent
214
+ if (effectiveBg.a > 0 && effectiveBg.r !== -1) {
215
+ fillRect(buffer, x, y, w, h, effectiveBg, parentClip)
216
+ }
217
+
218
+ // Collect hit region data (applied by render effect, not here - no side effects!)
219
+ // Children are rendered after parents, so their regions will overwrite in order
220
+ hitRegions.push({ x, y, width: w, height: h, componentIndex: index })
221
+
222
+ // Get border configuration
223
+ const borderStyles = getBorderStyles(index)
224
+ const borderColors = getBorderColors(index)
225
+ const hasAnyBorder = hasBorder(index)
226
+
227
+ // Draw borders
228
+ if (hasAnyBorder && w >= 2 && h >= 2) {
229
+ const config: BorderConfig = {
230
+ styles: borderStyles,
231
+ colors: {
232
+ top: opacity < 1 ? { ...borderColors.top, a: Math.round(borderColors.top.a * opacity) } : borderColors.top,
233
+ right: opacity < 1 ? { ...borderColors.right, a: Math.round(borderColors.right.a * opacity) } : borderColors.right,
234
+ bottom: opacity < 1 ? { ...borderColors.bottom, a: Math.round(borderColors.bottom.a * opacity) } : borderColors.bottom,
235
+ left: opacity < 1 ? { ...borderColors.left, a: Math.round(borderColors.left.a * opacity) } : borderColors.left,
236
+ },
237
+ }
238
+ drawBorder(buffer, x, y, w, h, config, undefined, parentClip)
239
+ }
240
+
241
+ // Calculate content area (inside borders and padding)
242
+ const padTop = (unwrap(spacing.paddingTop[index]) || 0) + (hasAnyBorder && borderStyles.top > 0 ? 1 : 0)
243
+ const padRight = (unwrap(spacing.paddingRight[index]) || 0) + (hasAnyBorder && borderStyles.right > 0 ? 1 : 0)
244
+ const padBottom = (unwrap(spacing.paddingBottom[index]) || 0) + (hasAnyBorder && borderStyles.bottom > 0 ? 1 : 0)
245
+ const padLeft = (unwrap(spacing.paddingLeft[index]) || 0) + (hasAnyBorder && borderStyles.left > 0 ? 1 : 0)
246
+
247
+ const contentX = x + padLeft
248
+ const contentY = y + padTop
249
+ const contentW = w - padLeft - padRight
250
+ const contentH = h - padTop - padBottom
251
+
252
+ // Create content clip rect (for children and text)
253
+ const contentBounds = createClipRect(contentX, contentY, contentW, contentH)
254
+ const contentClip = parentClip
255
+ ? intersectClipRects(contentBounds, parentClip)
256
+ : contentBounds
257
+
258
+ if (!contentClip || contentW <= 0 || contentH <= 0) {
259
+ // No content area visible, but we still rendered the component itself
260
+ return
261
+ }
262
+
263
+ // Render based on component type
264
+ switch (core.componentType[index]) {
265
+ case ComponentType.BOX:
266
+ // Box just has background and border, already drawn
267
+ // Render children below
268
+ break
269
+
270
+ case ComponentType.TEXT:
271
+ renderText(buffer, index, contentX, contentY, contentW, contentH, effectiveFg, contentClip)
272
+ break
273
+
274
+ case ComponentType.INPUT:
275
+ renderInput(buffer, index, contentX, contentY, contentW, contentH, effectiveFg, contentClip)
276
+ break
277
+
278
+ case ComponentType.PROGRESS:
279
+ renderProgress(buffer, index, x, y, w, h, effectiveFg, parentClip)
280
+ break
281
+
282
+ case ComponentType.SELECT:
283
+ renderSelect(buffer, index, contentX, contentY, contentW, contentH, effectiveFg, contentClip)
284
+ break
285
+
286
+ // CANVAS is handled via canvasCells array if needed
287
+ }
288
+
289
+ // Render children (only for BOX containers)
290
+ if (core.componentType[index] === ComponentType.BOX) {
291
+ const children = childMap.get(index) || []
292
+
293
+ // Get this component's scroll offset (scrollable comes from layout, offset from interaction)
294
+ const isScrollable = (computedLayout.scrollable[index] ?? 0) === 1
295
+ const scrollY = isScrollable ? (unwrap(interaction.scrollOffsetY[index]) || 0) : 0
296
+ const scrollX = isScrollable ? (unwrap(interaction.scrollOffsetX[index]) || 0) : 0
297
+
298
+ // Accumulated scroll for children
299
+ const childScrollY = parentScrollY + scrollY
300
+ const childScrollX = parentScrollX + scrollX
301
+
302
+ for (const childIdx of children) {
303
+ renderComponent(
304
+ buffer,
305
+ childIdx,
306
+ computedLayout,
307
+ childMap,
308
+ hitRegions,
309
+ contentClip,
310
+ childScrollY,
311
+ childScrollX
312
+ )
313
+ }
314
+ }
315
+ }
316
+
317
+ // =============================================================================
318
+ // COMPONENT RENDERERS
319
+ // =============================================================================
320
+
321
+ /**
322
+ * Render text component content with wrapping.
323
+ */
324
+ function renderText(
325
+ buffer: FrameBuffer,
326
+ index: number,
327
+ x: number,
328
+ y: number,
329
+ w: number,
330
+ h: number,
331
+ fg: RGBA,
332
+ clip: ClipRect
333
+ ): void {
334
+ const rawValue = text.textContent[index]
335
+ const unwrapped = unwrap(rawValue)
336
+ const content = unwrapped == null ? '' : String(unwrapped)
337
+ if (!content) return
338
+
339
+ const attrs = unwrap(text.textAttrs[index]) || 0
340
+ const align = unwrap(text.textAlign[index]) || 0
341
+
342
+ // Word wrap the text
343
+ const lines = wrapText(content, w)
344
+
345
+ for (let lineIdx = 0; lineIdx < lines.length && lineIdx < h; lineIdx++) {
346
+ const line = lines[lineIdx] ?? ''
347
+ const lineY = y + lineIdx
348
+
349
+ // Skip if outside clip
350
+ if (lineY < clip.y || lineY >= clip.y + clip.height) continue
351
+
352
+ switch (align) {
353
+ case 0: // left
354
+ drawText(buffer, x, lineY, line, fg, undefined, attrs, clip)
355
+ break
356
+ case 1: // center
357
+ drawTextCentered(buffer, x, lineY, w, line, fg, undefined, attrs, clip)
358
+ break
359
+ case 2: // right
360
+ drawTextRight(buffer, x, lineY, w, line, fg, undefined, attrs, clip)
361
+ break
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Render input component content.
368
+ */
369
+ function renderInput(
370
+ buffer: FrameBuffer,
371
+ index: number,
372
+ x: number,
373
+ y: number,
374
+ w: number,
375
+ h: number,
376
+ fg: RGBA,
377
+ clip: ClipRect
378
+ ): void {
379
+ const content = unwrap(text.textContent[index]) || ''
380
+ const attrs = unwrap(text.textAttrs[index]) || 0
381
+ const cursorPos = unwrap(interaction.cursorPosition[index]) || 0
382
+
383
+ if (w <= 0) return
384
+
385
+ // Calculate visible portion of text (scroll to keep cursor visible)
386
+ let displayText = content
387
+ let displayOffset = 0
388
+
389
+ if (content.length > w) {
390
+ // Scroll to keep cursor in view
391
+ if (cursorPos > w - 1) {
392
+ displayOffset = cursorPos - w + 1
393
+ }
394
+ displayText = content.slice(displayOffset, displayOffset + w)
395
+ }
396
+
397
+ // Draw input text
398
+ drawText(buffer, x, y, displayText, fg, undefined, attrs, clip)
399
+
400
+ // Draw cursor if focused
401
+ if (interaction.focusedIndex.value === index) {
402
+ const cursorX = x + Math.min(cursorPos - displayOffset, w - 1)
403
+ if (cursorX >= clip.x && cursorX < clip.x + clip.width && y >= clip.y && y < clip.y + clip.height) {
404
+ // Draw cursor as inverse block
405
+ const cell = buffer.cells[y]?.[cursorX]
406
+ if (cell) {
407
+ const cursorChar = content[cursorPos] || ' '
408
+ // Swap fg/bg for cursor visibility
409
+ cell.char = cursorChar.codePointAt(0) ?? 32
410
+ cell.fg = getInheritedBg(index)
411
+ cell.bg = fg
412
+ }
413
+ }
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Render progress component.
419
+ */
420
+ function renderProgress(
421
+ buffer: FrameBuffer,
422
+ index: number,
423
+ x: number,
424
+ y: number,
425
+ w: number,
426
+ h: number,
427
+ fg: RGBA,
428
+ clip?: ClipRect
429
+ ): void {
430
+ const valueStr = unwrap(text.textContent[index]) || '0'
431
+ const progress = Math.max(0, Math.min(1, parseFloat(valueStr) || 0))
432
+ const filled = Math.round(progress * w)
433
+
434
+ const dimFg = { ...fg, a: Math.floor(fg.a * 0.3) }
435
+
436
+ for (let px = 0; px < w; px++) {
437
+ const cellX = x + px
438
+ if (clip && (cellX < clip.x || cellX >= clip.x + clip.width)) continue
439
+
440
+ const isFilled = px < filled
441
+ const char = isFilled ? '█' : '░'
442
+ const color = isFilled ? fg : dimFg
443
+
444
+ drawText(buffer, cellX, y, char, color, undefined, 0, clip)
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Render select component.
450
+ */
451
+ function renderSelect(
452
+ buffer: FrameBuffer,
453
+ index: number,
454
+ x: number,
455
+ y: number,
456
+ w: number,
457
+ h: number,
458
+ fg: RGBA,
459
+ clip: ClipRect
460
+ ): void {
461
+ const content = unwrap(text.textContent[index]) || ''
462
+ const attrs = unwrap(text.textAttrs[index]) || 0
463
+
464
+ // For now, just show current selection
465
+ const displayText = truncateText(content, w - 2) // Leave room for dropdown indicator
466
+
467
+ drawText(buffer, x, y, displayText, fg, undefined, attrs, clip)
468
+
469
+ // Draw dropdown indicator
470
+ if (w > 2) {
471
+ drawText(buffer, x + w - 2, y, '▼', fg, undefined, 0, clip)
472
+ }
473
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * TUI Framework - Layout Module
3
+ *
4
+ * THE terminal-native layout system - TITAN ENGINE.
5
+ *
6
+ * Features:
7
+ * - Block layout (vertical stacking)
8
+ * - Complete Flexbox (grow/shrink/wrap/justify/align)
9
+ * - Absolute/Fixed positioning
10
+ * - Beats Yoga by trusting fine-grained reactivity
11
+ *
12
+ * Philosophy:
13
+ * - Read from existing arrays (triggers reactivity)
14
+ * - Compute in minimal passes
15
+ * - Return plain output arrays
16
+ */
17
+
18
+ import { derived, signal } from '@rlabs-inc/signals'
19
+ import { getAllocatedIndices } from '../../engine/registry'
20
+ import { computeLayoutTitan, resetTitanArrays } from './titan-engine'
21
+ import type { ComputedLayout } from './types'
22
+ import type { RenderMode } from '../../types'
23
+
24
+ // Re-export reset function for memory cleanup
25
+ export { resetTitanArrays }
26
+
27
+ // =============================================================================
28
+ // RENDER MODE
29
+ // =============================================================================
30
+
31
+ /**
32
+ * Current render mode.
33
+ * - fullscreen: Alt screen buffer, fixed terminal dimensions
34
+ * - inline: Normal buffer, content-determined height, terminal scroll works
35
+ * - append: Like inline, but for CLI-style appending output
36
+ */
37
+ export const renderMode = signal<RenderMode>('fullscreen')
38
+
39
+ // =============================================================================
40
+ // TERMINAL SIZE
41
+ // =============================================================================
42
+
43
+ export const terminalWidth = signal(process.stdout.columns || 80)
44
+ export const terminalHeight = signal(process.stdout.rows || 24)
45
+
46
+ /**
47
+ * Update terminal size from process.stdout.
48
+ * Called on resize events.
49
+ */
50
+ export function updateTerminalSize(): void {
51
+ const w = process.stdout.columns || 80
52
+ const h = process.stdout.rows || 24
53
+
54
+ if (w !== terminalWidth.value || h !== terminalHeight.value) {
55
+ terminalWidth.value = w
56
+ terminalHeight.value = h
57
+ }
58
+ }
59
+
60
+ // =============================================================================
61
+ // LAYOUT DERIVED
62
+ // =============================================================================
63
+
64
+ /**
65
+ * The main layout derived.
66
+ *
67
+ * Reads from all component arrays and produces computed positions/sizes.
68
+ * Automatically re-runs when any dependency changes.
69
+ *
70
+ * This is where the magic happens - reactive layout computation!
71
+ */
72
+ export const layoutDerived = derived((): ComputedLayout => {
73
+ // Read terminal size (creates dependency)
74
+ const tw = terminalWidth.value
75
+ const th = terminalHeight.value
76
+
77
+ // Read render mode (creates dependency)
78
+ const mode = renderMode.value
79
+
80
+ // Get all allocated indices (creates dependency on component add/remove)
81
+ const indices = getAllocatedIndices()
82
+
83
+ // Constrain height only in fullscreen mode
84
+ // Inline/append modes let content determine its own height
85
+ const constrainHeight = mode === 'fullscreen'
86
+
87
+ // TITAN ENGINE: Read arrays, compute, return.
88
+ // Reactivity tracks dependencies as we read - no manual tracking needed.
89
+ return computeLayoutTitan(tw, th, indices, constrainHeight)
90
+ })
91
+
92
+ // =============================================================================
93
+ // EXPORTS
94
+ // =============================================================================
95
+
96
+ export type { ComputedLayout } from './types'
97
+ export {
98
+ FlexDirection,
99
+ FlexWrap,
100
+ JustifyContent,
101
+ AlignItems,
102
+ Position,
103
+ Display,
104
+ Overflow,
105
+ } from './types'