@qontinui/ui-bridge-native 0.6.9 → 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.
Files changed (45) hide show
  1. package/dist/control/action-executor.d.ts.map +1 -1
  2. package/dist/control/index.js +2 -1
  3. package/dist/control/index.js.map +1 -1
  4. package/dist/control/index.mjs +2 -1
  5. package/dist/control/index.mjs.map +1 -1
  6. package/dist/core/index.js +232 -1
  7. package/dist/core/index.js.map +1 -1
  8. package/dist/core/index.mjs +232 -2
  9. package/dist/core/index.mjs.map +1 -1
  10. package/dist/core/registry.d.ts +46 -1
  11. package/dist/core/registry.d.ts.map +1 -1
  12. package/dist/core/types.d.ts +114 -0
  13. package/dist/core/types.d.ts.map +1 -1
  14. package/dist/core/vision-fields.d.ts +142 -0
  15. package/dist/core/vision-fields.d.ts.map +1 -0
  16. package/dist/debug/index.js.map +1 -1
  17. package/dist/debug/index.mjs.map +1 -1
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +371 -20
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +365 -22
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/react/UIBridgeNativeProvider.d.ts.map +1 -1
  25. package/dist/react/index.js +323 -20
  26. package/dist/react/index.js.map +1 -1
  27. package/dist/react/index.mjs +324 -21
  28. package/dist/react/index.mjs.map +1 -1
  29. package/dist/react/useUIElement.d.ts.map +1 -1
  30. package/dist/server/bind-retry.d.ts +64 -0
  31. package/dist/server/bind-retry.d.ts.map +1 -0
  32. package/dist/server/handlers.d.ts.map +1 -1
  33. package/dist/server/http-server.d.ts +9 -1
  34. package/dist/server/http-server.d.ts.map +1 -1
  35. package/dist/server/index.d.ts +2 -0
  36. package/dist/server/index.d.ts.map +1 -1
  37. package/dist/server/index.js +275 -13
  38. package/dist/server/index.js.map +1 -1
  39. package/dist/server/index.mjs +274 -14
  40. package/dist/server/index.mjs.map +1 -1
  41. package/dist/server/observability.d.ts +4 -3
  42. package/dist/server/observability.d.ts.map +1 -1
  43. package/dist/server/types.d.ts +10 -0
  44. package/dist/server/types.d.ts.map +1 -1
  45. package/package.json +1 -1
@@ -1,5 +1,178 @@
1
1
  'use strict';
2
2
 
