@ccheever/exact-renderer 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 (80) hide show
  1. package/package.json +118 -0
  2. package/src/__tests__/adapter-window-state.test.tsx +190 -0
  3. package/src/__tests__/attrs.test.ts +157 -0
  4. package/src/__tests__/classname.test.ts +332 -0
  5. package/src/__tests__/color.test.ts +169 -0
  6. package/src/__tests__/dom-mirror.test.ts +682 -0
  7. package/src/__tests__/dom-shim.test.ts +274 -0
  8. package/src/__tests__/fixtures/SvelteCounter.svelte +7 -0
  9. package/src/__tests__/fixtures/SvelteInput.svelte +8 -0
  10. package/src/__tests__/host-config.test.ts +51 -0
  11. package/src/__tests__/host-ops.test.ts +2234 -0
  12. package/src/__tests__/image-source.test.ts +135 -0
  13. package/src/__tests__/liquid-glass.test.ts +72 -0
  14. package/src/__tests__/multi-root.test.ts +118 -0
  15. package/src/__tests__/native-view-events.test.ts +102 -0
  16. package/src/__tests__/nodes.test.ts +399 -0
  17. package/src/__tests__/normalize.test.ts +576 -0
  18. package/src/__tests__/paragraph-lowering.test.tsx +144 -0
  19. package/src/__tests__/props.test.ts +518 -0
  20. package/src/__tests__/protocol-encoder.test.ts +732 -0
  21. package/src/__tests__/protocol-fixture-bytes.test.ts +41 -0
  22. package/src/__tests__/reconciler.test.tsx +241 -0
  23. package/src/__tests__/svelte-adapter.test.ts +166 -0
  24. package/src/__tests__/svg-source.test.ts +71 -0
  25. package/src/__tests__/tags.test.ts +354 -0
  26. package/src/__tests__/toggle.test.ts +441 -0
  27. package/src/__tests__/transitions.test.ts +106 -0
  28. package/src/__tests__/web-primitives.test.tsx +454 -0
  29. package/src/__tests__/window-hooks.test.tsx +447 -0
  30. package/src/adapter-contract.ts +68 -0
  31. package/src/attrs.ts +596 -0
  32. package/src/classname-contract.ts +87 -0
  33. package/src/classname-resolve.ts +553 -0
  34. package/src/classname-runtime.ts +29 -0
  35. package/src/components.ts +214 -0
  36. package/src/css-variable-context.ts +83 -0
  37. package/src/dom-hydration.ts +160 -0
  38. package/src/dom-mirror.ts +1459 -0
  39. package/src/dom-shim.ts +1736 -0
  40. package/src/group-context.ts +69 -0
  41. package/src/host-config.ts +431 -0
  42. package/src/host-ops.ts +3167 -0
  43. package/src/image-source.native.ts +703 -0
  44. package/src/image-source.ts +554 -0
  45. package/src/index.ts +278 -0
  46. package/src/inspector-runtime.ts +244 -0
  47. package/src/inspector.ts +3570 -0
  48. package/src/jsx-augmentations.ts +54 -0
  49. package/src/keyboard-avoidance.ts +217 -0
  50. package/src/native-primitives.ts +43 -0
  51. package/src/native-view-events.ts +322 -0
  52. package/src/native-view.ts +60 -0
  53. package/src/nodes/index.ts +41 -0
  54. package/src/nodes/node.ts +531 -0
  55. package/src/peer-context.ts +100 -0
  56. package/src/primitives.native.ts +8 -0
  57. package/src/primitives.ts +8 -0
  58. package/src/props/index.ts +14 -0
  59. package/src/props/normalize.ts +816 -0
  60. package/src/protocol/encoder.ts +940 -0
  61. package/src/protocol/index.ts +33 -0
  62. package/src/reconciler.ts +581 -0
  63. package/src/runtime.ts +11 -0
  64. package/src/safe-area.ts +543 -0
  65. package/src/solid.ts +490 -0
  66. package/src/style/color.js +1 -0
  67. package/src/style/color.ts +15 -0
  68. package/src/style/index.js +1 -0
  69. package/src/style/index.ts +22 -0
  70. package/src/style/normalize.js +1 -0
  71. package/src/style/normalize.ts +1426 -0
  72. package/src/svelte.ts +349 -0
  73. package/src/svg-source.ts +222 -0
  74. package/src/tags/index.ts +21 -0
  75. package/src/tags/tag-map.ts +289 -0
  76. package/src/text/paragraph-lowering.ts +310 -0
  77. package/src/types.ts +1175 -0
  78. package/src/vue.ts +535 -0
  79. package/src/web-host.ts +19 -0
  80. package/src/web-primitives.ts +1654 -0
