@qontinui/ui-bridge-native 0.1.6 → 0.2.0

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 (94) hide show
  1. package/dist/control/{action-executor.d.ts → index.d.mts} +14 -6
  2. package/dist/control/index.d.ts +95 -5
  3. package/dist/control/index.js +74 -11
  4. package/dist/control/index.js.map +1 -1
  5. package/dist/control/index.mjs +74 -11
  6. package/dist/control/index.mjs.map +1 -1
  7. package/dist/core/index.d.mts +46 -0
  8. package/dist/core/index.d.ts +43 -6
  9. package/dist/core/index.js +69 -1
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/core/index.mjs +69 -1
  12. package/dist/core/index.mjs.map +1 -1
  13. package/dist/debug/{inspector.d.ts → index.d.mts} +6 -3
  14. package/dist/debug/index.d.ts +44 -4
  15. package/dist/debug/index.js.map +1 -1
  16. package/dist/debug/index.mjs.map +1 -1
  17. package/dist/design/{design-inspector-native.d.ts → index.d.mts} +11 -7
  18. package/dist/design/index.d.ts +60 -5
  19. package/dist/{design/design-types.d.ts → design-types-BHr6Q1FX.d.mts} +11 -10
  20. package/dist/design-types-BHr6Q1FX.d.ts +205 -0
  21. package/dist/http-server-DCdR8WlL.d.mts +622 -0
  22. package/dist/http-server-Dj3grQW4.d.ts +622 -0
  23. package/dist/index.d.mts +89 -0
  24. package/dist/index.d.ts +86 -28
  25. package/dist/index.js +391 -12
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +386 -13
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/react/{useAutoRegister.d.ts → index.d.mts} +15 -7
  30. package/dist/react/index.d.ts +69 -8
  31. package/dist/react/index.js +388 -12
  32. package/dist/react/index.js.map +1 -1
  33. package/dist/react/index.mjs +386 -13
  34. package/dist/react/index.mjs.map +1 -1
  35. package/dist/registry-Cahd8Gn2.d.mts +230 -0
  36. package/dist/{core/registry.d.ts → registry-z-3WJsxp.d.ts} +28 -10
  37. package/dist/server/index.d.mts +119 -0
  38. package/dist/server/index.d.ts +117 -13
  39. package/dist/server/index.js.map +1 -1
  40. package/dist/server/index.mjs.map +1 -1
  41. package/dist/types-DAfCeWXc.d.mts +197 -0
  42. package/dist/{core/types.d.ts → types-Oz13YLZq.d.mts} +124 -30
  43. package/dist/types-Oz13YLZq.d.ts +477 -0
  44. package/dist/{control/types.d.ts → types-ajakyp-l.d.ts} +36 -15
  45. package/dist/useUIBridgeToast-D32KXyL1.d.ts +720 -0
  46. package/dist/useUIBridgeToast-GmT1TzUe.d.mts +720 -0
  47. package/package.json +1 -1
  48. package/dist/control/action-executor.d.ts.map +0 -1
  49. package/dist/control/index.d.ts.map +0 -1
  50. package/dist/control/types.d.ts.map +0 -1
  51. package/dist/core/element-identifier.d.ts +0 -41
  52. package/dist/core/element-identifier.d.ts.map +0 -1
  53. package/dist/core/index.d.ts.map +0 -1
  54. package/dist/core/registry.d.ts.map +0 -1
  55. package/dist/core/types.d.ts.map +0 -1
  56. package/dist/debug/index.d.ts.map +0 -1
  57. package/dist/debug/inspector.d.ts.map +0 -1
  58. package/dist/design/design-inspector-native.d.ts.map +0 -1
  59. package/dist/design/design-types.d.ts.map +0 -1
  60. package/dist/design/index.d.ts.map +0 -1
  61. package/dist/index.d.ts.map +0 -1
  62. package/dist/react/UIBridgeNativeProvider.d.ts +0 -147
  63. package/dist/react/UIBridgeNativeProvider.d.ts.map +0 -1
  64. package/dist/react/index.d.ts.map +0 -1
  65. package/dist/react/useAutoRegister.d.ts.map +0 -1
  66. package/dist/react/useUIBridge.d.ts +0 -103
  67. package/dist/react/useUIBridge.d.ts.map +0 -1
  68. package/dist/react/useUIComponent.d.ts +0 -112
  69. package/dist/react/useUIComponent.d.ts.map +0 -1
  70. package/dist/react/useUIElement.d.ts +0 -162
  71. package/dist/react/useUIElement.d.ts.map +0 -1
  72. package/dist/server/design-handlers.d.ts +0 -47
  73. package/dist/server/design-handlers.d.ts.map +0 -1
  74. package/dist/server/handlers.d.ts +0 -20
  75. package/dist/server/handlers.d.ts.map +0 -1
  76. package/dist/server/http-server.d.ts +0 -247
  77. package/dist/server/http-server.d.ts.map +0 -1
  78. package/dist/server/index.d.ts.map +0 -1
  79. package/dist/server/types.d.ts +0 -200
  80. package/dist/server/types.d.ts.map +0 -1
  81. package/dist/server/ws-connection.d.ts +0 -48
  82. package/dist/server/ws-connection.d.ts.map +0 -1
  83. package/dist/server/ws-event-bridge.d.ts +0 -113
  84. package/dist/server/ws-event-bridge.d.ts.map +0 -1
  85. package/dist/server/ws-protocol.d.ts +0 -48
  86. package/dist/server/ws-protocol.d.ts.map +0 -1
  87. package/dist/server/ws-types.d.ts +0 -65
  88. package/dist/server/ws-types.d.ts.map +0 -1
  89. package/dist/transport/CloudRelayClient.d.ts +0 -51
  90. package/dist/transport/CloudRelayClient.d.ts.map +0 -1
  91. package/dist/transport/DeviceAnnouncer.d.ts +0 -75
  92. package/dist/transport/DeviceAnnouncer.d.ts.map +0 -1
  93. package/dist/transport/index.d.ts +0 -3
  94. package/dist/transport/index.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -55,9 +55,34 @@ var NativeUIBridgeRegistry = class {
55
55
  this.components = /* @__PURE__ */ new Map();
56
56
  this.workflows = /* @__PURE__ */ new Map();
57
57
  this.eventListeners = /* @__PURE__ */ new Map();
58
+ this.enrichers = {};
59
+ this.snapshotExtras = /* @__PURE__ */ new Map();
58
60
  this.config = config;
59
61
  }
