@qontinui/ui-bridge-native 0.6.4 → 0.6.6

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/index.mjs CHANGED
@@ -14,6 +14,10 @@ function extractHandlerNames(props) {
14
14
  if (!props) return [];
15
15
  return Object.keys(props).filter((k) => typeof props[k] === "function");
16
16
  }
17
+ function matchesCurrentRoute(registrationRoute, currentRoute) {
18
+ if (registrationRoute == null || registrationRoute === "") return true;
19
+ return registrationRoute === currentRoute;
20
+ }
17
21
  function inferActions(type) {
18
22
  const baseActions = ["focus", "blur"];
19
23
  switch (type) {
@@ -219,13 +223,20 @@ var NativeUIBridgeRegistry = class {
219
223
  if (this.config.verbose) {
220
224
  console.log(`[ui-bridge-native] Registered element: ${id} (${type})`);
221
225
  }
222
- this.updateElementState(id, {
226
+ const seededState = {
223
227
  mounted: true,
224
228
  visible: true,
225
229
  enabled: true,
226
230
  focused: false,
227
231
  layout: null
228
- });
232
+ };
233
+ if (type === "input" && typeof props?.value === "string") {
234
+ seededState.value = props.value;
235
+ }
236
+ if (type === "text" && typeof label === "string") {
237
+ seededState.value = label;
238
+ }
239
+ this.updateElementState(id, seededState);
229
240
  return registered;
230
241
  }
231
242
  /**
@@ -387,6 +398,9 @@ var NativeUIBridgeRegistry = class {
387
398
  })
388
399
  };
389
400
  this.elements.set(id, updated);
401
+ if (element.type === "text" && labelChanged && typeof nextLabel === "string") {
402
+ this.updateElementState(id, { value: nextLabel });
403
+ }
390
404
  this.emit("element:registered", {
391
405
  id,
392
406
  type: element.type,
@@ -395,7 +409,22 @@ var NativeUIBridgeRegistry = class {
395
409
  return true;
396
410
  }
397
411
  /**
398
- * Update element props (for action execution)
412
+ * Update element props (for action execution).
413
+ *
414
+ * Side effect for `type: 'input'` elements: when `props.value` is a string,
415
+ * mirror it into `state.value` so AI drivers reading the registry don't see
416
+ * `state.value === undefined` until the user types. Before this mirror,
417
+ * controlled-input consumers (`<TextInput value={x} onChangeText={setX} />`
418
+ * with `captureProps({ value: x, onChangeText: setX })`) registered an
419
+ * input whose `state.value` only became defined after the FIRST
420
+ * `onChangeText` call — bridge consumers couldn't read the default value
421
+ * (e.g. a pre-populated API-URL input) until someone typed into it.
422
+ *
423
+ * Re-runs on every `updateElementProps` so a parent passing a fresh
424
+ * `value` prop (controlled re-render) keeps the registry in sync. The
425
+ * mirror is gated on `type === 'input'` to avoid touching non-input
426
+ * elements that may carry an unrelated `value` prop (e.g. a Switch's
427
+ * boolean value, which lives on `state.checked`).
399
428
  */
400
429
  updateElementProps(id, props) {
401
430
  const element = this.elements.get(id);
@@ -405,6 +434,12 @@ var NativeUIBridgeRegistry = class {
405
434
  props: { ...element.props, ...props }
406
435
  };
407
436
  this.elements.set(id, updated);
437
+ if (element.type === "input" && Object.prototype.hasOwnProperty.call(props, "value") && typeof props.value === "string") {
438
+ const currentState = updated.getState();
439
+ if (currentState.value !== props.value) {
440
+ this.updateElementState(id, { value: props.value });
441
+ }
442
+ }
408
443
  }
409
444
  }
410
445
  /**
@@ -615,7 +650,7 @@ var NativeUIBridgeRegistry = class {
615
650
  let elements = options?.visibleOnly ? this.getMountedVisibleElements() : this.getAllElements();
616
651
  if (options?.currentRouteOnly && resolvedRoute.currentRoute) {
617
652
  const currentRoute = resolvedRoute.currentRoute;
618
- elements = elements.filter((e) => e.registrationRoute === currentRoute);
653
+ elements = elements.filter((e) => matchesCurrentRoute(e.registrationRoute, currentRoute));
619
654
  }
620
655
  const snapshot = {
621
656
  timestamp: Date.now(),
@@ -1606,15 +1641,9 @@ function success(data) {
1606
1641
  function error(message, code) {
1607
1642
  return { success: false, error: message, code, timestamp: Date.now() };
1608
1643
  }
1609
- function createDesignHandlers(registry) {
1644
+ function createDesignHandlers(registry, viewportProvider) {
1610
1645
  function getScreenDimensions() {
1611
- try {
1612
- const { Dimensions: Dimensions2 } = __require("react-native");
1613
- const screen = Dimensions2.get("screen");
1614
- return { width: screen.width, height: screen.height };
1615
- } catch {
1616
- return { width: 0, height: 0 };
1617
- }
1646
+ return viewportProvider?.() ?? { width: 0, height: 0 };
1618
1647
  }
1619
1648
  return {
1620
1649
  // 1. GET /design/element/:id/styles
@@ -2244,15 +2273,6 @@ async function executeWorkflowStep(step, registry, executor) {
2244
2273
  };
2245
2274
  }
2246
2275
  }
2247
- function getWindowDimensions() {
2248
- try {
2249
- const { Dimensions: Dimensions2 } = __require("react-native");
2250
- const win = Dimensions2.get("window");
2251
- return { width: win.width, height: win.height };
2252
- } catch {
2253
- return { width: 0, height: 0 };
2254
- }
2255
- }
2256
2276
  function success2(data) {
2257
2277
  return {
2258
2278
  success: true,
@@ -2269,7 +2289,7 @@ function error2(message, code) {
2269
2289
  };
2270
2290
  }
2271
2291
  function createServerHandlers(registry, executor, config) {
2272
- const designHandlers = createDesignHandlers(registry);
2292
+ const designHandlers = createDesignHandlers(registry, config?.viewportProvider);
2273
2293
  return {
2274
2294
  // Elements
2275
2295
  getElements: async (ctx) => {
@@ -2277,7 +2297,7 @@ function createServerHandlers(registry, executor, config) {
2277
2297
  const forRoute = ctx.query?.route || ctx.body?.route;
2278
2298
  let allElements = visibleOnly ? registry.getMountedVisibleElements() : registry.getAllElements();
2279
2299
  if (forRoute) {
2280
- allElements = allElements.filter((e) => e.registrationRoute === forRoute);
2300
+ allElements = allElements.filter((e) => matchesCurrentRoute(e.registrationRoute, forRoute));
2281
2301
  }
2282
2302
  const elements = allElements.map((e) => {
2283
2303
  const handlers = extractHandlerNames(e.props);
@@ -2460,7 +2480,7 @@ function createServerHandlers(registry, executor, config) {
2460
2480
  },
2461
2481
  getPageHealth: async (ctx) => {
2462
2482
  const bodyViewport = ctx.body?.viewport;
2463
- const viewport = bodyViewport ?? getWindowDimensions();
2483
+ const viewport = bodyViewport ?? config?.viewportProvider?.() ?? { width: 0, height: 0 };
2464
2484
  const elements = registry.getAllElements().map((e) => ({
2465
2485
  type: e.type,
2466
2486
  state: e.getState()
@@ -3431,7 +3451,8 @@ var NativeUIBridgeServer = class {
3431
3451
  this.handlers = createServerHandlers(registry, executor, {
3432
3452
  appInfo: this.config.appInfo,
3433
3453
  consoleErrorBuffer: this.consoleErrorBuffer,
3434
- networkRequestBuffer: this.networkRequestBuffer
3454
+ networkRequestBuffer: this.networkRequestBuffer,
3455
+ viewportProvider: this.config.viewportProvider
3435
3456
  });
3436
3457
  this.handlers.getMethods = async () => {
3437
3458
  const testHooks = this.config.testHooks === true;
@@ -5197,7 +5218,17 @@ function UIBridgeNativeProvider({
5197
5218
  serverPort: config.serverPort || 8087,
5198
5219
  cors: true,
5199
5220
  appInfo: config.appInfo,
5200
- testHooks: features.testHooks === true
5221
+ testHooks: features.testHooks === true,
5222
+ // Injected device-viewport getter for POST /control/page-health. The
5223
+ // server doesn't import `react-native` itself — the require/import
5224
+ // pattern there crashed the host RN app in 0.6.3/0.6.4 (Metro/Hermes
5225
+ // raised `unknownModuleError` past every try/catch). This provider
5226
+ // already has a live react-native import, so it's the safe injection
5227
+ // point. See feedback_metro_require_gotcha for the full incident.
5228
+ viewportProvider: () => {
5229
+ const win = Dimensions.get("window");
5230
+ return { width: win.width, height: win.height };
5231
+ }
5201
5232
  });
5202
5233
  server.setAdapter(serverAdapter);
5203
5234
  if (navigationProvider) {
@@ -5292,7 +5323,11 @@ function UIBridgeNativeProvider({
5292
5323
  serverPort: config.serverPort || 8087,
5293
5324
  cors: true,
5294
5325
  appInfo: config.appInfo,
5295
- testHooks: features.testHooks === true
5326
+ testHooks: features.testHooks === true,
5327
+ viewportProvider: () => {
5328
+ const win = Dimensions.get("window");
5329
+ return { width: win.width, height: win.height };
5330
+ }
5296
5331
  });
5297
5332
  if (navigationProvider) bareServer.setNavigationProvider(navigationProvider);
5298
5333
  if (screenshotProvider) bareServer.setScreenshotProvider(screenshotProvider);