@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.
- package/package.json +118 -0
- package/src/__tests__/adapter-window-state.test.tsx +190 -0
- package/src/__tests__/attrs.test.ts +157 -0
- package/src/__tests__/classname.test.ts +332 -0
- package/src/__tests__/color.test.ts +169 -0
- package/src/__tests__/dom-mirror.test.ts +682 -0
- package/src/__tests__/dom-shim.test.ts +274 -0
- package/src/__tests__/fixtures/SvelteCounter.svelte +7 -0
- package/src/__tests__/fixtures/SvelteInput.svelte +8 -0
- package/src/__tests__/host-config.test.ts +51 -0
- package/src/__tests__/host-ops.test.ts +2234 -0
- package/src/__tests__/image-source.test.ts +135 -0
- package/src/__tests__/liquid-glass.test.ts +72 -0
- package/src/__tests__/multi-root.test.ts +118 -0
- package/src/__tests__/native-view-events.test.ts +102 -0
- package/src/__tests__/nodes.test.ts +399 -0
- package/src/__tests__/normalize.test.ts +576 -0
- package/src/__tests__/paragraph-lowering.test.tsx +144 -0
- package/src/__tests__/props.test.ts +518 -0
- package/src/__tests__/protocol-encoder.test.ts +732 -0
- package/src/__tests__/protocol-fixture-bytes.test.ts +41 -0
- package/src/__tests__/reconciler.test.tsx +241 -0
- package/src/__tests__/svelte-adapter.test.ts +166 -0
- package/src/__tests__/svg-source.test.ts +71 -0
- package/src/__tests__/tags.test.ts +354 -0
- package/src/__tests__/toggle.test.ts +441 -0
- package/src/__tests__/transitions.test.ts +106 -0
- package/src/__tests__/web-primitives.test.tsx +454 -0
- package/src/__tests__/window-hooks.test.tsx +447 -0
- package/src/adapter-contract.ts +68 -0
- package/src/attrs.ts +596 -0
- package/src/classname-contract.ts +87 -0
- package/src/classname-resolve.ts +553 -0
- package/src/classname-runtime.ts +29 -0
- package/src/components.ts +214 -0
- package/src/css-variable-context.ts +83 -0
- package/src/dom-hydration.ts +160 -0
- package/src/dom-mirror.ts +1459 -0
- package/src/dom-shim.ts +1736 -0
- package/src/group-context.ts +69 -0
- package/src/host-config.ts +431 -0
- package/src/host-ops.ts +3167 -0
- package/src/image-source.native.ts +703 -0
- package/src/image-source.ts +554 -0
- package/src/index.ts +278 -0
- package/src/inspector-runtime.ts +244 -0
- package/src/inspector.ts +3570 -0
- package/src/jsx-augmentations.ts +54 -0
- package/src/keyboard-avoidance.ts +217 -0
- package/src/native-primitives.ts +43 -0
- package/src/native-view-events.ts +322 -0
- package/src/native-view.ts +60 -0
- package/src/nodes/index.ts +41 -0
- package/src/nodes/node.ts +531 -0
- package/src/peer-context.ts +100 -0
- package/src/primitives.native.ts +8 -0
- package/src/primitives.ts +8 -0
- package/src/props/index.ts +14 -0
- package/src/props/normalize.ts +816 -0
- package/src/protocol/encoder.ts +940 -0
- package/src/protocol/index.ts +33 -0
- package/src/reconciler.ts +581 -0
- package/src/runtime.ts +11 -0
- package/src/safe-area.ts +543 -0
- package/src/solid.ts +490 -0
- package/src/style/color.js +1 -0
- package/src/style/color.ts +15 -0
- package/src/style/index.js +1 -0
- package/src/style/index.ts +22 -0
- package/src/style/normalize.js +1 -0
- package/src/style/normalize.ts +1426 -0
- package/src/svelte.ts +349 -0
- package/src/svg-source.ts +222 -0
- package/src/tags/index.ts +21 -0
- package/src/tags/tag-map.ts +289 -0
- package/src/text/paragraph-lowering.ts +310 -0
- package/src/types.ts +1175 -0
- package/src/vue.ts +535 -0
- package/src/web-host.ts +19 -0
- package/src/web-primitives.ts +1654 -0
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
// @system @ref LLP 0003 — Renderer-to-kernel protocol encoding
|
|
2
|
+
/**
|
|
3
|
+
* Protocol encoding helpers.
|
|
4
|
+
*
|
|
5
|
+
* This module keeps canonical-style and transition conversion near the
|
|
6
|
+
* BufferWriter without introducing a second encoder abstraction.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
BufferWriter,
|
|
11
|
+
type Dimension,
|
|
12
|
+
type StyleProps,
|
|
13
|
+
type TransitionProps,
|
|
14
|
+
} from '@exact/core/protocol/buffer-writer';
|
|
15
|
+
import {
|
|
16
|
+
EventType,
|
|
17
|
+
NodeType,
|
|
18
|
+
PropId,
|
|
19
|
+
TransitionEasingId,
|
|
20
|
+
TransitionKindId,
|
|
21
|
+
TransitionPropertyId,
|
|
22
|
+
} from '@exact/core/protocol/opcodes';
|
|
23
|
+
import {
|
|
24
|
+
getWindowViewportForRoot,
|
|
25
|
+
} from '@exact/core/window-state';
|
|
26
|
+
import { recordProtocolFrame } from '@exact/core/agent';
|
|
27
|
+
import type {
|
|
28
|
+
CanonicalStyle,
|
|
29
|
+
CanonicalProps,
|
|
30
|
+
CanonicalTransitionMap,
|
|
31
|
+
CanonicalTransitionConfig,
|
|
32
|
+
DimensionValue,
|
|
33
|
+
SelectableMode,
|
|
34
|
+
} from '../types.js';
|
|
35
|
+
import type { ElementNode, TextNode, RootNode } from '../nodes/node.js';
|
|
36
|
+
import { getChildIds, NodeKind } from '../nodes/node.js';
|
|
37
|
+
import { getTagConfig, defaultTagConfig } from '../tags/index.js';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Development mode flag - gate verbose logging.
|
|
41
|
+
*/
|
|
42
|
+
const __DEV__ = process.env.NODE_ENV !== 'production';
|
|
43
|
+
|
|
44
|
+
const STRING_PROP_ENCODINGS = [
|
|
45
|
+
['imageSource', PropId.ImageSource],
|
|
46
|
+
['svgSource', PropId.SvgSource],
|
|
47
|
+
['svgColors', PropId.SvgColors],
|
|
48
|
+
['svgPixelDensity', PropId.SvgPixelDensity],
|
|
49
|
+
['svgObjectPosition', PropId.SvgObjectPosition],
|
|
50
|
+
['videoPlayerId', PropId.VideoPlayerId],
|
|
51
|
+
['videoViewConfig', PropId.VideoViewConfig],
|
|
52
|
+
['selectionCopyText', PropId.SelectionCopyText],
|
|
53
|
+
['nativeViewModuleName', PropId.NativeViewModuleName],
|
|
54
|
+
['nativeViewProps', PropId.NativeViewProps],
|
|
55
|
+
['nativeViewSelectionTier', PropId.NativeViewSelectionTier],
|
|
56
|
+
['nativeViewGesturePolicy', PropId.NativeViewGesturePolicy],
|
|
57
|
+
['placeholder', PropId.Placeholder],
|
|
58
|
+
['accessibilityLabel', PropId.AccessibilityLabel],
|
|
59
|
+
['tintColor', PropId.TintColor],
|
|
60
|
+
['href', PropId.Href],
|
|
61
|
+
] as const satisfies readonly (readonly [keyof CanonicalProps, PropId])[];
|
|
62
|
+
|
|
63
|
+
const SCALAR_PROP_ENCODINGS = [
|
|
64
|
+
['focusScope', PropId.FocusScope],
|
|
65
|
+
['inert', PropId.Inert],
|
|
66
|
+
['scrollLocked', PropId.ScrollLocked],
|
|
67
|
+
['portalTarget', PropId.PortalTarget],
|
|
68
|
+
['accessibilityRole', PropId.AccessibilityRole],
|
|
69
|
+
['accessibilityHint', PropId.AccessibilityHint],
|
|
70
|
+
['accessibilityModal', PropId.AccessibilityModal],
|
|
71
|
+
['accessibilityExpanded', PropId.AccessibilityExpanded],
|
|
72
|
+
['accessibilitySelected', PropId.AccessibilitySelected],
|
|
73
|
+
['accessibilityChecked', PropId.AccessibilityChecked],
|
|
74
|
+
['accessibilityDisabled', PropId.AccessibilityDisabled],
|
|
75
|
+
['accessibilityLive', PropId.AccessibilityLive],
|
|
76
|
+
['accessibilityValueNow', PropId.AccessibilityValueNow],
|
|
77
|
+
['accessibilityValueMin', PropId.AccessibilityValueMin],
|
|
78
|
+
['accessibilityValueMax', PropId.AccessibilityValueMax],
|
|
79
|
+
['accessibilityValueText', PropId.AccessibilityValueText],
|
|
80
|
+
['focusable', PropId.Focusable],
|
|
81
|
+
['testId', PropId.TestId],
|
|
82
|
+
['nativeID', PropId.NativeID],
|
|
83
|
+
['accessibilityLabelledBy', PropId.AccessibilityLabelledBy],
|
|
84
|
+
['accessibilityDescribedBy', PropId.AccessibilityDescribedBy],
|
|
85
|
+
['accessibilityBusy', PropId.AccessibilityBusy],
|
|
86
|
+
['accessibilityActions', PropId.AccessibilityActions],
|
|
87
|
+
['accessibilityElementsHidden', PropId.AccessibilityElementsHidden],
|
|
88
|
+
['accessibilityOrder', PropId.AccessibilityOrder],
|
|
89
|
+
['tabIndex', PropId.TabIndex],
|
|
90
|
+
['allowFontScaling', PropId.AllowFontScaling],
|
|
91
|
+
['maxFontSizeMultiplier', PropId.MaxFontSizeMultiplier],
|
|
92
|
+
['minimumFontSize', PropId.MinimumFontSize],
|
|
93
|
+
['accessibilitySynthetic', PropId.AccessibilitySynthetic],
|
|
94
|
+
['accessibilityHeadingLevel', PropId.AccessibilityHeadingLevel],
|
|
95
|
+
['toggleValue', PropId.ToggleValue],
|
|
96
|
+
['glassEffect', PropId.GlassEffect],
|
|
97
|
+
['disabled', PropId.Disabled],
|
|
98
|
+
['showsScrollIndicator', PropId.ShowsScrollIndicator],
|
|
99
|
+
['selectionStart', PropId.SelectionStart],
|
|
100
|
+
['selectionEnd', PropId.SelectionEnd],
|
|
101
|
+
] as const satisfies readonly (readonly [keyof CanonicalProps, PropId])[];
|
|
102
|
+
|
|
103
|
+
type CanonicalDimensionStyleKey = Extract<keyof CanonicalStyle,
|
|
104
|
+
| 'width'
|
|
105
|
+
| 'height'
|
|
106
|
+
| 'minWidth'
|
|
107
|
+
| 'minHeight'
|
|
108
|
+
| 'maxWidth'
|
|
109
|
+
| 'maxHeight'
|
|
110
|
+
| 'paddingTop'
|
|
111
|
+
| 'paddingRight'
|
|
112
|
+
| 'paddingBottom'
|
|
113
|
+
| 'paddingLeft'
|
|
114
|
+
| 'marginTop'
|
|
115
|
+
| 'marginRight'
|
|
116
|
+
| 'marginBottom'
|
|
117
|
+
| 'marginLeft'
|
|
118
|
+
| 'flexBasis'
|
|
119
|
+
| 'top'
|
|
120
|
+
| 'right'
|
|
121
|
+
| 'bottom'
|
|
122
|
+
| 'left'
|
|
123
|
+
>;
|
|
124
|
+
|
|
125
|
+
const STYLE_DIMENSION_ENCODINGS = [
|
|
126
|
+
'width',
|
|
127
|
+
'height',
|
|
128
|
+
'minWidth',
|
|
129
|
+
'minHeight',
|
|
130
|
+
'maxWidth',
|
|
131
|
+
'maxHeight',
|
|
132
|
+
'paddingTop',
|
|
133
|
+
'paddingRight',
|
|
134
|
+
'paddingBottom',
|
|
135
|
+
'paddingLeft',
|
|
136
|
+
'marginTop',
|
|
137
|
+
'marginRight',
|
|
138
|
+
'marginBottom',
|
|
139
|
+
'marginLeft',
|
|
140
|
+
'flexBasis',
|
|
141
|
+
'top',
|
|
142
|
+
'right',
|
|
143
|
+
'bottom',
|
|
144
|
+
'left',
|
|
145
|
+
] as const satisfies readonly CanonicalDimensionStyleKey[];
|
|
146
|
+
|
|
147
|
+
const STYLE_DIRECT_ENCODINGS = [
|
|
148
|
+
'flexDirection',
|
|
149
|
+
'flexWrap',
|
|
150
|
+
'justifyContent',
|
|
151
|
+
'alignItems',
|
|
152
|
+
'alignSelf',
|
|
153
|
+
'flexGrow',
|
|
154
|
+
'flexShrink',
|
|
155
|
+
'rowGap',
|
|
156
|
+
'columnGap',
|
|
157
|
+
'positionType',
|
|
158
|
+
'zIndex',
|
|
159
|
+
'transformX',
|
|
160
|
+
'transformY',
|
|
161
|
+
'transformScale',
|
|
162
|
+
'transformRotate',
|
|
163
|
+
'display',
|
|
164
|
+
'backgroundColor',
|
|
165
|
+
'opacity',
|
|
166
|
+
'borderRadius',
|
|
167
|
+
'borderTopLeftRadius',
|
|
168
|
+
'borderTopRightRadius',
|
|
169
|
+
'borderBottomLeftRadius',
|
|
170
|
+
'borderBottomRightRadius',
|
|
171
|
+
'borderWidth',
|
|
172
|
+
'borderColor',
|
|
173
|
+
'borderTopWidth',
|
|
174
|
+
'borderRightWidth',
|
|
175
|
+
'borderBottomWidth',
|
|
176
|
+
'borderLeftWidth',
|
|
177
|
+
'borderTopColor',
|
|
178
|
+
'borderRightColor',
|
|
179
|
+
'borderBottomColor',
|
|
180
|
+
'borderLeftColor',
|
|
181
|
+
'shadowColor',
|
|
182
|
+
'shadowOffsetX',
|
|
183
|
+
'shadowOffsetY',
|
|
184
|
+
'shadowRadius',
|
|
185
|
+
'shadowOpacity',
|
|
186
|
+
'backdropBlur',
|
|
187
|
+
'overflow',
|
|
188
|
+
'aspectRatio',
|
|
189
|
+
'resizeMode',
|
|
190
|
+
'fontFamily',
|
|
191
|
+
'fontSize',
|
|
192
|
+
'textColor',
|
|
193
|
+
'direction',
|
|
194
|
+
'writingMode',
|
|
195
|
+
'fontWeight',
|
|
196
|
+
'fontStyle',
|
|
197
|
+
'lineHeight',
|
|
198
|
+
'letterSpacing',
|
|
199
|
+
'textDecorationLine',
|
|
200
|
+
'numberOfLines',
|
|
201
|
+
'ellipsizeMode',
|
|
202
|
+
] as const satisfies readonly (keyof CanonicalStyle & keyof StyleProps)[];
|
|
203
|
+
|
|
204
|
+
function hasOwnEnumerableProperties(value: object): boolean {
|
|
205
|
+
for (const key in value) {
|
|
206
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Declare the global exact object provided by the native runtime.
|
|
215
|
+
*/
|
|
216
|
+
declare const exact: {
|
|
217
|
+
dispatch: (buffer: Uint8Array) => void;
|
|
218
|
+
dispatchWithDebugContext?: (buffer: Uint8Array, debugContextJSON: string) => void;
|
|
219
|
+
__resizeTraceContext?: unknown;
|
|
220
|
+
screenWidth?: number;
|
|
221
|
+
screenHeight?: number;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Convert a DimensionValue to the protocol Dimension format.
|
|
226
|
+
*/
|
|
227
|
+
function toProtocolDimension(dim: DimensionValue): Dimension {
|
|
228
|
+
switch (dim.type) {
|
|
229
|
+
case 'auto':
|
|
230
|
+
return { type: 'auto' };
|
|
231
|
+
case 'points':
|
|
232
|
+
return { type: 'points', value: dim.value };
|
|
233
|
+
case 'percent':
|
|
234
|
+
return { type: 'percent', value: dim.value };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Convert CanonicalStyle to StyleProps for the BufferWriter.
|
|
240
|
+
*/
|
|
241
|
+
function canonicalStyleToProtocol(style: CanonicalStyle): StyleProps {
|
|
242
|
+
const props: StyleProps = {};
|
|
243
|
+
const protocolProps = props as Record<string, unknown>;
|
|
244
|
+
|
|
245
|
+
for (const key of STYLE_DIMENSION_ENCODINGS) {
|
|
246
|
+
const value = style[key];
|
|
247
|
+
if (value !== undefined) {
|
|
248
|
+
protocolProps[key] = toProtocolDimension(value);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (const key of STYLE_DIRECT_ENCODINGS) {
|
|
253
|
+
const value = style[key];
|
|
254
|
+
if (value !== undefined) {
|
|
255
|
+
protocolProps[key] = value;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (style.fontVariantNumeric !== undefined && style.fontVariantNumeric !== 0) {
|
|
260
|
+
props.fontVariantNumeric = style.fontVariantNumeric;
|
|
261
|
+
}
|
|
262
|
+
if (style.textAlign !== undefined && style.textAlign !== 'auto') {
|
|
263
|
+
props.textAlign = style.textAlign;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return props;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function transitionPropertyToProtocol(property: keyof CanonicalTransitionMap): TransitionPropertyId {
|
|
270
|
+
switch (property) {
|
|
271
|
+
case 'opacity':
|
|
272
|
+
return TransitionPropertyId.Opacity;
|
|
273
|
+
case 'transform':
|
|
274
|
+
return TransitionPropertyId.Transform;
|
|
275
|
+
case 'backgroundColor':
|
|
276
|
+
return TransitionPropertyId.BackgroundColor;
|
|
277
|
+
case 'borderRadius':
|
|
278
|
+
return TransitionPropertyId.BorderRadius;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function transitionEasingToProtocol(config: CanonicalTransitionConfig): {
|
|
283
|
+
easingType: TransitionEasingId;
|
|
284
|
+
params: [number, number, number, number];
|
|
285
|
+
} {
|
|
286
|
+
if (config.type !== 'timing') {
|
|
287
|
+
return {
|
|
288
|
+
easingType: TransitionEasingId.EaseInOut,
|
|
289
|
+
params: [config.damping, config.stiffness, config.mass, config.velocity],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (typeof config.easing === 'string') {
|
|
294
|
+
switch (config.easing) {
|
|
295
|
+
case 'linear':
|
|
296
|
+
return { easingType: TransitionEasingId.Linear, params: [0, 0, 0, 0] };
|
|
297
|
+
case 'easeIn':
|
|
298
|
+
return { easingType: TransitionEasingId.EaseIn, params: [0, 0, 0, 0] };
|
|
299
|
+
case 'easeOut':
|
|
300
|
+
return { easingType: TransitionEasingId.EaseOut, params: [0, 0, 0, 0] };
|
|
301
|
+
case 'easeInOut':
|
|
302
|
+
default:
|
|
303
|
+
return { easingType: TransitionEasingId.EaseInOut, params: [0, 0, 0, 0] };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
easingType: TransitionEasingId.CubicBezier,
|
|
309
|
+
params: [config.easing[0], config.easing[1], config.easing[2], config.easing[3]],
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function canonicalTransitionsToProtocol(transitions: CanonicalTransitionMap): TransitionProps[] {
|
|
314
|
+
const entries: TransitionProps[] = [];
|
|
315
|
+
|
|
316
|
+
for (const [property, config] of Object.entries(transitions) as Array<[keyof CanonicalTransitionMap, CanonicalTransitionConfig]>) {
|
|
317
|
+
if (config.type === 'timing') {
|
|
318
|
+
const { easingType, params } = transitionEasingToProtocol(config);
|
|
319
|
+
entries.push({
|
|
320
|
+
propertyId: transitionPropertyToProtocol(property),
|
|
321
|
+
kind: TransitionKindId.Timing,
|
|
322
|
+
durationMs: config.duration,
|
|
323
|
+
delayMs: config.delay,
|
|
324
|
+
respectsReducedMotion: config.respectsReducedMotion,
|
|
325
|
+
easingType,
|
|
326
|
+
params,
|
|
327
|
+
});
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
entries.push({
|
|
332
|
+
propertyId: transitionPropertyToProtocol(property),
|
|
333
|
+
kind: TransitionKindId.Spring,
|
|
334
|
+
durationMs: config.duration,
|
|
335
|
+
delayMs: config.delay,
|
|
336
|
+
respectsReducedMotion: config.respectsReducedMotion,
|
|
337
|
+
easingType: TransitionEasingId.EaseInOut,
|
|
338
|
+
params: [config.damping, config.stiffness, config.mass, config.velocity],
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return entries;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export type ProtocolEncoder = BufferWriter;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Create a protocol encoder backed directly by BufferWriter.
|
|
349
|
+
*/
|
|
350
|
+
export function createProtocolEncoder(
|
|
351
|
+
capacity: number = 1024 * 1024,
|
|
352
|
+
rootId: number = 0
|
|
353
|
+
): ProtocolEncoder {
|
|
354
|
+
return new BufferWriter(capacity, 1, rootId);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Encode a SetStyle operation from canonical style.
|
|
359
|
+
*/
|
|
360
|
+
export function setStyle(
|
|
361
|
+
encoder: ProtocolEncoder,
|
|
362
|
+
nodeId: number,
|
|
363
|
+
style: CanonicalStyle
|
|
364
|
+
): void {
|
|
365
|
+
const props = canonicalStyleToProtocol(style);
|
|
366
|
+
if (hasOwnEnumerableProperties(props)) {
|
|
367
|
+
encoder.setStyle(nodeId, props);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function setTransform(
|
|
372
|
+
encoder: ProtocolEncoder,
|
|
373
|
+
nodeId: number,
|
|
374
|
+
transformX: number,
|
|
375
|
+
transformY: number,
|
|
376
|
+
transformScale: number,
|
|
377
|
+
transformRotate: number,
|
|
378
|
+
): void {
|
|
379
|
+
encoder.setTransform(nodeId, transformX, transformY, transformScale, transformRotate);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function setOpacity(
|
|
383
|
+
encoder: ProtocolEncoder,
|
|
384
|
+
nodeId: number,
|
|
385
|
+
opacity: number,
|
|
386
|
+
): void {
|
|
387
|
+
encoder.setOpacity(nodeId, opacity);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function setBackgroundColor(
|
|
391
|
+
encoder: ProtocolEncoder,
|
|
392
|
+
nodeId: number,
|
|
393
|
+
color: NonNullable<CanonicalStyle['backgroundColor']>,
|
|
394
|
+
): void {
|
|
395
|
+
encoder.setBackgroundColor(nodeId, color);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Encode a SetTransition operation from canonical transition metadata.
|
|
400
|
+
*/
|
|
401
|
+
export function setTransition(
|
|
402
|
+
encoder: ProtocolEncoder,
|
|
403
|
+
nodeId: number,
|
|
404
|
+
transitions: CanonicalTransitionMap
|
|
405
|
+
): void {
|
|
406
|
+
encoder.setTransition(nodeId, canonicalTransitionsToProtocol(transitions));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function setStringProp(
|
|
410
|
+
encoder: ProtocolEncoder,
|
|
411
|
+
nodeId: number,
|
|
412
|
+
propId: PropId,
|
|
413
|
+
value: string
|
|
414
|
+
): void {
|
|
415
|
+
encoder.setPropString(nodeId, propId, value);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function setPropValue(
|
|
419
|
+
encoder: ProtocolEncoder,
|
|
420
|
+
nodeId: number,
|
|
421
|
+
propId: PropId,
|
|
422
|
+
value: string | number | boolean
|
|
423
|
+
): void {
|
|
424
|
+
setStringProp(encoder, nodeId, propId, String(value));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function encodeStringPropTable(
|
|
428
|
+
encoder: ProtocolEncoder,
|
|
429
|
+
nodeId: number,
|
|
430
|
+
props: CanonicalProps,
|
|
431
|
+
): void {
|
|
432
|
+
for (const [propName, propId] of STRING_PROP_ENCODINGS) {
|
|
433
|
+
const value = props[propName];
|
|
434
|
+
if (typeof value === 'string') {
|
|
435
|
+
setStringProp(encoder, nodeId, propId, value);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function encodeScalarPropTable(
|
|
441
|
+
encoder: ProtocolEncoder,
|
|
442
|
+
nodeId: number,
|
|
443
|
+
props: CanonicalProps,
|
|
444
|
+
): void {
|
|
445
|
+
for (const [propName, propId] of SCALAR_PROP_ENCODINGS) {
|
|
446
|
+
const value = props[propName];
|
|
447
|
+
if (value !== undefined) {
|
|
448
|
+
setPropValue(encoder, nodeId, propId, value as string | number | boolean);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function setTextContent(encoder: ProtocolEncoder, nodeId: number, text: string): void {
|
|
454
|
+
setStringProp(encoder, nodeId, PropId.TextContent, text);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function setImageSource(encoder: ProtocolEncoder, nodeId: number, source: string): void {
|
|
458
|
+
setStringProp(encoder, nodeId, PropId.ImageSource, source);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function setSvgSource(encoder: ProtocolEncoder, nodeId: number, source: string): void {
|
|
462
|
+
setStringProp(encoder, nodeId, PropId.SvgSource, source);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function setSvgColors(encoder: ProtocolEncoder, nodeId: number, colors: string): void {
|
|
466
|
+
setStringProp(encoder, nodeId, PropId.SvgColors, colors);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function setSvgPixelDensity(
|
|
470
|
+
encoder: ProtocolEncoder,
|
|
471
|
+
nodeId: number,
|
|
472
|
+
pixelDensity: string,
|
|
473
|
+
): void {
|
|
474
|
+
setStringProp(encoder, nodeId, PropId.SvgPixelDensity, pixelDensity);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export function setSvgObjectPosition(
|
|
478
|
+
encoder: ProtocolEncoder,
|
|
479
|
+
nodeId: number,
|
|
480
|
+
objectPosition: string,
|
|
481
|
+
): void {
|
|
482
|
+
setStringProp(encoder, nodeId, PropId.SvgObjectPosition, objectPosition);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export function setVideoPlayerId(
|
|
486
|
+
encoder: ProtocolEncoder,
|
|
487
|
+
nodeId: number,
|
|
488
|
+
playerId: string,
|
|
489
|
+
): void {
|
|
490
|
+
setStringProp(encoder, nodeId, PropId.VideoPlayerId, playerId);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export function setVideoViewConfig(
|
|
494
|
+
encoder: ProtocolEncoder,
|
|
495
|
+
nodeId: number,
|
|
496
|
+
config: string,
|
|
497
|
+
): void {
|
|
498
|
+
setStringProp(encoder, nodeId, PropId.VideoViewConfig, config);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function setSelectionCopyText(
|
|
502
|
+
encoder: ProtocolEncoder,
|
|
503
|
+
nodeId: number,
|
|
504
|
+
text: string,
|
|
505
|
+
): void {
|
|
506
|
+
setStringProp(encoder, nodeId, PropId.SelectionCopyText, text);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export function setLang(
|
|
510
|
+
encoder: ProtocolEncoder,
|
|
511
|
+
nodeId: number,
|
|
512
|
+
lang: string,
|
|
513
|
+
): void {
|
|
514
|
+
setStringProp(encoder, nodeId, PropId.Lang, lang);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export function setNativeViewModuleName(
|
|
518
|
+
encoder: ProtocolEncoder,
|
|
519
|
+
nodeId: number,
|
|
520
|
+
moduleName: string,
|
|
521
|
+
): void {
|
|
522
|
+
setStringProp(encoder, nodeId, PropId.NativeViewModuleName, moduleName);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export function setNativeViewProps(
|
|
526
|
+
encoder: ProtocolEncoder,
|
|
527
|
+
nodeId: number,
|
|
528
|
+
props: string,
|
|
529
|
+
): void {
|
|
530
|
+
setStringProp(encoder, nodeId, PropId.NativeViewProps, props);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export function setNativeViewSelectionTier(
|
|
534
|
+
encoder: ProtocolEncoder,
|
|
535
|
+
nodeId: number,
|
|
536
|
+
tier: string,
|
|
537
|
+
): void {
|
|
538
|
+
setStringProp(encoder, nodeId, PropId.NativeViewSelectionTier, tier);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export function setNativeViewGesturePolicy(
|
|
542
|
+
encoder: ProtocolEncoder,
|
|
543
|
+
nodeId: number,
|
|
544
|
+
gesturePolicy: string,
|
|
545
|
+
): void {
|
|
546
|
+
setStringProp(encoder, nodeId, PropId.NativeViewGesturePolicy, gesturePolicy);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export function setPlaceholder(
|
|
550
|
+
encoder: ProtocolEncoder,
|
|
551
|
+
nodeId: number,
|
|
552
|
+
placeholder: string
|
|
553
|
+
): void {
|
|
554
|
+
setStringProp(encoder, nodeId, PropId.Placeholder, placeholder);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export function setAccessibilityLabel(
|
|
558
|
+
encoder: ProtocolEncoder,
|
|
559
|
+
nodeId: number,
|
|
560
|
+
label: string
|
|
561
|
+
): void {
|
|
562
|
+
setStringProp(encoder, nodeId, PropId.AccessibilityLabel, label);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function setToggleValue(
|
|
566
|
+
encoder: ProtocolEncoder,
|
|
567
|
+
nodeId: number,
|
|
568
|
+
value: boolean
|
|
569
|
+
): void {
|
|
570
|
+
setStringProp(encoder, nodeId, PropId.ToggleValue, value ? 'true' : 'false');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export function setGlassEffect(
|
|
574
|
+
encoder: ProtocolEncoder,
|
|
575
|
+
nodeId: number,
|
|
576
|
+
enabled: boolean
|
|
577
|
+
): void {
|
|
578
|
+
setStringProp(encoder, nodeId, PropId.GlassEffect, enabled ? 'true' : 'false');
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export function setTintColor(encoder: ProtocolEncoder, nodeId: number, color: string): void {
|
|
582
|
+
setStringProp(encoder, nodeId, PropId.TintColor, color);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export function setDisabled(
|
|
586
|
+
encoder: ProtocolEncoder,
|
|
587
|
+
nodeId: number,
|
|
588
|
+
disabled: boolean
|
|
589
|
+
): void {
|
|
590
|
+
setStringProp(encoder, nodeId, PropId.Disabled, disabled ? 'true' : 'false');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export function setShowsScrollIndicator(
|
|
594
|
+
encoder: ProtocolEncoder,
|
|
595
|
+
nodeId: number,
|
|
596
|
+
showsScrollIndicator: boolean
|
|
597
|
+
): void {
|
|
598
|
+
setStringProp(
|
|
599
|
+
encoder,
|
|
600
|
+
nodeId,
|
|
601
|
+
PropId.ShowsScrollIndicator,
|
|
602
|
+
showsScrollIndicator ? 'true' : 'false'
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function setSelectable(
|
|
607
|
+
encoder: ProtocolEncoder,
|
|
608
|
+
nodeId: number,
|
|
609
|
+
selectable: SelectableMode
|
|
610
|
+
): void {
|
|
611
|
+
const value =
|
|
612
|
+
selectable === true
|
|
613
|
+
? 'true'
|
|
614
|
+
: selectable === false
|
|
615
|
+
? 'false'
|
|
616
|
+
: selectable;
|
|
617
|
+
setStringProp(encoder, nodeId, PropId.Selectable, value);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function isInteractiveSelectionBoundary(node: ElementNode): boolean {
|
|
621
|
+
return (
|
|
622
|
+
node.tagType === 'pressable' ||
|
|
623
|
+
node.originalTag === 'button' ||
|
|
624
|
+
node.events.has(EventType.Press) ||
|
|
625
|
+
node.events.has(EventType.PressIn) ||
|
|
626
|
+
node.events.has(EventType.PressOut) ||
|
|
627
|
+
node.events.has(EventType.LongPress)
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function resolveInheritedSelectable(node: ElementNode): SelectableMode | undefined {
|
|
632
|
+
let current = node.parent as ElementNode | TextNode | RootNode | null;
|
|
633
|
+
|
|
634
|
+
while (current && current.kind !== NodeKind.Root) {
|
|
635
|
+
if (current.kind === NodeKind.Element && current.props.selectable !== undefined) {
|
|
636
|
+
return current.props.selectable;
|
|
637
|
+
}
|
|
638
|
+
current = current.parent as ElementNode | TextNode | RootNode | null;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return undefined;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function hasInteractiveAncestor(node: ElementNode): boolean {
|
|
645
|
+
let current = node.parent as ElementNode | TextNode | RootNode | null;
|
|
646
|
+
|
|
647
|
+
while (current && current.kind !== NodeKind.Root) {
|
|
648
|
+
if (current.kind === NodeKind.Element && isInteractiveSelectionBoundary(current)) {
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
current = current.parent as ElementNode | TextNode | RootNode | null;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function resolveSelectableMode(node: ElementNode): SelectableMode | undefined {
|
|
658
|
+
if (node.props.selectable !== undefined) {
|
|
659
|
+
return node.props.selectable;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const inherited = resolveInheritedSelectable(node);
|
|
663
|
+
if (inherited !== undefined) {
|
|
664
|
+
return inherited;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (node.tagType === 'text') {
|
|
668
|
+
return hasInteractiveAncestor(node) ? false : true;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return undefined;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function shouldEncodeSelectable(node: ElementNode, selectable: SelectableMode | undefined): boolean {
|
|
675
|
+
if (node.props.selectable !== undefined) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return node.tagType === 'text' && selectable === false;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function pixelOwnerIsDomMirror(): boolean {
|
|
683
|
+
// When the DOM mirror is installed (plain web, no protocol host), it owns
|
|
684
|
+
// pixels by rendering the host-ops node tree directly — the absence of a
|
|
685
|
+
// protocol consumer is expected then, not an error.
|
|
686
|
+
return (
|
|
687
|
+
(globalThis as typeof globalThis & { __exactDomMirror?: unknown }).__exactDomMirror !==
|
|
688
|
+
undefined
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Dispatch the current buffer to the native runtime and reset it for reuse.
|
|
694
|
+
*/
|
|
695
|
+
export function dispatchProtocol(encoder: ProtocolEncoder): void {
|
|
696
|
+
const data = encoder.getUint8Array();
|
|
697
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
698
|
+
recordProtocolFrame(data);
|
|
699
|
+
}
|
|
700
|
+
const debugContextJSON = getExactResizeTraceContextJSON();
|
|
701
|
+
markExactLoadTiming('protocolDispatchStart');
|
|
702
|
+
|
|
703
|
+
if (typeof exact !== 'undefined') {
|
|
704
|
+
if (debugContextJSON && typeof exact.dispatchWithDebugContext === 'function') {
|
|
705
|
+
exact.dispatchWithDebugContext(data, debugContextJSON);
|
|
706
|
+
} else if (typeof exact.dispatch === 'function') {
|
|
707
|
+
exact.dispatch(data);
|
|
708
|
+
} else if (process.env.NODE_ENV !== 'production' && !pixelOwnerIsDomMirror()) {
|
|
709
|
+
console.error('[ExactRenderer] Missing exact.dispatch during protocol flush', {
|
|
710
|
+
byteLength: data.byteLength,
|
|
711
|
+
hasExact: typeof exact !== 'undefined',
|
|
712
|
+
dispatchType: typeof (globalThis as typeof globalThis & {
|
|
713
|
+
exact?: {
|
|
714
|
+
dispatch?: unknown;
|
|
715
|
+
};
|
|
716
|
+
}).exact?.dispatch,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
} else if (process.env.NODE_ENV !== 'production' && !pixelOwnerIsDomMirror()) {
|
|
720
|
+
console.error('[ExactRenderer] Missing exact.dispatch during protocol flush', {
|
|
721
|
+
byteLength: data.byteLength,
|
|
722
|
+
hasExact: typeof exact !== 'undefined',
|
|
723
|
+
dispatchType: typeof (globalThis as typeof globalThis & {
|
|
724
|
+
exact?: {
|
|
725
|
+
dispatch?: unknown;
|
|
726
|
+
};
|
|
727
|
+
}).exact?.dispatch,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
markExactLoadTiming('protocolDispatchEnd');
|
|
732
|
+
encoder.reset();
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function markExactLoadTiming(name: string): void {
|
|
736
|
+
const timings = (globalThis as typeof globalThis & {
|
|
737
|
+
__exactLoadTimings?: Record<string, number>;
|
|
738
|
+
}).__exactLoadTimings;
|
|
739
|
+
if (timings && timings[name] == null) {
|
|
740
|
+
timings[name] = Date.now();
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function getExactResizeTraceContextJSON(): string | null {
|
|
745
|
+
if (typeof exact === 'undefined') {
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const context = exact.__resizeTraceContext;
|
|
750
|
+
if (context == null) {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (typeof context === 'string') {
|
|
755
|
+
return context.length > 0 ? context : null;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
try {
|
|
759
|
+
const json = JSON.stringify(context);
|
|
760
|
+
return json && json.length > 0 ? json : null;
|
|
761
|
+
} catch {
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Get the screen dimensions from the runtime.
|
|
768
|
+
*/
|
|
769
|
+
export function getScreenDimensions(rootId: number = 0): { width: number; height: number } {
|
|
770
|
+
const runtimeViewport =
|
|
771
|
+
typeof exact !== 'undefined' &&
|
|
772
|
+
typeof exact.screenWidth === 'number' &&
|
|
773
|
+
isFinite(exact.screenWidth) &&
|
|
774
|
+
exact.screenWidth > 0 &&
|
|
775
|
+
typeof exact.screenHeight === 'number' &&
|
|
776
|
+
isFinite(exact.screenHeight) &&
|
|
777
|
+
exact.screenHeight > 0
|
|
778
|
+
? {
|
|
779
|
+
width: exact.screenWidth,
|
|
780
|
+
height: exact.screenHeight,
|
|
781
|
+
}
|
|
782
|
+
: null;
|
|
783
|
+
const rootViewport = getWindowViewportForRoot(rootId);
|
|
784
|
+
if (
|
|
785
|
+
rootId === 0 &&
|
|
786
|
+
runtimeViewport &&
|
|
787
|
+
(rootViewport.width !== runtimeViewport.width || rootViewport.height !== runtimeViewport.height)
|
|
788
|
+
) {
|
|
789
|
+
return runtimeViewport;
|
|
790
|
+
}
|
|
791
|
+
if (rootViewport.width > 0 && rootViewport.height > 0) {
|
|
792
|
+
return {
|
|
793
|
+
width: rootViewport.width,
|
|
794
|
+
height: rootViewport.height,
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (runtimeViewport) {
|
|
799
|
+
return runtimeViewport;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const hostWindow = (
|
|
803
|
+
globalThis as typeof globalThis & {
|
|
804
|
+
window?: {
|
|
805
|
+
innerWidth?: unknown;
|
|
806
|
+
innerHeight?: unknown;
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
).window;
|
|
810
|
+
if (
|
|
811
|
+
typeof hostWindow?.innerWidth === 'number' &&
|
|
812
|
+
isFinite(hostWindow.innerWidth) &&
|
|
813
|
+
hostWindow.innerWidth > 0 &&
|
|
814
|
+
typeof hostWindow?.innerHeight === 'number' &&
|
|
815
|
+
isFinite(hostWindow.innerHeight) &&
|
|
816
|
+
hostWindow.innerHeight > 0
|
|
817
|
+
) {
|
|
818
|
+
return {
|
|
819
|
+
width: hostWindow.innerWidth,
|
|
820
|
+
height: hostWindow.innerHeight,
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
return { width: 393, height: 852 };
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// ===========================================================================
|
|
827
|
+
// High-Level Encoding Functions
|
|
828
|
+
// ===========================================================================
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Encode the creation of a new element node.
|
|
832
|
+
*
|
|
833
|
+
* @param encoder - The protocol encoder
|
|
834
|
+
* @param node - The element node to encode
|
|
835
|
+
*/
|
|
836
|
+
export function encodeCreateElement(encoder: ProtocolEncoder, node: ElementNode): void {
|
|
837
|
+
const config = getTagConfig(node.originalTag) ?? defaultTagConfig;
|
|
838
|
+
|
|
839
|
+
// Create the view
|
|
840
|
+
encoder.createView(node.id, config.nodeType);
|
|
841
|
+
|
|
842
|
+
// Set initial style
|
|
843
|
+
if (hasOwnEnumerableProperties(node.style)) {
|
|
844
|
+
setStyle(encoder, node.id, node.style);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (hasOwnEnumerableProperties(node.transitions)) {
|
|
848
|
+
setTransition(encoder, node.id, node.transitions);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Set props
|
|
852
|
+
encodeProps(encoder, node);
|
|
853
|
+
|
|
854
|
+
// Bind events
|
|
855
|
+
encodeEvents(encoder, node);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Encode the creation of a new text node.
|
|
860
|
+
*
|
|
861
|
+
* @param encoder - The protocol encoder
|
|
862
|
+
* @param node - The text node to encode
|
|
863
|
+
*/
|
|
864
|
+
export function encodeCreateText(encoder: ProtocolEncoder, node: TextNode): void {
|
|
865
|
+
encoder.createView(node.id, NodeType.Text);
|
|
866
|
+
setTextContent(encoder, node.id, node.text);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Encode props for an element node.
|
|
871
|
+
*
|
|
872
|
+
* @param encoder - The protocol encoder
|
|
873
|
+
* @param node - The element node
|
|
874
|
+
*/
|
|
875
|
+
export function encodeProps(encoder: ProtocolEncoder, node: ElementNode): void {
|
|
876
|
+
const props = node.props;
|
|
877
|
+
const selectable = resolveSelectableMode(node);
|
|
878
|
+
|
|
879
|
+
if (typeof props.lang === 'string' && props.lang.length > 0) {
|
|
880
|
+
setLang(encoder, node.id, node.resolvedLang);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (props.textContent !== undefined) {
|
|
884
|
+
setTextContent(encoder, node.id, props.textContent);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (props.value !== undefined) {
|
|
888
|
+
setTextContent(encoder, node.id, props.value);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
encodeStringPropTable(encoder, node.id, props);
|
|
892
|
+
encodeScalarPropTable(encoder, node.id, props);
|
|
893
|
+
|
|
894
|
+
if (shouldEncodeSelectable(node, selectable) && selectable !== undefined) {
|
|
895
|
+
setSelectable(encoder, node.id, selectable);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Encode events for an element node.
|
|
902
|
+
*
|
|
903
|
+
* @param encoder - The protocol encoder
|
|
904
|
+
* @param node - The element node
|
|
905
|
+
*/
|
|
906
|
+
export function encodeEvents(encoder: ProtocolEncoder, node: ElementNode): void {
|
|
907
|
+
for (const [eventType, binding] of node.events) {
|
|
908
|
+
encoder.bindEvent(node.id, eventType, binding.handlerId);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Encode children for a node.
|
|
914
|
+
*
|
|
915
|
+
* @param encoder - The protocol encoder
|
|
916
|
+
* @param node - The parent node
|
|
917
|
+
*/
|
|
918
|
+
export function encodeChildren(encoder: ProtocolEncoder, node: RootNode | ElementNode): void {
|
|
919
|
+
if (node.kind === NodeKind.Root) {
|
|
920
|
+
// The native host can reset the kernel between JS bundles while the
|
|
921
|
+
// renderer process itself is still alive. Re-emitting the root create op
|
|
922
|
+
// on every root children sync makes root attachment idempotent, so later
|
|
923
|
+
// root children, style, and layout ops never target a missing root view
|
|
924
|
+
// after a reload/reset boundary.
|
|
925
|
+
encoder.createView(node.id, NodeType.View);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const childIds = getChildIds(node);
|
|
929
|
+
encoder.setChildren(node.id, childIds);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Encode the destruction of a node.
|
|
934
|
+
*
|
|
935
|
+
* @param encoder - The protocol encoder
|
|
936
|
+
* @param node - The node to destroy
|
|
937
|
+
*/
|
|
938
|
+
export function encodeDestroy(encoder: ProtocolEncoder, node: ElementNode | TextNode): void {
|
|
939
|
+
encoder.destroyView(node.id);
|
|
940
|
+
}
|