@qontinui/ui-bridge-native 0.6.10 → 0.6.12
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 +329 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +329 -2
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/registry.d.ts +91 -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 +412 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +408 -14
- package/dist/index.mjs.map +1 -1
- package/dist/react/UIBridgeNativeProvider.d.ts.map +1 -1
- package/dist/react/index.js +406 -12
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +407 -13
- 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 +220 -5
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +220 -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/server/index.mjs
CHANGED
|
@@ -207,6 +207,179 @@ var UI_BRIDGE_NATIVE_ROUTES = {
|
|
|
207
207
|
}
|
|
208
208
|
};
|
|
209
209
|
|
|
210
|
+
// src/core/vision-fields.ts
|
|
211
|
+
var INTERACTIVE_HANDLER_NAMES = /* @__PURE__ */ new Set([
|
|
212
|
+
"onPress",
|
|
213
|
+
"onPressIn",
|
|
214
|
+
"onPressOut",
|
|
215
|
+
"onLongPress",
|
|
216
|
+
"onChangeText",
|
|
217
|
+
"onValueChange",
|
|
218
|
+
"onChange",
|
|
219
|
+
"onSubmitEditing",
|
|
220
|
+
"onClick",
|
|
221
|
+
"onTouchStart",
|
|
222
|
+
"onTouchEnd"
|
|
223
|
+
]);
|
|
224
|
+
var INTERACTIVE_TYPES = /* @__PURE__ */ new Set([
|
|
225
|
+
"button",
|
|
226
|
+
"input",
|
|
227
|
+
"switch",
|
|
228
|
+
"checkbox",
|
|
229
|
+
"radio",
|
|
230
|
+
"touchable",
|
|
231
|
+
"pressable",
|
|
232
|
+
"listItem"
|
|
233
|
+
]);
|
|
234
|
+
function roleForType(type) {
|
|
235
|
+
switch (type) {
|
|
236
|
+
case "button":
|
|
237
|
+
case "touchable":
|
|
238
|
+
case "pressable":
|
|
239
|
+
return "button";
|
|
240
|
+
case "input":
|
|
241
|
+
return "textbox";
|
|
242
|
+
case "text":
|
|
243
|
+
return "text";
|
|
244
|
+
case "switch":
|
|
245
|
+
return "switch";
|
|
246
|
+
case "checkbox":
|
|
247
|
+
return "checkbox";
|
|
248
|
+
case "radio":
|
|
249
|
+
return "radio";
|
|
250
|
+
case "image":
|
|
251
|
+
return "img";
|
|
252
|
+
case "list":
|
|
253
|
+
return "list";
|
|
254
|
+
case "listItem":
|
|
255
|
+
return "listitem";
|
|
256
|
+
case "modal":
|
|
257
|
+
return "dialog";
|
|
258
|
+
case "scroll":
|
|
259
|
+
case "view":
|
|
260
|
+
case "custom":
|
|
261
|
+
default:
|
|
262
|
+
return void 0;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function deriveInteractable(type, handlerNames) {
|
|
266
|
+
if (INTERACTIVE_TYPES.has(type)) return true;
|
|
267
|
+
if (handlerNames) {
|
|
268
|
+
for (const name of handlerNames) {
|
|
269
|
+
if (INTERACTIVE_HANDLER_NAMES.has(name)) return true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
function deriveText(state, label) {
|
|
275
|
+
const candidates = [state.value, state.textContent, label];
|
|
276
|
+
for (const c of candidates) {
|
|
277
|
+
if (typeof c === "string") {
|
|
278
|
+
const trimmed = c.trim();
|
|
279
|
+
if (trimmed.length > 0) return trimmed;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return void 0;
|
|
283
|
+
}
|
|
284
|
+
var NAMED_COLORS = {
|
|
285
|
+
black: { r: 0, g: 0, b: 0 },
|
|
286
|
+
white: { r: 255, g: 255, b: 255 },
|
|
287
|
+
red: { r: 255, g: 0, b: 0 },
|
|
288
|
+
green: { r: 0, g: 128, b: 0 },
|
|
289
|
+
blue: { r: 0, g: 0, b: 255 },
|
|
290
|
+
gray: { r: 128, g: 128, b: 128 },
|
|
291
|
+
grey: { r: 128, g: 128, b: 128 },
|
|
292
|
+
silver: { r: 192, g: 192, b: 192 },
|
|
293
|
+
transparent: { r: 0, g: 0, b: 0 },
|
|
294
|
+
orange: { r: 255, g: 165, b: 0 },
|
|
295
|
+
yellow: { r: 255, g: 255, b: 0 },
|
|
296
|
+
purple: { r: 128, g: 0, b: 128 },
|
|
297
|
+
navy: { r: 0, g: 0, b: 128 }
|
|
298
|
+
};
|
|
299
|
+
var clamp255 = (n) => Math.max(0, Math.min(255, Math.round(n)));
|
|
300
|
+
function parseColorToRgb(input) {
|
|
301
|
+
if (typeof input !== "string") return void 0;
|
|
302
|
+
const raw = input.trim().toLowerCase();
|
|
303
|
+
if (raw.length === 0) return void 0;
|
|
304
|
+
if (Object.prototype.hasOwnProperty.call(NAMED_COLORS, raw)) {
|
|
305
|
+
return NAMED_COLORS[raw];
|
|
306
|
+
}
|
|
307
|
+
if (raw.startsWith("#")) {
|
|
308
|
+
const hex = raw.slice(1);
|
|
309
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
310
|
+
const r = parseInt(hex[0] + hex[0], 16);
|
|
311
|
+
const g = parseInt(hex[1] + hex[1], 16);
|
|
312
|
+
const b = parseInt(hex[2] + hex[2], 16);
|
|
313
|
+
if ([r, g, b].every((v) => Number.isFinite(v))) return { r, g, b };
|
|
314
|
+
return void 0;
|
|
315
|
+
}
|
|
316
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
317
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
318
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
319
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
320
|
+
if ([r, g, b].every((v) => Number.isFinite(v))) return { r, g, b };
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
return void 0;
|
|
324
|
+
}
|
|
325
|
+
const rgbMatch = raw.match(/^rgba?\(([^)]+)\)$/);
|
|
326
|
+
if (rgbMatch) {
|
|
327
|
+
const parts = rgbMatch[1].split(",").map((p) => p.trim());
|
|
328
|
+
if (parts.length < 3) return void 0;
|
|
329
|
+
const channel = (p) => {
|
|
330
|
+
if (p.endsWith("%")) {
|
|
331
|
+
const pct = parseFloat(p.slice(0, -1));
|
|
332
|
+
if (!Number.isFinite(pct)) return void 0;
|
|
333
|
+
return clamp255(pct / 100 * 255);
|
|
334
|
+
}
|
|
335
|
+
const n = parseFloat(p);
|
|
336
|
+
if (!Number.isFinite(n)) return void 0;
|
|
337
|
+
return clamp255(n);
|
|
338
|
+
};
|
|
339
|
+
const r = channel(parts[0]);
|
|
340
|
+
const g = channel(parts[1]);
|
|
341
|
+
const b = channel(parts[2]);
|
|
342
|
+
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
343
|
+
return { r, g, b };
|
|
344
|
+
}
|
|
345
|
+
return void 0;
|
|
346
|
+
}
|
|
347
|
+
function parseFontSizePx(input) {
|
|
348
|
+
if (typeof input === "number") {
|
|
349
|
+
return Number.isFinite(input) && input > 0 ? input : void 0;
|
|
350
|
+
}
|
|
351
|
+
if (typeof input === "string") {
|
|
352
|
+
const m = input.trim().match(/^(-?\d*\.?\d+)/);
|
|
353
|
+
if (m) {
|
|
354
|
+
const n = parseFloat(m[1]);
|
|
355
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return void 0;
|
|
359
|
+
}
|
|
360
|
+
function projectVisionFields(args) {
|
|
361
|
+
const { type, label, handlerNames, state, flatStyle } = args;
|
|
362
|
+
const fields = {
|
|
363
|
+
interactable: deriveInteractable(type, handlerNames)
|
|
364
|
+
};
|
|
365
|
+
const text = deriveText(state, label);
|
|
366
|
+
if (text !== void 0) fields.text = text;
|
|
367
|
+
const role = roleForType(type);
|
|
368
|
+
if (role !== void 0) fields.role = role;
|
|
369
|
+
if (flatStyle) {
|
|
370
|
+
const fg = parseColorToRgb(flatStyle.color);
|
|
371
|
+
if (fg !== void 0) fields.fg_color = fg;
|
|
372
|
+
const bg = parseColorToRgb(flatStyle.backgroundColor);
|
|
373
|
+
if (bg !== void 0) fields.bg_color = bg;
|
|
374
|
+
const size = parseFontSizePx(flatStyle.fontSize);
|
|
375
|
+
if (size !== void 0) fields.font_size_px = size;
|
|
376
|
+
if (typeof flatStyle.fontFamily === "string" && flatStyle.fontFamily.trim().length > 0) {
|
|
377
|
+
fields.font_family = flatStyle.fontFamily;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return fields;
|
|
381
|
+
}
|
|
382
|
+
|
|
210
383
|
// src/core/registry.ts
|
|
211
384
|
function extractHandlerNames(props) {
|
|
212
385
|
if (!props) return [];
|
|
@@ -216,6 +389,25 @@ function matchesCurrentRoute(registrationRoute, currentRoute) {
|
|
|
216
389
|
if (registrationRoute == null || registrationRoute === "") return true;
|
|
217
390
|
return registrationRoute === currentRoute;
|
|
218
391
|
}
|
|
392
|
+
function projectBbox(state, visibility, pixelRatio = 1) {
|
|
393
|
+
if (visibility !== "visible") return void 0;
|
|
394
|
+
const layout = state.layout;
|
|
395
|
+
if (!layout) return void 0;
|
|
396
|
+
const ratio = pixelRatio > 0 && Number.isFinite(pixelRatio) ? pixelRatio : 1;
|
|
397
|
+
const toPx = (v) => Math.max(0, Math.round(v * ratio));
|
|
398
|
+
const hasPage = typeof layout.pageX === "number" && typeof layout.pageY === "number" && Number.isFinite(layout.pageX) && Number.isFinite(layout.pageY);
|
|
399
|
+
const originX = hasPage ? layout.pageX : layout.x;
|
|
400
|
+
const originY = hasPage ? layout.pageY : layout.y;
|
|
401
|
+
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)) {
|
|
402
|
+
return void 0;
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
x: toPx(originX),
|
|
406
|
+
y: toPx(originY),
|
|
407
|
+
w: toPx(layout.width),
|
|
408
|
+
h: toPx(layout.height)
|
|
409
|
+
};
|
|
410
|
+
}
|
|
219
411
|
|
|
220
412
|
// src/design/design-inspector-native.ts
|
|
221
413
|
function toCssValue(value) {
|
|
@@ -693,7 +885,7 @@ function createDesignHandlers(registry, viewportProvider) {
|
|
|
693
885
|
var GRID_SIZE = 20;
|
|
694
886
|
var NAV_TYPES = /* @__PURE__ */ new Set(["button", "heading", "badge", "status-message"]);
|
|
695
887
|
var SKIP_TEXT_TYPES = /* @__PURE__ */ new Set(["button", "link", "tab", "menuitem"]);
|
|
696
|
-
var
|
|
888
|
+
var INTERACTIVE_TYPES2 = /* @__PURE__ */ new Set([
|
|
697
889
|
"button",
|
|
698
890
|
"input",
|
|
699
891
|
"switch",
|
|
@@ -890,7 +1082,7 @@ function diagnosePageHealth(elements, viewport) {
|
|
|
890
1082
|
let interactiveTotal = 0;
|
|
891
1083
|
let interactiveDisabled = 0;
|
|
892
1084
|
for (const el of elements) {
|
|
893
|
-
if (!
|
|
1085
|
+
if (!INTERACTIVE_TYPES2.has(el.type)) continue;
|
|
894
1086
|
interactiveTotal++;
|
|
895
1087
|
if (el.state.enabled === false) interactiveDisabled++;
|
|
896
1088
|
}
|
|
@@ -1163,6 +1355,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1163
1355
|
return {
|
|
1164
1356
|
// Elements
|
|
1165
1357
|
getElements: async (ctx) => {
|
|
1358
|
+
await registry.refreshMeasurements();
|
|
1166
1359
|
const visibleOnly = ctx.query?.visibleOnly === "true" || ctx.body?.visibleOnly === true;
|
|
1167
1360
|
const forRoute = ctx.query?.route || ctx.body?.route;
|
|
1168
1361
|
let allElements = visibleOnly ? registry.getMountedVisibleElements() : registry.getAllElements();
|
|
@@ -1173,6 +1366,16 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1173
1366
|
const handlers = extractHandlerNames(e.props);
|
|
1174
1367
|
const state = e.getState();
|
|
1175
1368
|
const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
|
|
1369
|
+
const bbox = projectBbox(state, visibility, registry.getPixelRatio());
|
|
1370
|
+
const vision = projectVisionFields({
|
|
1371
|
+
type: e.type,
|
|
1372
|
+
label: e.label,
|
|
1373
|
+
// Real handler names (onPress/...), NOT inferred actions which
|
|
1374
|
+
// synthesize press/click onto every element. See projectVisionFields.
|
|
1375
|
+
handlerNames: handlers,
|
|
1376
|
+
state,
|
|
1377
|
+
flatStyle: e.flatStyle
|
|
1378
|
+
});
|
|
1176
1379
|
return {
|
|
1177
1380
|
id: e.id,
|
|
1178
1381
|
type: e.type,
|
|
@@ -1183,7 +1386,9 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1183
1386
|
customActions: e.customActions ? Object.keys(e.customActions) : void 0,
|
|
1184
1387
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
1185
1388
|
registrationRoute: e.registrationRoute,
|
|
1186
|
-
visibility
|
|
1389
|
+
visibility,
|
|
1390
|
+
...bbox ? { bbox } : {},
|
|
1391
|
+
...vision
|
|
1187
1392
|
};
|
|
1188
1393
|
});
|
|
1189
1394
|
return success2({ elements });
|
|
@@ -1344,6 +1549,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1344
1549
|
});
|
|
1345
1550
|
},
|
|
1346
1551
|
getSnapshot: async (ctx) => {
|
|
1552
|
+
await registry.refreshMeasurements();
|
|
1347
1553
|
const visibleOnly = ctx.query?.visibleOnly === "true" || ctx.body?.visibleOnly === true;
|
|
1348
1554
|
const currentRouteOnly = ctx.query?.currentRouteOnly === "true" || ctx.body?.currentRouteOnly === true;
|
|
1349
1555
|
const snapshot = registry.createSnapshot(void 0, { visibleOnly, currentRouteOnly });
|
|
@@ -1659,6 +1865,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1659
1865
|
// that don't measure (the fallback only matches single-rooted screens but
|
|
1660
1866
|
// is good enough for the unit suite).
|
|
1661
1867
|
tapAt: async (ctx) => {
|
|
1868
|
+
await registry.refreshMeasurements();
|
|
1662
1869
|
const body = ctx.body;
|
|
1663
1870
|
const x = body?.x;
|
|
1664
1871
|
const y = body?.y;
|
|
@@ -1866,7 +2073,7 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1866
2073
|
};
|
|
1867
2074
|
if (config?.appInfo) {
|
|
1868
2075
|
response.uiBridge = {
|
|
1869
|
-
version: "0.6.
|
|
2076
|
+
version: "0.6.12",
|
|
1870
2077
|
...config.appInfo,
|
|
1871
2078
|
capabilities: ["elements", "components", "actions", "design"],
|
|
1872
2079
|
elementCount: stats.elements,
|
|
@@ -2458,6 +2665,14 @@ var NativeUIBridgeServer = class {
|
|
|
2458
2665
|
};
|
|
2459
2666
|
};
|
|
2460
2667
|
}
|
|
2668
|
+
/**
|
|
2669
|
+
* Whether the console/network capture should be installed. The dedicated
|
|
2670
|
+
* `observability` flag wins when set; otherwise falls back to `testHooks`
|
|
2671
|
+
* so consumers predating the flag keep their exact behavior.
|
|
2672
|
+
*/
|
|
2673
|
+
observabilityEnabled() {
|
|
2674
|
+
return this.config.observability ?? this.config.testHooks === true;
|
|
2675
|
+
}
|
|
2461
2676
|
/**
|
|
2462
2677
|
* Set the server adapter
|
|
2463
2678
|
*/
|
|
@@ -2671,7 +2886,7 @@ var NativeUIBridgeServer = class {
|
|
|
2671
2886
|
console.warn("[ui-bridge-native] See documentation for supported adapters.");
|
|
2672
2887
|
return;
|
|
2673
2888
|
}
|
|
2674
|
-
if (this.
|
|
2889
|
+
if (this.observabilityEnabled()) {
|
|
2675
2890
|
this.consoleErrorBuffer.install();
|
|
2676
2891
|
this.networkRequestBuffer.install();
|
|
2677
2892
|
}
|