3
+ // src/core/vision-fields.ts
4
+ var INTERACTIVE_HANDLER_NAMES = /* @__PURE__ */ new Set([
5
+ "onPress",
6
+ "onPressIn",
7
+ "onPressOut",
8
+ "onLongPress",
9
+ "onChangeText",
10
+ "onValueChange",
11
+ "onChange",
12
+ "onSubmitEditing",
13
+ "onClick",
14
+ "onTouchStart",
15
+ "onTouchEnd"
16
+ ]);
17
+ var INTERACTIVE_TYPES = /* @__PURE__ */ new Set([
18
+ "button",
19
+ "input",
20
+ "switch",
21
+ "checkbox",
22
+ "radio",
23
+ "touchable",
24
+ "pressable",
25
+ "listItem"
26
+ ]);
27
+ function roleForType(type) {
28
+ switch (type) {
29
+ case "button":
30
+ case "touchable":
31
+ case "pressable":
32
+ return "button";
33
+ case "input":
34
+ return "textbox";
35
+ case "text":
36
+ return "text";
37
+ case "switch":
38
+ return "switch";
39
+ case "checkbox":
40
+ return "checkbox";
41
+ case "radio":
42
+ return "radio";
43
+ case "image":
44
+ return "img";
45
+ case "list":
46
+ return "list";
47
+ case "listItem":
48
+ return "listitem";
49
+ case "modal":
50
+ return "dialog";
51
+ case "scroll":
52
+ case "view":
53
+ case "custom":
54
+ default:
55
+ return void 0;
56
+ }
57
+ }
58
+ function deriveInteractable(type, handlerNames) {
59
+ if (INTERACTIVE_TYPES.has(type)) return true;
60
+ if (handlerNames) {
61
+ for (const name of handlerNames) {
62
+ if (INTERACTIVE_HANDLER_NAMES.has(name)) return true;
63
+ }
64
+ }
65
+ return false;
66
+ }
67
+ function deriveText(state, label) {
68
+ const candidates = [state.value, state.textContent, label];
69
+ for (const c of candidates) {
70
+ if (typeof c === "string") {
71
+ const trimmed = c.trim();
72
+ if (trimmed.length > 0) return trimmed;
73
+ }
74
+ }
75
+ return void 0;
76
+ }
77
+ var NAMED_COLORS = {
78
+ black: { r: 0, g: 0, b: 0 },
79
+ white: { r: 255, g: 255, b: 255 },
80
+ red: { r: 255, g: 0, b: 0 },
81
+ green: { r: 0, g: 128, b: 0 },
82
+ blue: { r: 0, g: 0, b: 255 },
83
+ gray: { r: 128, g: 128, b: 128 },
84
+ grey: { r: 128, g: 128, b: 128 },
85
+ silver: { r: 192, g: 192, b: 192 },
86
+ transparent: { r: 0, g: 0, b: 0 },
87
+ orange: { r: 255, g: 165, b: 0 },
88
+ yellow: { r: 255, g: 255, b: 0 },
89
+ purple: { r: 128, g: 0, b: 128 },
90
+ navy: { r: 0, g: 0, b: 128 }
91
+ };
92
+ var clamp255 = (n) => Math.max(0, Math.min(255, Math.round(n)));
93
+ function parseColorToRgb(input) {
94
+ if (typeof input !== "string") return void 0;
95
+ const raw = input.trim().toLowerCase();
96
+ if (raw.length === 0) return void 0;
97
+ if (Object.prototype.hasOwnProperty.call(NAMED_COLORS, raw)) {
98
+ return NAMED_COLORS[raw];
99
+ }
100
+ if (raw.startsWith("#")) {
101
+ const hex = raw.slice(1);
102
+ if (hex.length === 3 || hex.length === 4) {
103
+ const r = parseInt(hex[0] + hex[0], 16);
104
+ const g = parseInt(hex[1] + hex[1], 16);
105
+ const b = parseInt(hex[2] + hex[2], 16);
106
+ if ([r, g, b].every((v) => Number.isFinite(v))) return { r, g, b };
107
+ return void 0;
108
+ }
109
+ if (hex.length === 6 || hex.length === 8) {
110
+ const r = parseInt(hex.slice(0, 2), 16);
111
+ const g = parseInt(hex.slice(2, 4), 16);
112
+ const b = parseInt(hex.slice(4, 6), 16);
113
+ if ([r, g, b].every((v) => Number.isFinite(v))) return { r, g, b };
114
+ return void 0;
115
+ }
116
+ return void 0;
117
+ }
118
+ const rgbMatch = raw.match(/^rgba?\(([^)]+)\)$/);
119
+ if (rgbMatch) {
120
+ const parts = rgbMatch[1].split(",").map((p) => p.trim());
121
+ if (parts.length < 3) return void 0;
122
+ const channel = (p) => {
123
+ if (p.endsWith("%")) {
124
+ const pct = parseFloat(p.slice(0, -1));
125
+ if (!Number.isFinite(pct)) return void 0;
126
+ return clamp255(pct / 100 * 255);
127
+ }
128
+ const n = parseFloat(p);
129
+ if (!Number.isFinite(n)) return void 0;
130
+ return clamp255(n);
131
+ };
132
+ const r = channel(parts[0]);
133
+ const g = channel(parts[1]);
134
+ const b = channel(parts[2]);
135
+ if (r === void 0 || g === void 0 || b === void 0) return void 0;
136
+ return { r, g, b };
137
+ }
138
+ return void 0;
139
+ }
140
+ function parseFontSizePx(input) {
141
+ if (typeof input === "number") {
142
+ return Number.isFinite(input) && input > 0 ? input : void 0;
143
+ }
144
+ if (typeof input === "string") {
145
+ const m = input.trim().match(/^(-?\d*\.?\d+)/);
146
+ if (m) {
147
+ const n = parseFloat(m[1]);
148
+ return Number.isFinite(n) && n > 0 ? n : void 0;
149
+ }
150
+ }
151
+ return void 0;
152
+ }
153
+ function projectVisionFields(args) {
154
+ const { type, label, handlerNames, state, flatStyle } = args;
155
+ const fields = {
156
+ interactable: deriveInteractable(type, handlerNames)
157
+ };
158
+ const text = deriveText(state, label);
159
+ if (text !== void 0) fields.text = text;
160
+ const role = roleForType(type);
161
+ if (role !== void 0) fields.role = role;
162
+ if (flatStyle) {
163
+ const fg = parseColorToRgb(flatStyle.color);
164
+ if (fg !== void 0) fields.fg_color = fg;
165
+ const bg = parseColorToRgb(flatStyle.backgroundColor);
166
+ if (bg !== void 0) fields.bg_color = bg;
167
+ const size = parseFontSizePx(flatStyle.fontSize);
168
+ if (size !== void 0) fields.font_size_px = size;
169
+ if (typeof flatStyle.fontFamily === "string" && flatStyle.fontFamily.trim().length > 0) {
170
+ fields.font_family = flatStyle.fontFamily;
171
+ }
172
+ }
173
+ return fields;
174
+ }
175
+
3
176
  // src/core/registry.ts