60
62
  // ============================================================================
63
+ // Snapshot Enricher Slots
64
+ // ============================================================================
65
+ /**
66
+ * Register/replace canonical enrichers (modal/toast/undo). HMR-safe — calling
67
+ * with a partial set merges into existing slots instead of clobbering them.
68
+ */
69
+ setEnrichers(e) {
70
+ this.enrichers = { ...this.enrichers, ...e };
71
+ }
72
+ /**
73
+ * Register a custom snapshot enricher. The returned object will be
74
+ * `Object.assign`ed onto the snapshot, so use unique top-level keys to avoid
75
+ * clobbering canonical fields. Returns a disposer.
76
+ */
77
+ registerSnapshotEnricher(name, fn) {
78
+ this.snapshotExtras.set(name, fn);
79
+ return () => this.unregisterSnapshotEnricher(name);
80
+ }
81
+ /** Remove a custom snapshot enricher by name */
82
+ unregisterSnapshotEnricher(name) {
83
+ this.snapshotExtras.delete(name);
84
+ }
85
+ // ============================================================================
61
86
  // Element Management
62
87
  // ============================================================================
63
88
  /**
@@ -420,7 +445,7 @@ var NativeUIBridgeRegistry = class {
420
445
  const currentRoute = routeInfo.currentRoute;
421
446
  elements = elements.filter((e) => e.registrationRoute === currentRoute);
422
447
  }
423
- return {
448
+ const snapshot = {
424
449
  timestamp: Date.now(),
425
450
  elements: elements.map((e) => {
426
451
  const handlers = extractHandlerNames(e.props);
@@ -452,6 +477,49 @@ var NativeUIBridgeRegistry = class {
452
477
  currentRoute: routeInfo?.currentRoute ?? null,
453
478
  segments: routeInfo?.segments
454
479
  };
480
+ if (this.enrichers.modalDetector) {
481
+ try {
482
+ snapshot.modalStack = this.enrichers.modalDetector.getSnapshotModalContext();
483
+ } catch (error3) {
484
+ if (this.config.verbose) {
485
+ console.warn(`[ui-bridge-native] modalDetector enricher threw:`, error3);
486
+ }
487
+ }
488
+ }
489
+ if (this.enrichers.toastCapture) {
490
+ try {
491
+ snapshot.toasts = this.enrichers.toastCapture.getSnapshotToastContext();
492
+ } catch (error3) {
493
+ if (this.config.verbose) {
494
+ console.warn(`[ui-bridge-native] toastCapture enricher threw:`, error3);
495
+ }
496
+ }
497
+ }
498
+ if (this.enrichers.undoTracker) {
499
+ try {
500
+ snapshot.undoRedo = this.enrichers.undoTracker.getSnapshotUndoContext();
501
+ } catch (error3) {
502
+ if (this.config.verbose) {
503
+ console.warn(`[ui-bridge-native] undoTracker enricher threw:`, error3);
504
+ }
505
+ }
506
+ }
507
+ if (this.snapshotExtras.size > 0) {
508
+ const ctx = { elements, currentRoute: routeInfo?.currentRoute ?? null };
509
+ for (const [name, fn] of this.snapshotExtras) {
510
+ try {
511
+ const extra = fn(ctx);
512
+ if (extra && typeof extra === "object") {
513
+ Object.assign(snapshot, extra);
514
+ }
515
+ } catch (error3) {
516
+ if (this.config.verbose) {
517
+ console.warn(`[ui-bridge-native] snapshot enricher "${name}" threw:`, error3);
518
+ }
519
+ }
520
+ }
521
+ }
522
+ return snapshot;
455
523
  }
456
524
  /**
457
525
  * Get registry statistics
@@ -597,6 +665,23 @@ function sleep(ms) {
597
665
  var DefaultNativeActionExecutor = class {
598
666
  constructor(registry) {
599
667
  this.registry = registry;
668
+ this.actionListeners = /* @__PURE__ */ new Set();
669
+ }
670
+ onActionExecuted(listener) {
671
+ this.actionListeners.add(listener);
672
+ return () => {
673
+ this.actionListeners.delete(listener);
674
+ };
675
+ }
676
+ /** Notify listeners out-of-band; isolate each invocation so one bad listener can't poison the rest. */
677
+ emitActionEvent(event) {
678
+ for (const listener of this.actionListeners) {
679
+ try {
680
+ listener(event);
681
+ } catch (error3) {
682
+ console.warn(`[ui-bridge-native] Action listener error:`, error3);
683
+ }
684
+ }
600
685
  }