package/src/types.ts ADDED
@@ -0,0 +1,1175 @@
1
+ /**
2
+ * Exact Renderer Type Definitions
3
+ *
4
+ * This module defines the core types used throughout the renderer.
5
+ * Types are organized into categories:
6
+ * - Style types (layout, spacing, appearance, typography)
7
+ * - Prop types (component-specific props)
8
+ * - Internal types (reconciler and protocol internals)
9
+ *
10
+ * Design Principles:
11
+ * - Web semantics are primary (CSS flexbox defaults)
12
+ * - React Native compatibility is achieved through aliases
13
+ * - All types are strictly typed for safety
14
+ */
15
+
16
+ import type { ReactNode } from 'react';
17
+
18
+ import type { AssetRef } from '@exact/core/assets-fonts-state';
19
+ import type {
20
+ KeyboardState,
21
+ SafeAreaInsets,
22
+ SafeAreaRegionInsets,
23
+ SafeAreaRegionName,
24
+ } from '@exact/core/window-types';
25
+
26
+ // =============================================================================
27
+ // JSX Intrinsic Elements
28
+ // =============================================================================
29
+
30
+ // Forward-declare ToggleProps for JSX.IntrinsicElements
31
+ // The actual interface is defined below
32
+ interface _TogglePropsForJSX {
33
+ style?: ViewStyle;
34
+ value?: boolean;
35
+ defaultValue?: boolean;
36
+ onValueChange?: (value: boolean) => void;
37
+ glassEffect?: boolean;
38
+ tintColor?: string;
39
+ label?: string;
40
+ children?: ReactNode;
41
+ disabled?: boolean;
42
+ }
43
+
44
+ /**
45
+ * Extend JSX intrinsic elements with Exact custom elements.
46
+ * This allows using lowercase elements like <toggle> in JSX.
47
+ */
48
+ declare global {
49
+ // eslint-disable-next-line @typescript-eslint/no-namespace
50
+ namespace JSX {
51
+ interface IntrinsicElements {
52
+ /** Native toggle/switch element with iOS liquid glass effect support */
53
+ toggle: _TogglePropsForJSX;
54
+ }
55
+ }
56
+ }
57
+
58
+ // =============================================================================
59
+ // RGBA Color Type
60
+ // =============================================================================
61
+
62
+ // Defined in @exact/core (LLP 0157 F0 relocation); re-exported here for
63
+ // back-compat with existing renderer consumers.
64
+ import type { RGBAColor } from '@exact/core/style/color';
65
+
66
+ export type { RGBAColor };
67
+
68
+ // =============================================================================
69
+ // Dimension Types
70
+ // =============================================================================
71
+
72
+ /**
73
+ * Dimension type for sizes that can be auto, points, or percent.
74
+ */
75
+ export type DimensionValue =
76
+ | { readonly type: 'auto' }
77
+ | { readonly type: 'points'; readonly value: number }
78
+ | { readonly type: 'percent'; readonly value: number };
79
+
80
+ /**
81
+ * Dimension input - can be a number (interpreted as points) or explicit.
82
+ */
83
+ export type DimensionInput = number | string | DimensionValue;
84
+
85
+ // =============================================================================
86
+ // Flexbox Types (CSS Standard)
87
+ // =============================================================================
88
+
89
+ export type FlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
90
+ export type FlexWrap = 'nowrap' | 'wrap' | 'wrap-reverse';
91
+ export type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
92
+ export type AlignItems = 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';
93
+ export type AlignSelf = 'auto' | 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';
94
+ export type AlignContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'stretch';
95
+ export type Direction = 'ltr' | 'rtl' | 'auto';
96
+ export type ResolvedDirection = 'ltr' | 'rtl';
97
+ export type WritingMode = 'horizontal-tb' | 'vertical-rl' | 'vertical-lr';
98
+
99
+ // =============================================================================
100
+ // Position Types
101
+ // =============================================================================
102
+
103
+ export type PositionType = 'relative' | 'absolute';
104
+ export type Overflow = 'visible' | 'hidden' | 'scroll';
105
+ export type Display = 'flex' | 'grid' | 'none';
106
+ export type AccessibilityCheckedState = boolean | 'mixed';
107
+ export type AccessibilityLiveMode = 'off' | 'polite' | 'assertive' | string;
108
+ export interface AccessibilityActionDescriptor {
109
+ readonly name: string;
110
+ readonly label?: string;
111
+ }
112
+ export type FocusScopeMode = boolean | 'contain' | 'restore' | 'containRestore' | string;
113
+ export type SafeAreaEdge = 'top' | 'right' | 'bottom' | 'left';
114
+ export type SafeAreaEdgeMode = 'additive' | 'maximum' | 'off';
115
+ export type SafeAreaRegionSet =
116
+ | 'container'
117
+ | 'keyboard'
118
+ | 'all'
119
+ | 'none'
120
+ | SafeAreaRegionName[]
121
+ | readonly SafeAreaRegionName[];
122
+ export type SafeAreaProp =
123
+ | 'none'
124
+ | {
125
+ regions?: SafeAreaRegionSet;
126
+ top?: SafeAreaEdgeMode;
127
+ right?: SafeAreaEdgeMode;
128
+ bottom?: SafeAreaEdgeMode;
129
+ left?: SafeAreaEdgeMode;
130
+ };
131
+
132
+ export interface SafeAreaInsetProp {
133
+ edge: SafeAreaEdge;
134
+ size: number;
135
+ }
136
+
137
+ // =============================================================================
138
+ // Typography Types
139
+ // =============================================================================
140
+
141
+ export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number;
142
+ export type FontStyle = 'normal' | 'italic';
143
+ export type TextAlign = 'auto' | 'left' | 'right' | 'center' | 'justify';
144
+ export type TextDecorationLine = 'none' | 'underline' | 'line-through' | 'underline line-through';
145
+ export type EllipsizeMode = 'head' | 'middle' | 'tail' | 'clip';
146
+
147
+ // =============================================================================
148
+ // Image Types
149
+ // =============================================================================
150
+
151
+ export type ResizeMode = 'cover' | 'contain' | 'stretch' | 'center';
152
+ export type ImageObjectFit = 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
153
+ export type ImageLoading = 'auto' | 'eager' | 'lazy';
154
+
155
+ export interface ImageCandidate {
156
+ uri: string;
157
+ headers?: Record<string, string>;
158
+ cacheKey?: string;
159
+ width?: number;
160
+ height?: number;
161
+ pixelDensity?: number;
162
+ type?: string;
163
+ }
164
+
165
+ export type ImageColorScheme = 'light' | 'dark';
166
+ export type ImageThemeTreatment = 'none' | 'invert-in-dark' | 'invert-in-light';
167
+
168
+ export interface ColorSchemeImageSource {
169
+ light?: ImageSource;
170
+ dark?: ImageSource;
171
+ fallback?: ImageSource;
172
+ }
173
+
174
+ /**
175
+ * Image source - either a URI or a local asset reference.
176
+ */
177
+ export type ImageSource =
178
+ | string
179
+ | AssetRef
180
+ | { uri: string; headers?: Record<string, string>; cacheKey?: string }
181
+ | { thumbhash: string }
182
+ | ColorSchemeImageSource
183
+ | readonly ImageCandidate[]
184
+ | number;
185
+
186
+ export interface SvgUriSource {
187
+ uri: string;
188
+ headers?: Record<string, string>;
189
+ cacheKey?: string;
190
+ }
191
+
192
+ export type SvgSource =
193
+ | string
194
+ | AssetRef
195
+ | SvgUriSource;
196
+
197
+ export type ImagePlaceholder =
198
+ | string
199
+ | AssetRef
200
+ | { thumbhash: string }
201
+ | { color: string }
202
+ | ImageSource;
203
+
204
+ export type SelectableMode = boolean | 'contain' | 'all';
205
+ export type NativeViewSelectionTier = 'atomic' | 'structured' | 'delegated';
206
+ export type NativeViewSelectionGesturePolicy = 'external' | 'internal';
207
+
208
+ // =============================================================================
209
+ // Style Interfaces
210
+ // =============================================================================
211
+
212
+ /**
213
+ * Flexbox layout properties.
214
+ */
215
+ export interface FlexStyle {
216
+ flex?: number;
217
+ flexGrow?: number;
218
+ flexShrink?: number;
219
+ flexBasis?: DimensionInput;
220
+ flexDirection?: FlexDirection;
221
+ flexWrap?: FlexWrap;
222
+ justifyContent?: JustifyContent;
223
+ alignItems?: AlignItems;
224
+ alignSelf?: AlignSelf;
225
+ alignContent?: AlignContent;
226
+ direction?: Direction;
227
+ gap?: number;
228
+ rowGap?: number;
229
+ columnGap?: number;
230
+ }
231
+
232
+ /**
233
+ * Dimension and sizing properties.
234
+ */
235
+ export interface LayoutStyle {
236
+ width?: DimensionInput;
237
+ height?: DimensionInput;
238
+ minWidth?: DimensionInput;
239
+ minHeight?: DimensionInput;
240
+ maxWidth?: DimensionInput;
241
+ maxHeight?: DimensionInput;
242
+ aspectRatio?: number;
243
+ }
244
+
245
+ /**
246
+ * Padding and margin properties.
247
+ */
248
+ export interface SpacingStyle {
249
+ padding?: DimensionInput;
250
+ paddingTop?: DimensionInput;
251
+ paddingRight?: DimensionInput;
252
+ paddingBottom?: DimensionInput;
253
+ paddingLeft?: DimensionInput;
254
+ paddingHorizontal?: DimensionInput;
255
+ paddingVertical?: DimensionInput;
256
+ paddingStart?: DimensionInput;
257
+ paddingEnd?: DimensionInput;
258
+ paddingInlineStart?: DimensionInput;
259
+ paddingInlineEnd?: DimensionInput;
260
+ margin?: DimensionInput;
261
+ marginTop?: DimensionInput;
262
+ marginRight?: DimensionInput;
263
+ marginBottom?: DimensionInput;
264
+ marginLeft?: DimensionInput;
265
+ marginHorizontal?: DimensionInput;
266
+ marginVertical?: DimensionInput;
267
+ marginStart?: DimensionInput;
268
+ marginEnd?: DimensionInput;
269
+ marginInlineStart?: DimensionInput;
270
+ marginInlineEnd?: DimensionInput;
271
+ }
272
+
273
+ /**
274
+ * Position properties.
275
+ */
276
+ export interface PositionStyle {
277
+ position?: PositionType;
278
+ top?: DimensionInput;
279
+ right?: DimensionInput;
280
+ bottom?: DimensionInput;
281
+ left?: DimensionInput;
282
+ start?: DimensionInput;
283
+ end?: DimensionInput;
284
+ insetInlineStart?: DimensionInput;
285
+ insetInlineEnd?: DimensionInput;
286
+ zIndex?: number;
287
+ }
288
+
289
+ /**
290
+ * Visual appearance properties.
291
+ */
292
+ export interface AppearanceStyle {
293
+ display?: Display;
294
+ backgroundColor?: string;
295
+ opacity?: number;
296
+ borderRadius?: number;
297
+ borderTopLeftRadius?: number;
298
+ borderTopRightRadius?: number;
299
+ borderBottomLeftRadius?: number;
300
+ borderBottomRightRadius?: number;
301
+ borderWidth?: number;
302
+ borderColor?: string;
303
+ // Per-side borders (ENG-22085).
304
+ borderTopWidth?: number;
305
+ borderRightWidth?: number;
306
+ borderBottomWidth?: number;
307
+ borderLeftWidth?: number;
308
+ borderTopColor?: string;
309
+ borderRightColor?: string;
310
+ borderBottomColor?: string;
311
+ borderLeftColor?: string;
312
+ shadowColor?: string;
313
+ shadowOffset?: {
314
+ width: number;
315
+ height: number;
316
+ };
317
+ shadowRadius?: number;
318
+ shadowOpacity?: number;
319
+ boxShadow?: string;
320
+ backdropBlur?: number;
321
+ overflow?: Overflow;
322
+ }
323
+
324
+ /**
325
+ * Transform properties for animations.
326
+ */
327
+ export interface TransformStyle {
328
+ transform?: Array<
329
+ | { translateX: number }
330
+ | { translateY: number }
331
+ | { scale: number }
332
+ | { scaleX: number }
333
+ | { scaleY: number }
334
+ | { rotate: string }
335
+ >;
336
+ }
337
+
338
+ // =============================================================================
339
+ // Transition Types
340
+ // =============================================================================
341
+
342
+ // Defined in @exact/core (LLP 0157 F0 relocation) so framework-neutral
343
+ // packages can describe motion; re-exported here for back-compat.
344
+ import type {
345
+ CubicBezierEasing,
346
+ NoneTransition,
347
+ SpringTransition,
348
+ StyleTransition,
349
+ TimingTransition,
350
+ TransitionConfig,
351
+ TransitionEasing,
352
+ TransitionEasingName,
353
+ TransitionMap,
354
+ TransitionPhase,
355
+ TransitionPropertyName,
356
+ } from '@exact/core/style/transitions';
357
+
358
+ export type {
359
+ CubicBezierEasing,
360
+ NoneTransition,
361
+ SpringTransition,
362
+ StyleTransition,
363
+ TimingTransition,
364
+ TransitionConfig,
365
+ TransitionEasing,
366
+ TransitionEasingName,
367
+ TransitionMap,
368
+ TransitionPhase,
369
+ TransitionPropertyName,
370
+ };
371
+
372
+ export interface TransitionStyle {
373
+ transition?: StyleTransition;
374
+ }
375
+
376
+ /**
377
+ * Typography properties for text elements.
378
+ */
379
+ export interface TextStyle {
380
+ color?: string;
381
+ fontFamily?: string | readonly string[];
382
+ fontSize?: number;
383
+ fontWeight?: FontWeight;
384
+ fontStyle?: FontStyle;
385
+ fontVariantNumeric?: string;
386
+ textAlign?: TextAlign;
387
+ writingMode?: WritingMode;
388
+ lineHeight?: number;
389
+ letterSpacing?: number;
390
+ textDecorationLine?: TextDecorationLine;
391
+ numberOfLines?: number;
392
+ ellipsizeMode?: EllipsizeMode;
393
+ }
394
+
395
+ /**
396
+ * Combined view style (all non-text styles).
397
+ */
398
+ export type ViewStyle = FlexStyle & LayoutStyle & SpacingStyle & PositionStyle & AppearanceStyle & TransformStyle & TransitionStyle;
399
+
400
+ /**
401
+ * Full style including text properties.
402
+ */
403
+ export type FullStyle = ViewStyle & TextStyle;
404
+
405
+ // =============================================================================
406
+ // Event Types
407
+ // =============================================================================
408
+
409
+ /**
410
+ * Press event payload from native.
411
+ */
412
+ export interface PressEvent {
413
+ nativeEvent: {
414
+ locationX: number;
415
+ locationY: number;
416
+ timestamp: number;
417
+ };
418
+ }
419
+
420
+ /**
421
+ * Scroll event payload.
422
+ */
423
+ export interface ScrollEvent {
424
+ nativeEvent: {
425
+ contentOffset: {
426
+ x: number;
427
+ y: number;
428
+ };
429
+ contentSize: {
430
+ width: number;
431
+ height: number;
432
+ };
433
+ layoutMeasurement: {
434
+ width: number;
435
+ height: number;
436
+ };
437
+ timestamp: number;
438
+ };
439
+ }
440
+
441
+ /**
442
+ * Change event for inputs.
443
+ */
444
+ export interface ChangeEvent {
445
+ nativeEvent: {
446
+ text: string;
447
+ selection?: TextInputSelection;
448
+ textChanged?: boolean;
449
+ isComposing?: boolean;
450
+ compositionRange?: TextInputSelection;
451
+ textRevision?: number;
452
+ classification?: string;
453
+ };
454
+ }
455
+
456
+ export interface TextInputSelection {
457
+ start: number;
458
+ end: number;
459
+ }
460
+
461
+ export interface SelectionChangeEvent {
462
+ nativeEvent: {
463
+ selection: TextInputSelection;
464
+ text?: string;
465
+ isComposing?: boolean;
466
+ compositionRange?: TextInputSelection;
467
+ textRevision?: number;
468
+ classification?: string;
469
+ };
470
+ }
471
+
472
+ /**
473
+ * Transition completion event payload.
474
+ */
475
+ export interface TransitionEndEvent {
476
+ propertyName: TransitionPropertyName;
477
+ finished: boolean;
478
+ phase?: TransitionPhase;
479
+ }
480
+
481
+ /**
482
+ * Focus event payload from native or the web wrapper layer.
483
+ */
484
+ export interface FocusChangeEvent {
485
+ nativeEvent: {
486
+ timestamp: number;
487
+ relatedTarget?: number | null;
488
+ };
489
+ }
490
+
491
+ /**
492
+ * Keyboard event payload from native or the web wrapper layer.
493
+ */
494
+ export interface KeyEvent {
495
+ nativeEvent: {
496
+ key: string;
497
+ code?: string;
498
+ altKey: boolean;
499
+ ctrlKey: boolean;
500
+ metaKey: boolean;
501
+ shiftKey: boolean;
502
+ repeat: boolean;
503
+ timestamp: number;
504
+ };
505
+ preventDefault?: () => void;
506
+ defaultPrevented?: boolean;
507
+ }
508
+
509
+ // =============================================================================
510
+ // Component Props Interfaces
511
+ // =============================================================================
512
+
513
+ export type AgentSemanticScalar = string | number | boolean | null;
514
+
515
+ export interface AgentSemanticOption {
516
+ value: AgentSemanticScalar;
517
+ label?: string;
518
+ }
519
+
520
+ /**
521
+ * Renderer-local agent metadata. This never goes into the binary protocol; it
522
+ * only exists so the authenticated / agent-facing projections can preserve the
523
+ * author's semantic intent instead of inferring everything heuristically.
524
+ */
525
+ export interface AgentSemantics {
526
+ kind: string;
527
+ label?: string;
528
+ value?: AgentSemanticScalar | readonly AgentSemanticScalar[];
529
+ intent?: "default" | "primary" | "destructive" | "success" | "warning" | "info";
530
+ to?: string;
531
+ disabledReason?: string;
532
+ hint?: string;
533
+ options?: readonly (AgentSemanticScalar | AgentSemanticOption)[];
534
+ confirmation?: string;
535
+ }
536
+
537
+ /**
538
+ * Base props shared by all elements.
539
+ */
540
+ /**
541
+ * Liquid Glass material variant. `regular` is the default adaptive glass;
542
+ * `clear` is the more transparent variant Apple uses over media-rich content.
543
+ */
544
+ export type GlassVariant = 'regular' | 'clear';
545
+
546
+ export interface BaseProps {
547
+ key?: string | number;
548
+ className?: string;
549
+ testID?: string;
550
+ testId?: string;
551
+ /**
552
+ * Declarative safe-area participation. The renderer consumes this locally and
553
+ * resolves it into concrete padding/content-inset style before protocol
554
+ * encoding, which keeps the cross-platform payload simple.
555
+ */
556
+ safeArea?: SafeAreaProp;
557
+ /**
558
+ * Fixed sibling safe-area expansion. This is the v1 explicit-size form of
559
+ * `safeAreaInset(edge:)`.
560
+ */
561
+ safeAreaInset?: SafeAreaInsetProp;
562
+ /**
563
+ * Tier-2-only agent hint used by the authenticated agent surface.
564
+ *
565
+ * This is metadata rather than visual UI. The public markdown path must not
566
+ * consume it, so read-only agent requests do not see anything richer than the
567
+ * corresponding HTML page already exposes.
568
+ */
569
+ agentSummary?: string;
570
+ agentId?: string;
571
+ agentSemantics?: AgentSemantics;
572
+ selectable?: SelectableMode;
573
+ selectionCopyText?: string;
574
+ disabledReason?: string;
575
+ /**
576
+ * Liquid Glass (Apple iOS 26 / macOS 26). `glassEffect` enables the effect on
577
+ * any container surface; the modifiers refine it. The renderer composes these
578
+ * into a single `glassEffect` spec string before protocol encoding and falls
579
+ * back to a translucent blur on web and pre-26 OSes.
580
+ */
581
+ glassEffect?: boolean;
582
+ glassVariant?: GlassVariant;
583
+ glassTint?: string;
584
+ glassInteractive?: boolean;
585
+ focusScope?: FocusScopeMode;
586
+ inert?: boolean;
587
+ scrollLocked?: boolean;
588
+ portalTarget?: string;
589
+ accessibilityLabel?: string;
590
+ accessibilityRole?: string;
591
+ accessibilityHint?: string;
592
+ accessibilityModal?: boolean;
593
+ accessibilityExpanded?: boolean;
594
+ accessibilitySelected?: boolean;
595
+ accessibilityChecked?: AccessibilityCheckedState;
596
+ accessibilityDisabled?: boolean;
597
+ accessibilityLive?: AccessibilityLiveMode;
598
+ accessibilityValueNow?: number;
599
+ accessibilityValueMin?: number;
600
+ accessibilityValueMax?: number;
601
+ accessibilityValueText?: string;
602
+ focusable?: boolean;
603
+ tabIndex?: number;
604
+ nativeID?: string;
605
+ accessibilityFor?: string;
606
+ accessibilityLabelledBy?: string;
607
+ accessibilityDescribedBy?: string;
608
+ accessibilityBusy?: boolean;
609
+ accessibilityActions?: readonly AccessibilityActionDescriptor[];
610
+ accessibilityElementsHidden?: boolean;
611
+ accessibilityOrder?: readonly string[];
612
+ accessibilityHeadingLevel?: number;
613
+ /**
614
+ * Internal-only flag used by `<AccessibilityChildren>` to keep synthetic
615
+ * semantics nodes out of the visual surface.
616
+ */
617
+ accessibilitySynthetic?: boolean;
618
+ allowFontScaling?: boolean;
619
+ maxFontSizeMultiplier?: number;
620
+ minimumFontSize?: number;
621
+ lang?: string;
622
+ onFocus?: (event: FocusChangeEvent) => void;
623
+ onBlur?: (event: FocusChangeEvent) => void;
624
+ onKeyDown?: (event: KeyEvent) => void;
625
+ onKeyUp?: (event: KeyEvent) => void;
626
+ onFocusIn?: (event: FocusChangeEvent) => void;
627
+ onFocusOut?: (event: FocusChangeEvent) => void;
628
+ onAccessibilityAction?: (event: { nativeEvent: { actionName: string } }) => void;
629
+ onTransitionEnd?: (event: TransitionEndEvent) => void;
630
+ __exactPresencePhase?: string;
631
+ __exactPortalLevel?: string;
632
+ __exactPortalPresentation?: string;
633
+ __exactDismissableLayer?: boolean;
634
+ __exactDismissAction?: string;
635
+ __exactFocusRestore?: boolean;
636
+ __exactAnchorTarget?: string;
637
+ __exactAnchorPlacement?: string;
638
+ __exactAnchorStrategy?: string;
639
+ __exactAnchorOffset?: number;
640
+ __exactComponentName?: string;
641
+ __exactComponentSlot?: string;
642
+ __exactSourceFilePath?: string;
643
+ __exactVariantProps?: string;
644
+ __exactInteractionState?: string;
645
+ __exactRenderMode?: string;
646
+ /**
647
+ * Internal performance hint for fixed-size text surfaces whose content changes
648
+ * must not affect layout.
649
+ */
650
+ __exactTextLayoutStable?: boolean;
651
+ }
652
+
653
+ /**
654
+ * Props for container elements (div, View).
655
+ */
656
+ export interface ContainerProps extends BaseProps {
657
+ style?: ViewStyle;
658
+ children?: ReactNode;
659
+ }
660
+
661
+ /**
662
+ * Props for text elements.
663
+ */
664
+ export interface TextElementProps extends BaseProps {
665
+ style?: FullStyle;
666
+ children?: ReactNode;
667
+ textContent?: string;
668
+ numberOfLines?: number;
669
+ ellipsizeMode?: EllipsizeMode;
670
+ }
671
+
672
+ /**
673
+ * Props for pressable/touchable elements.
674
+ */
675
+ export interface PressableElementProps extends ContainerProps {
676
+ onPress?: (event: PressEvent) => void;
677
+ onPressIn?: (event: PressEvent) => void;
678
+ onPressOut?: (event: PressEvent) => void;
679
+ onLongPress?: (event: PressEvent) => void;
680
+ disabled?: boolean;
681
+ delayLongPress?: number;
682
+ }
683
+
684
+ /**
685
+ * Props for web-style image (img).
686
+ */
687
+ export interface ImageElementProps extends BaseProps {
688
+ style?: ViewStyle;
689
+ src: string;
690
+ lightSrc?: string;
691
+ darkSrc?: string;
692
+ alt?: string;
693
+ width?: number;
694
+ height?: number;
695
+ objectFit?: ImageObjectFit;
696
+ objectPosition?: string;
697
+ loading?: 'eager' | 'lazy';
698
+ themeTreatment?: ImageThemeTreatment;
699
+ }
700
+
701
+ /**
702
+ * Props for RN-style image (Image).
703
+ */
704
+ export interface RNImageProps extends BaseProps {
705
+ style?: ViewStyle;
706
+ source: ImageSource;
707
+ lightSrc?: string;
708
+ darkSrc?: string;
709
+ resizeMode?: ResizeMode;
710
+ objectFit?: ImageObjectFit;
711
+ objectPosition?: string;
712
+ alt?: string;
713
+ decorative?: boolean;
714
+ longDescription?: string;
715
+ placeholder?: ImagePlaceholder;
716
+ loading?: ImageLoading;
717
+ themeTreatment?: ImageThemeTreatment;
718
+ onLoadStart?: () => void;
719
+ onLoad?: (event: {
720
+ source: { uri: string; width: number; height: number };
721
+ cacheType: 'none' | 'disk' | 'memory';
722
+ }) => void;
723
+ onError?: (event: { error: string }) => void;
724
+ onDisplay?: () => void;
725
+ }
726
+
727
+ export interface SvgProps extends BaseProps {
728
+ style?: ViewStyle;
729
+ source: SvgSource;
730
+ objectFit?: ImageObjectFit;
731
+ objectPosition?: string;
732
+ alt?: string;
733
+ decorative?: boolean;
734
+ tintColor?: string;
735
+ colors?: Record<string, string>;
736
+ pixelDensity?: number;
737
+ }
738
+
739
+ /**
740
+ * Props for scroll containers.
741
+ */
742
+ export interface ScrollContainerProps extends ContainerProps {
743
+ horizontal?: boolean;
744
+ showsVerticalScrollIndicator?: boolean;
745
+ showsHorizontalScrollIndicator?: boolean;
746
+ scrollEventThrottle?: number;
747
+ onScroll?: (event: ScrollEvent) => void;
748
+ contentContainerStyle?: ViewStyle;
749
+ }
750
+
751
+ /**
752
+ * Props for text input elements.
753
+ */
754
+ export interface TextInputProps extends BaseProps {
755
+ style?: FullStyle;
756
+ value?: string;
757
+ defaultValue?: string;
758
+ selection?: TextInputSelection;
759
+ placeholder?: string;
760
+ placeholderTextColor?: string;
761
+ multiline?: boolean;
762
+ numberOfLines?: number;
763
+ maxLength?: number;
764
+ editable?: boolean;
765
+ autoFocus?: boolean;
766
+ secureTextEntry?: boolean;
767
+ keyboardType?: 'default' | 'numeric' | 'email-address' | 'phone-pad';
768
+ returnKeyType?: 'done' | 'go' | 'next' | 'search' | 'send';
769
+ onChangeText?: (text: string) => void;
770
+ onChange?: (event: ChangeEvent) => void;
771
+ onSelectionChange?: (event: SelectionChangeEvent) => void;
772
+ onSubmitEditing?: () => void;
773
+ }
774
+
775
+ /**
776
+ * Toggle value change event.
777
+ */
778
+ export interface ToggleChangeEvent {
779
+ nativeEvent: {
780
+ value: boolean;
781
+ };
782
+ }
783
+
784
+ /**
785
+ * Props for toggle/switch elements.
786
+ */
787
+ export interface ToggleProps extends BaseProps {
788
+ style?: ViewStyle;
789
+ /** Current value of the toggle (controlled) */
790
+ value?: boolean;
791
+ /** Default value for uncontrolled usage */
792
+ defaultValue?: boolean;
793
+ /** Callback when value changes */
794
+ onValueChange?: (value: boolean) => void;
795
+ /** Enable iOS 26+ liquid glass effect */
796
+ glassEffect?: boolean;
797
+ /** Tint color for the toggle when on */
798
+ tintColor?: string;
799
+ /** Label text (rendered natively on iOS) */
800
+ label?: string;
801
+ /** Alternative to label prop - children text content */
802
+ children?: ReactNode;
803
+ /** Whether the toggle is disabled */
804
+ disabled?: boolean;
805
+ }
806
+
807
+ // =============================================================================
808
+ // Internal Types (for reconciler and protocol)
809
+ // =============================================================================
810
+
811
+ /**
812
+ * Canonical internal style representation after normalization.
813
+ * All values are in their final form ready for protocol encoding.
814
+ */
815
+ export interface CanonicalStyle {
816
+ display?: Display;
817
+ // Dimensions
818
+ width?: DimensionValue;
819
+ height?: DimensionValue;
820
+ minWidth?: DimensionValue;
821
+ minHeight?: DimensionValue;
822
+ maxWidth?: DimensionValue;
823
+ maxHeight?: DimensionValue;
824
+
825
+ // Padding
826
+ paddingTop?: DimensionValue;
827
+ paddingRight?: DimensionValue;
828
+ paddingBottom?: DimensionValue;
829
+ paddingLeft?: DimensionValue;
830
+
831
+ // Margin
832
+ marginTop?: DimensionValue;
833
+ marginRight?: DimensionValue;
834
+ marginBottom?: DimensionValue;
835
+ marginLeft?: DimensionValue;
836
+
837
+ // Flexbox
838
+ direction?: ResolvedDirection;
839
+ flexDirection?: FlexDirection;
840
+ flexWrap?: FlexWrap;
841
+ justifyContent?: JustifyContent;
842
+ alignItems?: AlignItems;
843
+ alignSelf?: AlignSelf;
844
+ flexGrow?: number;
845
+ flexShrink?: number;
846
+ flexBasis?: DimensionValue;
847
+ rowGap?: number;
848
+ columnGap?: number;
849
+
850
+ // Position
851
+ positionType?: PositionType;
852
+ top?: DimensionValue;
853
+ right?: DimensionValue;
854
+ bottom?: DimensionValue;
855
+ left?: DimensionValue;
856
+ zIndex?: number;
857
+
858
+ // Transform
859
+ transformX?: number;
860
+ transformY?: number;
861
+ transformScale?: number;
862
+ transformRotate?: number;
863
+
864
+ // Appearance
865
+ backgroundColor?: RGBAColor;
866
+ opacity?: number;
867
+ borderRadius?: number;
868
+ borderTopLeftRadius?: number;
869
+ borderTopRightRadius?: number;
870
+ borderBottomLeftRadius?: number;
871
+ borderBottomRightRadius?: number;
872
+ borderWidth?: number;
873
+ borderColor?: RGBAColor;
874
+ // Per-side borders (ENG-22085).
875
+ borderTopWidth?: number;
876
+ borderRightWidth?: number;
877
+ borderBottomWidth?: number;
878
+ borderLeftWidth?: number;
879
+ borderTopColor?: RGBAColor;
880
+ borderRightColor?: RGBAColor;
881
+ borderBottomColor?: RGBAColor;
882
+ borderLeftColor?: RGBAColor;
883
+ shadowColor?: RGBAColor;
884
+ shadowOffsetX?: number;
885
+ shadowOffsetY?: number;
886
+ shadowRadius?: number;
887
+ shadowOpacity?: number;
888
+ backdropBlur?: number;
889
+ overflow?: Overflow;
890
+ aspectRatio?: number;
891
+ resizeMode?: ResizeMode;
892
+
893
+ // Typography
894
+ fontFamily?: number;
895
+ fontSize?: number;
896
+ textColor?: RGBAColor;
897
+ fontWeight?: number;
898
+ fontStyle?: FontStyle;
899
+ fontVariantNumeric?: number;
900
+ textAlign?: TextAlign;
901
+ writingMode?: WritingMode;
902
+ lineHeight?: number;
903
+ letterSpacing?: number;
904
+ textDecorationLine?: TextDecorationLine;
905
+ numberOfLines?: number;
906
+ ellipsizeMode?: EllipsizeMode;
907
+ }
908
+
909
+ export type CanonicalTransitionProperty = TransitionPropertyName;
910
+ export type CanonicalTransitionEasing = TransitionEasing;
911
+
912
+ export interface CanonicalTimingTransition {
913
+ readonly type: 'timing';
914
+ readonly duration: number;
915
+ readonly delay: number;
916
+ readonly easing: CanonicalTransitionEasing;
917
+ readonly respectsReducedMotion: boolean;
918
+ }
919
+
920
+ export interface CanonicalSpringTransition {
921
+ readonly type: 'spring';
922
+ readonly duration?: number;
923
+ readonly delay: number;
924
+ readonly damping: number;
925
+ readonly stiffness: number;
926
+ readonly mass: number;
927
+ readonly velocity: number;
928
+ readonly respectsReducedMotion: boolean;
929
+ }
930
+
931
+ export type CanonicalTransitionConfig = CanonicalTimingTransition | CanonicalSpringTransition;
932
+ export type CanonicalTransitionMap = Partial<Record<CanonicalTransitionProperty, CanonicalTransitionConfig>>;
933
+
934
+ export interface SafeAreaEdgeModes {
935
+ top: SafeAreaEdgeMode;
936
+ right: SafeAreaEdgeMode;
937
+ bottom: SafeAreaEdgeMode;
938
+ left: SafeAreaEdgeMode;
939
+ }
940
+
941
+ export type SafeAreaStrategy = 'framePadding' | 'contentInset';
942
+
943
+ /**
944
+ * Resolved safe-area declaration for a node.
945
+ *
946
+ * The renderer keeps this separate from `CanonicalProps` because it is not a
947
+ * protocol concept. It influences padding/content inset derivation and feeds
948
+ * the inspector/diagnostics surfaces.
949
+ */
950
+ export interface ResolvedSafeAreaConfig {
951
+ explicit: boolean;
952
+ regions: SafeAreaRegionName[];
953
+ edges: SafeAreaEdgeModes;
954
+ strategy: SafeAreaStrategy;
955
+ }
956
+
957
+ /**
958
+ * Safe-area context propagated from parent to child. Edge-anchored regions are
959
+ * tracked as "remaining" insets, while keyboard stays rect-based so floating
960
+ * and split keyboards do not get flattened too early.
961
+ */
962
+ export interface SafeAreaPropagationState {
963
+ rootId: number;
964
+ regions: Readonly<SafeAreaRegionInsets>;
965
+ keyboard: Readonly<KeyboardState>;
966
+ }
967
+
968
+ /**
969
+ * Inspector-facing breakdown of what safe-area logic actually applied to a
970
+ * node during style resolution.
971
+ */
972
+ export interface ResolvedSafeAreaState {
973
+ config: ResolvedSafeAreaConfig;
974
+ strategy: SafeAreaStrategy;
975
+ sourceRegions: Readonly<SafeAreaRegionInsets>;
976
+ siblingExpansion: Readonly<SafeAreaInsets>;
977
+ availableInsets: Readonly<SafeAreaInsets>;
978
+ keyboardOverlap: Readonly<SafeAreaInsets>;
979
+ appliedInsets: Readonly<SafeAreaInsets>;
980
+ remainingRegions: Readonly<SafeAreaRegionInsets>;
981
+ keyboardState: Readonly<KeyboardState>;
982
+ }
983
+
984
+ /**
985
+ * Canonical internal props representation after normalization.
986
+ */
987
+ export interface CanonicalProps {
988
+ // Text content
989
+ textContent?: string;
990
+ selectable?: SelectableMode;
991
+ selectionCopyText?: string;
992
+ selectionStart?: number;
993
+ selectionEnd?: number;
994
+ agentSummary?: string;
995
+ agentId?: string;
996
+ agentSemantics?: string;
997
+
998
+ // Image properties
999
+ imageSource?: string;
1000
+ resizeMode?: ResizeMode;
1001
+ svgSource?: string;
1002
+ svgColors?: string;
1003
+ svgObjectPosition?: string;
1004
+ svgPixelDensity?: string;
1005
+ videoPlayerId?: string;
1006
+ videoViewConfig?: string;
1007
+ nativeViewModuleName?: string;
1008
+ nativeViewProps?: string;
1009
+ nativeViewSelectionTier?: NativeViewSelectionTier;
1010
+ nativeViewGesturePolicy?: NativeViewSelectionGesturePolicy;
1011
+
1012
+ // Accessibility
1013
+ accessibilityLabel?: string;
1014
+ lang?: string;
1015
+ focusScope?: FocusScopeMode;
1016
+ inert?: boolean;
1017
+ scrollLocked?: boolean;
1018
+ portalTarget?: string;
1019
+ accessibilityRole?: string;
1020
+ accessibilityHint?: string;
1021
+ accessibilityModal?: boolean;
1022
+ accessibilityExpanded?: boolean;
1023
+ accessibilitySelected?: boolean;
1024
+ accessibilityChecked?: AccessibilityCheckedState;
1025
+ accessibilityDisabled?: boolean;
1026
+ accessibilityLive?: AccessibilityLiveMode;
1027
+ accessibilityValueNow?: number;
1028
+ accessibilityValueMin?: number;
1029
+ accessibilityValueMax?: number;
1030
+ accessibilityValueText?: string;
1031
+ focusable?: boolean;
1032
+ tabIndex?: number;
1033
+ testId?: string;
1034
+ nativeID?: string;
1035
+ /** In-document anchor/URL for link pressables (e.g. a footnote ref's `#fn-1`).
1036
+ * Encoded to native so the host can resolve in-page anchor scrolls. */
1037
+ href?: string;
1038
+ accessibilityLabelledBy?: string;
1039
+ accessibilityDescribedBy?: string;
1040
+ accessibilityBusy?: boolean;
1041
+ accessibilityActions?: string;
1042
+ accessibilityElementsHidden?: boolean;
1043
+ accessibilityOrder?: string;
1044
+ accessibilityHeadingLevel?: number;
1045
+ accessibilitySynthetic?: boolean;
1046
+ allowFontScaling?: boolean;
1047
+ maxFontSizeMultiplier?: number;
1048
+ minimumFontSize?: number;
1049
+ disabledReason?: string;
1050
+ __exactPresencePhase?: string;
1051
+ __exactPortalLevel?: string;
1052
+ __exactPortalPresentation?: string;
1053
+ __exactDismissableLayer?: boolean;
1054
+ __exactDismissAction?: string;
1055
+ __exactFocusRestore?: boolean;
1056
+ __exactAnchorTarget?: string;
1057
+ __exactAnchorPlacement?: string;
1058
+ __exactAnchorStrategy?: string;
1059
+ __exactAnchorOffset?: number;
1060
+ __exactComponentName?: string;
1061
+ __exactComponentSlot?: string;
1062
+ __exactSourceFilePath?: string;
1063
+ __exactVariantProps?: string;
1064
+ __exactInteractionState?: string;
1065
+ __exactRenderMode?: string;
1066
+
1067
+ // Input properties
1068
+ placeholder?: string;
1069
+ value?: string;
1070
+ editable?: boolean;
1071
+ multiline?: boolean;
1072
+ maxLength?: number;
1073
+ secureTextEntry?: boolean;
1074
+
1075
+ // Scroll properties
1076
+ horizontal?: boolean;
1077
+ showsScrollIndicator?: boolean;
1078
+
1079
+ // Toggle properties
1080
+ toggleValue?: boolean;
1081
+ /**
1082
+ * Liquid Glass spec. For toggles this is a boolean (`true` enables the
1083
+ * control glass). For container surfaces the renderer composes the `glass*`
1084
+ * inputs into a spec string — `"true" | "regular" | "clear"` optionally
1085
+ * followed by `;tint=<color>` and/or `;interactive` — which the native host
1086
+ * parses (see ExactEngine glass spec read). The encoder stringifies it onto
1087
+ * the existing `PropId.GlassEffect` wire prop, so no new protocol id is
1088
+ * needed; the legacy `"true"` value stays truthy for the toggle path.
1089
+ */
1090
+ glassEffect?: boolean | string;
1091
+ tintColor?: string;
1092
+ disabled?: boolean;
1093
+ }
1094
+
1095
+ /**
1096
+ * Event handler registration.
1097
+ */
1098
+ export interface EventBinding {
1099
+ handlerId: number;
1100
+ handler: Function;
1101
+ }
1102
+
1103
+ // =============================================================================
1104
+ // Tag Types
1105
+ // =============================================================================
1106
+
1107
+ /**
1108
+ * Internal tag type used by the renderer.
1109
+ * All user-facing tags (web and RN) are resolved to these canonical types.
1110
+ */
1111
+ export type CanonicalTagType =
1112
+ | 'view' // Generic container
1113
+ | 'text' // Text content
1114
+ | 'image' // Image
1115
+ | 'svg' // SVG image
1116
+ | 'video' // Video view
1117
+ | 'nativeView' // Generic module-backed native view
1118
+ | 'scroll' // Scroll container
1119
+ | 'list' // Host-owned virtual list container
1120
+ | 'input' // Text input
1121
+ | 'pressable' // Touchable wrapper
1122
+ | 'toggle' // Toggle/switch
1123
+ ;
1124
+
1125
+ /**
1126
+ * Web element tags supported by the renderer.
1127
+ */
1128
+ export type WebTagType =
1129
+ | 'div'
1130
+ | 'span'
1131
+ | 'text'
1132
+ | 'img'
1133
+ | 'input'
1134
+ | 'button'
1135
+ | 'toggle'
1136
+ | 'p'
1137
+ | 'h1'
1138
+ | 'h2'
1139
+ | 'h3'
1140
+ | 'h4'
1141
+ | 'h5'
1142
+ | 'h6'
1143
+ | 'section'
1144
+ | 'main'
1145
+ | 'article'
1146
+ | 'nav'
1147
+ | 'aside'
1148
+ | 'header'
1149
+ | 'footer'
1150
+ | 'form'
1151
+ | 'label'
1152
+ | 'textarea'
1153
+ | 'a'
1154
+ | 'svg';
1155
+
1156
+ /**
1157
+ * React Native component tags supported by the renderer.
1158
+ */
1159
+ export type RNTagType =
1160
+ | 'View'
1161
+ | 'Text'
1162
+ | 'Image'
1163
+ | 'Svg'
1164
+ | 'VideoView'
1165
+ | 'ScrollView'
1166
+ | 'List'
1167
+ | 'TextInput'
1168
+ | 'Pressable'
1169
+ | 'Toggle'
1170
+ | 'Switch';
1171
+
1172
+ /**
1173
+ * All supported tag types.
1174
+ */
1175
+ export type TagType = WebTagType | RNTagType;