4
177
  function extractHandlerNames(props) {
5
178
  if (!props) return [];
@@ -9,6 +182,25 @@ function matchesCurrentRoute(registrationRoute, currentRoute) {
9
182
  if (registrationRoute == null || registrationRoute === "") return true;
10
183
  return registrationRoute === currentRoute;
11
184
  }
185
+ function projectBbox(state, visibility, pixelRatio = 1) {
186
+ if (visibility !== "visible") return void 0;
187
+ const layout = state.layout;
188
+ if (!layout) return void 0;
189
+ const ratio = pixelRatio > 0 && Number.isFinite(pixelRatio) ? pixelRatio : 1;
190
+ const toPx = (v) => Math.max(0, Math.round(v * ratio));
191
+ const hasPage = typeof layout.pageX === "number" && typeof layout.pageY === "number" && Number.isFinite(layout.pageX) && Number.isFinite(layout.pageY);
192
+ const originX = hasPage ? layout.pageX : layout.x;
193
+ const originY = hasPage ? layout.pageY : layout.y;
194
+ 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)) {
195
+ return void 0;
196
+ }
197
+ return {
198
+ x: toPx(originX),
199
+ y: toPx(originY),
200
+ w: toPx(layout.width),
201
+ h: toPx(layout.height)
202
+ };
203
+ }
12
204
  function inferActions(type) {
13
205
  const baseActions = ["focus", "blur"];
14
206
  switch (type) {
@@ -73,6 +265,15 @@ var NativeUIBridgeRegistry = class {
73
265
  * already installs.
74
266
  */
75
267
  this.routeProvider = null;
268
+ /**
269
+ * Device pixel ratio used to project `state.layout` (logical dp) into the
270
+ * physical-pixel `bbox` emitted on each snapshot element. Defaults to `1`
271
+ * (dp ≈ px) so callers/tests that never inject a ratio still get a usable
272
+ * box; the React provider wires the real `PixelRatio.get()` via
273
+ * {@link setPixelRatio}. Read from a non-React core module on purpose — see
274
+ * the `projectBbox` doc comment for why we don't import `react-native` here.
275
+ */
276
+ this.pixelRatio = 1;
76
277
  /**
77
278
  * Sticky flag: flips `true` the first time any element is registered and
78
279
  * stays `true` even after elements are unregistered. Lets agents distinguish
@@ -110,6 +311,21 @@ var NativeUIBridgeRegistry = class {
110
311
  getRouteProvider() {
111
312
  return this.routeProvider;
112
313
  }
314
+ /**
315
+ * Set the device pixel ratio used to project per-element `bbox` geometry
316
+ * into physical pixels. The React provider passes `PixelRatio.get()` here
317
+ * once at startup. Ignores non-finite / non-positive values (keeps the
318
+ * previous ratio) so a bad call can't zero out every bbox.
319
+ */
320
+ setPixelRatio(ratio) {
321
+ if (typeof ratio === "number" && Number.isFinite(ratio) && ratio > 0) {
322
+ this.pixelRatio = ratio;
323
+ }
324
+ }
325
+ /** Read-only accessor for the device pixel ratio used for bbox projection. */
326
+ getPixelRatio() {
327
+ return this.pixelRatio;
328
+ }
113
329
  // ============================================================================
114
330
  // Snapshot Enricher Slots
115
331
  // ============================================================================
@@ -649,6 +865,18 @@ var NativeUIBridgeRegistry = class {
649
865
  const handlers = extractHandlerNames(e.props);
650
866
  const state = e.getState();
651
867
  const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
868
+ const bbox = projectBbox(state, visibility, this.pixelRatio);
869
+ const vision = projectVisionFields({
870
+ type: e.type,
871
+ label: e.label,
872
+ // Real interactivity signal: the names of props that are actually
873
+ // functions (onPress/onChangeText/...). NOT `e.actions`, which
874
+ // `inferActions` synthesizes press/click onto for every element —
875
+ // using it would mark plain text/view nodes interactable.
876
+ handlerNames: handlers,
877
+ state,
878
+ flatStyle: e.flatStyle
879
+ });
652
880
  return {
653
881
  id: e.id,
654
882
  type: e.type,
@@ -659,7 +887,9 @@ var NativeUIBridgeRegistry = class {
659
887
  customActions: e.customActions ? Object.keys(e.customActions) : void 0,
660
888
  registeredHandlers: handlers.length > 0 ? handlers : void 0,
661
889
  registrationRoute: e.registrationRoute,
662
- visibility
890
+ visibility,
891
+ ...bbox ? { bbox } : {},
892
+ ...vision
663
893
  };
664
894
  }),
665
895
  components: this.getAllComponents().map((c) => ({
@@ -884,6 +1114,7 @@ exports.getGlobalRegistry = getGlobalRegistry;
884
1114
  exports.matchesCurrentRoute = matchesCurrentRoute;
885
1115
  exports.matchesIdentifier = matchesIdentifier;
886
1116
  exports.parseTreePath = parseTreePath;
1117
+ exports.projectBbox = projectBbox;
887
1118
  exports.resetGlobalRegistry = resetGlobalRegistry;
888
1119
  exports.setGlobalRegistry = setGlobalRegistry;
889
1120
  //# sourceMappingURL=index.js.map