601
686
  /**
602
687
  * Execute an action on an element
@@ -610,11 +695,23 @@ var DefaultNativeActionExecutor = class {
610
695
  registered = findElementByIdentifier(elementId) ?? void 0;
611
696
  }
612
697
  if (!registered) {
698
+ const failTime = Date.now();
699
+ const errorMsg = `Element not found: ${elementId}`;
700
+ this.emitActionEvent({
701
+ elementId,
702
+ action: request.action,
703
+ params: request.params,
704
+ success: false,
705
+ error: errorMsg,
706
+ timestamp: failTime,
707
+ requestId: request.requestId,
708
+ durationMs: failTime - startTime
709
+ });
613
710
  return {
614
711
  success: false,
615
- error: `Element not found: ${elementId}`,
616
- durationMs: Date.now() - startTime,
617
- timestamp: Date.now(),
712
+ error: errorMsg,
713
+ durationMs: failTime - startTime,
714
+ timestamp: failTime,
618
715
  requestId: request.requestId
619
716
  };
620
717
  }
@@ -622,33 +719,67 @@ var DefaultNativeActionExecutor = class {
622
719
  const waitResult = await this.waitForElementInternal(registered.id, request.waitOptions);
623
720
  waitDurationMs = waitResult.waitedMs;
624
721
  if (!waitResult.met) {
722
+ const failTime = Date.now();
723
+ const errorMsg = waitResult.error || "Wait condition not met";
724
+ this.emitActionEvent({
725
+ elementId,
726
+ action: request.action,
727
+ params: request.params,
728
+ success: false,
729
+ error: errorMsg,
730
+ timestamp: failTime,
731
+ requestId: request.requestId,
732
+ durationMs: failTime - startTime
733
+ });
625
734
  return {
626
735
  success: false,
627
- error: waitResult.error || "Wait condition not met",
628
- durationMs: Date.now() - startTime,
629
- timestamp: Date.now(),
736
+ error: errorMsg,
737
+ durationMs: failTime - startTime,
738
+ timestamp: failTime,
630
739
  requestId: request.requestId,
631
740
  waitDurationMs
632
741
  };
633
742
  }
634
743
  }
635
744
  const result = await this.performAction(registered, request.action, request.params);
745
+ const successTime = Date.now();
746
+ this.emitActionEvent({
747
+ elementId,
748
+ action: request.action,
749
+ params: request.params,
750
+ success: true,
751
+ timestamp: successTime,
752
+ requestId: request.requestId,
753
+ durationMs: successTime - startTime
754
+ });
636
755
  return {
637
756
  success: true,
638
757
  elementState: registered.getState(),
639
758
  result,
640
- durationMs: Date.now() - startTime,
641
- timestamp: Date.now(),
759
+ durationMs: successTime - startTime,
760
+ timestamp: successTime,
642
761
  requestId: request.requestId,
643
762
  waitDurationMs
644
763
  };
645
764
  } catch (error3) {
765
+ const failTime = Date.now();
766
+ const errorMsg = error3 instanceof Error ? error3.message : String(error3);
767
+ this.emitActionEvent({
768
+ elementId,
769
+ action: request.action,
770
+ params: request.params,
771
+ success: false,
772
+ error: errorMsg,
773
+ timestamp: failTime,
774
+ requestId: request.requestId,
775
+ durationMs: failTime - startTime
776
+ });
646
777
  return {
647
778
  success: false,
648
- error: error3 instanceof Error ? error3.message : String(error3),
779
+ error: errorMsg,
649
780
  stack: error3 instanceof Error ? error3.stack : void 0,
650
- durationMs: Date.now() - startTime,
651
- timestamp: Date.now(),
781
+ durationMs: failTime - startTime,
782
+ timestamp: failTime,
652
783
  requestId: request.requestId,
653
784
  waitDurationMs
654
785
  };
@@ -3566,6 +3697,186 @@ function parseQueryParams(path) {
3566
3697
  }
3567
3698
  return result;
3568
3699
  }
3700
+
3701
+ // src/modal/modal-detector.ts
3702
+ var ModalDetector = class {
3703
+ constructor() {
3704
+ /** Map preserves O(1) idempotent replace by id; ordering comes from detectedAt. */
3705
+ this.modals = /* @__PURE__ */ new Map();
3706
+ }
3707
+ pushModal(modal) {
3708
+ const existing = this.modals.get(modal.id);
3709
+ const detectedAt = existing ? existing.detectedAt : Date.now();
3710
+ const next = {
3711
+ id: modal.id,
3712
+ title: modal.title,
3713
+ type: modal.type ?? "modal",
3714
+ blocking: modal.blocking ?? true,
3715
+ dismissible: modal.dismissible ?? true,
3716
+ detectedAt
3717
+ };
3718
+ this.modals.set(modal.id, next);
3719
+ }
3720
+ dismissModal(id) {
3721
+ this.modals.delete(id);
3722
+ }
3723
+ getSnapshotModalContext() {
3724
+ const active = this.getActive();
3725
+ const ctx = {
3726
+ modals: active,
3727
+ hasBlockingModal: active.some((m) => m.blocking),
3728
+ count: active.length
3729
+ };
3730
+ if (active.length > 0) {
3731
+ ctx.topModal = active[active.length - 1];
3732
+ }
3733
+ return ctx;
3734
+ }
3735
+ getActive() {
3736
+ return Array.from(this.modals.values()).sort((a, b) => a.detectedAt - b.detectedAt);
3737
+ }
3738
+ clear() {
3739
+ this.modals.clear();
3740
+ }
3741
+ };
3742
+
3743
+ // src/toast/toast-capture.ts
3744
+ var DEFAULT_MAX_RECENT = 50;
3745
+ var ToastCapture = class {
3746
+ constructor(config = {}) {
3747
+ this.active = /* @__PURE__ */ new Map();
3748
+ this.recent = [];
3749
+ this.totalCaptured = 0;
3750
+ this.maxRecent = config.maxRecent ?? DEFAULT_MAX_RECENT;
3751
+ }
3752
+ recordToast(toast) {
3753
+ if (this.active.has(toast.id)) {
3754
+ return;
3755
+ }
3756
+ const appearedAt = toast.appearedAt ?? Date.now();
3757
+ const entry = {
3758
+ id: toast.id,
3759
+ message: toast.message,
3760
+ level: toast.level ?? "info",
3761
+ appearedAt,
3762
+ visible: true,
3763
+ durationMs: 0
3764
+ };
3765
+ this.active.set(toast.id, entry);
3766
+ this.totalCaptured += 1;
3767
+ }
3768
+ dismissToast(id) {
3769
+ const entry = this.active.get(id);
3770
+ if (!entry) {
3771
+ return;
3772
+ }
3773
+ this.active.delete(id);
3774
+ const dismissedAt = Date.now();
3775
+ const dismissed = {
3776
+ ...entry,
3777
+ dismissedAt,
3778
+ visible: false,
3779
+ durationMs: dismissedAt - entry.appearedAt
3780
+ };
3781
+ this.recent.push(dismissed);
3782
+ if (this.recent.length > this.maxRecent) {
3783
+ this.recent.shift();
3784
+ }
3785
+ }
3786
+ getSnapshotToastContext() {
3787
+ const now = Date.now();
3788
+ const active = Array.from(this.active.values()).map((entry) => ({
3789
+ ...entry,
3790
+ durationMs: now - entry.appearedAt
3791
+ }));
3792
+ return {
3793
+ active,
3794
+ recent: [...this.recent],
3795
+ totalCaptured: this.totalCaptured
3796
+ };
3797
+ }
3798
+ clear() {
3799
+ this.active.clear();
3800
+ this.recent = [];
3801
+ this.totalCaptured = 0;
3802
+ }
3803
+ };
3804
+
3805
+ // src/undo/undo-tracker.ts
3806
+ var DEFAULT_MAX_HISTORY = 50;
3807
+ function buildSummary(ctx) {
3808
+ if (!ctx.canUndo && !ctx.canRedo) {
3809
+ return "no undo available";
3810
+ }
3811
+ const parts = [];
3812
+ if (ctx.canUndo) {
3813
+ parts.push(`undo: '${ctx.undoDescription ?? "available"}'`);
3814
+ }
3815
+ if (ctx.canRedo) {
3816
+ parts.push(`redo: '${ctx.redoDescription ?? "available"}'`);
3817
+ }
3818
+ return parts.join(" / ");
3819
+ }
3820
+ var UndoTracker = class {
3821
+ constructor(config = {}) {
3822
+ this.actions = [];
3823
+ this.declaredState = null;
3824
+ this.maxHistory = config.maxHistory ?? DEFAULT_MAX_HISTORY;
3825
+ }
3826
+ recordAction(record) {
3827
+ this.actions.push(record);
3828
+ if (this.actions.length > this.maxHistory) {
3829
+ this.actions = this.actions.slice(-this.maxHistory);
3830
+ }
3831
+ }
3832
+ setDeclaredState(state) {
3833
+ this.declaredState = state;
3834
+ }
3835
+ getActionHistory() {
3836
+ return this.actions.slice();
3837
+ }
3838
+ getSnapshotUndoContext() {
3839
+ const declared = this.declaredState;
3840
+ if (declared) {
3841
+ const summary2 = buildSummary({
3842
+ canUndo: declared.canUndo,
3843
+ canRedo: declared.canRedo,
3844
+ undoDescription: declared.undoDescription,
3845
+ redoDescription: declared.redoDescription
3846
+ });
3847
+ return {
3848
+ canUndo: declared.canUndo,
3849
+ canRedo: declared.canRedo,
3850
+ undoDescription: declared.undoDescription,
3851
+ redoDescription: declared.redoDescription,
3852
+ undoDepth: declared.undoDepth,
3853
+ redoDepth: declared.redoDepth,
3854
+ summary: summary2
3855
+ };
3856
+ }
3857
+ const canUndo = this.actions.some((a) => a.reversible !== false);
3858
+ const canRedo = false;
3859
+ const lastReversible = [...this.actions].reverse().find((a) => a.reversible !== false);
3860
+ const undoDescription = lastReversible?.description;
3861
+ const summary = buildSummary({ canUndo, canRedo, undoDescription });
3862
+ return {
3863
+ canUndo,
3864
+ canRedo,
3865
+ undoDescription,
3866
+ summary
3867
+ };
3868
+ }
3869
+ executeUndo() {
3870
+ return { ok: false, reason: "Not implemented in native" };
3871
+ }
3872
+ executeRedo() {
3873
+ return { ok: false, reason: "Not implemented in native" };
3874
+ }
3875
+ clear() {
3876
+ this.actions = [];
3877
+ this.declaredState = null;
3878
+ }
3879
+ };
3569
3880
  var UIBridgeNativeContext = react.createContext(null);
