@qontinui/ui-bridge-native 0.6.10 → 0.6.11
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/dist/core/index.js +232 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +232 -2
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/registry.d.ts +46 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/types.d.ts +114 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/vision-fields.d.ts +142 -0
- package/dist/core/vision-fields.d.ts.map +1 -0
- package/dist/debug/index.js.map +1 -1
- package/dist/debug/index.mjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +311 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +307 -13
- package/dist/index.mjs.map +1 -1
- package/dist/react/UIBridgeNativeProvider.d.ts.map +1 -1
- package/dist/react/index.js +305 -11
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +306 -12
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useUIElement.d.ts.map +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/http-server.d.ts +9 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/index.js +217 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +217 -5
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/observability.d.ts +4 -3
- package/dist/server/observability.d.ts.map +1 -1
- package/dist/server/types.d.ts +10 -0
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useRef, useState, useCallback, useEffect, useMemo, use } from 'react';
|
|
2
|
-
import { Dimensions, StyleSheet, Platform, TouchableOpacity, Text, Modal, View, ScrollView } from 'react-native';
|
|
2
|
+
import { Dimensions, StyleSheet, Platform, PixelRatio, TouchableOpacity, Text, Modal, View, ScrollView } from 'react-native';
|
|
3
3
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
@@ -9,6 +9,179 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
9
9
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
// src/core/vision-fields.ts
|
|
13
|
+
var INTERACTIVE_HANDLER_NAMES = /* @__PURE__ */ new Set([
|
|
14
|
+
"onPress",
|
|
15
|
+
"onPressIn",
|
|
16
|
+
"onPressOut",
|
|
17
|
+
"onLongPress",
|
|
18
|
+
"onChangeText",
|
|
19
|
+
"onValueChange",
|
|
20
|
+
"onChange",
|
|
21
|
+
"onSubmitEditing",
|
|
22
|
+
"onClick",
|
|
23
|
+
"onTouchStart",
|
|
24
|
+
"onTouchEnd"
|
|
25
|
+
]);
|
|
26
|
+
var INTERACTIVE_TYPES = /* @__PURE__ */ new Set([
|
|
27
|
+
"button",
|
|
28
|
+
"input",
|
|
29
|
+
"switch",
|
|
30
|
+
"checkbox",
|
|
31
|
+
"radio",
|
|
32
|
+
"touchable",
|
|
33
|
+
"pressable",
|
|
34
|
+
"listItem"
|
|
35
|
+
]);
|
|
36
|
+
function roleForType(type) {
|
|
37
|
+
switch (type) {
|
|
38
|
+
case "button":
|
|
39
|
+
case "touchable":
|
|
40
|
+
case "pressable":
|
|
41
|
+
return "button";
|
|
42
|
+
case "input":
|
|
43
|
+
return "textbox";
|
|
44
|
+
case "text":
|
|
45
|
+
return "text";
|
|
46
|
+
case "switch":
|
|
47
|
+
return "switch";
|
|
48
|
+
case "checkbox":
|
|
49
|
+
return "checkbox";
|
|
50
|
+
case "radio":
|
|
51
|
+
return "radio";
|
|
52
|
+
case "image":
|
|
53
|
+
return "img";
|
|
54
|
+
case "list":
|
|
55
|
+
return "list";
|
|
56
|
+
case "listItem":
|
|
57
|
+
return "listitem";
|
|
58
|
+
case "modal":
|
|
59
|
+
return "dialog";
|
|
60
|
+
case "scroll":
|
|
61
|
+
case "view":
|
|
62
|
+
case "custom":
|
|
63
|
+
default:
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function deriveInteractable(type, handlerNames) {
|
|
68
|
+
if (INTERACTIVE_TYPES.has(type)) return true;
|
|
69
|
+
if (handlerNames) {
|
|
70
|
+
for (const name of handlerNames) {
|
|
71
|
+
if (INTERACTIVE_HANDLER_NAMES.has(name)) return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function deriveText(state, label) {
|
|
77
|
+
const candidates = [state.value, state.textContent, label];
|
|
78
|
+
for (const c of candidates) {
|
|
79
|
+
if (typeof c === "string") {
|
|
80
|
+
const trimmed = c.trim();
|
|
81
|
+
if (trimmed.length > 0) return trimmed;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return void 0;
|
|
85
|
+
}
|
|
86
|
+
var NAMED_COLORS = {
|
|
87
|
+
black: { r: 0, g: 0, b: 0 },
|
|
88
|
+
white: { r: 255, g: 255, b: 255 },
|
|
89
|
+
red: { r: 255, g: 0, b: 0 },
|
|
90
|
+
green: { r: 0, g: 128, b: 0 },
|
|
91
|
+
blue: { r: 0, g: 0, b: 255 },
|
|
92
|
+
gray: { r: 128, g: 128, b: 128 },
|
|
93
|
+
grey: { r: 128, g: 128, b: 128 },
|
|
94
|
+
silver: { r: 192, g: 192, b: 192 },
|
|
95
|
+
transparent: { r: 0, g: 0, b: 0 },
|
|
96
|
+
orange: { r: 255, g: 165, b: 0 },
|
|
97
|
+
yellow: { r: 255, g: 255, b: 0 },
|
|
98
|
+
purple: { r: 128, g: 0, b: 128 },
|
|
99
|
+
navy: { r: 0, g: 0, b: 128 }
|
|
100
|
+
};
|
|
101
|
+
var clamp255 = (n) => Math.max(0, Math.min(255, Math.round(n)));
|
|
102
|
+
function parseColorToRgb(input) {
|
|
103
|
+
if (typeof input !== "string") return void 0;
|
|
104
|
+
const raw = input.trim().toLowerCase();
|
|
105
|
+
if (raw.length === 0) return void 0;
|
|
106
|
+
if (Object.prototype.hasOwnProperty.call(NAMED_COLORS, raw)) {
|
|
107
|
+
return NAMED_COLORS[raw];
|
|
108
|
+
}
|
|
109
|
+
if (raw.startsWith("#")) {
|
|
110
|
+
const hex = raw.slice(1);
|
|
111
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
112
|
+
const r = parseInt(hex[0] + hex[0], 16);
|
|
113
|
+
const g = parseInt(hex[1] + hex[1], 16);
|
|
114
|
+
const b = parseInt(hex[2] + hex[2], 16);
|
|
115
|
+
if ([r, g, b].every((v) => Number.isFinite(v))) return { r, g, b };
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
119
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
120
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
121
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
122
|
+
if ([r, g, b].every((v) => Number.isFinite(v))) return { r, g, b };
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
const rgbMatch = raw.match(/^rgba?\(([^)]+)\)$/);
|
|
128
|
+
if (rgbMatch) {
|
|
129
|
+
const parts = rgbMatch[1].split(",").map((p) => p.trim());
|
|
130
|
+
if (parts.length < 3) return void 0;
|
|
131
|
+
const channel = (p) => {
|
|
132
|
+
if (p.endsWith("%")) {
|
|
133
|
+
const pct = parseFloat(p.slice(0, -1));
|
|
134
|
+
if (!Number.isFinite(pct)) return void 0;
|
|
135
|
+
return clamp255(pct / 100 * 255);
|
|
136
|
+
}
|
|
137
|
+
const n = parseFloat(p);
|
|
138
|
+
if (!Number.isFinite(n)) return void 0;
|
|
139
|
+
return clamp255(n);
|
|
140
|
+
};
|
|
141
|
+
const r = channel(parts[0]);
|
|
142
|
+
const g = channel(parts[1]);
|
|
143
|
+
const b = channel(parts[2]);
|
|
144
|
+
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
145
|
+
return { r, g, b };
|
|
146
|
+
}
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
function parseFontSizePx(input) {
|
|
150
|
+
if (typeof input === "number") {
|
|
151
|
+
return Number.isFinite(input) && input > 0 ? input : void 0;
|
|
152
|
+
}
|
|
153
|
+
if (typeof input === "string") {
|
|
154
|
+
const m = input.trim().match(/^(-?\d*\.?\d+)/);
|
|
155
|
+
if (m) {
|
|
156
|
+
const n = parseFloat(m[1]);
|
|
157
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
function projectVisionFields(args) {
|
|
163
|
+
const { type, label, handlerNames, state, flatStyle } = args;
|
|
164
|
+
const fields = {
|
|
165
|
+
interactable: deriveInteractable(type, handlerNames)
|
|
166
|
+
};
|
|
167
|
+
const text = deriveText(state, label);
|
|
168
|
+
if (text !== void 0) fields.text = text;
|
|
169
|
+
const role = roleForType(type);
|
|
170
|
+
if (role !== void 0) fields.role = role;
|
|
171
|
+
if (flatStyle) {
|
|
172
|
+
const fg = parseColorToRgb(flatStyle.color);
|
|
173
|
+
if (fg !== void 0) fields.fg_color = fg;
|
|
174
|
+
const bg = parseColorToRgb(flatStyle.backgroundColor);
|
|
175
|
+
if (bg !== void 0) fields.bg_color = bg;
|
|
176
|
+
const size = parseFontSizePx(flatStyle.fontSize);
|
|
177
|
+
if (size !== void 0) fields.font_size_px = size;
|
|
178
|
+
if (typeof flatStyle.fontFamily === "string" && flatStyle.fontFamily.trim().length > 0) {
|
|
179
|
+
fields.font_family = flatStyle.fontFamily;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return fields;
|
|
183
|
+
}
|
|
184
|
+
|
|
12
185
|
// src/core/registry.ts
|
|
13
186
|
function extractHandlerNames(props) {
|
|
14
187
|
if (!props) return [];
|
|
@@ -18,6 +191,25 @@ function matchesCurrentRoute(registrationRoute, currentRoute) {
|
|
|
18
191
|
if (registrationRoute == null || registrationRoute === "") return true;
|
|
19
192
|
return registrationRoute === currentRoute;
|
|
20
193
|
}
|
|
194
|
+
function projectBbox(state, visibility, pixelRatio = 1) {
|
|
195
|
+
if (visibility !== "visible") return void 0;
|
|
196
|
+
const layout = state.layout;
|
|
197
|
+
if (!layout) return void 0;
|
|
198
|
+
const ratio = pixelRatio > 0 && Number.isFinite(pixelRatio) ? pixelRatio : 1;
|
|
199
|
+
const toPx = (v) => Math.max(0, Math.round(v * ratio));
|
|
200
|
+
const hasPage = typeof layout.pageX === "number" && typeof layout.pageY === "number" && Number.isFinite(layout.pageX) && Number.isFinite(layout.pageY);
|
|
201
|
+
const originX = hasPage ? layout.pageX : layout.x;
|
|
202
|
+
const originY = hasPage ? layout.pageY : layout.y;
|
|
203
|
+
if (typeof originX !== "number" || typeof originY !== "number" || typeof layout.width !== "number" || typeof layout.height !== "number" || !Number.isFinite(originX) || !Number.isFinite(originY) || !Number.isFinite(layout.width) || !Number.isFinite(layout.height)) {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
x: toPx(originX),
|
|
208
|
+
y: toPx(originY),
|
|
209
|
+
w: toPx(layout.width),
|
|
210
|
+
h: toPx(layout.height)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
21
213
|
function inferActions(type) {
|
|
22
214
|
const baseActions = ["focus", "blur"];
|
|
23
215
|
switch (type) {
|
|
@@ -82,6 +274,15 @@ var NativeUIBridgeRegistry = class {
|
|
|
82
274
|
* already installs.
|
|
83
275
|
*/
|
|
84
276
|
this.routeProvider = null;
|
|
277
|
+
/**
|
|
278
|
+
* Device pixel ratio used to project `state.layout` (logical dp) into the
|
|
279
|
+
* physical-pixel `bbox` emitted on each snapshot element. Defaults to `1`
|
|
280
|
+
* (dp ≈ px) so callers/tests that never inject a ratio still get a usable
|
|
281
|
+
* box; the React provider wires the real `PixelRatio.get()` via
|
|
282
|
+
* {@link setPixelRatio}. Read from a non-React core module on purpose — see
|
|
283
|
+
* the `projectBbox` doc comment for why we don't import `react-native` here.
|
|
284
|
+
*/
|
|
285
|
+
this.pixelRatio = 1;
|
|
85
286
|
/**
|
|
86
287
|
* Sticky flag: flips `true` the first time any element is registered and
|
|
87
288
|
* stays `true` even after elements are unregistered. Lets agents distinguish
|
|
@@ -119,6 +320,21 @@ var NativeUIBridgeRegistry = class {
|
|
|
119
320
|
getRouteProvider() {
|
|
120
321
|
return this.routeProvider;
|
|
121
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Set the device pixel ratio used to project per-element `bbox` geometry
|
|
325
|
+
* into physical pixels. The React provider passes `PixelRatio.get()` here
|
|
326
|
+
* once at startup. Ignores non-finite / non-positive values (keeps the
|
|
327
|
+
* previous ratio) so a bad call can't zero out every bbox.
|
|
328
|
+
*/
|
|
329
|
+
setPixelRatio(ratio) {
|
|
330
|
+
if (typeof ratio === "number" && Number.isFinite(ratio) && ratio > 0) {
|
|
331
|
+
this.pixelRatio = ratio;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/** Read-only accessor for the device pixel ratio used for bbox projection. */
|
|
335
|
+
getPixelRatio() {
|
|
336
|
+
return this.pixelRatio;
|
|
337
|
+
}
|
|
122
338
|
// ============================================================================
|
|
123
339
|
// Snapshot Enricher Slots
|
|
124
340
|
// ============================================================================
|
|
@@ -658,6 +874,18 @@ var NativeUIBridgeRegistry = class {
|
|
|
658
874
|
const handlers = extractHandlerNames(e.props);
|
|
659
875
|
const state = e.getState();
|
|
660
876
|
const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
|
|
877
|
+
const bbox = projectBbox(state, visibility, this.pixelRatio);
|
|
878
|
+
const vision = projectVisionFields({
|
|
879
|
+
type: e.type,
|
|
880
|
+
label: e.label,
|
|
881
|
+
// Real interactivity signal: the names of props that are actually
|
|
882
|
+
// functions (onPress/onChangeText/...). NOT `e.actions`, which
|
|
883
|
+
// `inferActions` synthesizes press/click onto for every element —
|
|
884
|
+
// using it would mark plain text/view nodes interactable.
|
|
885
|
+
handlerNames: handlers,
|
|
886
|
+
state,
|
|
887
|
+
flatStyle: e.flatStyle
|
|
888
|
+
});
|
|
661
889
|
return {
|
|
662
890
|
id: e.id,
|
|
663
891
|
type: e.type,
|
|
@@ -668,7 +896,9 @@ var NativeUIBridgeRegistry = class {
|
|
|
668
896
|
customActions: e.customActions ? Object.keys(e.customActions) : void 0,
|
|
669
897
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
670
898
|
registrationRoute: e.registrationRoute,
|
|
671
|
-
visibility
|
|
899
|
+
visibility,
|
|
900
|
+
...bbox ? { bbox } : {},
|
|
901
|
+
...vision
|
|
672
902
|
};
|
|
673
903
|
}),
|
|
674
904
|
components: this.getAllComponents().map((c) => ({
|
|
@@ -1894,7 +2124,7 @@ function createDesignHandlers(registry, viewportProvider) {
|
|
|
1894
2124
|
var GRID_SIZE = 20;
|
|
1895
2125
|
var NAV_TYPES = /* @__PURE__ */ new Set(["button", "heading", "badge", "status-message"]);
|
|
1896
2126
|
var SKIP_TEXT_TYPES = /* @__PURE__ */ new Set(["button", "link", "tab", "menuitem"]);
|
|
1897
|
-
var
|
|
2127
|
+
var INTERACTIVE_TYPES2 = /* @__PURE__ */ new Set([
|
|
1898
2128
|
"button",
|
|
1899
2129
|
"input",
|
|
1900
2130
|
"switch",
|
|
@@ -2091,7 +2321,7 @@ function diagnosePageHealth(elements, viewport) {
|
|
|
2091
2321
|
let interactiveTotal = 0;
|
|
2092
2322
|
let interactiveDisabled = 0;
|
|
2093
2323
|
for (const el of elements) {
|
|
2094
|
-
if (!
|
|
2324
|
+
if (!INTERACTIVE_TYPES2.has(el.type)) continue;
|
|
2095
2325
|
interactiveTotal++;
|
|
2096
2326
|
if (el.state.enabled === false) interactiveDisabled++;
|
|
2097
2327
|
}
|
|
@@ -2374,6 +2604,16 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2374
2604
|
const handlers = extractHandlerNames(e.props);
|
|
2375
2605
|
const state = e.getState();
|
|
2376
2606
|
const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
|
|
2607
|
+
const bbox = projectBbox(state, visibility, registry.getPixelRatio());
|
|
2608
|
+
const vision = projectVisionFields({
|
|
2609
|
+
type: e.type,
|
|
2610
|
+
label: e.label,
|
|
2611
|
+
// Real handler names (onPress/...), NOT inferred actions which
|
|
2612
|
+
// synthesize press/click onto every element. See projectVisionFields.
|
|
2613
|
+
handlerNames: handlers,
|
|
2614
|
+
state,
|
|
2615
|
+
flatStyle: e.flatStyle
|
|
2616
|
+
});
|
|
2377
2617
|
return {
|
|
2378
2618
|
id: e.id,
|
|
2379
2619
|
type: e.type,
|
|
@@ -2384,7 +2624,9 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2384
2624
|
customActions: e.customActions ? Object.keys(e.customActions) : void 0,
|
|
2385
2625
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
2386
2626
|
registrationRoute: e.registrationRoute,
|
|
2387
|
-
visibility
|
|
2627
|
+
visibility,
|
|
2628
|
+
...bbox ? { bbox } : {},
|
|
2629
|
+
...vision
|
|
2388
2630
|
};
|
|
2389
2631
|
});
|
|
2390
2632
|
return success2({ elements });
|
|
@@ -3067,7 +3309,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
3067
3309
|
};
|
|
3068
3310
|
if (config?.appInfo) {
|
|
3069
3311
|
response.uiBridge = {
|
|
3070
|
-
version: "0.6.
|
|
3312
|
+
version: "0.6.11",
|
|
3071
3313
|
...config.appInfo,
|
|
3072
3314
|
capabilities: ["elements", "components", "actions", "design"],
|
|
3073
3315
|
elementCount: stats.elements,
|
|
@@ -3659,6 +3901,14 @@ var NativeUIBridgeServer = class {
|
|
|
3659
3901
|
};
|
|
3660
3902
|
};
|
|
3661
3903
|
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Whether the console/network capture should be installed. The dedicated
|
|
3906
|
+
* `observability` flag wins when set; otherwise falls back to `testHooks`
|
|
3907
|
+
* so consumers predating the flag keep their exact behavior.
|
|
3908
|
+
*/
|
|
3909
|
+
observabilityEnabled() {
|
|
3910
|
+
return this.config.observability ?? this.config.testHooks === true;
|
|
3911
|
+
}
|
|
3662
3912
|
/**
|
|
3663
3913
|
* Set the server adapter
|
|
3664
3914
|
*/
|
|
@@ -3872,7 +4122,7 @@ var NativeUIBridgeServer = class {
|
|
|
3872
4122
|
console.warn("[ui-bridge-native] See documentation for supported adapters.");
|
|
3873
4123
|
return;
|
|
3874
4124
|
}
|
|
3875
|
-
if (this.
|
|
4125
|
+
if (this.observabilityEnabled()) {
|
|
3876
4126
|
this.consoleErrorBuffer.install();
|
|
3877
4127
|
this.networkRequestBuffer.install();
|
|
3878
4128
|
}
|
|
@@ -5374,6 +5624,7 @@ function UIBridgeNativeProvider({
|
|
|
5374
5624
|
verbose: config.verbose,
|
|
5375
5625
|
onEvent
|
|
5376
5626
|
});
|
|
5627
|
+
registryRef.current.setPixelRatio(PixelRatio.get());
|
|
5377
5628
|
setGlobalRegistry(registryRef.current);
|
|
5378
5629
|
}
|
|
5379
5630
|
const registry = registryRef.current;
|
|
@@ -5404,6 +5655,10 @@ function UIBridgeNativeProvider({
|
|
|
5404
5655
|
cors: true,
|
|
5405
5656
|
appInfo: config.appInfo,
|
|
5406
5657
|
testHooks: features.testHooks === true,
|
|
5658
|
+
// Dedicated console/network-capture flag; the server defaults it to
|
|
5659
|
+
// the `testHooks` value when undefined, so passing it through verbatim
|
|
5660
|
+
// keeps existing consumers unchanged.
|
|
5661
|
+
observability: features.observability,
|
|
5407
5662
|
// Injected device-viewport getter for POST /control/page-health. The
|
|
5408
5663
|
// server doesn't import `react-native` itself — the require/import
|
|
5409
5664
|
// pattern there crashed the host RN app in 0.6.3/0.6.4 (Metro/Hermes
|
|
@@ -5471,6 +5726,7 @@ function UIBridgeNativeProvider({
|
|
|
5471
5726
|
}, [
|
|
5472
5727
|
features.server,
|
|
5473
5728
|
features.testHooks,
|
|
5729
|
+
features.observability,
|
|
5474
5730
|
config.serverPort,
|
|
5475
5731
|
config.appInfo,
|
|
5476
5732
|
registry,
|
|
@@ -5509,6 +5765,7 @@ function UIBridgeNativeProvider({
|
|
|
5509
5765
|
cors: true,
|
|
5510
5766
|
appInfo: config.appInfo,
|
|
5511
5767
|
testHooks: features.testHooks === true,
|
|
5768
|
+
observability: features.observability,
|
|
5512
5769
|
viewportProvider: () => {
|
|
5513
5770
|
const win = Dimensions.get("window");
|
|
5514
5771
|
return { width: win.width, height: win.height };
|
|
@@ -5712,12 +5969,41 @@ function scheduleDeadButtonWarning(bridge, id, type) {
|
|
|
5712
5969
|
}
|
|
5713
5970
|
function useUIElement(options) {
|
|
5714
5971
|
const bridge = useUIBridgeNativeOptional();
|
|
5715
|
-
const ref = useRef(null);
|
|
5716
5972
|
const [registered, setRegistered] = useState(false);
|
|
5717
5973
|
const [_layout, setLayout] = useState(null);
|
|
5718
5974
|
const propsRef = useRef({});
|
|
5719
5975
|
const registeredRef = useRef(false);
|
|
5720
5976
|
const registeredIdRef = useRef(null);
|
|
5977
|
+
const bridgeRef = useRef(bridge);
|
|
5978
|
+
bridgeRef.current = bridge;
|
|
5979
|
+
const nodeRef = useRef(null);
|
|
5980
|
+
const refInterceptorRef = useRef(null);
|
|
5981
|
+
if (refInterceptorRef.current === null) {
|
|
5982
|
+
refInterceptorRef.current = {
|
|
5983
|
+
get current() {
|
|
5984
|
+
return nodeRef.current;
|
|
5985
|
+
},
|
|
5986
|
+
set current(node) {
|
|
5987
|
+
const prev = nodeRef.current;
|
|
5988
|
+
nodeRef.current = node;
|
|
5989
|
+
const liveBridge = bridgeRef.current;
|
|
5990
|
+
const registeredId = registeredIdRef.current;
|
|
5991
|
+
if (!liveBridge || !registeredRef.current || registeredId === null) return;
|
|
5992
|
+
if (node == null && prev != null) {
|
|
5993
|
+
const state = liveBridge.registry.getElement(registeredId)?.getState();
|
|
5994
|
+
if (state && state.visible !== false) {
|
|
5995
|
+
liveBridge.registry.updateElementState(registeredId, { visible: false });
|
|
5996
|
+
}
|
|
5997
|
+
} else if (node != null && prev == null) {
|
|
5998
|
+
const state = liveBridge.registry.getElement(registeredId)?.getState();
|
|
5999
|
+
if (state && state.visible !== true) {
|
|
6000
|
+
liveBridge.registry.updateElementState(registeredId, { visible: true });
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
};
|
|
6005
|
+
}
|
|
6006
|
+
const ref = refInterceptorRef.current;
|
|
5721
6007
|
const {
|
|
5722
6008
|
id,
|
|
5723
6009
|
type = "custom",
|
|
@@ -5761,7 +6047,7 @@ function useUIElement(options) {
|
|
|
5761
6047
|
}
|
|
5762
6048
|
setRegistered(true);
|
|
5763
6049
|
scheduleDeadButtonWarning(bridge, id, type);
|
|
5764
|
-
}, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp, valueProp]);
|
|
6050
|
+
}, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp, valueProp, ref]);
|
|
5765
6051
|
const unregister = useCallback(() => {
|
|
5766
6052
|
if (!bridge || !registeredRef.current) return;
|
|
5767
6053
|
bridge.registry.unregisterElement(registeredIdRef.current ?? id);
|
|
@@ -5797,7 +6083,8 @@ function useUIElement(options) {
|
|
|
5797
6083
|
writeLayout({ x, y, width, height, pageX: x, pageY: y });
|
|
5798
6084
|
}
|
|
5799
6085
|
},
|
|
5800
|
-
|
|
6086
|
+
// `ref` is the once-per-hook interceptor object — identity-stable.
|
|
6087
|
+
[bridge, id, ref]
|
|
5801
6088
|
);
|
|
5802
6089
|
const registerRef = useRef(register);
|
|
5803
6090
|
const unregisterRef = useRef(unregister);
|
|
@@ -5818,7 +6105,14 @@ function useUIElement(options) {
|
|
|
5818
6105
|
useEffect(() => {
|
|
5819
6106
|
if (!bridge || !registered) return;
|
|
5820
6107
|
const node = ref.current;
|
|
5821
|
-
if (!node
|
|
6108
|
+
if (!node) {
|
|
6109
|
+
const seeded = bridge.registry.getElement(id)?.getState();
|
|
6110
|
+
if (seeded && seeded.visible !== false) {
|
|
6111
|
+
bridge.registry.updateElementState(id, { visible: false });
|
|
6112
|
+
}
|
|
6113
|
+
return;
|
|
6114
|
+
}
|
|
6115
|
+
if (typeof node.measureInWindow !== "function") return;
|
|
5822
6116
|
let cancelled = false;
|
|
5823
6117
|
let pendingTimer = null;
|
|
5824
6118
|
const backoffMs = [50, 200, 500, 1e3, 2500];
|
|
@@ -5857,7 +6151,7 @@ function useUIElement(options) {
|
|
|
5857
6151
|
cancelled = true;
|
|
5858
6152
|
if (pendingTimer != null) clearTimeout(pendingTimer);
|
|
5859
6153
|
};
|
|
5860
|
-
}, [bridge, registered, id]);
|
|
6154
|
+
}, [bridge, registered, id, ref]);
|
|
5861
6155
|
const registeredHandlerKeysRef = useRef([]);
|
|
5862
6156
|
useEffect(() => {
|
|
5863
6157
|
if (!bridge || !registered || !handlersProp) return;
|
|
@@ -7245,6 +7539,6 @@ var styles = StyleSheet.create({
|
|
|
7245
7539
|
}
|
|
7246
7540
|
});
|
|
7247
7541
|
|
|
7248
|
-
export { CloudRelayClient, DefaultNativeActionExecutor, DeviceAnnouncer, ModalDetector, NativeUIBridgeRegistry, NativeUIBridgeServer, State, ToastCapture, TransitionTo, UIBridgeInspector, UIBridgeNativeProvider, UI_BRIDGE_NATIVE_ROUTES, UndoTracker, WebSocketConnection, WebSocketEventBridge, bindWithRetry, buildTreePath, buildUpgradeResponse, captureNativeResponsiveSnapshot, captureNativeStateVariations, computeAcceptKey, createDesignHandlers, createNativeActionExecutor, createNativeElementIdentifier, createNativeServer, createServerHandlers, extractHandlerNames, findAllByPattern, findElementByIdentifier, getGlobalRegistry, getNativeElementDesignData, isAddrInUseError, mapNativeStyleToExtended, matchesIdentifier, parseTreePath, resetGlobalRegistry, setGlobalRegistry, useToastRecorder, useUIBridge, useUIBridgeModal, useUIBridgeNative, useUIBridgeNativeOptional, useUIBridgeNativeRequired, useUIBridgeRequired, useUIBridgeToast, useUIComponent, useUIComponentAction, useUIElement, useUIElementWithProps };
|
|
7542
|
+
export { CloudRelayClient, DefaultNativeActionExecutor, DeviceAnnouncer, ModalDetector, NativeUIBridgeRegistry, NativeUIBridgeServer, State, ToastCapture, TransitionTo, UIBridgeInspector, UIBridgeNativeProvider, UI_BRIDGE_NATIVE_ROUTES, UndoTracker, WebSocketConnection, WebSocketEventBridge, bindWithRetry, buildTreePath, buildUpgradeResponse, captureNativeResponsiveSnapshot, captureNativeStateVariations, computeAcceptKey, createDesignHandlers, createNativeActionExecutor, createNativeElementIdentifier, createNativeServer, createServerHandlers, deriveInteractable, deriveText, extractHandlerNames, findAllByPattern, findElementByIdentifier, getGlobalRegistry, getNativeElementDesignData, isAddrInUseError, mapNativeStyleToExtended, matchesIdentifier, parseColorToRgb, parseFontSizePx, parseTreePath, projectVisionFields, resetGlobalRegistry, roleForType, setGlobalRegistry, useToastRecorder, useUIBridge, useUIBridgeModal, useUIBridgeNative, useUIBridgeNativeOptional, useUIBridgeNativeRequired, useUIBridgeRequired, useUIBridgeToast, useUIComponent, useUIComponentAction, useUIElement, useUIElementWithProps };
|
|
7249
7543
|
//# sourceMappingURL=index.mjs.map
|
|
7250
7544
|
//# sourceMappingURL=index.mjs.map
|