@thelacanians/vue-native-runtime 0.6.5 → 0.7.1

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.cjs CHANGED
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ Easing: () => Easing,
24
25
  ErrorBoundary: () => ErrorBoundary,
26
+ KeepAlive: () => KeepAlive,
25
27
  NativeBridge: () => NativeBridge,
26
28
  VActionSheet: () => VActionSheet,
27
29
  VActivityIndicator: () => VActivityIndicator,
@@ -32,12 +34,14 @@ __export(index_exports, {
32
34
  VDrawerItem: () => VDrawerItem,
33
35
  VDrawerSection: () => VDrawerSection,
34
36
  VDropdown: () => VDropdown,
37
+ VErrorBoundary: () => ErrorBoundary,
35
38
  VFlatList: () => VFlatList,
36
39
  VImage: () => VImage,
37
40
  VInput: () => VInput,
38
41
  VKeyboardAvoiding: () => VKeyboardAvoiding,
39
42
  VList: () => VList,
40
43
  VModal: () => VModal,
44
+ VOutlineView: () => VOutlineView,
41
45
  VPicker: () => VPicker,
42
46
  VPressable: () => VPressable,
43
47
  VProgressBar: () => VProgressBar,
@@ -48,10 +52,15 @@ __export(index_exports, {
48
52
  VSectionList: () => VSectionList,
49
53
  VSegmentedControl: () => VSegmentedControl,
50
54
  VSlider: () => VSlider,
55
+ VSplitView: () => VSplitView,
51
56
  VStatusBar: () => VStatusBar,
57
+ VSuspense: () => VSuspense,
52
58
  VSwitch: () => VSwitch,
53
59
  VTabBar: () => VTabBar,
54
60
  VText: () => VText,
61
+ VToolbar: () => VToolbar,
62
+ VTransition: () => VTransition,
63
+ VTransitionGroup: () => VTransitionGroup,
55
64
  VVideo: () => VVideo,
56
65
  VView: () => VView,
57
66
  VWebView: () => VWebView,
@@ -64,6 +73,7 @@ __export(index_exports, {
64
73
  createStyleSheet: () => createStyleSheet,
65
74
  createTextNode: () => createTextNode,
66
75
  createTheme: () => createTheme,
76
+ defineAsyncComponent: () => defineAsyncComponent,
67
77
  getRegisteredSharedElements: () => getRegisteredSharedElements,
68
78
  getSharedElementViewId: () => getSharedElementViewId,
69
79
  measureViewFrame: () => measureViewFrame,
@@ -119,7 +129,6 @@ __export(index_exports, {
119
129
  validStyleProperties: () => validStyleProperties
120
130
  });
121
131
  module.exports = __toCommonJS(index_exports);
122
- var import_runtime_core66 = require("@vue/runtime-core");
123
132
 
124
133
  // src/renderer.ts
125
134
  var import_runtime_core = require("@vue/runtime-core");
@@ -186,6 +195,24 @@ function createCommentNode(_text) {
186
195
 
187
196
  // src/bridge.ts
188
197
  var bridgeGlobals = globalThis;
198
+ var appTeardowns = /* @__PURE__ */ new Set();
199
+ function registerAppTeardown(teardown) {
200
+ appTeardowns.add(teardown);
201
+ return () => {
202
+ appTeardowns.delete(teardown);
203
+ };
204
+ }
205
+ function teardownMountedApps() {
206
+ const teardowns = [...appTeardowns];
207
+ appTeardowns.clear();
208
+ for (const teardown of teardowns) {
209
+ try {
210
+ teardown();
211
+ } catch (err) {
212
+ console.error("[VueNative] Error unmounting app during teardown:", err);
213
+ }
214
+ }
215
+ }
189
216
  var _NativeBridgeImpl = class _NativeBridgeImpl {
190
217
  constructor() {
191
218
  /** Pending operations waiting to be flushed to native */
@@ -453,12 +480,12 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
453
480
  });
454
481
  }
455
482
  /**
456
- * Invoke a native module method synchronously.
457
- * This sends the operation immediately and expects no callback.
458
- * Use sparingly -- prefer the async variant.
483
+ * @deprecated Native module invocations cannot safely return a value through
484
+ * the batched JSON bridge. This compatibility alias now uses the reliable
485
+ * asynchronous callback path; migrate callers to invokeNativeModule().
459
486
  */
460
487
  invokeNativeModuleSync(moduleName, methodName, args = []) {
461
- this.enqueue("invokeNativeModuleSync", [moduleName, methodName, args]);
488
+ return this.invokeNativeModule(moduleName, methodName, args);
462
489
  }
463
490
  /**
464
491
  * Called from Swift via globalThis.__VN_resolveCallback when an async
@@ -529,15 +556,17 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
529
556
  payload = {};
530
557
  }
531
558
  const handlers = this.globalEventHandlers.get(eventName);
532
- if (handlers) {
533
- handlers.forEach((h33) => {
534
- try {
535
- h33(payload);
536
- } catch (err) {
537
- console.error(`[VueNative] Error in global event handler "${eventName}":`, err);
538
- }
539
- });
559
+ if (!handlers || handlers.size === 0) {
560
+ return false;
540
561
  }
562
+ handlers.forEach((h35) => {
563
+ try {
564
+ h35(payload);
565
+ } catch (err) {
566
+ console.error(`[VueNative] Error in global event handler "${eventName}":`, err);
567
+ }
568
+ });
569
+ return true;
541
570
  }
542
571
  // ---------------------------------------------------------------------------
543
572
  // Cleanup
@@ -570,17 +599,32 @@ bridgeGlobals.__VN_handleEvent = NativeBridge.handleNativeEvent.bind(NativeBridg
570
599
  bridgeGlobals.__VN_resolveCallback = NativeBridge.resolveCallback.bind(NativeBridge);
571
600
  bridgeGlobals.__VN_handleGlobalEvent = NativeBridge.handleGlobalEvent.bind(NativeBridge);
572
601
  bridgeGlobals.__VN_teardown = () => {
602
+ teardownMountedApps();
573
603
  NativeBridge.reset();
574
604
  resetNodeId();
575
605
  };
576
606
 
577
607
  // src/renderer.ts
578
608
  function toEventName(key) {
579
- return key.slice(2).toLowerCase();
609
+ const name = key.slice(2);
610
+ return name.charAt(0).toLowerCase() + name.slice(1);
580
611
  }
581
612
  function isEventHandler(value) {
582
613
  return typeof value === "function";
583
614
  }
615
+ function getEventHandler(value) {
616
+ if (isEventHandler(value)) {
617
+ return value;
618
+ }
619
+ if (Array.isArray(value) && value.length > 0 && value.every(isEventHandler)) {
620
+ return (payload) => {
621
+ for (const handler of value) {
622
+ handler(payload);
623
+ }
624
+ };
625
+ }
626
+ return null;
627
+ }
584
628
  function patchStyle(nodeId, prevStyle, nextStyle) {
585
629
  try {
586
630
  const prev = typeof prevStyle === "object" && prevStyle !== null ? prevStyle : {};
@@ -606,6 +650,14 @@ function patchStyle(nodeId, prevStyle, nextStyle) {
606
650
  console.error(`[VueNative] Error patching style on node ${nodeId}:`, err);
607
651
  }
608
652
  }
653
+ function releaseSubtree(node) {
654
+ const children = node.children.splice(0);
655
+ for (const child of children) {
656
+ child.parent = null;
657
+ releaseSubtree(child);
658
+ }
659
+ releaseNodeId(node.id);
660
+ }
609
661
  var nodeOps = {
610
662
  /**
611
663
  * Create a native element node.
@@ -642,8 +694,12 @@ var nodeOps = {
642
694
  * Set the text content of an element, replacing all its children.
643
695
  */
644
696
  setElementText(node, text) {
645
- for (const child of node.children) {
697
+ for (const child of [...node.children]) {
698
+ if (child.type !== "__COMMENT__") {
699
+ NativeBridge.removeChild(node.id, child.id);
700
+ }
646
701
  child.parent = null;
702
+ releaseSubtree(child);
647
703
  }
648
704
  node.children = [];
649
705
  NativeBridge.setElementText(node.id, text);
@@ -657,13 +713,15 @@ var nodeOps = {
657
713
  */
658
714
  patchProp(el, key, prevValue, nextValue) {
659
715
  try {
660
- if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase()) {
716
+ const previousHandler = getEventHandler(prevValue);
717
+ const nextHandler = getEventHandler(nextValue);
718
+ if (/^on[A-Z]/.test(key) && (previousHandler || nextHandler)) {
661
719
  const eventName = toEventName(key);
662
- if (prevValue) {
720
+ if (previousHandler) {
663
721
  NativeBridge.removeEventListener(el.id, eventName);
664
722
  }
665
- if (isEventHandler(nextValue)) {
666
- NativeBridge.addEventListener(el.id, eventName, nextValue);
723
+ if (nextHandler) {
724
+ NativeBridge.addEventListener(el.id, eventName, nextHandler);
667
725
  }
668
726
  return;
669
727
  }
@@ -738,7 +796,7 @@ var nodeOps = {
738
796
  } catch (err) {
739
797
  console.error(`[VueNative] Error removing node ${child.id}:`, err);
740
798
  }
741
- releaseNodeId(child.id);
799
+ releaseSubtree(child);
742
800
  }
743
801
  },
744
802
  /**
@@ -832,6 +890,10 @@ var import_runtime_core4 = require("@vue/runtime-core");
832
890
  var VButton = (0, import_runtime_core4.defineComponent)({
833
891
  name: "VButton",
834
892
  props: {
893
+ /** Convenience label rendered as VText when no default slot is provided. */
894
+ title: String,
895
+ /** Text styling for the title shorthand. */
896
+ titleStyle: Object,
835
897
  style: Object,
836
898
  disabled: {
837
899
  type: Boolean,
@@ -849,15 +911,25 @@ var VButton = (0, import_runtime_core4.defineComponent)({
849
911
  accessibilityState: Object
850
912
  },
851
913
  setup(props, { slots }) {
852
- return () => (0, import_runtime_core4.h)(
853
- "VButton",
854
- {
855
- ...props,
856
- onPress: props.disabled ? void 0 : props.onPress,
857
- onLongPress: props.disabled ? void 0 : props.onLongPress
858
- },
859
- slots.default?.()
860
- );
914
+ return () => {
915
+ const slotContent = slots.default?.();
916
+ const content = slotContent?.length ? slotContent : props.title !== void 0 ? [(0, import_runtime_core4.h)(VText, { style: props.titleStyle }, () => props.title)] : [];
917
+ return (0, import_runtime_core4.h)(
918
+ "VButton",
919
+ {
920
+ style: props.style,
921
+ disabled: props.disabled,
922
+ activeOpacity: props.activeOpacity,
923
+ onPress: props.disabled ? void 0 : props.onPress,
924
+ onLongPress: props.disabled ? void 0 : props.onLongPress,
925
+ accessibilityLabel: props.accessibilityLabel,
926
+ accessibilityRole: props.accessibilityRole,
927
+ accessibilityHint: props.accessibilityHint,
928
+ accessibilityState: props.accessibilityState
929
+ },
930
+ content
931
+ );
932
+ };
861
933
  }
862
934
  });
863
935
 
@@ -1192,6 +1264,10 @@ var VSafeArea = (0, import_runtime_core11.defineComponent)({
1192
1264
 
1193
1265
  // src/components/VSlider.ts
1194
1266
  var import_runtime_core12 = require("@vue/runtime-core");
1267
+ function getSliderValue(payload) {
1268
+ const value = typeof payload === "object" && payload !== null && "value" in payload ? payload.value : payload;
1269
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1270
+ }
1195
1271
  var VSlider = (0, import_runtime_core12.defineComponent)({
1196
1272
  name: "VSlider",
1197
1273
  props: {
@@ -1215,9 +1291,11 @@ var VSlider = (0, import_runtime_core12.defineComponent)({
1215
1291
  accessibilityRole: props.accessibilityRole,
1216
1292
  accessibilityHint: props.accessibilityHint,
1217
1293
  accessibilityState: props.accessibilityState,
1218
- onChange: (val) => {
1219
- emit("update:modelValue", val);
1220
- emit("change", val);
1294
+ onChange: (payload) => {
1295
+ const value = getSliderValue(payload);
1296
+ if (value === null) return;
1297
+ emit("update:modelValue", value);
1298
+ emit("change", value);
1221
1299
  }
1222
1300
  });
1223
1301
  }
@@ -1469,13 +1547,15 @@ var VAlertDialog = (0, import_runtime_core15.defineComponent)({
1469
1547
  }
1470
1548
  }
1471
1549
  return (0, import_runtime_core15.h)("VAlertDialog", {
1472
- visible: debouncedVisible.value,
1473
1550
  title: props.title,
1474
1551
  message: props.message,
1475
1552
  buttons: resolvedButtons,
1476
1553
  onConfirm: (event) => emit("confirm", event),
1477
1554
  onCancel: () => emit("cancel"),
1478
- onAction: (event) => emit("action", event)
1555
+ onAction: (event) => emit("action", event),
1556
+ // Native factories present synchronously when this becomes true. Keep
1557
+ // it last so content and handlers are installed before presentation.
1558
+ visible: debouncedVisible.value
1479
1559
  });
1480
1560
  };
1481
1561
  }
@@ -1521,9 +1601,12 @@ var VWebView = (0, import_runtime_core17.defineComponent)({
1521
1601
  return source;
1522
1602
  });
1523
1603
  return () => (0, import_runtime_core17.h)("VWebView", {
1604
+ // Native WKWebView factories begin navigation as soon as `source` is
1605
+ // applied. Keep this first so the initial navigation observes the
1606
+ // requested JavaScript policy as well as subsequent navigations.
1607
+ javaScriptEnabled: props.javaScriptEnabled,
1524
1608
  source: sanitizedSource.value,
1525
1609
  style: props.style,
1526
- javaScriptEnabled: props.javaScriptEnabled,
1527
1610
  onLoad: (event) => emit("load", event),
1528
1611
  onError: (event) => emit("error", event),
1529
1612
  onMessage: (event) => emit("message", event)
@@ -1617,12 +1700,14 @@ var VActionSheet = (0, import_runtime_core21.defineComponent)({
1617
1700
  emits: ["action", "cancel"],
1618
1701
  setup(props, { emit }) {
1619
1702
  return () => (0, import_runtime_core21.h)("VActionSheet", {
1620
- visible: props.visible,
1621
1703
  title: props.title,
1622
1704
  message: props.message,
1623
1705
  actions: props.actions,
1624
1706
  onAction: (e) => emit("action", e),
1625
- onCancel: () => emit("cancel")
1707
+ onCancel: () => emit("cancel"),
1708
+ // Presentation is triggered by the visible update on native. Apply it
1709
+ // only after content and event handlers have reached the bridge.
1710
+ visible: props.visible
1626
1711
  });
1627
1712
  }
1628
1713
  });
@@ -1957,12 +2042,14 @@ var VVideo = (0, import_runtime_core28.defineComponent)({
1957
2042
  loop: { type: Boolean, default: false },
1958
2043
  muted: { type: Boolean, default: false },
1959
2044
  paused: { type: Boolean, default: false },
2045
+ /** Reserved for native transport controls; currently has no effect. */
1960
2046
  controls: { type: Boolean, default: true },
1961
2047
  volume: { type: Number, default: 1 },
1962
2048
  resizeMode: {
1963
2049
  type: String,
1964
2050
  default: "cover"
1965
2051
  },
2052
+ /** Reserved for native poster rendering; currently has no effect. */
1966
2053
  poster: String,
1967
2054
  style: Object,
1968
2055
  testID: String,
@@ -1971,7 +2058,21 @@ var VVideo = (0, import_runtime_core28.defineComponent)({
1971
2058
  emits: ["ready", "play", "pause", "end", "error", "progress"],
1972
2059
  setup(props, { emit }) {
1973
2060
  return () => (0, import_runtime_core28.h)("VVideo", {
1974
- ...props,
2061
+ // Playback intent must reach native before a source can become ready.
2062
+ // In particular, the default paused=false must not override
2063
+ // autoplay=false by eagerly starting a newly-created player.
2064
+ autoplay: props.autoplay,
2065
+ paused: props.paused,
2066
+ source: props.source,
2067
+ loop: props.loop,
2068
+ volume: props.volume,
2069
+ muted: props.muted,
2070
+ controls: props.controls,
2071
+ resizeMode: props.resizeMode,
2072
+ poster: props.poster,
2073
+ style: props.style,
2074
+ testID: props.testID,
2075
+ accessibilityLabel: props.accessibilityLabel,
1975
2076
  onReady: (event) => emit("ready", event),
1976
2077
  onPlay: () => emit("play"),
1977
2078
  onPause: () => emit("pause"),
@@ -2178,23 +2279,35 @@ var VTabBar = (0, import_runtime_core30.defineComponent)({
2178
2279
  /** Currently active tab ID */
2179
2280
  activeTab: {
2180
2281
  type: String,
2181
- required: true
2282
+ default: void 0
2283
+ },
2284
+ /** Currently active tab name/id for v-model */
2285
+ modelValue: {
2286
+ type: String,
2287
+ default: void 0
2182
2288
  },
2183
2289
  /** Position: 'top' | 'bottom' */
2184
2290
  position: {
2185
2291
  type: String,
2186
2292
  default: "bottom"
2187
- }
2293
+ },
2294
+ activeColor: { type: String, default: "#007AFF" },
2295
+ inactiveColor: { type: String, default: "#8E8E93" },
2296
+ backgroundColor: { type: String, default: "#fff" }
2188
2297
  },
2189
- emits: ["change"],
2298
+ emits: ["change", "update:modelValue"],
2190
2299
  setup(props, { emit }) {
2191
- const activeTab = (0, import_runtime_core30.ref)(props.activeTab);
2300
+ const activeTab = (0, import_runtime_core30.ref)(props.modelValue ?? props.activeTab ?? "");
2192
2301
  (0, import_runtime_core30.watch)(() => props.activeTab, (newVal) => {
2193
- activeTab.value = newVal;
2302
+ if (newVal !== void 0) activeTab.value = newVal;
2303
+ });
2304
+ (0, import_runtime_core30.watch)(() => props.modelValue, (newVal) => {
2305
+ if (newVal !== void 0) activeTab.value = newVal;
2194
2306
  });
2195
2307
  const switchTab = (tabId) => {
2196
2308
  activeTab.value = tabId;
2197
2309
  emit("change", tabId);
2310
+ emit("update:modelValue", tabId);
2198
2311
  };
2199
2312
  return () => (0, import_runtime_core30.h)(VView, {
2200
2313
  style: {
@@ -2202,35 +2315,36 @@ var VTabBar = (0, import_runtime_core30.defineComponent)({
2202
2315
  [props.position]: 0,
2203
2316
  left: 0,
2204
2317
  right: 0,
2205
- backgroundColor: "#fff",
2206
- borderTopWidth: 1,
2207
- borderTopColor: "#e0e0e0",
2318
+ backgroundColor: props.backgroundColor,
2319
+ ...props.position === "top" ? { borderBottomWidth: 1, borderBottomColor: "#e0e0e0" } : { borderTopWidth: 1, borderTopColor: "#e0e0e0" },
2208
2320
  flexDirection: "row",
2209
2321
  height: 60
2210
2322
  }
2211
2323
  }, () => props.tabs.map((tab) => {
2212
- const isActive = activeTab.value === tab.id;
2324
+ const tabId = tab.name ?? tab.id;
2325
+ if (tabId === void 0 || tabId === "") return null;
2326
+ const isActive = activeTab.value === tabId;
2213
2327
  return (0, import_runtime_core30.h)(VPressable, {
2214
- key: tab.id,
2328
+ key: tabId,
2215
2329
  style: {
2216
2330
  flex: 1,
2217
2331
  justifyContent: "center",
2218
2332
  alignItems: "center"
2219
2333
  },
2220
- onPress: () => switchTab(tab.id),
2334
+ onPress: () => switchTab(tabId),
2221
2335
  accessibilityLabel: tab.label,
2222
2336
  accessibilityRole: "tab",
2223
2337
  accessibilityState: { selected: isActive }
2224
2338
  }, () => [
2225
- tab.icon ? (0, import_runtime_core30.h)(VText, { style: { fontSize: 24, marginBottom: 4 } }, () => tab.icon) : null,
2339
+ tab.icon ? (0, import_runtime_core30.h)(VText, { style: { fontSize: 24, marginBottom: 4, color: isActive ? props.activeColor : props.inactiveColor } }, () => tab.icon) : null,
2226
2340
  (0, import_runtime_core30.h)(VText, {
2227
2341
  style: {
2228
2342
  fontSize: 12,
2229
2343
  fontWeight: isActive ? "600" : "400",
2230
- color: isActive ? "#007AFF" : "#8E8E93"
2344
+ color: isActive ? props.activeColor : props.inactiveColor
2231
2345
  }
2232
2346
  }, () => tab.label),
2233
- tab.badge ? (0, import_runtime_core30.h)(VView, {
2347
+ tab.badge !== void 0 && tab.badge !== null && tab.badge !== "" ? (0, import_runtime_core30.h)(VView, {
2234
2348
  style: {
2235
2349
  position: "absolute",
2236
2350
  top: 8,
@@ -2255,10 +2369,82 @@ var VTabBar = (0, import_runtime_core30.defineComponent)({
2255
2369
  }
2256
2370
  });
2257
2371
 
2258
- // src/components/VDrawer.ts
2372
+ // src/components/VToolbar.ts
2259
2373
  var import_runtime_core31 = require("@vue/runtime-core");
2374
+ var VToolbar = (0, import_runtime_core31.defineComponent)({
2375
+ name: "VToolbar",
2376
+ props: {
2377
+ items: { type: Array, required: true },
2378
+ displayMode: {
2379
+ type: String,
2380
+ default: "iconAndLabel"
2381
+ },
2382
+ showsBaselineSeparator: { type: Boolean, default: true },
2383
+ style: Object
2384
+ },
2385
+ emits: ["itemClick"],
2386
+ setup(props, { emit }) {
2387
+ return () => (0, import_runtime_core31.h)("VToolbar", {
2388
+ ...props,
2389
+ onItemClick: (e) => emit("itemClick", e)
2390
+ });
2391
+ }
2392
+ });
2393
+
2394
+ // src/components/VSplitView.ts
2395
+ var import_runtime_core32 = require("@vue/runtime-core");
2396
+ var VSplitView = (0, import_runtime_core32.defineComponent)({
2397
+ name: "VSplitView",
2398
+ props: {
2399
+ direction: {
2400
+ type: String,
2401
+ default: "horizontal"
2402
+ },
2403
+ dividerStyle: {
2404
+ type: String,
2405
+ default: "thin"
2406
+ },
2407
+ dividerColor: String,
2408
+ dividerPosition: Number,
2409
+ style: Object
2410
+ },
2411
+ emits: ["resize"],
2412
+ setup(props, { emit, slots }) {
2413
+ return () => (0, import_runtime_core32.h)("VSplitView", {
2414
+ ...props,
2415
+ onResize: (e) => emit("resize", e)
2416
+ }, slots.default?.());
2417
+ }
2418
+ });
2419
+
2420
+ // src/components/VOutlineView.ts
2421
+ var import_runtime_core33 = require("@vue/runtime-core");
2422
+ var VOutlineView = (0, import_runtime_core33.defineComponent)({
2423
+ name: "VOutlineView",
2424
+ props: {
2425
+ data: { type: Array, required: true },
2426
+ expandAll: { type: Boolean, default: false },
2427
+ selectionMode: {
2428
+ type: String,
2429
+ default: "single"
2430
+ },
2431
+ style: Object
2432
+ },
2433
+ emits: ["select", "expand", "collapse"],
2434
+ setup(props, { emit }) {
2435
+ return () => (0, import_runtime_core33.h)("VOutlineView", {
2436
+ ...props,
2437
+ onSelect: (e) => emit("select", e),
2438
+ onExpand: (e) => emit("expand", e),
2439
+ onCollapse: (e) => emit("collapse", e)
2440
+ });
2441
+ }
2442
+ });
2443
+
2444
+ // src/components/VDrawer.ts
2445
+ var import_runtime_core34 = require("@vue/runtime-core");
2260
2446
  var drawerContextKey = /* @__PURE__ */ Symbol("VDrawerContext");
2261
- var VDrawer = (0, import_runtime_core31.defineComponent)({
2447
+ var VDrawer = (0, import_runtime_core34.defineComponent)({
2262
2448
  name: "VDrawer",
2263
2449
  props: {
2264
2450
  /** Whether the drawer is open */
@@ -2276,24 +2462,37 @@ var VDrawer = (0, import_runtime_core31.defineComponent)({
2276
2462
  type: Number,
2277
2463
  default: 280
2278
2464
  },
2465
+ /** Backdrop color shown behind the drawer */
2466
+ overlayColor: {
2467
+ type: String,
2468
+ default: "rgba(0, 0, 0, 0.5)"
2469
+ },
2279
2470
  /** Close on item press */
2280
2471
  closeOnPress: {
2281
2472
  type: Boolean,
2282
2473
  default: true
2474
+ },
2475
+ /** Close when the backdrop is pressed */
2476
+ closeOnPressOutside: {
2477
+ type: Boolean,
2478
+ default: true
2283
2479
  }
2284
2480
  },
2285
- emits: ["update:open", "close"],
2481
+ emits: ["update:open", "open", "close"],
2286
2482
  setup(props, { attrs, slots, emit }) {
2287
- const isOpen = (0, import_runtime_core31.ref)(props.open);
2288
- (0, import_runtime_core31.watch)(() => props.open, (value) => {
2483
+ const isOpen = (0, import_runtime_core34.ref)(props.open);
2484
+ (0, import_runtime_core34.watch)(() => props.open, (value) => {
2485
+ if (isOpen.value === value) return;
2289
2486
  isOpen.value = value;
2487
+ emit(value ? "open" : "close");
2290
2488
  });
2291
2489
  const closeDrawer = () => {
2490
+ if (!isOpen.value) return;
2292
2491
  isOpen.value = false;
2293
2492
  emit("update:open", false);
2294
2493
  emit("close");
2295
2494
  };
2296
- (0, import_runtime_core31.provide)(drawerContextKey, {
2495
+ (0, import_runtime_core34.provide)(drawerContextKey, {
2297
2496
  close: closeDrawer,
2298
2497
  shouldCloseOnPress: () => props.closeOnPress
2299
2498
  });
@@ -2307,7 +2506,7 @@ var VDrawer = (0, import_runtime_core31.defineComponent)({
2307
2506
  left: 0,
2308
2507
  right: 0,
2309
2508
  bottom: 0,
2310
- backgroundColor: "rgba(0, 0, 0, 0.5)",
2509
+ backgroundColor: props.overlayColor,
2311
2510
  opacity: isOpen.value ? 1 : 0,
2312
2511
  zIndex: 999
2313
2512
  };
@@ -2337,20 +2536,24 @@ var VDrawer = (0, import_runtime_core31.defineComponent)({
2337
2536
  if (slots.default) {
2338
2537
  drawerChildren.push(...slots.default({ close: closeDrawer }) ?? []);
2339
2538
  }
2539
+ if (slots.footer) {
2540
+ drawerChildren.push(...slots.footer());
2541
+ }
2340
2542
  return [
2341
2543
  // Overlay
2342
- isOpen.value ? (0, import_runtime_core31.h)(VPressable, {
2544
+ isOpen.value ? (0, import_runtime_core34.h)(VPressable, {
2343
2545
  style: overlayStyle,
2344
- onPress: closeDrawer,
2345
- accessibilityLabel: "Close menu"
2546
+ onPress: props.closeOnPressOutside ? closeDrawer : void 0,
2547
+ accessibilityLabel: "Close menu",
2548
+ accessibilityState: { disabled: !props.closeOnPressOutside }
2346
2549
  }) : null,
2347
2550
  // Drawer
2348
- (0, import_runtime_core31.h)(VView, drawerProps, () => drawerChildren)
2551
+ (0, import_runtime_core34.h)(VView, drawerProps, () => drawerChildren)
2349
2552
  ];
2350
2553
  };
2351
2554
  }
2352
2555
  });
2353
- var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2556
+ var VDrawerItem = (0, import_runtime_core34.defineComponent)({
2354
2557
  name: "VDrawerItem",
2355
2558
  props: {
2356
2559
  /** Icon (emoji or icon name) */
@@ -2363,6 +2566,11 @@ var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2363
2566
  type: String,
2364
2567
  required: true
2365
2568
  },
2569
+ /** Whether this item represents the current destination */
2570
+ active: {
2571
+ type: Boolean,
2572
+ default: false
2573
+ },
2366
2574
  /** Badge count */
2367
2575
  badge: {
2368
2576
  type: [Number, String],
@@ -2376,7 +2584,7 @@ var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2376
2584
  },
2377
2585
  emits: ["press"],
2378
2586
  setup(props, { slots, emit }) {
2379
- const drawer = (0, import_runtime_core31.inject)(drawerContextKey, null);
2587
+ const drawer = (0, import_runtime_core34.inject)(drawerContextKey, null);
2380
2588
  const handlePress = () => {
2381
2589
  if (props.disabled) return;
2382
2590
  emit("press");
@@ -2384,25 +2592,26 @@ var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2384
2592
  drawer.close();
2385
2593
  }
2386
2594
  };
2387
- return () => (0, import_runtime_core31.h)(VPressable, {
2595
+ return () => (0, import_runtime_core34.h)(VPressable, {
2388
2596
  style: {
2389
2597
  flexDirection: "row",
2390
2598
  alignItems: "center",
2391
2599
  padding: 16,
2392
2600
  borderBottomWidth: 1,
2393
2601
  borderBottomColor: "#f0f0f0",
2602
+ backgroundColor: props.active ? "#EAF3FF" : "transparent",
2394
2603
  opacity: props.disabled ? 0.5 : 1
2395
2604
  },
2396
2605
  onPress: handlePress,
2397
2606
  disabled: props.disabled,
2398
2607
  accessibilityLabel: props.label,
2399
2608
  accessibilityRole: "menuitem",
2400
- accessibilityState: { disabled: props.disabled }
2609
+ accessibilityState: { disabled: props.disabled, selected: props.active }
2401
2610
  }, () => {
2402
2611
  const children = [];
2403
2612
  if (props.icon) {
2404
2613
  children.push(
2405
- (0, import_runtime_core31.h)(VText, {
2614
+ (0, import_runtime_core34.h)(VText, {
2406
2615
  style: {
2407
2616
  fontSize: 24,
2408
2617
  marginRight: 16,
@@ -2413,17 +2622,17 @@ var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2413
2622
  );
2414
2623
  }
2415
2624
  children.push(
2416
- (0, import_runtime_core31.h)(VText, {
2625
+ (0, import_runtime_core34.h)(VText, {
2417
2626
  style: {
2418
2627
  flex: 1,
2419
2628
  fontSize: 16,
2420
- color: props.disabled ? "#999" : "#333"
2629
+ color: props.disabled ? "#999" : props.active ? "#007AFF" : "#333"
2421
2630
  }
2422
2631
  }, () => props.label)
2423
2632
  );
2424
- if (props.badge) {
2633
+ if (props.badge !== null && props.badge !== void 0 && props.badge !== "") {
2425
2634
  children.push(
2426
- (0, import_runtime_core31.h)(VView, {
2635
+ (0, import_runtime_core34.h)(VView, {
2427
2636
  style: {
2428
2637
  backgroundColor: "#007AFF",
2429
2638
  borderRadius: 12,
@@ -2433,7 +2642,7 @@ var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2433
2642
  alignItems: "center",
2434
2643
  paddingHorizontal: 8
2435
2644
  }
2436
- }, () => (0, import_runtime_core31.h)(VText, {
2645
+ }, () => (0, import_runtime_core34.h)(VText, {
2437
2646
  style: {
2438
2647
  color: "#fff",
2439
2648
  fontSize: 12,
@@ -2449,7 +2658,7 @@ var VDrawerItem = (0, import_runtime_core31.defineComponent)({
2449
2658
  });
2450
2659
  }
2451
2660
  });
2452
- var VDrawerSection = (0, import_runtime_core31.defineComponent)({
2661
+ var VDrawerSection = (0, import_runtime_core34.defineComponent)({
2453
2662
  name: "VDrawerSection",
2454
2663
  props: {
2455
2664
  /** Section title */
@@ -2459,7 +2668,7 @@ var VDrawerSection = (0, import_runtime_core31.defineComponent)({
2459
2668
  }
2460
2669
  },
2461
2670
  setup(props, { slots }) {
2462
- return () => (0, import_runtime_core31.h)(VView, {
2671
+ return () => (0, import_runtime_core34.h)(VView, {
2463
2672
  style: {
2464
2673
  paddingVertical: 8,
2465
2674
  paddingHorizontal: 16,
@@ -2471,7 +2680,7 @@ var VDrawerSection = (0, import_runtime_core31.defineComponent)({
2471
2680
  const children = [];
2472
2681
  if (props.title) {
2473
2682
  children.push(
2474
- (0, import_runtime_core31.h)(VText, {
2683
+ (0, import_runtime_core34.h)(VText, {
2475
2684
  style: {
2476
2685
  fontSize: 13,
2477
2686
  fontWeight: "600",
@@ -2493,7 +2702,7 @@ VDrawer.Item = VDrawerItem;
2493
2702
  VDrawer.Section = VDrawerSection;
2494
2703
 
2495
2704
  // src/components/VTransition.ts
2496
- var import_runtime_core32 = require("@vue/runtime-core");
2705
+ var import_runtime_core35 = require("@vue/runtime-core");
2497
2706
 
2498
2707
  // src/composables/useAnimation.ts
2499
2708
  var Easing = {
@@ -2573,9 +2782,23 @@ function resolveAnimationTarget(el) {
2573
2782
  if (typeof el === "object" && "id" in el) return el.id;
2574
2783
  return void 0;
2575
2784
  }
2576
- var VTransition = (0, import_runtime_core32.defineComponent)({
2785
+ function getElementFromVNode(vnode) {
2786
+ try {
2787
+ const el = vnode.el;
2788
+ if (!el) return void 0;
2789
+ return resolveAnimationTarget(el);
2790
+ } catch {
2791
+ return void 0;
2792
+ }
2793
+ }
2794
+ function resolveTransitionTarget(el) {
2795
+ return resolveAnimationTarget(el) ?? getElementFromVNode(el);
2796
+ }
2797
+ var VTransition = (0, import_runtime_core35.defineComponent)({
2577
2798
  name: "VTransition",
2799
+ inheritAttrs: false,
2578
2800
  props: {
2801
+ show: { type: Boolean, default: true },
2579
2802
  name: { type: String, default: "" },
2580
2803
  appear: { type: Boolean, default: false },
2581
2804
  persist: { type: Boolean, default: false },
@@ -2593,31 +2816,56 @@ var VTransition = (0, import_runtime_core32.defineComponent)({
2593
2816
  appearClass: { type: String, default: "" },
2594
2817
  appearActiveClass: { type: String, default: "" },
2595
2818
  appearToClass: { type: String, default: "" },
2596
- duration: [Number, Object]
2819
+ duration: [Number, Object],
2820
+ enterFrom: Object,
2821
+ enterTo: Object,
2822
+ leaveFrom: Object,
2823
+ leaveTo: Object,
2824
+ easing: { type: String, default: "ease" }
2597
2825
  },
2598
- setup(transitionProps, { slots, expose }) {
2826
+ setup(transitionProps, { slots, expose, attrs }) {
2599
2827
  const { timing } = useAnimation();
2600
- const isAppearing = (0, import_runtime_core32.ref)(false);
2601
- const isLeaving = (0, import_runtime_core32.ref)(false);
2602
- const hasEntered = (0, import_runtime_core32.ref)(!transitionProps.appear);
2603
- function getElementFromVNode(vnode) {
2604
- try {
2605
- const el = vnode.el;
2606
- if (!el) return void 0;
2607
- return resolveAnimationTarget(el);
2608
- } catch {
2609
- return void 0;
2828
+ const isAppearing = (0, import_runtime_core35.ref)(false);
2829
+ const isLeaving = (0, import_runtime_core35.ref)(false);
2830
+ const hasEntered = (0, import_runtime_core35.ref)(!transitionProps.appear);
2831
+ function presetStyles() {
2832
+ if (transitionProps.name === "slide") {
2833
+ return {
2834
+ enterFrom: { opacity: 0, translateX: -30 },
2835
+ enterTo: { opacity: 1, translateX: 0 },
2836
+ leaveFrom: { opacity: 1, translateX: 0 },
2837
+ leaveTo: { opacity: 0, translateX: -30 }
2838
+ };
2839
+ }
2840
+ return {
2841
+ enterFrom: { opacity: 0 },
2842
+ enterTo: { opacity: 1 },
2843
+ leaveFrom: { opacity: 1 },
2844
+ leaveTo: { opacity: 0 }
2845
+ };
2846
+ }
2847
+ function callListener(name, ...args) {
2848
+ const listener = attrs[name];
2849
+ if (Array.isArray(listener)) {
2850
+ listener.forEach((fn) => typeof fn === "function" && fn(...args));
2851
+ } else if (typeof listener === "function") {
2852
+ listener(...args);
2610
2853
  }
2611
2854
  }
2612
2855
  async function doEnter(el) {
2613
- const viewId = getElementFromVNode(el);
2614
- if (!viewId) return;
2856
+ const viewId = resolveTransitionTarget(el);
2857
+ if (viewId == null) return;
2615
2858
  isAppearing.value = true;
2616
2859
  const enterDuration = typeof transitionProps.duration === "object" ? transitionProps.duration.enter ?? DefaultDuration : transitionProps.duration ?? DefaultDuration;
2617
- const enterStyles = { opacity: 1 };
2860
+ const presets = presetStyles();
2861
+ const enterFrom = transitionProps.enterFrom ?? presets.enterFrom;
2862
+ const enterTo = transitionProps.enterTo ?? presets.enterTo;
2618
2863
  try {
2619
- await timing(viewId, { opacity: 0 }, { duration: 0 });
2620
- await timing(viewId, enterStyles, { duration: enterDuration, easing: "easeOut" });
2864
+ await timing(viewId, enterFrom, { duration: 0 });
2865
+ await timing(viewId, enterTo, {
2866
+ duration: enterDuration,
2867
+ easing: transitionProps.easing
2868
+ });
2621
2869
  isAppearing.value = false;
2622
2870
  hasEntered.value = true;
2623
2871
  } catch (e) {
@@ -2627,47 +2875,60 @@ var VTransition = (0, import_runtime_core32.defineComponent)({
2627
2875
  }
2628
2876
  }
2629
2877
  async function doLeave(el) {
2630
- const viewId = getElementFromVNode(el);
2631
- if (!viewId) return;
2878
+ const viewId = resolveTransitionTarget(el);
2879
+ if (viewId == null) return;
2632
2880
  isLeaving.value = true;
2633
2881
  const leaveDuration = typeof transitionProps.duration === "object" ? transitionProps.duration.leave ?? DefaultDuration : transitionProps.duration ?? DefaultDuration;
2634
2882
  try {
2635
- await timing(viewId, { opacity: 0 }, { duration: leaveDuration, easing: "easeIn" });
2883
+ const presets = presetStyles();
2884
+ await timing(viewId, transitionProps.leaveFrom ?? presets.leaveFrom, { duration: 0 });
2885
+ await timing(viewId, transitionProps.leaveTo ?? presets.leaveTo, {
2886
+ duration: leaveDuration,
2887
+ easing: transitionProps.easing
2888
+ });
2636
2889
  } catch (e) {
2637
2890
  console.warn("[VueNative Transition] leave animation failed:", e);
2638
2891
  } finally {
2639
2892
  isLeaving.value = false;
2640
2893
  }
2641
2894
  }
2642
- function onEnter(_el, done) {
2643
- if (!hasEntered.value || transitionProps.appear) {
2644
- doEnter(_el).then(() => done());
2645
- } else {
2646
- done();
2647
- }
2895
+ function onBeforeEnter(el) {
2896
+ callListener("onBeforeEnter", el);
2897
+ }
2898
+ function onEnter(el, done) {
2899
+ callListener("onEnter", el);
2900
+ doEnter(el).then(() => done());
2648
2901
  }
2649
2902
  function onLeave(el, done) {
2903
+ callListener("onLeave", el);
2650
2904
  doLeave(el).then(() => done());
2651
2905
  }
2652
2906
  function onAfterEnter() {
2907
+ callListener("onAfterEnter");
2653
2908
  }
2654
2909
  function onAfterLeave() {
2910
+ callListener("onAfterLeave");
2655
2911
  }
2656
2912
  function onEnterCancelled() {
2657
2913
  isAppearing.value = false;
2914
+ callListener("onEnterCancelled");
2658
2915
  }
2659
2916
  function onLeaveCancelled() {
2660
2917
  isLeaving.value = false;
2918
+ callListener("onLeaveCancelled");
2661
2919
  }
2662
2920
  function onAppear(el, done) {
2663
2921
  isAppearing.value = true;
2922
+ callListener("onAppear", el);
2664
2923
  doEnter(el).then(() => done());
2665
2924
  }
2666
2925
  function onAfterAppear() {
2667
2926
  isAppearing.value = false;
2927
+ callListener("onAfterAppear");
2668
2928
  }
2669
2929
  expose({
2670
2930
  onEnter,
2931
+ onBeforeEnter,
2671
2932
  onLeave,
2672
2933
  onAfterEnter,
2673
2934
  onAfterLeave,
@@ -2680,30 +2941,15 @@ var VTransition = (0, import_runtime_core32.defineComponent)({
2680
2941
  hasEntered
2681
2942
  });
2682
2943
  return () => {
2683
- const children = slots.default?.() ?? [];
2684
- const hasDefault = children.length > 0;
2685
- if (!hasDefault) {
2686
- return (0, import_runtime_core32.h)("", {}, []);
2687
- }
2688
- let finalChildren = children;
2689
- if (transitionProps.mode === "out-in") {
2690
- if (isLeaving.value) {
2691
- finalChildren = [children[children.length - 1]];
2692
- } else if (!hasEntered.value) {
2693
- finalChildren = [];
2694
- }
2695
- }
2696
- if (transitionProps.mode === "in-out") {
2697
- if (isAppearing.value && children.length > 1) {
2698
- finalChildren = [children[0]];
2699
- }
2700
- }
2701
- return (0, import_runtime_core32.h)("Transition", {
2944
+ const finalChildren = transitionProps.show ? slots.default?.() ?? [] : [];
2945
+ return (0, import_runtime_core35.h)(import_runtime_core35.BaseTransition, {
2702
2946
  name: transitionProps.name || "v",
2703
2947
  appear: transitionProps.appear,
2704
2948
  persist: transitionProps.persist || transitionProps.name === "persist",
2949
+ mode: transitionProps.mode === "default" ? void 0 : transitionProps.mode,
2705
2950
  css: transitionProps.css,
2706
2951
  type: transitionProps.type,
2952
+ onBeforeEnter,
2707
2953
  onEnter,
2708
2954
  onLeave,
2709
2955
  onAfterEnter,
@@ -2716,7 +2962,7 @@ var VTransition = (0, import_runtime_core32.defineComponent)({
2716
2962
  };
2717
2963
  }
2718
2964
  });
2719
- var VTransitionGroup = (0, import_runtime_core32.defineComponent)({
2965
+ var VTransitionGroup = (0, import_runtime_core35.defineComponent)({
2720
2966
  name: "VTransitionGroup",
2721
2967
  props: {
2722
2968
  tag: { type: String, default: void 0 },
@@ -2730,172 +2976,58 @@ var VTransitionGroup = (0, import_runtime_core32.defineComponent)({
2730
2976
  const { timing } = useAnimation();
2731
2977
  function onMove(_el) {
2732
2978
  }
2979
+ function onBeforeEnter(el) {
2980
+ const viewId = resolveTransitionTarget(el);
2981
+ if (viewId == null) return;
2982
+ timing(viewId, { opacity: 0 }, { duration: 0 }).catch(() => {
2983
+ });
2984
+ }
2733
2985
  function onEnter(el, done) {
2734
- const viewId = resolveAnimationTarget(el);
2735
- if (!viewId) {
2986
+ const viewId = resolveTransitionTarget(el);
2987
+ if (viewId == null) {
2736
2988
  done();
2737
2989
  return;
2738
2990
  }
2739
2991
  timing(viewId, { opacity: 1 }, { duration: groupProps.duration ?? 300, easing: "easeOut" }).then(() => done()).catch(() => done());
2740
2992
  }
2741
2993
  function onLeave(el, done) {
2742
- const viewId = resolveAnimationTarget(el);
2743
- if (!viewId) {
2994
+ const viewId = resolveTransitionTarget(el);
2995
+ if (viewId == null) {
2744
2996
  done();
2745
2997
  return;
2746
2998
  }
2747
2999
  timing(viewId, { opacity: 0 }, { duration: groupProps.duration ?? 300, easing: "easeIn" }).then(() => done()).catch(() => done());
2748
3000
  }
2749
3001
  expose({
3002
+ onBeforeEnter,
2750
3003
  onEnter,
2751
3004
  onLeave,
2752
3005
  onMove
2753
3006
  });
2754
3007
  return () => {
2755
3008
  const children = slots.default?.() ?? [];
2756
- return (0, import_runtime_core32.h)("TransitionGroup", {
2757
- tag: groupProps.tag,
3009
+ return (0, import_runtime_core35.h)(groupProps.tag || "VView", {}, children.map((child, index) => (0, import_runtime_core35.h)(import_runtime_core35.BaseTransition, {
3010
+ key: child.key ?? index,
2758
3011
  name: groupProps.name,
2759
3012
  appear: groupProps.appear,
2760
3013
  persist: groupProps.persist,
2761
- moveClass: groupProps.moveClass,
3014
+ css: false,
3015
+ onBeforeEnter,
2762
3016
  onEnter,
2763
- onLeave,
2764
- onMove
2765
- }, () => children);
3017
+ onLeave
3018
+ }, () => [child])));
2766
3019
  };
2767
3020
  }
2768
3021
  });
2769
3022
 
2770
3023
  // src/components/KeepAlive.ts
2771
- var import_runtime_core33 = require("@vue/runtime-core");
2772
- function matches(pattern, name) {
2773
- if (!pattern) return false;
2774
- if (typeof pattern === "string") {
2775
- return pattern === name;
2776
- }
2777
- if (pattern instanceof RegExp) {
2778
- return pattern.test(name);
2779
- }
2780
- if (Array.isArray(pattern)) {
2781
- return pattern.includes(name);
2782
- }
2783
- return false;
2784
- }
2785
- function getComponentName(vnode) {
2786
- const component = vnode.type;
2787
- if (typeof component === "object" && component !== null && "name" in component) {
2788
- return component.name;
2789
- }
2790
- if (typeof component === "function") {
2791
- return component.name;
2792
- }
2793
- return void 0;
2794
- }
2795
- var KeepAlive = (0, import_runtime_core33.defineComponent)({
2796
- name: "KeepAlive",
2797
- props: {
2798
- include: [String, RegExp, Array],
2799
- exclude: [String, RegExp, Array],
2800
- max: [Number, String]
2801
- },
2802
- setup(props, { slots }) {
2803
- const cache = /* @__PURE__ */ new Map();
2804
- const keys = /* @__PURE__ */ new Set();
2805
- const maxCacheSize = typeof props.max === "string" ? parseInt(props.max, 10) : props.max;
2806
- function pruneCache(filter) {
2807
- cache.forEach((_, key) => {
2808
- if (!filter(key)) {
2809
- cache.delete(key);
2810
- keys.delete(key);
2811
- }
2812
- });
2813
- }
2814
- function pruneCacheEntry(key) {
2815
- cache.delete(key);
2816
- keys.delete(key);
2817
- }
2818
- (0, import_runtime_core33.watch)(
2819
- () => [props.include, props.exclude],
2820
- () => {
2821
- if (!props.include && !props.exclude) return;
2822
- pruneCache((name) => {
2823
- if (props.include && !matches(props.include, name)) return false;
2824
- if (props.exclude && matches(props.exclude, name)) return false;
2825
- return true;
2826
- });
2827
- }
2828
- );
2829
- (0, import_runtime_core33.onUnmounted)(() => {
2830
- cache.clear();
2831
- keys.clear();
2832
- });
2833
- return () => {
2834
- const children = slots.default?.() ?? [];
2835
- if (!children.length) return children;
2836
- const vnode = children[0];
2837
- const name = getComponentName(vnode) ?? String(vnode.type);
2838
- const key = name;
2839
- if (props.include && !matches(props.include, name)) {
2840
- return vnode;
2841
- }
2842
- if (props.exclude && matches(props.exclude, name)) {
2843
- return vnode;
2844
- }
2845
- const cached = cache.get(key);
2846
- if (cached) {
2847
- keys.delete(key);
2848
- keys.add(key);
2849
- return cached.vnode;
2850
- }
2851
- if (maxCacheSize && cache.size >= maxCacheSize) {
2852
- const firstKey = keys.values().next().value;
2853
- if (firstKey) {
2854
- pruneCacheEntry(firstKey);
2855
- }
2856
- }
2857
- cache.set(key, { vnode, key });
2858
- keys.add(key);
2859
- return vnode;
2860
- };
2861
- }
2862
- });
2863
- KeepAlive.isKeepAlive = true;
3024
+ var import_runtime_core36 = require("@vue/runtime-core");
3025
+ var KeepAlive = import_runtime_core36.KeepAlive;
2864
3026
 
2865
3027
  // src/components/VSuspense.ts
2866
- var import_runtime_core34 = require("@vue/runtime-core");
2867
- var suspenseContextKey = /* @__PURE__ */ Symbol("suspense");
2868
- var VSuspense = (0, import_runtime_core34.defineComponent)({
2869
- name: "Suspense",
2870
- props: {
2871
- timeout: { type: Number, default: 3e4 }
2872
- },
2873
- setup(_props, { slots }) {
2874
- const hasError = (0, import_runtime_core34.ref)(false);
2875
- const error = (0, import_runtime_core34.shallowRef)(null);
2876
- const pendingCount = (0, import_runtime_core34.ref)(0);
2877
- const context = {
2878
- hasError,
2879
- error,
2880
- pendingCount,
2881
- resolve: () => {
2882
- pendingCount.value--;
2883
- },
2884
- reject: (err) => {
2885
- hasError.value = true;
2886
- error.value = err;
2887
- }
2888
- };
2889
- (0, import_runtime_core34.provide)(suspenseContextKey, context);
2890
- return () => {
2891
- if (hasError.value) {
2892
- return slots.fallback?.({ error: error.value }) ?? null;
2893
- }
2894
- const defaultSlots = slots.default?.() ?? [];
2895
- return defaultSlots;
2896
- };
2897
- }
2898
- });
3028
+ var import_runtime_core37 = require("@vue/runtime-core");
3029
+ var VSuspense = import_runtime_core37.Suspense;
3030
+ var defineAsyncComponent = import_runtime_core37.defineAsyncComponent;
2899
3031
 
2900
3032
  // src/components/index.ts
2901
3033
  var builtInComponents = {
@@ -2928,18 +3060,24 @@ var builtInComponents = {
2928
3060
  VVideo,
2929
3061
  VFlatList,
2930
3062
  VTabBar,
3063
+ VToolbar,
3064
+ VSplitView,
3065
+ VOutlineView,
2931
3066
  VDrawer,
2932
3067
  VDrawerItem,
2933
3068
  VDrawerSection,
2934
3069
  VTransition,
2935
3070
  VTransitionGroup,
2936
3071
  KeepAlive,
3072
+ // Vue types Suspense separately from ordinary components even though it is
3073
+ // valid in the global component registry and handled specially by the
3074
+ // renderer at runtime.
2937
3075
  VSuspense
2938
3076
  };
2939
3077
 
2940
3078
  // src/errorBoundary.ts
2941
- var import_runtime_core35 = require("@vue/runtime-core");
2942
- var ErrorBoundary = (0, import_runtime_core35.defineComponent)({
3079
+ var import_runtime_core38 = require("@vue/runtime-core");
3080
+ var ErrorBoundary = (0, import_runtime_core38.defineComponent)({
2943
3081
  name: "ErrorBoundary",
2944
3082
  props: {
2945
3083
  onError: Function,
@@ -2949,9 +3087,9 @@ var ErrorBoundary = (0, import_runtime_core35.defineComponent)({
2949
3087
  }
2950
3088
  },
2951
3089
  setup(props, { slots }) {
2952
- const error = (0, import_runtime_core35.ref)(null);
2953
- const errorInfo = (0, import_runtime_core35.ref)("");
2954
- (0, import_runtime_core35.onErrorCaptured)((err, _instance, info) => {
3090
+ const error = (0, import_runtime_core38.ref)(null);
3091
+ const errorInfo = (0, import_runtime_core38.ref)("");
3092
+ (0, import_runtime_core38.onErrorCaptured)((err, _instance, info) => {
2955
3093
  const normalizedError = err instanceof Error ? err : new Error(String(err));
2956
3094
  error.value = normalizedError;
2957
3095
  errorInfo.value = info;
@@ -2964,7 +3102,7 @@ var ErrorBoundary = (0, import_runtime_core35.defineComponent)({
2964
3102
  error.value = null;
2965
3103
  errorInfo.value = "";
2966
3104
  }
2967
- (0, import_runtime_core35.watch)(
3105
+ (0, import_runtime_core38.watch)(
2968
3106
  () => props.resetKeys,
2969
3107
  () => {
2970
3108
  if (error.value) {
@@ -3225,9 +3363,9 @@ function useAsyncStorage() {
3225
3363
  }
3226
3364
 
3227
3365
  // src/composables/useClipboard.ts
3228
- var import_runtime_core36 = require("@vue/runtime-core");
3366
+ var import_runtime_core39 = require("@vue/runtime-core");
3229
3367
  function useClipboard() {
3230
- const content = (0, import_runtime_core36.ref)("");
3368
+ const content = (0, import_runtime_core39.ref)("");
3231
3369
  function copy(text) {
3232
3370
  return NativeBridge.invokeNativeModule("Clipboard", "copy", [text]).then(() => void 0);
3233
3371
  }
@@ -3241,29 +3379,29 @@ function useClipboard() {
3241
3379
  }
3242
3380
 
3243
3381
  // src/composables/useDeviceInfo.ts
3244
- var import_runtime_core37 = require("@vue/runtime-core");
3382
+ var import_runtime_core40 = require("@vue/runtime-core");
3245
3383
  function useDeviceInfo() {
3246
- const model = (0, import_runtime_core37.ref)("");
3247
- const systemVersion = (0, import_runtime_core37.ref)("");
3248
- const systemName = (0, import_runtime_core37.ref)("");
3249
- const name = (0, import_runtime_core37.ref)("");
3250
- const screenWidth = (0, import_runtime_core37.ref)(0);
3251
- const screenHeight = (0, import_runtime_core37.ref)(0);
3252
- const scale = (0, import_runtime_core37.ref)(1);
3253
- const isLoaded = (0, import_runtime_core37.ref)(false);
3384
+ const model = (0, import_runtime_core40.ref)("");
3385
+ const systemVersion = (0, import_runtime_core40.ref)("");
3386
+ const systemName = (0, import_runtime_core40.ref)("");
3387
+ const name = (0, import_runtime_core40.ref)("");
3388
+ const screenWidth = (0, import_runtime_core40.ref)(0);
3389
+ const screenHeight = (0, import_runtime_core40.ref)(0);
3390
+ const scale = (0, import_runtime_core40.ref)(1);
3391
+ const isLoaded = (0, import_runtime_core40.ref)(false);
3254
3392
  async function fetchInfo() {
3255
3393
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
3256
3394
  model.value = info.model ?? "";
3257
3395
  systemVersion.value = info.systemVersion ?? "";
3258
3396
  systemName.value = info.systemName ?? "";
3259
- name.value = info.name ?? "";
3397
+ name.value = info.name ?? info.deviceName ?? "";
3260
3398
  screenWidth.value = info.screenWidth ?? 0;
3261
3399
  screenHeight.value = info.screenHeight ?? 0;
3262
- scale.value = info.scale ?? 1;
3400
+ scale.value = info.scale ?? info.screenScale ?? 1;
3263
3401
  isLoaded.value = true;
3264
3402
  }
3265
- (0, import_runtime_core37.onMounted)(() => {
3266
- fetchInfo();
3403
+ (0, import_runtime_core40.onMounted)(() => {
3404
+ void fetchInfo().catch(() => void 0);
3267
3405
  });
3268
3406
  return {
3269
3407
  model,
@@ -3279,10 +3417,10 @@ function useDeviceInfo() {
3279
3417
  }
3280
3418
 
3281
3419
  // src/composables/useKeyboard.ts
3282
- var import_runtime_core38 = require("@vue/runtime-core");
3420
+ var import_runtime_core41 = require("@vue/runtime-core");
3283
3421
  function useKeyboard() {
3284
- const isVisible = (0, import_runtime_core38.ref)(false);
3285
- const height = (0, import_runtime_core38.ref)(0);
3422
+ const isVisible = (0, import_runtime_core41.ref)(false);
3423
+ const height = (0, import_runtime_core41.ref)(0);
3286
3424
  function dismiss() {
3287
3425
  return NativeBridge.invokeNativeModule("Keyboard", "dismiss", []).then(() => void 0);
3288
3426
  }
@@ -3296,10 +3434,10 @@ function useKeyboard() {
3296
3434
  }
3297
3435
 
3298
3436
  // src/composables/useNetwork.ts
3299
- var import_runtime_core39 = require("@vue/runtime-core");
3437
+ var import_runtime_core42 = require("@vue/runtime-core");
3300
3438
  function useNetwork() {
3301
- const isConnected = (0, import_runtime_core39.ref)(true);
3302
- const connectionType = (0, import_runtime_core39.ref)("unknown");
3439
+ const isConnected = (0, import_runtime_core42.ref)(true);
3440
+ const connectionType = (0, import_runtime_core42.ref)("unknown");
3303
3441
  let lastEventTime = 0;
3304
3442
  const unsubscribe = NativeBridge.onGlobalEvent("network:change", (payload) => {
3305
3443
  lastEventTime = Date.now();
@@ -3315,14 +3453,14 @@ function useNetwork() {
3315
3453
  }).catch((err) => {
3316
3454
  if (__DEV__) console.warn("[vue-native] Network.getStatus failed:", err);
3317
3455
  });
3318
- (0, import_runtime_core39.onUnmounted)(unsubscribe);
3456
+ (0, import_runtime_core42.onUnmounted)(unsubscribe);
3319
3457
  return { isConnected, connectionType };
3320
3458
  }
3321
3459
 
3322
3460
  // src/composables/useAppState.ts
3323
- var import_runtime_core40 = require("@vue/runtime-core");
3461
+ var import_runtime_core43 = require("@vue/runtime-core");
3324
3462
  function useAppState() {
3325
- const state = (0, import_runtime_core40.ref)("active");
3463
+ const state = (0, import_runtime_core43.ref)("active");
3326
3464
  NativeBridge.invokeNativeModule("AppState", "getState").then((s) => {
3327
3465
  state.value = s;
3328
3466
  }).catch((err) => {
@@ -3331,7 +3469,7 @@ function useAppState() {
3331
3469
  const unsubscribe = NativeBridge.onGlobalEvent("appState:change", (payload) => {
3332
3470
  state.value = payload.state;
3333
3471
  });
3334
- (0, import_runtime_core40.onUnmounted)(unsubscribe);
3472
+ (0, import_runtime_core43.onUnmounted)(unsubscribe);
3335
3473
  return { state };
3336
3474
  }
3337
3475
 
@@ -3366,11 +3504,31 @@ function usePermissions() {
3366
3504
  }
3367
3505
 
3368
3506
  // src/composables/useGeolocation.ts
3369
- var import_runtime_core41 = require("@vue/runtime-core");
3507
+ var import_runtime_core44 = require("@vue/runtime-core");
3370
3508
  function useGeolocation() {
3371
- const coords = (0, import_runtime_core41.ref)(null);
3372
- const error = (0, import_runtime_core41.ref)(null);
3509
+ const coords = (0, import_runtime_core44.ref)(null);
3510
+ const error = (0, import_runtime_core44.ref)(null);
3373
3511
  let watchId = null;
3512
+ let unsubscribePosition = null;
3513
+ let unsubscribeError = null;
3514
+ let isDisposed = false;
3515
+ function removeWatchListeners() {
3516
+ unsubscribePosition?.();
3517
+ unsubscribeError?.();
3518
+ unsubscribePosition = null;
3519
+ unsubscribeError = null;
3520
+ }
3521
+ if ((0, import_runtime_core44.getCurrentInstance)()) {
3522
+ (0, import_runtime_core44.onUnmounted)(() => {
3523
+ isDisposed = true;
3524
+ removeWatchListeners();
3525
+ const activeWatchId = watchId;
3526
+ watchId = null;
3527
+ if (activeWatchId !== null) {
3528
+ void NativeBridge.invokeNativeModule("Geolocation", "clearWatch", [activeWatchId]).catch(() => void 0);
3529
+ }
3530
+ });
3531
+ }
3374
3532
  async function getCurrentPosition() {
3375
3533
  try {
3376
3534
  error.value = null;
@@ -3386,19 +3544,22 @@ function useGeolocation() {
3386
3544
  async function watchPosition() {
3387
3545
  try {
3388
3546
  error.value = null;
3547
+ if (watchId !== null) {
3548
+ await clearWatch(watchId);
3549
+ }
3550
+ removeWatchListeners();
3389
3551
  const id = await NativeBridge.invokeNativeModule("Geolocation", "watchPosition");
3552
+ if (isDisposed) {
3553
+ await NativeBridge.invokeNativeModule("Geolocation", "clearWatch", [id]);
3554
+ return id;
3555
+ }
3390
3556
  watchId = id;
3391
- const unsubscribe = NativeBridge.onGlobalEvent("location:update", (payload) => {
3557
+ unsubscribePosition = NativeBridge.onGlobalEvent("location:update", (payload) => {
3392
3558
  coords.value = payload;
3393
3559
  });
3394
- const unsubscribeError = NativeBridge.onGlobalEvent("location:error", (payload) => {
3560
+ unsubscribeError = NativeBridge.onGlobalEvent("location:error", (payload) => {
3395
3561
  error.value = payload.message;
3396
3562
  });
3397
- (0, import_runtime_core41.onUnmounted)(() => {
3398
- unsubscribe();
3399
- unsubscribeError();
3400
- if (watchId !== null) clearWatch(watchId);
3401
- });
3402
3563
  return id;
3403
3564
  } catch (e) {
3404
3565
  const msg = e instanceof Error ? e.message : String(e);
@@ -3408,13 +3569,16 @@ function useGeolocation() {
3408
3569
  }
3409
3570
  async function clearWatch(id) {
3410
3571
  await NativeBridge.invokeNativeModule("Geolocation", "clearWatch", [id]);
3411
- watchId = null;
3572
+ if (watchId === id) {
3573
+ watchId = null;
3574
+ removeWatchListeners();
3575
+ }
3412
3576
  }
3413
3577
  return { coords, error, getCurrentPosition, watchPosition, clearWatch };
3414
3578
  }
3415
3579
 
3416
3580
  // src/composables/useCamera.ts
3417
- var import_runtime_core42 = require("@vue/runtime-core");
3581
+ var import_runtime_core45 = require("@vue/runtime-core");
3418
3582
  function useCamera() {
3419
3583
  const qrCleanups = [];
3420
3584
  async function launchCamera(options = {}) {
@@ -3437,7 +3601,7 @@ function useCamera() {
3437
3601
  qrCleanups.push(unsubscribe);
3438
3602
  return unsubscribe;
3439
3603
  }
3440
- (0, import_runtime_core42.onUnmounted)(() => {
3604
+ (0, import_runtime_core45.onUnmounted)(() => {
3441
3605
  NativeBridge.invokeNativeModule("Camera", "stopQRScan").catch((err) => {
3442
3606
  if (__DEV__) console.warn("[vue-native] Camera.stopQRScan failed:", err);
3443
3607
  });
@@ -3448,10 +3612,10 @@ function useCamera() {
3448
3612
  }
3449
3613
 
3450
3614
  // src/composables/useNotifications.ts
3451
- var import_runtime_core43 = require("@vue/runtime-core");
3615
+ var import_runtime_core46 = require("@vue/runtime-core");
3452
3616
  function useNotifications() {
3453
- const isGranted = (0, import_runtime_core43.ref)(false);
3454
- const pushToken = (0, import_runtime_core43.ref)(null);
3617
+ const isGranted = (0, import_runtime_core46.ref)(false);
3618
+ const pushToken = (0, import_runtime_core46.ref)(null);
3455
3619
  async function requestPermission() {
3456
3620
  const granted = await NativeBridge.invokeNativeModule("Notifications", "requestPermission");
3457
3621
  isGranted.value = granted;
@@ -3471,7 +3635,7 @@ function useNotifications() {
3471
3635
  }
3472
3636
  function onNotification(handler) {
3473
3637
  const unsubscribe = NativeBridge.onGlobalEvent("notification:received", handler);
3474
- (0, import_runtime_core43.onUnmounted)(unsubscribe);
3638
+ (0, import_runtime_core46.onUnmounted)(unsubscribe);
3475
3639
  return unsubscribe;
3476
3640
  }
3477
3641
  async function registerForPush() {
@@ -3485,12 +3649,17 @@ function useNotifications() {
3485
3649
  pushToken.value = payload.token;
3486
3650
  handler(payload.token);
3487
3651
  });
3488
- (0, import_runtime_core43.onUnmounted)(unsubscribe);
3652
+ (0, import_runtime_core46.onUnmounted)(unsubscribe);
3489
3653
  return unsubscribe;
3490
3654
  }
3491
3655
  function onPushReceived(handler) {
3492
3656
  const unsubscribe = NativeBridge.onGlobalEvent("push:received", handler);
3493
- (0, import_runtime_core43.onUnmounted)(unsubscribe);
3657
+ (0, import_runtime_core46.onUnmounted)(unsubscribe);
3658
+ return unsubscribe;
3659
+ }
3660
+ function onPushError(handler) {
3661
+ const unsubscribe = NativeBridge.onGlobalEvent("push:error", handler);
3662
+ (0, import_runtime_core46.onUnmounted)(unsubscribe);
3494
3663
  return unsubscribe;
3495
3664
  }
3496
3665
  return {
@@ -3507,7 +3676,8 @@ function useNotifications() {
3507
3676
  registerForPush,
3508
3677
  getToken,
3509
3678
  onPushToken,
3510
- onPushReceived
3679
+ onPushReceived,
3680
+ onPushError
3511
3681
  };
3512
3682
  }
3513
3683
 
@@ -3526,23 +3696,39 @@ function useBiometry() {
3526
3696
  }
3527
3697
 
3528
3698
  // src/composables/useHttp.ts
3529
- var import_runtime_core44 = require("@vue/runtime-core");
3699
+ var import_runtime_core47 = require("@vue/runtime-core");
3530
3700
  function isQueryRequestOptions(value) {
3531
3701
  return "params" in value || "headers" in value;
3532
3702
  }
3703
+ function getHeader(headers, name) {
3704
+ const expected = name.toLowerCase();
3705
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === expected)?.[1];
3706
+ }
3707
+ async function parseResponseBody(response, headers) {
3708
+ if (response.status === 204 || response.status === 205) return void 0;
3709
+ const contentType = getHeader(headers, "content-type")?.toLowerCase() ?? "";
3710
+ const expectsJson = contentType === "" || contentType.includes("json") || contentType.includes("+json");
3711
+ if (!expectsJson && typeof response.text === "function") {
3712
+ return await response.text();
3713
+ }
3714
+ return await response.json();
3715
+ }
3533
3716
  function useHttp(config = {}) {
3717
+ let configurePinsPromise;
3534
3718
  if (config.pins && Object.keys(config.pins).length > 0) {
3535
3719
  const configurePins = globalThis.__VN_configurePins;
3536
3720
  if (typeof configurePins === "function") {
3537
3721
  configurePins(JSON.stringify(config.pins));
3722
+ configurePinsPromise = Promise.resolve();
3538
3723
  } else {
3539
- NativeBridge.invokeNativeModule("Http", "configurePins", [config.pins]);
3724
+ configurePinsPromise = NativeBridge.invokeNativeModule("Http", "configurePins", [config.pins]);
3540
3725
  }
3541
3726
  }
3542
- const loading = (0, import_runtime_core44.ref)(false);
3543
- const error = (0, import_runtime_core44.ref)(null);
3727
+ const loading = (0, import_runtime_core47.ref)(false);
3728
+ const error = (0, import_runtime_core47.ref)(null);
3729
+ let activeRequestCount = 0;
3544
3730
  let isMounted = true;
3545
- (0, import_runtime_core44.onUnmounted)(() => {
3731
+ (0, import_runtime_core47.onUnmounted)(() => {
3546
3732
  isMounted = false;
3547
3733
  });
3548
3734
  const BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
@@ -3565,21 +3751,23 @@ function useHttp(config = {}) {
3565
3751
  }
3566
3752
  async function request(method, url, options = {}) {
3567
3753
  const fullUrl = config.baseURL ? `${config.baseURL}${url}` : url;
3754
+ activeRequestCount++;
3568
3755
  loading.value = true;
3569
3756
  error.value = null;
3570
- let controller;
3757
+ const AbortControllerCtor = globalThis.AbortController;
3758
+ const controller = config.timeout && config.timeout > 0 && typeof AbortControllerCtor === "function" ? new AbortControllerCtor() : void 0;
3571
3759
  let timeoutId;
3572
- if (config.timeout && config.timeout > 0 && typeof AbortController !== "undefined") {
3573
- controller = new AbortController();
3574
- timeoutId = setTimeout(() => controller.abort(), config.timeout);
3575
- }
3576
3760
  try {
3761
+ if (configurePinsPromise) {
3762
+ await configurePinsPromise;
3763
+ }
3577
3764
  const upperMethod = method.toUpperCase();
3578
3765
  const mergedHeaders = {
3579
3766
  ...config.headers ?? {},
3580
3767
  ...options.headers ?? {}
3581
3768
  };
3582
- if (BODY_METHODS.has(upperMethod) && !mergedHeaders["Content-Type"]) {
3769
+ const hasContentType = getHeader(mergedHeaders, "content-type") !== void 0;
3770
+ if (BODY_METHODS.has(upperMethod) && !hasContentType && (options.body === void 0 || typeof options.body !== "string")) {
3583
3771
  mergedHeaders["Content-Type"] = "application/json";
3584
3772
  }
3585
3773
  const fetchOptions = {
@@ -3590,11 +3778,20 @@ function useHttp(config = {}) {
3590
3778
  fetchOptions.signal = controller.signal;
3591
3779
  }
3592
3780
  if (options.body !== void 0) {
3593
- fetchOptions.body = JSON.stringify(options.body);
3781
+ fetchOptions.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
3594
3782
  }
3595
- const response = await fetch(fullUrl, fetchOptions);
3596
- const data = await response.json();
3783
+ const fetchPromise = fetch(fullUrl, fetchOptions);
3784
+ const response = config.timeout && config.timeout > 0 ? await Promise.race([
3785
+ fetchPromise,
3786
+ new Promise((_resolve, reject) => {
3787
+ timeoutId = setTimeout(() => {
3788
+ controller?.abort();
3789
+ reject(new Error(`[VueNative] ${upperMethod} ${fullUrl} timed out after ${config.timeout}ms`));
3790
+ }, config.timeout);
3791
+ })
3792
+ ]) : await fetchPromise;
3597
3793
  const responseHeaders = parseResponseHeaders(response);
3794
+ const data = await parseResponseBody(response, responseHeaders);
3598
3795
  if (!isMounted) {
3599
3796
  return { data, status: response.status, ok: response.ok, headers: responseHeaders };
3600
3797
  }
@@ -3614,8 +3811,9 @@ function useHttp(config = {}) {
3614
3811
  if (timeoutId !== void 0) {
3615
3812
  clearTimeout(timeoutId);
3616
3813
  }
3814
+ activeRequestCount = Math.max(0, activeRequestCount - 1);
3617
3815
  if (isMounted) {
3618
- loading.value = false;
3816
+ loading.value = activeRequestCount > 0;
3619
3817
  }
3620
3818
  }
3621
3819
  }
@@ -3642,26 +3840,44 @@ function useHttp(config = {}) {
3642
3840
  }
3643
3841
 
3644
3842
  // src/composables/useColorScheme.ts
3645
- var import_runtime_core45 = require("@vue/runtime-core");
3843
+ var import_runtime_core48 = require("@vue/runtime-core");
3646
3844
  function useColorScheme() {
3647
- const colorScheme = (0, import_runtime_core45.ref)("light");
3648
- const isDark = (0, import_runtime_core45.ref)(false);
3845
+ const colorScheme = (0, import_runtime_core48.ref)("light");
3846
+ const isDark = (0, import_runtime_core48.ref)(false);
3847
+ let eventRevision = 0;
3848
+ let isActive = true;
3849
+ const applyColorScheme = (value) => {
3850
+ if (value !== "light" && value !== "dark") return;
3851
+ colorScheme.value = value;
3852
+ isDark.value = value === "dark";
3853
+ };
3854
+ (0, import_runtime_core48.onMounted)(() => {
3855
+ const revisionAtRequest = eventRevision;
3856
+ void NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []).then((info) => {
3857
+ if (isActive && eventRevision === revisionAtRequest) {
3858
+ applyColorScheme(info?.colorScheme);
3859
+ }
3860
+ }).catch(() => void 0);
3861
+ });
3649
3862
  const unsubscribe = NativeBridge.onGlobalEvent(
3650
3863
  "colorScheme:change",
3651
3864
  (payload) => {
3652
- colorScheme.value = payload.colorScheme;
3653
- isDark.value = payload.colorScheme === "dark";
3865
+ eventRevision++;
3866
+ applyColorScheme(payload?.colorScheme);
3654
3867
  }
3655
3868
  );
3656
- (0, import_runtime_core45.onUnmounted)(unsubscribe);
3869
+ (0, import_runtime_core48.onUnmounted)(() => {
3870
+ isActive = false;
3871
+ unsubscribe();
3872
+ });
3657
3873
  return { colorScheme, isDark };
3658
3874
  }
3659
3875
 
3660
3876
  // src/composables/useBackHandler.ts
3661
- var import_runtime_core46 = require("@vue/runtime-core");
3877
+ var import_runtime_core49 = require("@vue/runtime-core");
3662
3878
  function useBackHandler(handler) {
3663
3879
  let unsubscribe = null;
3664
- (0, import_runtime_core46.onMounted)(() => {
3880
+ (0, import_runtime_core49.onMounted)(() => {
3665
3881
  unsubscribe = NativeBridge.onGlobalEvent("hardware:backPress", () => {
3666
3882
  const handled = handler();
3667
3883
  if (!handled) {
@@ -3671,7 +3887,7 @@ function useBackHandler(handler) {
3671
3887
  }
3672
3888
  });
3673
3889
  });
3674
- (0, import_runtime_core46.onUnmounted)(() => {
3890
+ (0, import_runtime_core49.onUnmounted)(() => {
3675
3891
  unsubscribe?.();
3676
3892
  unsubscribe = null;
3677
3893
  });
@@ -3695,14 +3911,18 @@ function useSecureStorage() {
3695
3911
  }
3696
3912
 
3697
3913
  // src/composables/useI18n.ts
3698
- var import_runtime_core47 = require("@vue/runtime-core");
3914
+ var import_runtime_core50 = require("@vue/runtime-core");
3915
+ function normalizeLocale(value) {
3916
+ if (typeof value !== "string" || value.trim().length === 0) return "en";
3917
+ return value.trim();
3918
+ }
3699
3919
  function useI18n() {
3700
- const isRTL = (0, import_runtime_core47.ref)(false);
3701
- const locale = (0, import_runtime_core47.ref)("en");
3702
- (0, import_runtime_core47.onMounted)(async () => {
3920
+ const isRTL = (0, import_runtime_core50.ref)(false);
3921
+ const locale = (0, import_runtime_core50.ref)("en");
3922
+ (0, import_runtime_core50.onMounted)(async () => {
3703
3923
  try {
3704
- const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getDeviceInfo", []);
3705
- locale.value = info?.locale || "en";
3924
+ const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
3925
+ locale.value = normalizeLocale(info?.locale);
3706
3926
  isRTL.value = ["ar", "he", "fa", "ur"].some((l) => locale.value.startsWith(l));
3707
3927
  } catch {
3708
3928
  }
@@ -3711,31 +3931,40 @@ function useI18n() {
3711
3931
  }
3712
3932
 
3713
3933
  // src/composables/useDimensions.ts
3714
- var import_runtime_core48 = require("@vue/runtime-core");
3934
+ var import_runtime_core51 = require("@vue/runtime-core");
3715
3935
  function useDimensions() {
3716
- const width = (0, import_runtime_core48.ref)(0);
3717
- const height = (0, import_runtime_core48.ref)(0);
3718
- const scale = (0, import_runtime_core48.ref)(1);
3719
- (0, import_runtime_core48.onMounted)(async () => {
3936
+ const width = (0, import_runtime_core51.ref)(0);
3937
+ const height = (0, import_runtime_core51.ref)(0);
3938
+ const scale = (0, import_runtime_core51.ref)(1);
3939
+ let eventRevision = 0;
3940
+ let isActive = true;
3941
+ (0, import_runtime_core51.onMounted)(async () => {
3942
+ const revisionAtRequest = eventRevision;
3720
3943
  try {
3721
3944
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
3722
- width.value = info?.screenWidth || 0;
3723
- height.value = info?.screenHeight || 0;
3724
- scale.value = info?.scale || 1;
3945
+ if (isActive && eventRevision === revisionAtRequest) {
3946
+ width.value = info?.screenWidth || 0;
3947
+ height.value = info?.screenHeight || 0;
3948
+ scale.value = info?.scale ?? info?.screenScale ?? 1;
3949
+ }
3725
3950
  } catch {
3726
3951
  }
3727
3952
  });
3728
3953
  const cleanup = NativeBridge.onGlobalEvent("dimensionsChange", (payload) => {
3954
+ eventRevision++;
3729
3955
  if (payload.width != null) width.value = payload.width;
3730
3956
  if (payload.height != null) height.value = payload.height;
3731
3957
  if (payload.scale != null) scale.value = payload.scale;
3732
3958
  });
3733
- (0, import_runtime_core48.onUnmounted)(cleanup);
3959
+ (0, import_runtime_core51.onUnmounted)(() => {
3960
+ isActive = false;
3961
+ cleanup();
3962
+ });
3734
3963
  return { width, height, scale };
3735
3964
  }
3736
3965
 
3737
3966
  // src/composables/useWebSocket.ts
3738
- var import_runtime_core49 = require("@vue/runtime-core");
3967
+ var import_runtime_core52 = require("@vue/runtime-core");
3739
3968
  var connectionCounter = 0;
3740
3969
  function useWebSocket(url, options = {}) {
3741
3970
  const {
@@ -3745,9 +3974,9 @@ function useWebSocket(url, options = {}) {
3745
3974
  reconnectInterval = 1e3
3746
3975
  } = options;
3747
3976
  const connectionId = `ws_${++connectionCounter}_${Date.now()}`;
3748
- const status = (0, import_runtime_core49.ref)("CLOSED");
3749
- const lastMessage = (0, import_runtime_core49.ref)(null);
3750
- const error = (0, import_runtime_core49.ref)(null);
3977
+ const status = (0, import_runtime_core52.ref)("CLOSED");
3978
+ const lastMessage = (0, import_runtime_core52.ref)(null);
3979
+ const error = (0, import_runtime_core52.ref)(null);
3751
3980
  let reconnectAttempts = 0;
3752
3981
  let reconnectTimer = null;
3753
3982
  const MAX_PENDING_MESSAGES = 100;
@@ -3832,7 +4061,7 @@ function useWebSocket(url, options = {}) {
3832
4061
  if (autoConnect) {
3833
4062
  open();
3834
4063
  }
3835
- (0, import_runtime_core49.onUnmounted)(() => {
4064
+ (0, import_runtime_core52.onUnmounted)(() => {
3836
4065
  if (reconnectTimer) {
3837
4066
  clearTimeout(reconnectTimer);
3838
4067
  }
@@ -3902,12 +4131,12 @@ function useFileSystem() {
3902
4131
  }
3903
4132
 
3904
4133
  // src/composables/useSensors.ts
3905
- var import_runtime_core50 = require("@vue/runtime-core");
4134
+ var import_runtime_core53 = require("@vue/runtime-core");
3906
4135
  function useAccelerometer(options = {}) {
3907
- const x = (0, import_runtime_core50.ref)(0);
3908
- const y = (0, import_runtime_core50.ref)(0);
3909
- const z = (0, import_runtime_core50.ref)(0);
3910
- const isAvailable = (0, import_runtime_core50.ref)(false);
4136
+ const x = (0, import_runtime_core53.ref)(0);
4137
+ const y = (0, import_runtime_core53.ref)(0);
4138
+ const z = (0, import_runtime_core53.ref)(0);
4139
+ const isAvailable = (0, import_runtime_core53.ref)(false);
3911
4140
  let running = false;
3912
4141
  let unsubscribe = null;
3913
4142
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["accelerometer"]).then((result) => {
@@ -3939,16 +4168,16 @@ function useAccelerometer(options = {}) {
3939
4168
  if (__DEV__) console.warn("[vue-native] Sensors.stopAccelerometer failed:", err);
3940
4169
  });
3941
4170
  }
3942
- (0, import_runtime_core50.onUnmounted)(() => {
4171
+ (0, import_runtime_core53.onUnmounted)(() => {
3943
4172
  stop();
3944
4173
  });
3945
4174
  return { x, y, z, isAvailable, start, stop };
3946
4175
  }
3947
4176
  function useGyroscope(options = {}) {
3948
- const x = (0, import_runtime_core50.ref)(0);
3949
- const y = (0, import_runtime_core50.ref)(0);
3950
- const z = (0, import_runtime_core50.ref)(0);
3951
- const isAvailable = (0, import_runtime_core50.ref)(false);
4177
+ const x = (0, import_runtime_core53.ref)(0);
4178
+ const y = (0, import_runtime_core53.ref)(0);
4179
+ const z = (0, import_runtime_core53.ref)(0);
4180
+ const isAvailable = (0, import_runtime_core53.ref)(false);
3952
4181
  let running = false;
3953
4182
  let unsubscribe = null;
3954
4183
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["gyroscope"]).then((result) => {
@@ -3980,14 +4209,14 @@ function useGyroscope(options = {}) {
3980
4209
  if (__DEV__) console.warn("[vue-native] Sensors.stopGyroscope failed:", err);
3981
4210
  });
3982
4211
  }
3983
- (0, import_runtime_core50.onUnmounted)(() => {
4212
+ (0, import_runtime_core53.onUnmounted)(() => {
3984
4213
  stop();
3985
4214
  });
3986
4215
  return { x, y, z, isAvailable, start, stop };
3987
4216
  }
3988
4217
 
3989
4218
  // src/composables/useAudio.ts
3990
- var import_runtime_core51 = require("@vue/runtime-core");
4219
+ var import_runtime_core54 = require("@vue/runtime-core");
3991
4220
  function asRecord(value) {
3992
4221
  return typeof value === "object" && value !== null ? value : null;
3993
4222
  }
@@ -4000,11 +4229,11 @@ function getStringProp(record, key) {
4000
4229
  return typeof value === "string" ? value : void 0;
4001
4230
  }
4002
4231
  function useAudio() {
4003
- const duration = (0, import_runtime_core51.ref)(0);
4004
- const position = (0, import_runtime_core51.ref)(0);
4005
- const isPlaying = (0, import_runtime_core51.ref)(false);
4006
- const isRecording = (0, import_runtime_core51.ref)(false);
4007
- const error = (0, import_runtime_core51.ref)(null);
4232
+ const duration = (0, import_runtime_core54.ref)(0);
4233
+ const position = (0, import_runtime_core54.ref)(0);
4234
+ const isPlaying = (0, import_runtime_core54.ref)(false);
4235
+ const isRecording = (0, import_runtime_core54.ref)(false);
4236
+ const error = (0, import_runtime_core54.ref)(null);
4008
4237
  const unsubProgress = NativeBridge.onGlobalEvent("audio:progress", (payload) => {
4009
4238
  position.value = payload.currentTime ?? 0;
4010
4239
  duration.value = payload.duration ?? 0;
@@ -4017,7 +4246,7 @@ function useAudio() {
4017
4246
  error.value = payload.message ?? "Unknown audio error";
4018
4247
  isPlaying.value = false;
4019
4248
  });
4020
- (0, import_runtime_core51.onUnmounted)(() => {
4249
+ (0, import_runtime_core54.onUnmounted)(() => {
4021
4250
  unsubProgress();
4022
4251
  unsubComplete();
4023
4252
  unsubError();
@@ -4102,9 +4331,9 @@ function useAudio() {
4102
4331
  }
4103
4332
 
4104
4333
  // src/composables/useDatabase.ts
4105
- var import_runtime_core52 = require("@vue/runtime-core");
4334
+ var import_runtime_core55 = require("@vue/runtime-core");
4106
4335
  function useDatabase(name = "default") {
4107
- const isOpen = (0, import_runtime_core52.ref)(false);
4336
+ const isOpen = (0, import_runtime_core55.ref)(false);
4108
4337
  let opened = false;
4109
4338
  async function ensureOpen() {
4110
4339
  if (opened) return;
@@ -4147,7 +4376,7 @@ function useDatabase(name = "default") {
4147
4376
  opened = false;
4148
4377
  isOpen.value = false;
4149
4378
  }
4150
- (0, import_runtime_core52.onUnmounted)(() => {
4379
+ (0, import_runtime_core55.onUnmounted)(() => {
4151
4380
  if (opened) {
4152
4381
  NativeBridge.invokeNativeModule("Database", "close", [name]).catch((err) => {
4153
4382
  if (__DEV__) console.warn("[vue-native] Database.close failed:", err);
@@ -4160,12 +4389,12 @@ function useDatabase(name = "default") {
4160
4389
  }
4161
4390
 
4162
4391
  // src/composables/usePerformance.ts
4163
- var import_runtime_core53 = require("@vue/runtime-core");
4392
+ var import_runtime_core56 = require("@vue/runtime-core");
4164
4393
  function usePerformance() {
4165
- const isProfiling = (0, import_runtime_core53.ref)(false);
4166
- const fps = (0, import_runtime_core53.ref)(0);
4167
- const memoryMB = (0, import_runtime_core53.ref)(0);
4168
- const bridgeOps = (0, import_runtime_core53.ref)(0);
4394
+ const isProfiling = (0, import_runtime_core56.ref)(false);
4395
+ const fps = (0, import_runtime_core56.ref)(0);
4396
+ const memoryMB = (0, import_runtime_core56.ref)(0);
4397
+ const bridgeOps = (0, import_runtime_core56.ref)(0);
4169
4398
  let unsubscribe = null;
4170
4399
  function handleMetrics(payload) {
4171
4400
  fps.value = payload.fps ?? 0;
@@ -4190,7 +4419,7 @@ function usePerformance() {
4190
4419
  async function getMetrics() {
4191
4420
  return NativeBridge.invokeNativeModule("Performance", "getMetrics", []);
4192
4421
  }
4193
- (0, import_runtime_core53.onUnmounted)(() => {
4422
+ (0, import_runtime_core56.onUnmounted)(() => {
4194
4423
  if (isProfiling.value) {
4195
4424
  NativeBridge.invokeNativeModule("Performance", "stopProfiling", []).catch((err) => {
4196
4425
  if (__DEV__) console.warn("[vue-native] Performance.stopProfiling failed:", err);
@@ -4214,10 +4443,10 @@ function usePerformance() {
4214
4443
  }
4215
4444
 
4216
4445
  // src/composables/useSharedElementTransition.ts
4217
- var import_runtime_core54 = require("@vue/runtime-core");
4446
+ var import_runtime_core57 = require("@vue/runtime-core");
4218
4447
  var sharedElementRegistry = /* @__PURE__ */ new Map();
4219
4448
  function useSharedElementTransition(elementId) {
4220
- const viewId = (0, import_runtime_core54.ref)(null);
4449
+ const viewId = (0, import_runtime_core57.ref)(null);
4221
4450
  function register(nativeViewId) {
4222
4451
  viewId.value = nativeViewId;
4223
4452
  sharedElementRegistry.set(elementId, nativeViewId);
@@ -4226,7 +4455,7 @@ function useSharedElementTransition(elementId) {
4226
4455
  viewId.value = null;
4227
4456
  sharedElementRegistry.delete(elementId);
4228
4457
  }
4229
- (0, import_runtime_core54.onUnmounted)(() => {
4458
+ (0, import_runtime_core57.onUnmounted)(() => {
4230
4459
  unregister();
4231
4460
  });
4232
4461
  return {
@@ -4250,7 +4479,7 @@ function clearSharedElementRegistry() {
4250
4479
  }
4251
4480
 
4252
4481
  // src/composables/useIAP.ts
4253
- var import_runtime_core55 = require("@vue/runtime-core");
4482
+ var import_runtime_core58 = require("@vue/runtime-core");
4254
4483
  function getErrorMessage(error) {
4255
4484
  if (error instanceof Error) return error.message;
4256
4485
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4260,9 +4489,9 @@ function getErrorMessage(error) {
4260
4489
  return String(error);
4261
4490
  }
4262
4491
  function useIAP() {
4263
- const products = (0, import_runtime_core55.ref)([]);
4264
- const isReady = (0, import_runtime_core55.ref)(false);
4265
- const error = (0, import_runtime_core55.ref)(null);
4492
+ const products = (0, import_runtime_core58.ref)([]);
4493
+ const isReady = (0, import_runtime_core58.ref)(false);
4494
+ const error = (0, import_runtime_core58.ref)(null);
4266
4495
  const cleanups = [];
4267
4496
  const unsubscribe = NativeBridge.onGlobalEvent("iap:transactionUpdate", (payload) => {
4268
4497
  if (payload.state === "failed" && payload.error) {
@@ -4319,7 +4548,7 @@ function useIAP() {
4319
4548
  cleanups.push(unsub);
4320
4549
  return unsub;
4321
4550
  }
4322
- (0, import_runtime_core55.onUnmounted)(() => {
4551
+ (0, import_runtime_core58.onUnmounted)(() => {
4323
4552
  cleanups.forEach((fn) => fn());
4324
4553
  cleanups.length = 0;
4325
4554
  });
@@ -4336,7 +4565,7 @@ function useIAP() {
4336
4565
  }
4337
4566
 
4338
4567
  // src/composables/useAppleSignIn.ts
4339
- var import_runtime_core56 = require("@vue/runtime-core");
4568
+ var import_runtime_core59 = require("@vue/runtime-core");
4340
4569
  function getErrorMessage2(error) {
4341
4570
  if (error instanceof Error) return error.message;
4342
4571
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4359,9 +4588,9 @@ function normalizeSocialUser(value, provider) {
4359
4588
  };
4360
4589
  }
4361
4590
  function useAppleSignIn() {
4362
- const user = (0, import_runtime_core56.ref)(null);
4363
- const isAuthenticated = (0, import_runtime_core56.ref)(false);
4364
- const error = (0, import_runtime_core56.ref)(null);
4591
+ const user = (0, import_runtime_core59.ref)(null);
4592
+ const isAuthenticated = (0, import_runtime_core59.ref)(false);
4593
+ const error = (0, import_runtime_core59.ref)(null);
4365
4594
  const cleanups = [];
4366
4595
  const unsubscribe = NativeBridge.onGlobalEvent("auth:appleCredentialRevoked", () => {
4367
4596
  user.value = null;
@@ -4404,7 +4633,7 @@ function useAppleSignIn() {
4404
4633
  error.value = getErrorMessage2(err);
4405
4634
  }
4406
4635
  }
4407
- (0, import_runtime_core56.onUnmounted)(() => {
4636
+ (0, import_runtime_core59.onUnmounted)(() => {
4408
4637
  cleanups.forEach((fn) => fn());
4409
4638
  cleanups.length = 0;
4410
4639
  });
@@ -4412,7 +4641,7 @@ function useAppleSignIn() {
4412
4641
  }
4413
4642
 
4414
4643
  // src/composables/useGoogleSignIn.ts
4415
- var import_runtime_core57 = require("@vue/runtime-core");
4644
+ var import_runtime_core60 = require("@vue/runtime-core");
4416
4645
  function getErrorMessage3(error) {
4417
4646
  if (error instanceof Error) return error.message;
4418
4647
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4435,9 +4664,9 @@ function normalizeSocialUser2(value) {
4435
4664
  };
4436
4665
  }
4437
4666
  function useGoogleSignIn(clientId) {
4438
- const user = (0, import_runtime_core57.ref)(null);
4439
- const isAuthenticated = (0, import_runtime_core57.ref)(false);
4440
- const error = (0, import_runtime_core57.ref)(null);
4667
+ const user = (0, import_runtime_core60.ref)(null);
4668
+ const isAuthenticated = (0, import_runtime_core60.ref)(false);
4669
+ const error = (0, import_runtime_core60.ref)(null);
4441
4670
  const cleanups = [];
4442
4671
  NativeBridge.invokeNativeModule("SocialAuth", "getCurrentUser", ["google"]).then((result) => {
4443
4672
  const currentUser = normalizeSocialUser2(result);
@@ -4475,7 +4704,7 @@ function useGoogleSignIn(clientId) {
4475
4704
  error.value = getErrorMessage3(err);
4476
4705
  }
4477
4706
  }
4478
- (0, import_runtime_core57.onUnmounted)(() => {
4707
+ (0, import_runtime_core60.onUnmounted)(() => {
4479
4708
  cleanups.forEach((fn) => fn());
4480
4709
  cleanups.length = 0;
4481
4710
  });
@@ -4483,17 +4712,17 @@ function useGoogleSignIn(clientId) {
4483
4712
  }
4484
4713
 
4485
4714
  // src/composables/useBackgroundTask.ts
4486
- var import_runtime_core58 = require("@vue/runtime-core");
4715
+ var import_runtime_core61 = require("@vue/runtime-core");
4487
4716
  function useBackgroundTask() {
4488
4717
  const taskHandlers = /* @__PURE__ */ new Map();
4489
- const defaultHandler = (0, import_runtime_core58.ref)(null);
4718
+ const defaultHandler = (0, import_runtime_core61.ref)(null);
4490
4719
  const unsubscribe = NativeBridge.onGlobalEvent("background:taskExecute", (payload) => {
4491
4720
  const handler = taskHandlers.get(payload.taskId) || defaultHandler.value;
4492
4721
  if (handler) {
4493
4722
  handler(payload.taskId);
4494
4723
  }
4495
4724
  });
4496
- (0, import_runtime_core58.onUnmounted)(unsubscribe);
4725
+ (0, import_runtime_core61.onUnmounted)(unsubscribe);
4497
4726
  function registerTask(taskId) {
4498
4727
  return NativeBridge.invokeNativeModule("BackgroundTask", "registerTask", [taskId]).then(() => void 0);
4499
4728
  }
@@ -4532,7 +4761,8 @@ function useBackgroundTask() {
4532
4761
  }
4533
4762
 
4534
4763
  // src/composables/useOTAUpdate.ts
4535
- var import_runtime_core59 = require("@vue/runtime-core");
4764
+ var import_runtime_core62 = require("@vue/runtime-core");
4765
+ var SHA256_HEX_PATTERN = /^[a-f\d]{64}$/i;
4536
4766
  function getErrorMessage4(error) {
4537
4767
  if (error instanceof Error) return error.message;
4538
4768
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4542,18 +4772,18 @@ function getErrorMessage4(error) {
4542
4772
  return String(error);
4543
4773
  }
4544
4774
  function useOTAUpdate(serverUrl) {
4545
- const currentVersion = (0, import_runtime_core59.ref)("embedded");
4546
- const availableVersion = (0, import_runtime_core59.ref)(null);
4547
- const downloadProgress = (0, import_runtime_core59.ref)(0);
4548
- const isChecking = (0, import_runtime_core59.ref)(false);
4549
- const isDownloading = (0, import_runtime_core59.ref)(false);
4550
- const status = (0, import_runtime_core59.ref)("idle");
4551
- const error = (0, import_runtime_core59.ref)(null);
4775
+ const currentVersion = (0, import_runtime_core62.ref)("embedded");
4776
+ const availableVersion = (0, import_runtime_core62.ref)(null);
4777
+ const downloadProgress = (0, import_runtime_core62.ref)(0);
4778
+ const isChecking = (0, import_runtime_core62.ref)(false);
4779
+ const isDownloading = (0, import_runtime_core62.ref)(false);
4780
+ const status = (0, import_runtime_core62.ref)("idle");
4781
+ const error = (0, import_runtime_core62.ref)(null);
4552
4782
  let lastUpdateInfo = null;
4553
4783
  const unsubscribe = NativeBridge.onGlobalEvent("ota:downloadProgress", (payload) => {
4554
4784
  downloadProgress.value = payload.progress;
4555
4785
  });
4556
- (0, import_runtime_core59.onUnmounted)(unsubscribe);
4786
+ (0, import_runtime_core62.onUnmounted)(unsubscribe);
4557
4787
  NativeBridge.invokeNativeModule("OTA", "getCurrentVersion", []).then((info) => {
4558
4788
  currentVersion.value = info.version;
4559
4789
  }).catch((err) => {
@@ -4565,13 +4795,18 @@ function useOTAUpdate(serverUrl) {
4565
4795
  error.value = null;
4566
4796
  try {
4567
4797
  const info = await NativeBridge.invokeNativeModule("OTA", "checkForUpdate", [serverUrl]);
4798
+ if (info.updateAvailable) {
4799
+ if (!info.version || !info.downloadUrl || !SHA256_HEX_PATTERN.test(info.hash)) {
4800
+ throw new Error("Update server returned incomplete or invalid update metadata.");
4801
+ }
4802
+ }
4568
4803
  lastUpdateInfo = info;
4569
4804
  if (info.updateAvailable) {
4570
4805
  availableVersion.value = info.version;
4571
4806
  } else {
4572
4807
  availableVersion.value = null;
4573
4808
  }
4574
- status.value = info.updateAvailable ? "idle" : "idle";
4809
+ status.value = "idle";
4575
4810
  return info;
4576
4811
  } catch (err) {
4577
4812
  error.value = getErrorMessage4(err);
@@ -4581,21 +4816,34 @@ function useOTAUpdate(serverUrl) {
4581
4816
  isChecking.value = false;
4582
4817
  }
4583
4818
  }
4584
- async function downloadUpdate(url, hash) {
4819
+ async function downloadUpdate(url, hash, version) {
4585
4820
  const downloadUrl = url || lastUpdateInfo?.downloadUrl;
4586
4821
  const expectedHash = hash || lastUpdateInfo?.hash;
4822
+ const offeredVersion = version || lastUpdateInfo?.version;
4587
4823
  if (!downloadUrl) {
4588
4824
  const msg = "No download URL. Call checkForUpdate() first or provide a URL.";
4589
4825
  error.value = msg;
4590
4826
  status.value = "error";
4591
4827
  throw new Error(msg);
4592
4828
  }
4829
+ if (!expectedHash || !SHA256_HEX_PATTERN.test(expectedHash)) {
4830
+ const msg = "A valid 64-character SHA-256 hash is required for OTA updates.";
4831
+ error.value = msg;
4832
+ status.value = "error";
4833
+ throw new Error(msg);
4834
+ }
4835
+ if (!offeredVersion) {
4836
+ const msg = "No update version. Call checkForUpdate() first or provide a version.";
4837
+ error.value = msg;
4838
+ status.value = "error";
4839
+ throw new Error(msg);
4840
+ }
4593
4841
  isDownloading.value = true;
4594
4842
  downloadProgress.value = 0;
4595
4843
  status.value = "downloading";
4596
4844
  error.value = null;
4597
4845
  try {
4598
- await NativeBridge.invokeNativeModule("OTA", "downloadUpdate", [downloadUrl, expectedHash || ""]);
4846
+ await NativeBridge.invokeNativeModule("OTA", "downloadUpdate", [downloadUrl, expectedHash, offeredVersion]);
4599
4847
  status.value = "ready";
4600
4848
  } catch (err) {
4601
4849
  await NativeBridge.invokeNativeModule("OTA", "cleanupPartialDownload", []).catch((err2) => {
@@ -4616,6 +4864,9 @@ function useOTAUpdate(serverUrl) {
4616
4864
  try {
4617
4865
  await NativeBridge.invokeNativeModule("OTA", "verifyBundle", []);
4618
4866
  } catch (err) {
4867
+ await NativeBridge.invokeNativeModule("OTA", "cleanupPartialDownload", []).catch((cleanupError) => {
4868
+ if (__DEV__) console.warn("[vue-native] OTA.cleanupPartialDownload failed:", cleanupError);
4869
+ });
4619
4870
  status.value = "error";
4620
4871
  error.value = "Bundle verification failed: " + getErrorMessage4(err);
4621
4872
  throw err;
@@ -4667,7 +4918,7 @@ function useOTAUpdate(serverUrl) {
4667
4918
  }
4668
4919
 
4669
4920
  // src/composables/useBluetooth.ts
4670
- var import_runtime_core60 = require("@vue/runtime-core");
4921
+ var import_runtime_core63 = require("@vue/runtime-core");
4671
4922
  function getErrorMessage5(error) {
4672
4923
  if (error instanceof Error) return error.message;
4673
4924
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4677,11 +4928,11 @@ function getErrorMessage5(error) {
4677
4928
  return String(error);
4678
4929
  }
4679
4930
  function useBluetooth() {
4680
- const devices = (0, import_runtime_core60.ref)([]);
4681
- const connectedDevice = (0, import_runtime_core60.ref)(null);
4682
- const isScanning = (0, import_runtime_core60.ref)(false);
4683
- const isAvailable = (0, import_runtime_core60.ref)(false);
4684
- const error = (0, import_runtime_core60.ref)(null);
4931
+ const devices = (0, import_runtime_core63.ref)([]);
4932
+ const connectedDevice = (0, import_runtime_core63.ref)(null);
4933
+ const isScanning = (0, import_runtime_core63.ref)(false);
4934
+ const isAvailable = (0, import_runtime_core63.ref)(false);
4935
+ const error = (0, import_runtime_core63.ref)(null);
4685
4936
  const cleanups = [];
4686
4937
  NativeBridge.invokeNativeModule("Bluetooth", "getState").then((state) => {
4687
4938
  isAvailable.value = state === "poweredOn";
@@ -4760,7 +5011,7 @@ function useBluetooth() {
4760
5011
  ]);
4761
5012
  };
4762
5013
  }
4763
- (0, import_runtime_core60.onUnmounted)(() => {
5014
+ (0, import_runtime_core63.onUnmounted)(() => {
4764
5015
  if (isScanning.value) {
4765
5016
  NativeBridge.invokeNativeModule("Bluetooth", "stopScan").catch((err) => {
4766
5017
  if (__DEV__) console.warn("[vue-native] Bluetooth.stopScan failed:", err);
@@ -4786,7 +5037,7 @@ function useBluetooth() {
4786
5037
  }
4787
5038
 
4788
5039
  // src/composables/useCalendar.ts
4789
- var import_runtime_core61 = require("@vue/runtime-core");
5040
+ var import_runtime_core64 = require("@vue/runtime-core");
4790
5041
  function getErrorMessage6(error) {
4791
5042
  if (error instanceof Error) return error.message;
4792
5043
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4796,8 +5047,8 @@ function getErrorMessage6(error) {
4796
5047
  return String(error);
4797
5048
  }
4798
5049
  function useCalendar() {
4799
- const hasAccess = (0, import_runtime_core61.ref)(false);
4800
- const error = (0, import_runtime_core61.ref)(null);
5050
+ const hasAccess = (0, import_runtime_core64.ref)(false);
5051
+ const error = (0, import_runtime_core64.ref)(null);
4801
5052
  async function requestAccess() {
4802
5053
  try {
4803
5054
  const result = await NativeBridge.invokeNativeModule("Calendar", "requestAccess");
@@ -4830,7 +5081,7 @@ function useCalendar() {
4830
5081
  }
4831
5082
 
4832
5083
  // src/composables/useContacts.ts
4833
- var import_runtime_core62 = require("@vue/runtime-core");
5084
+ var import_runtime_core65 = require("@vue/runtime-core");
4834
5085
  function getErrorMessage7(error) {
4835
5086
  if (error instanceof Error) return error.message;
4836
5087
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4840,8 +5091,8 @@ function getErrorMessage7(error) {
4840
5091
  return String(error);
4841
5092
  }
4842
5093
  function useContacts() {
4843
- const hasAccess = (0, import_runtime_core62.ref)(false);
4844
- const error = (0, import_runtime_core62.ref)(null);
5094
+ const hasAccess = (0, import_runtime_core65.ref)(false);
5095
+ const error = (0, import_runtime_core65.ref)(null);
4845
5096
  async function requestAccess() {
4846
5097
  try {
4847
5098
  const result = await NativeBridge.invokeNativeModule("Contacts", "requestAccess");
@@ -4941,10 +5192,10 @@ function useFileDialog() {
4941
5192
  }
4942
5193
 
4943
5194
  // src/composables/useDragDrop.ts
4944
- var import_runtime_core63 = require("@vue/runtime-core");
5195
+ var import_runtime_core66 = require("@vue/runtime-core");
4945
5196
  function useDragDrop() {
4946
5197
  const { isMacOS } = usePlatform();
4947
- const isDragging = (0, import_runtime_core63.ref)(false);
5198
+ const isDragging = (0, import_runtime_core66.ref)(false);
4948
5199
  async function enableDropZone() {
4949
5200
  if (!isMacOS) return;
4950
5201
  await NativeBridge.invokeNativeModule("DragDrop", "enableDropZone", []);
@@ -4973,7 +5224,7 @@ function useDragDrop() {
4973
5224
  callback();
4974
5225
  });
4975
5226
  }
4976
- return { enableDropZone, onDrop, onDragEnter, onDragLeave, isDragging: (0, import_runtime_core63.readonly)(isDragging) };
5227
+ return { enableDropZone, onDrop, onDragEnter, onDragLeave, isDragging: (0, import_runtime_core66.readonly)(isDragging) };
4977
5228
  }
4978
5229
 
4979
5230
  // src/composables/useTeleport.ts
@@ -4985,7 +5236,7 @@ function useTeleport(target) {
4985
5236
  }
4986
5237
 
4987
5238
  // src/composables/useGesture.ts
4988
- var import_runtime_core64 = require("@vue/runtime-core");
5239
+ var import_runtime_core67 = require("@vue/runtime-core");
4989
5240
  function hasViewId2(value) {
4990
5241
  return typeof value === "object" && value !== null && "id" in value && typeof value.id === "number";
4991
5242
  }
@@ -5044,21 +5295,21 @@ var GestureManager = class {
5044
5295
  }
5045
5296
  };
5046
5297
  function useGesture(target, options = {}) {
5047
- const pan = (0, import_runtime_core64.ref)(null);
5048
- const pinch = (0, import_runtime_core64.ref)(null);
5049
- const rotate = (0, import_runtime_core64.ref)(null);
5050
- const swipeLeft = (0, import_runtime_core64.ref)(null);
5051
- const swipeRight = (0, import_runtime_core64.ref)(null);
5052
- const swipeUp = (0, import_runtime_core64.ref)(null);
5053
- const swipeDown = (0, import_runtime_core64.ref)(null);
5054
- const press = (0, import_runtime_core64.ref)(null);
5055
- const longPress = (0, import_runtime_core64.ref)(null);
5056
- const doubleTap = (0, import_runtime_core64.ref)(null);
5057
- const forceTouch = (0, import_runtime_core64.ref)(null);
5058
- const hover = (0, import_runtime_core64.ref)(null);
5059
- const gestureState = (0, import_runtime_core64.ref)(null);
5060
- const activeGesture = (0, import_runtime_core64.ref)(null);
5061
- const isGesturing = (0, import_runtime_core64.ref)(false);
5298
+ const pan = (0, import_runtime_core67.ref)(null);
5299
+ const pinch = (0, import_runtime_core67.ref)(null);
5300
+ const rotate = (0, import_runtime_core67.ref)(null);
5301
+ const swipeLeft = (0, import_runtime_core67.ref)(null);
5302
+ const swipeRight = (0, import_runtime_core67.ref)(null);
5303
+ const swipeUp = (0, import_runtime_core67.ref)(null);
5304
+ const swipeDown = (0, import_runtime_core67.ref)(null);
5305
+ const press = (0, import_runtime_core67.ref)(null);
5306
+ const longPress = (0, import_runtime_core67.ref)(null);
5307
+ const doubleTap = (0, import_runtime_core67.ref)(null);
5308
+ const forceTouch = (0, import_runtime_core67.ref)(null);
5309
+ const hover = (0, import_runtime_core67.ref)(null);
5310
+ const gestureState = (0, import_runtime_core67.ref)(null);
5311
+ const activeGesture = (0, import_runtime_core67.ref)(null);
5312
+ const isGesturing = (0, import_runtime_core67.ref)(false);
5062
5313
  const manager = new GestureManager();
5063
5314
  const cleanupFns = [];
5064
5315
  function attach(t) {
@@ -5213,7 +5464,7 @@ function useGesture(target, options = {}) {
5213
5464
  cleanupFns.push(dispose);
5214
5465
  }
5215
5466
  }
5216
- (0, import_runtime_core64.onUnmounted)(() => {
5467
+ (0, import_runtime_core67.onUnmounted)(() => {
5217
5468
  detach();
5218
5469
  });
5219
5470
  if (target !== void 0) {
@@ -5241,14 +5492,14 @@ function useGesture(target, options = {}) {
5241
5492
  };
5242
5493
  }
5243
5494
  function useComposedGestures(target, options = {}) {
5244
- const pan = (0, import_runtime_core64.ref)(null);
5245
- const pinch = (0, import_runtime_core64.ref)(null);
5246
- const rotate = (0, import_runtime_core64.ref)(null);
5247
- const gestureState = (0, import_runtime_core64.ref)(null);
5248
- const activeGesture = (0, import_runtime_core64.ref)(null);
5249
- const isGesturing = (0, import_runtime_core64.ref)(false);
5250
- const isPinchingAndRotating = (0, import_runtime_core64.ref)(false);
5251
- const isPanningAndPinching = (0, import_runtime_core64.ref)(false);
5495
+ const pan = (0, import_runtime_core67.ref)(null);
5496
+ const pinch = (0, import_runtime_core67.ref)(null);
5497
+ const rotate = (0, import_runtime_core67.ref)(null);
5498
+ const gestureState = (0, import_runtime_core67.ref)(null);
5499
+ const activeGesture = (0, import_runtime_core67.ref)(null);
5500
+ const isGesturing = (0, import_runtime_core67.ref)(false);
5501
+ const isPinchingAndRotating = (0, import_runtime_core67.ref)(false);
5502
+ const isPanningAndPinching = (0, import_runtime_core67.ref)(false);
5252
5503
  const manager = new GestureManager();
5253
5504
  manager.attach(target);
5254
5505
  const panConfig = typeof options.pan === "object" ? options.pan : {};
@@ -5283,7 +5534,7 @@ function useComposedGestures(target, options = {}) {
5283
5534
  isPinchingAndRotating.value = pinch.value !== null && (state.state === "began" || state.state === "changed");
5284
5535
  }, rotateConfig));
5285
5536
  }
5286
- (0, import_runtime_core64.onUnmounted)(() => {
5537
+ (0, import_runtime_core67.onUnmounted)(() => {
5287
5538
  for (const fn of cleanupFns) fn();
5288
5539
  manager.detach();
5289
5540
  });
@@ -5300,10 +5551,10 @@ function useComposedGestures(target, options = {}) {
5300
5551
  }
5301
5552
 
5302
5553
  // src/theme.ts
5303
- var import_runtime_core65 = require("@vue/runtime-core");
5554
+ var import_runtime_core68 = require("@vue/runtime-core");
5304
5555
  function createTheme(definition) {
5305
5556
  const key = /* @__PURE__ */ Symbol("vue-native-theme");
5306
- const ThemeProvider = (0, import_runtime_core65.defineComponent)({
5557
+ const ThemeProvider = (0, import_runtime_core68.defineComponent)({
5307
5558
  name: "ThemeProvider",
5308
5559
  props: {
5309
5560
  initialColorScheme: {
@@ -5312,8 +5563,8 @@ function createTheme(definition) {
5312
5563
  }
5313
5564
  },
5314
5565
  setup(props, { slots }) {
5315
- const colorScheme = (0, import_runtime_core65.ref)(props.initialColorScheme);
5316
- const theme = (0, import_runtime_core65.computed)(() => {
5566
+ const colorScheme = (0, import_runtime_core68.ref)(props.initialColorScheme);
5567
+ const theme = (0, import_runtime_core68.computed)(() => {
5317
5568
  return colorScheme.value === "dark" ? definition.dark : definition.light;
5318
5569
  });
5319
5570
  const ctx = {
@@ -5326,12 +5577,12 @@ function createTheme(definition) {
5326
5577
  colorScheme.value = scheme;
5327
5578
  }
5328
5579
  };
5329
- (0, import_runtime_core65.provide)(key, ctx);
5580
+ (0, import_runtime_core68.provide)(key, ctx);
5330
5581
  return () => slots.default?.();
5331
5582
  }
5332
5583
  });
5333
5584
  function useTheme() {
5334
- const ctx = (0, import_runtime_core65.inject)(key);
5585
+ const ctx = (0, import_runtime_core68.inject)(key);
5335
5586
  if (!ctx) {
5336
5587
  throw new Error(
5337
5588
  "[Vue Native] useTheme() was called outside of a <ThemeProvider>. Wrap your app root with <ThemeProvider> to provide theme context."
@@ -5342,7 +5593,7 @@ function createTheme(definition) {
5342
5593
  return { ThemeProvider, useTheme };
5343
5594
  }
5344
5595
  function createDynamicStyleSheet(theme, factory) {
5345
- return (0, import_runtime_core65.computed)(() => createStyleSheet(factory(theme.value)));
5596
+ return (0, import_runtime_core68.computed)(() => createStyleSheet(factory(theme.value)));
5346
5597
  }
5347
5598
 
5348
5599
  // src/index.ts
@@ -5376,20 +5627,65 @@ function createApp(rootComponent, rootProps) {
5376
5627
  console.warn(`[VueNative] Warning in ${componentName}: ${msg}`);
5377
5628
  };
5378
5629
  }
5630
+ let mountedRoot = null;
5631
+ let unregisterTeardown = null;
5632
+ let hasUnmounted = false;
5633
+ const unmount = app.unmount.bind(app);
5634
+ app.unmount = () => {
5635
+ const root = mountedRoot;
5636
+ const wasMounted = root !== null || app._instance !== null;
5637
+ if (!wasMounted) return;
5638
+ hasUnmounted = true;
5639
+ unregisterTeardown?.();
5640
+ unregisterTeardown = null;
5641
+ mountedRoot = null;
5642
+ unmount();
5643
+ if (root) {
5644
+ NativeBridge.removeChild(0, root.id);
5645
+ releaseNodeId(root.id);
5646
+ }
5647
+ };
5379
5648
  app.start = () => {
5649
+ if (mountedRoot) {
5650
+ return mountedRoot;
5651
+ }
5652
+ if (hasUnmounted) {
5653
+ throw new Error("[VueNative] This app has been unmounted and cannot be restarted. Create a new app instance.");
5654
+ }
5655
+ if (app._instance) {
5656
+ throw new Error("[VueNative] This app is already mounted. Use either app.mount() or app.start(), not both.");
5657
+ }
5380
5658
  const root = createNativeNode("__ROOT__");
5381
5659
  NativeBridge.createNode(root.id, "__ROOT__");
5382
5660
  NativeBridge.setRootView(root.id);
5383
- const vnode = (0, import_runtime_core66.createVNode)(rootComponent, rootProps);
5384
- vnode.appContext = app._context;
5385
- render(vnode, root);
5661
+ mountedRoot = root;
5662
+ try {
5663
+ app.mount(root);
5664
+ } catch (error) {
5665
+ mountedRoot = null;
5666
+ hasUnmounted = true;
5667
+ if (app._instance) {
5668
+ try {
5669
+ unmount();
5670
+ } catch {
5671
+ }
5672
+ }
5673
+ NativeBridge.removeChild(0, root.id);
5674
+ releaseNodeId(root.id);
5675
+ throw error;
5676
+ }
5677
+ if (mountedRoot === root) {
5678
+ unregisterTeardown = registerAppTeardown(() => app.unmount());
5679
+ }
5386
5680
  return root;
5387
5681
  };
5388
5682
  return app;
5389
5683
  }
5390
5684
  // Annotate the CommonJS export names for ESM import in node:
5391
5685
  0 && (module.exports = {
5686
+ Easing,
5392
5687
  ErrorBoundary,
5688
+ KeepAlive,
5393
5689
  NativeBridge,
5394
5690
  VActionSheet,
5395
5691
  VActivityIndicator,
@@ -5400,12 +5696,14 @@ function createApp(rootComponent, rootProps) {
5400
5696
  VDrawerItem,
5401
5697
  VDrawerSection,
5402
5698
  VDropdown,
5699
+ VErrorBoundary,
5403
5700
  VFlatList,
5404
5701
  VImage,
5405
5702
  VInput,
5406
5703
  VKeyboardAvoiding,
5407
5704
  VList,
5408
5705
  VModal,
5706
+ VOutlineView,
5409
5707
  VPicker,
5410
5708
  VPressable,
5411
5709
  VProgressBar,
@@ -5416,10 +5714,15 @@ function createApp(rootComponent, rootProps) {
5416
5714
  VSectionList,
5417
5715
  VSegmentedControl,
5418
5716
  VSlider,
5717
+ VSplitView,
5419
5718
  VStatusBar,
5719
+ VSuspense,
5420
5720
  VSwitch,
5421
5721
  VTabBar,
5422
5722
  VText,
5723
+ VToolbar,
5724
+ VTransition,
5725
+ VTransitionGroup,
5423
5726
  VVideo,
5424
5727
  VView,
5425
5728
  VWebView,
@@ -5432,6 +5735,7 @@ function createApp(rootComponent, rootProps) {
5432
5735
  createStyleSheet,
5433
5736
  createTextNode,
5434
5737
  createTheme,
5738
+ defineAsyncComponent,
5435
5739
  getRegisteredSharedElements,
5436
5740
  getSharedElementViewId,
5437
5741
  measureViewFrame,