3570
3881
  function UIBridgeNativeProvider({
3571
3882
  children,
@@ -3582,6 +3893,9 @@ function UIBridgeNativeProvider({
3582
3893
  }) {
3583
3894
  const registryRef = react.useRef(null);
3584
3895
  const executorRef = react.useRef(null);
3896
+ const modalDetectorRef = react.useRef(null);
3897
+ const toastCaptureRef = react.useRef(null);
3898
+ const undoTrackerRef = react.useRef(null);
3585
3899
  const [serverRunning, setServerRunning] = react.useState(false);
3586
3900
  if (!registryRef.current) {
3587
3901
  registryRef.current = new NativeUIBridgeRegistry({
@@ -3595,6 +3909,12 @@ function UIBridgeNativeProvider({
3595
3909
  executorRef.current = createNativeActionExecutor(registry);
3596
3910
  }
3597
3911
  const executor = executorRef.current;
3912
+ if (!modalDetectorRef.current) modalDetectorRef.current = new ModalDetector();
3913
+ if (!toastCaptureRef.current) toastCaptureRef.current = new ToastCapture();
3914
+ if (!undoTrackerRef.current) undoTrackerRef.current = new UndoTracker();
3915
+ const modalDetector = modalDetectorRef.current;
3916
+ const toastCapture = toastCaptureRef.current;
3917
+ const undoTracker = undoTrackerRef.current;
3598
3918
  const serverRef = react.useRef(null);
3599
3919
  const eventBridgeRef = react.useRef(null);
3600
3920
  const announcerRef = react.useRef(null);
@@ -3783,6 +4103,22 @@ function UIBridgeNativeProvider({
3783
4103
  lastRoute = current;
3784
4104
  });
3785
4105
  }, [routeProvider, registry]);
4106
+ react.useEffect(() => {
4107
+ registry.setEnrichers({ modalDetector, toastCapture, undoTracker });
4108
+ }, [registry, modalDetector, toastCapture, undoTracker]);
4109
+ react.useEffect(() => {
4110
+ const unsubscribe = executor.onActionExecuted((event) => {
4111
+ if (!event.success) return;
4112
+ undoTracker.recordAction({
4113
+ id: event.requestId ?? `action-${event.timestamp}`,
4114
+ type: event.action,
4115
+ targetId: event.elementId,
4116
+ timestamp: event.timestamp,
4117
+ reversible: true
4118
+ });
4119
+ });
4120
+ return unsubscribe;
4121
+ }, [executor, undoTracker]);
3786
4122
  const on = react.useCallback(
3787
4123
  (type, listener) => registry.on(type, listener),
3788
4124
  [registry]
@@ -3797,6 +4133,9 @@ function UIBridgeNativeProvider({
3797
4133
  config,
3798
4134
  registry,
3799
4135
  executor,
4136
+ modalDetector,
4137
+ toastCapture,
4138
+ undoTracker,
3800
4139
  getElements,
3801
4140
  getComponents,
3802
4141
  createSnapshot,
@@ -3813,6 +4152,9 @@ function UIBridgeNativeProvider({
3813
4152
  config,
3814
4153
  registry,
3815
4154
  executor,
4155
+ modalDetector,
4156
+ toastCapture,
4157
+ undoTracker,
3816
4158
  getElements,
3817
4159
  getComponents,
3818
4160
  createSnapshot,
@@ -4293,6 +4635,37 @@ function useUIBridgeRequired() {
4293
4635
  }
4294
4636
  return result;
4295
4637
  }
4638
+ function useUIBridgeModal(opts) {
4639
+ const { modalDetector } = useUIBridgeNative();
4640
+ const { id, title, type, blocking, dismissible } = opts;
4641
+ react.useEffect(() => {
4642
+ modalDetector.pushModal({ id, title, type, blocking, dismissible });
4643
+ return () => {
4644
+ modalDetector.dismissModal(id);
4645
+ };
4646
+ }, [modalDetector, id, title, type, blocking, dismissible]);
4647
+ }
4648
+ function useUIBridgeToast(opts) {
4649
+ const { toastCapture } = useUIBridgeNative();
4650
+ const { id, message, level } = opts;
4651
+ react.useEffect(() => {
4652
+ toastCapture.recordToast({ id, message, level });
4653
+ return () => {
4654
+ toastCapture.dismissToast(id);
4655
+ };
4656
+ }, [toastCapture, id, message, level]);
4657
+ }
4658
+ function useToastRecorder() {
4659
+ const { toastCapture } = useUIBridgeNative();
4660
+ const recordToast = react.useCallback(
4661
+ (toast) => {
4662
+ toastCapture.recordToast(toast);
4663
+ },
4664
+ [toastCapture]
4665
+ );
4666
+ const dismissToast = react.useCallback((id) => toastCapture.dismissToast(id), [toastCapture]);
4667
+ return { recordToast, dismissToast };
4668
+ }
4296
4669
 
4297
4670
  // src/server/types.ts
4298
4671
  var UI_BRIDGE_NATIVE_ROUTES = {
@@ -5197,11 +5570,14 @@ var styles = reactNative.StyleSheet.create({
5197
5570
  exports.CloudRelayClient = CloudRelayClient;
5198
5571
  exports.DefaultNativeActionExecutor = DefaultNativeActionExecutor;
5199
5572
  exports.DeviceAnnouncer = DeviceAnnouncer;
5573
+ exports.ModalDetector = ModalDetector;
5200
5574
  exports.NativeUIBridgeRegistry = NativeUIBridgeRegistry;
5201
5575
  exports.NativeUIBridgeServer = NativeUIBridgeServer;
5576
+ exports.ToastCapture = ToastCapture;
5202
5577
  exports.UIBridgeInspector = UIBridgeInspector;
5203
5578
  exports.UIBridgeNativeProvider = UIBridgeNativeProvider;
5204
5579
  exports.UI_BRIDGE_NATIVE_ROUTES = UI_BRIDGE_NATIVE_ROUTES;
5580
+ exports.UndoTracker = UndoTracker;
5205
5581
  exports.WebSocketConnection = WebSocketConnection;
5206
5582
  exports.WebSocketEventBridge = WebSocketEventBridge;
5207
5583
  exports.buildTreePath = buildTreePath;
@@ -5224,11 +5600,14 @@ exports.matchesIdentifier = matchesIdentifier;
5224
5600
  exports.parseTreePath = parseTreePath;
5225
5601
  exports.resetGlobalRegistry = resetGlobalRegistry;
5226
5602
  exports.setGlobalRegistry = setGlobalRegistry;
5603
+ exports.useToastRecorder = useToastRecorder;
5227
5604
  exports.useUIBridge = useUIBridge;
5605
+ exports.useUIBridgeModal = useUIBridgeModal;
5228
5606
  exports.useUIBridgeNative = useUIBridgeNative;
5229
5607
  exports.useUIBridgeNativeOptional = useUIBridgeNativeOptional;
5230
5608
  exports.useUIBridgeNativeRequired = useUIBridgeNativeRequired;
5231
5609
  exports.useUIBridgeRequired = useUIBridgeRequired;
5610
+ exports.useUIBridgeToast = useUIBridgeToast;
5232
5611
  exports.useUIComponent = useUIComponent;
5233
5612
  exports.useUIComponentAction = useUIComponentAction;
5234
5613
  exports.useUIElement = useUIElement;