@qontinui/ui-bridge-native 0.5.1 → 0.6.2
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 +83 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +83 -4
- package/dist/core/index.mjs.map +1 -1
- package/dist/core/registry.d.ts +49 -1
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/types.d.ts +18 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +240 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +240 -30
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.js +215 -30
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +215 -30
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/useUIElement.d.ts +92 -35
- 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.map +1 -1
- package/dist/server/index.js +84 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +84 -3
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/types.d.ts +4 -0
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/react/index.mjs
CHANGED
|
@@ -254,7 +254,18 @@ var NativeUIBridgeRegistry = class {
|
|
|
254
254
|
return Array.from(this.elements.values());
|
|
255
255
|
}
|
|
256
256
|
/**
|
|
257
|
-
* Get all registered elements that are visible
|
|
257
|
+
* Get all registered elements that are visible AND have a measured layout.
|
|
258
|
+
*
|
|
259
|
+
* This is the *strict* visibility filter: requires `state.visible === true`
|
|
260
|
+
* AND `state.layout !== null`. Use this when you need both signals
|
|
261
|
+
* (e.g. coord-based tap, where you need an actual rect to hit-test against).
|
|
262
|
+
*
|
|
263
|
+
* Callers driving an LLM agent typically want
|
|
264
|
+
* {@link getMountedVisibleElements} instead — that variant accepts
|
|
265
|
+
* elements that *should* be on screen (visible:true) but whose first
|
|
266
|
+
* `onLayout` has not yet fired. Without it, a snapshot taken in the gap
|
|
267
|
+
* between mount and the first layout pass returns 0 elements on a fully
|
|
268
|
+
* populated screen.
|
|
258
269
|
*/
|
|
259
270
|
getVisibleElements() {
|
|
260
271
|
return this.getAllElements().filter((e) => {
|
|
@@ -262,6 +273,26 @@ var NativeUIBridgeRegistry = class {
|
|
|
262
273
|
return state.visible && state.layout !== null;
|
|
263
274
|
});
|
|
264
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Get all registered elements marked as visible (regardless of layout).
|
|
278
|
+
*
|
|
279
|
+
* Looser than {@link getVisibleElements}: includes elements whose
|
|
280
|
+
* `onLayout` callback has not yet fired (so `layout === null`). React
|
|
281
|
+
* Native's `onLayout` is async — there's a one-tick window after mount
|
|
282
|
+
* where elements are in the tree, the user *sees* them, but the
|
|
283
|
+
* registry hasn't received their measurements yet. Excluding those
|
|
284
|
+
* elements makes `getSnapshot?visibleOnly=true` return 0 on a populated
|
|
285
|
+
* screen, which is a worse failure mode than including a few elements
|
|
286
|
+
* we can't yet locate spatially.
|
|
287
|
+
*
|
|
288
|
+
* Used by `createSnapshot`'s `visibleOnly` filter and by the
|
|
289
|
+
* `getSnapshot` / `getElements` handlers. Each emitted element carries a
|
|
290
|
+
* `visibility` discriminator (`"visible"` / `"likely-visible"`) so
|
|
291
|
+
* agents that *do* require a known rect can branch downstream.
|
|
292
|
+
*/
|
|
293
|
+
getMountedVisibleElements() {
|
|
294
|
+
return this.getAllElements().filter((e) => e.getState().visible);
|
|
295
|
+
}
|
|
265
296
|
/**
|
|
266
297
|
* Get elements registered on a specific route (for page-scoped filtering)
|
|
267
298
|
*/
|
|
@@ -318,6 +349,51 @@ var NativeUIBridgeRegistry = class {
|
|
|
318
349
|
this.emit("element:stateChanged", { id, state: newState });
|
|
319
350
|
}
|
|
320
351
|
}
|
|
352
|
+
/**
|
|
353
|
+
* Update an element's descriptive metadata (label / accessibilityLabel /
|
|
354
|
+
* testId). Sister to {@link updateElementState} (which carries layout +
|
|
355
|
+
* visibility) and {@link updateElementProps} (which carries event handlers).
|
|
356
|
+
*
|
|
357
|
+
* Idempotent — if every passed field equals the existing value, the call
|
|
358
|
+
* returns without mutating the entry or emitting an event. This keeps
|
|
359
|
+
* `useUIElement().updateLabel(...)` cheap when consumers re-publish from a
|
|
360
|
+
* render path that produces the same string on every tick.
|
|
361
|
+
*
|
|
362
|
+
* Returns `true` when the entry was mutated, `false` otherwise. Callers
|
|
363
|
+
* that want to gate a `console.warn` on no-op invocations can use the
|
|
364
|
+
* return value instead of recomputing equality.
|
|
365
|
+
*/
|
|
366
|
+
updateElementMeta(id, meta) {
|
|
367
|
+
const element = this.elements.get(id);
|
|
368
|
+
if (!element) return false;
|
|
369
|
+
const currentIdentifier = element.getIdentifier();
|
|
370
|
+
const nextLabel = meta.label !== void 0 ? meta.label : element.label;
|
|
371
|
+
const nextAccessibilityLabel = meta.accessibilityLabel !== void 0 ? meta.accessibilityLabel : currentIdentifier.accessibilityLabel;
|
|
372
|
+
const nextTestId = meta.testId !== void 0 ? meta.testId : currentIdentifier.testId;
|
|
373
|
+
const labelChanged = nextLabel !== element.label;
|
|
374
|
+
const accessibilityLabelChanged = nextAccessibilityLabel !== currentIdentifier.accessibilityLabel;
|
|
375
|
+
const testIdChanged = nextTestId !== currentIdentifier.testId;
|
|
376
|
+
if (!labelChanged && !accessibilityLabelChanged && !testIdChanged) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
const updated = {
|
|
380
|
+
...element,
|
|
381
|
+
label: nextLabel,
|
|
382
|
+
getIdentifier: () => ({
|
|
383
|
+
uiId: id,
|
|
384
|
+
testId: nextTestId,
|
|
385
|
+
accessibilityLabel: nextAccessibilityLabel,
|
|
386
|
+
treePath: currentIdentifier.treePath
|
|
387
|
+
})
|
|
388
|
+
};
|
|
389
|
+
this.elements.set(id, updated);
|
|
390
|
+
this.emit("element:registered", {
|
|
391
|
+
id,
|
|
392
|
+
type: element.type,
|
|
393
|
+
label: nextLabel
|
|
394
|
+
});
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
321
397
|
/**
|
|
322
398
|
* Update element props (for action execution)
|
|
323
399
|
*/
|
|
@@ -536,7 +612,7 @@ var NativeUIBridgeRegistry = class {
|
|
|
536
612
|
}
|
|
537
613
|
return { currentRoute: null, segments: void 0 };
|
|
538
614
|
})();
|
|
539
|
-
let elements = options?.visibleOnly ? this.
|
|
615
|
+
let elements = options?.visibleOnly ? this.getMountedVisibleElements() : this.getAllElements();
|
|
540
616
|
if (options?.currentRouteOnly && resolvedRoute.currentRoute) {
|
|
541
617
|
const currentRoute = resolvedRoute.currentRoute;
|
|
542
618
|
elements = elements.filter((e) => e.registrationRoute === currentRoute);
|
|
@@ -545,16 +621,19 @@ var NativeUIBridgeRegistry = class {
|
|
|
545
621
|
timestamp: Date.now(),
|
|
546
622
|
elements: elements.map((e) => {
|
|
547
623
|
const handlers = extractHandlerNames(e.props);
|
|
624
|
+
const state = e.getState();
|
|
625
|
+
const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
|
|
548
626
|
return {
|
|
549
627
|
id: e.id,
|
|
550
628
|
type: e.type,
|
|
551
629
|
label: e.label,
|
|
552
630
|
identifier: e.getIdentifier(),
|
|
553
|
-
state
|
|
631
|
+
state,
|
|
554
632
|
actions: e.actions,
|
|
555
633
|
customActions: e.customActions ? Object.keys(e.customActions) : void 0,
|
|
556
634
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
557
|
-
registrationRoute: e.registrationRoute
|
|
635
|
+
registrationRoute: e.registrationRoute,
|
|
636
|
+
visibility
|
|
558
637
|
};
|
|
559
638
|
}),
|
|
560
639
|
components: this.getAllComponents().map((c) => ({
|
|
@@ -1896,22 +1975,25 @@ function createServerHandlers(registry, executor, config) {
|
|
|
1896
1975
|
getElements: async (ctx) => {
|
|
1897
1976
|
const visibleOnly = ctx.query?.visibleOnly === "true" || ctx.body?.visibleOnly === true;
|
|
1898
1977
|
const forRoute = ctx.query?.route || ctx.body?.route;
|
|
1899
|
-
let allElements = visibleOnly ? registry.
|
|
1978
|
+
let allElements = visibleOnly ? registry.getMountedVisibleElements() : registry.getAllElements();
|
|
1900
1979
|
if (forRoute) {
|
|
1901
1980
|
allElements = allElements.filter((e) => e.registrationRoute === forRoute);
|
|
1902
1981
|
}
|
|
1903
1982
|
const elements = allElements.map((e) => {
|
|
1904
1983
|
const handlers = extractHandlerNames(e.props);
|
|
1984
|
+
const state = e.getState();
|
|
1985
|
+
const visibility = !state.visible ? "hidden" : state.layout !== null ? "visible" : "likely-visible";
|
|
1905
1986
|
return {
|
|
1906
1987
|
id: e.id,
|
|
1907
1988
|
type: e.type,
|
|
1908
1989
|
label: e.label,
|
|
1909
1990
|
identifier: e.getIdentifier(),
|
|
1910
|
-
state
|
|
1991
|
+
state,
|
|
1911
1992
|
actions: e.actions,
|
|
1912
1993
|
customActions: e.customActions ? Object.keys(e.customActions) : void 0,
|
|
1913
1994
|
registeredHandlers: handlers.length > 0 ? handlers : void 0,
|
|
1914
|
-
registrationRoute: e.registrationRoute
|
|
1995
|
+
registrationRoute: e.registrationRoute,
|
|
1996
|
+
visibility
|
|
1915
1997
|
};
|
|
1916
1998
|
});
|
|
1917
1999
|
return success2({ elements });
|
|
@@ -2174,6 +2256,37 @@ function createServerHandlers(registry, executor, config) {
|
|
|
2174
2256
|
"NOT_SUPPORTED"
|
|
2175
2257
|
);
|
|
2176
2258
|
},
|
|
2259
|
+
// AI helpers — runner-only endpoints that have no React Native analog.
|
|
2260
|
+
//
|
|
2261
|
+
// The mobile bridge mounts explicit NOT_SUPPORTED stubs at the runner's
|
|
2262
|
+
// cheatsheet paths so callers (and the /manual-test skill in particular)
|
|
2263
|
+
// get a structured envelope explaining the gap rather than a confusing
|
|
2264
|
+
// HTTP 404. Each error message names an in-tree mobile replacement so
|
|
2265
|
+
// operators aren't left guessing what to use instead.
|
|
2266
|
+
aiForms: async () => {
|
|
2267
|
+
return error2(
|
|
2268
|
+
'ai/forms (form discovery) is runner-only; mobile React Native has no DOM to walk. Use GET /ui-bridge/control/snapshot and filter for type === "input" instead.',
|
|
2269
|
+
"NOT_SUPPORTED"
|
|
2270
|
+
);
|
|
2271
|
+
},
|
|
2272
|
+
aiIdleStatus: async () => {
|
|
2273
|
+
return error2(
|
|
2274
|
+
'ai/idle-status (page-idle signal) is runner-only; mobile React Native has no DOM-mutation / network-idle signals. Use the registry event stream (WS subscribe to "element:registered"/"element:stateChanged") or poll /control/snapshot for stability.',
|
|
2275
|
+
"NOT_SUPPORTED"
|
|
2276
|
+
);
|
|
2277
|
+
},
|
|
2278
|
+
aiChangeBufferEnable: async () => {
|
|
2279
|
+
return error2(
|
|
2280
|
+
'ai/change-buffer (DOM mutation diff buffer) is runner-only; mobile React Native has no DOM. Use the WS event bridge (subscribe to "element:registered"/"element:unregistered"/"element:stateChanged") for change notifications.',
|
|
2281
|
+
"NOT_SUPPORTED"
|
|
2282
|
+
);
|
|
2283
|
+
},
|
|
2284
|
+
aiWaitForElement: async () => {
|
|
2285
|
+
return error2(
|
|
2286
|
+
'POST /ai/wait-for-element is runner-only on mobile. Use the WS "waitForElement" JSON-RPC method or "waitForCondition" instead \u2014 same semantics, no HTTP polling overhead.',
|
|
2287
|
+
"NOT_SUPPORTED"
|
|
2288
|
+
);
|
|
2289
|
+
},
|
|
2177
2290
|
// Meta / Introspection (stub — server constructor overrides with real route table)
|
|
2178
2291
|
getMethods: async () => {
|
|
2179
2292
|
return error2("Methods introspection not configured", "NOT_SUPPORTED");
|
|
@@ -2829,6 +2942,24 @@ var WS_ROUTES = [
|
|
|
2829
2942
|
{ pattern: "meta/methods", handler: "getMethods", httpMethods: ["GET"] },
|
|
2830
2943
|
// AI helpers — mobile parity with the runner's `/ui-bridge/ai/*` family.
|
|
2831
2944
|
{ pattern: "ai/fill-form", handler: "fillForm", httpMethods: ["POST"] },
|
|
2945
|
+
// AI helpers — NOT_SUPPORTED stubs. These endpoints exist on the runner-side
|
|
2946
|
+
// UI Bridge surface (and the /manual-test skill's cheatsheet documents them
|
|
2947
|
+
// as if they're universally available). Mobile React Native has no DOM, so
|
|
2948
|
+
// the mobile bridge mounts explicit stubs so callers get a structured
|
|
2949
|
+
// NOT_SUPPORTED envelope (with a hint pointing at the mobile-native
|
|
2950
|
+
// replacement) instead of a confusing HTTP 404.
|
|
2951
|
+
{ pattern: "ai/forms", handler: "aiForms", httpMethods: ["GET"] },
|
|
2952
|
+
{ pattern: "ai/idle-status", handler: "aiIdleStatus", httpMethods: ["GET"] },
|
|
2953
|
+
{
|
|
2954
|
+
pattern: "ai/change-buffer/enable",
|
|
2955
|
+
handler: "aiChangeBufferEnable",
|
|
2956
|
+
httpMethods: ["POST"]
|
|
2957
|
+
},
|
|
2958
|
+
{
|
|
2959
|
+
pattern: "ai/wait-for-element",
|
|
2960
|
+
handler: "aiWaitForElement",
|
|
2961
|
+
httpMethods: ["POST"]
|
|
2962
|
+
},
|
|
2832
2963
|
// Coord-based tap — synthesizes a press at the given screen coords by
|
|
2833
2964
|
// matching against registered elements' layout rects.
|
|
2834
2965
|
{ pattern: "control/tap", handler: "tapAt", httpMethods: ["POST"] },
|
|
@@ -2901,6 +3032,10 @@ var HANDLER_DESCRIPTIONS = {
|
|
|
2901
3032
|
diffBaseline: "Diff current UI against a saved baseline",
|
|
2902
3033
|
getMethods: "List every handler name (legacy introspection)",
|
|
2903
3034
|
fillForm: "Fill multiple inputs in one call: body { fields: [{elementId, value}] } \u2014 each field dispatches `type` + `clear:true`",
|
|
3035
|
+
aiForms: 'STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Use /control/snapshot + filter type==="input" instead.',
|
|
3036
|
+
aiIdleStatus: "STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Subscribe to registry events over WS for change signals.",
|
|
3037
|
+
aiChangeBufferEnable: "STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Use WS event subscriptions for change notifications.",
|
|
3038
|
+
aiWaitForElement: "STUB \u2014 runner-only; mobile returns NOT_SUPPORTED. Use the WS waitForElement / waitForCondition JSON-RPC methods.",
|
|
2904
3039
|
tapAt: "Synthesize a press at given screen coords by matching registered element layout rects: body { x, y, action? }",
|
|
2905
3040
|
getConsoleErrors: "TEST HOOK \u2014 last-N console.error/console.warn entries; ?since=<ms>&limit=<n>",
|
|
2906
3041
|
getNetworkRequests: "TEST HOOK \u2014 last-N fetch/XHR entries with status + duration; ?since=<ms>&limit=<n>",
|
|
@@ -5021,6 +5156,25 @@ function flattenStateStyles(stateStyles) {
|
|
|
5021
5156
|
if (stateStyles.disabled != null) result.disabled = flattenStyle(stateStyles.disabled);
|
|
5022
5157
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
5023
5158
|
}
|
|
5159
|
+
var warnedDeadButtonIds = /* @__PURE__ */ new Set();
|
|
5160
|
+
var warnedRedundantUpdateLabelIds = /* @__PURE__ */ new Set();
|
|
5161
|
+
function scheduleDeadButtonWarning(bridge, id, type) {
|
|
5162
|
+
if (type !== "button" && type !== "pressable") return;
|
|
5163
|
+
if (warnedDeadButtonIds.has(id)) return;
|
|
5164
|
+
if (!bridge) return;
|
|
5165
|
+
setTimeout(() => {
|
|
5166
|
+
if (warnedDeadButtonIds.has(id)) return;
|
|
5167
|
+
const element = bridge.registry.getElement(id);
|
|
5168
|
+
if (!element) return;
|
|
5169
|
+
const props = element.props ?? {};
|
|
5170
|
+
const hasPress = typeof props.onPress === "function" || typeof props.onPressIn === "function" || typeof props.onResponderRelease === "function";
|
|
5171
|
+
if (hasPress) return;
|
|
5172
|
+
warnedDeadButtonIds.add(id);
|
|
5173
|
+
console.warn(
|
|
5174
|
+
`[UI Bridge] Registered \`${type}\` element "${id}" without an \`onPress\` handler \u2014 UI Bridge \`press\` actions on this element will fail with "No press handler found on element". Use \`useUIElementWithProps\` and \`captureProps({onPress})\` instead.`
|
|
5175
|
+
);
|
|
5176
|
+
}, 0);
|
|
5177
|
+
}
|
|
5024
5178
|
function useUIElement(options) {
|
|
5025
5179
|
const bridge = useUIBridgeNativeOptional();
|
|
5026
5180
|
const ref = useRef(null);
|
|
@@ -5067,6 +5221,7 @@ function useUIElement(options) {
|
|
|
5067
5221
|
registeredRef.current = true;
|
|
5068
5222
|
registeredIdRef.current = id;
|
|
5069
5223
|
setRegistered(true);
|
|
5224
|
+
scheduleDeadButtonWarning(bridge, id, type);
|
|
5070
5225
|
}, [bridge, id, type, label, actions, customActions, treePath, style, stateStylesProp]);
|
|
5071
5226
|
const unregister = useCallback(() => {
|
|
5072
5227
|
if (!bridge || !registeredRef.current) return;
|
|
@@ -5126,30 +5281,42 @@ function useUIElement(options) {
|
|
|
5126
5281
|
const node = ref.current;
|
|
5127
5282
|
if (!node || typeof node.measureInWindow !== "function") return;
|
|
5128
5283
|
let cancelled = false;
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5284
|
+
let pendingTimer = null;
|
|
5285
|
+
const backoffMs = [50, 200, 500, 1e3, 2500];
|
|
5286
|
+
let attempt = 0;
|
|
5287
|
+
const tryMeasure = () => {
|
|
5288
|
+
if (cancelled || !node.measureInWindow) return;
|
|
5289
|
+
node.measureInWindow((pageX, pageY, w, h) => {
|
|
5290
|
+
if (cancelled) return;
|
|
5291
|
+
if (w > 0 && h > 0) {
|
|
5292
|
+
const newLayout = {
|
|
5293
|
+
x: pageX,
|
|
5294
|
+
y: pageY,
|
|
5295
|
+
width: w,
|
|
5296
|
+
height: h,
|
|
5297
|
+
pageX,
|
|
5298
|
+
pageY
|
|
5299
|
+
};
|
|
5300
|
+
setLayout(newLayout);
|
|
5301
|
+
const newState = {
|
|
5302
|
+
mounted: true,
|
|
5303
|
+
visible: true,
|
|
5304
|
+
enabled: true,
|
|
5305
|
+
focused: false,
|
|
5306
|
+
layout: newLayout
|
|
5307
|
+
};
|
|
5308
|
+
bridge.registry.updateElementState(id, newState);
|
|
5309
|
+
onStateChangeRef.current?.(newState);
|
|
5310
|
+
return;
|
|
5311
|
+
}
|
|
5312
|
+
if (attempt >= backoffMs.length) return;
|
|
5313
|
+
pendingTimer = setTimeout(tryMeasure, backoffMs[attempt++]);
|
|
5314
|
+
});
|
|
5315
|
+
};
|
|
5316
|
+
tryMeasure();
|
|
5151
5317
|
return () => {
|
|
5152
5318
|
cancelled = true;
|
|
5319
|
+
if (pendingTimer != null) clearTimeout(pendingTimer);
|
|
5153
5320
|
};
|
|
5154
5321
|
}, [bridge, registered, id]);
|
|
5155
5322
|
const registeredHandlerKeysRef = useRef([]);
|
|
@@ -5219,6 +5386,23 @@ function useUIElement(options) {
|
|
|
5219
5386
|
},
|
|
5220
5387
|
[bridge, id]
|
|
5221
5388
|
);
|
|
5389
|
+
const updateLabel = useCallback(
|
|
5390
|
+
(newLabel) => {
|
|
5391
|
+
if (!bridge || !registered) return;
|
|
5392
|
+
const changed = bridge.registry.updateElementMeta(id, {
|
|
5393
|
+
label: newLabel,
|
|
5394
|
+
accessibilityLabel: newLabel,
|
|
5395
|
+
testId: id
|
|
5396
|
+
});
|
|
5397
|
+
if (!changed && !warnedRedundantUpdateLabelIds.has(id)) {
|
|
5398
|
+
warnedRedundantUpdateLabelIds.add(id);
|
|
5399
|
+
console.warn(
|
|
5400
|
+
`[UI Bridge] updateLabel("${newLabel}") on element "${id}" was a no-op (label unchanged). Gate the call behind a useEffect with [label] deps to avoid redundant publishes.`
|
|
5401
|
+
);
|
|
5402
|
+
}
|
|
5403
|
+
},
|
|
5404
|
+
[bridge, registered, id]
|
|
5405
|
+
);
|
|
5222
5406
|
const updateStyle = useCallback(
|
|
5223
5407
|
(newStyle, newStateStyles) => {
|
|
5224
5408
|
if (!bridge || !registered) return;
|
|
@@ -5244,7 +5428,8 @@ function useUIElement(options) {
|
|
|
5244
5428
|
register,
|
|
5245
5429
|
unregister,
|
|
5246
5430
|
registeredElement,
|
|
5247
|
-
updateStyle
|
|
5431
|
+
updateStyle,
|
|
5432
|
+
updateLabel
|
|
5248
5433
|
};
|
|
5249
5434
|
}
|
|
5250
5435
|
function useUIElementWithProps(options) {
|