@thelacanians/vue-native-runtime 0.6.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,3 @@
1
- // src/index.ts
2
- import { createVNode } from "@vue/runtime-core";
3
-
4
1
  // src/renderer.ts
5
2
  import { createRenderer } from "@vue/runtime-core";
6
3
 
@@ -66,6 +63,24 @@ function createCommentNode(_text) {
66
63
 
67
64
  // src/bridge.ts
68
65
  var bridgeGlobals = globalThis;
66
+ var appTeardowns = /* @__PURE__ */ new Set();
67
+ function registerAppTeardown(teardown) {
68
+ appTeardowns.add(teardown);
69
+ return () => {
70
+ appTeardowns.delete(teardown);
71
+ };
72
+ }
73
+ function teardownMountedApps() {
74
+ const teardowns = [...appTeardowns];
75
+ appTeardowns.clear();
76
+ for (const teardown of teardowns) {
77
+ try {
78
+ teardown();
79
+ } catch (err) {
80
+ console.error("[VueNative] Error unmounting app during teardown:", err);
81
+ }
82
+ }
83
+ }
69
84
  var _NativeBridgeImpl = class _NativeBridgeImpl {
70
85
  constructor() {
71
86
  /** Pending operations waiting to be flushed to native */
@@ -333,12 +348,12 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
333
348
  });
334
349
  }
335
350
  /**
336
- * Invoke a native module method synchronously.
337
- * This sends the operation immediately and expects no callback.
338
- * Use sparingly -- prefer the async variant.
351
+ * @deprecated Native module invocations cannot safely return a value through
352
+ * the batched JSON bridge. This compatibility alias now uses the reliable
353
+ * asynchronous callback path; migrate callers to invokeNativeModule().
339
354
  */
340
355
  invokeNativeModuleSync(moduleName, methodName, args = []) {
341
- this.enqueue("invokeNativeModuleSync", [moduleName, methodName, args]);
356
+ return this.invokeNativeModule(moduleName, methodName, args);
342
357
  }
343
358
  /**
344
359
  * Called from Swift via globalThis.__VN_resolveCallback when an async
@@ -409,15 +424,17 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
409
424
  payload = {};
410
425
  }
411
426
  const handlers = this.globalEventHandlers.get(eventName);
412
- if (handlers) {
413
- handlers.forEach((h33) => {
414
- try {
415
- h33(payload);
416
- } catch (err) {
417
- console.error(`[VueNative] Error in global event handler "${eventName}":`, err);
418
- }
419
- });
427
+ if (!handlers || handlers.size === 0) {
428
+ return false;
420
429
  }
430
+ handlers.forEach((h35) => {
431
+ try {
432
+ h35(payload);
433
+ } catch (err) {
434
+ console.error(`[VueNative] Error in global event handler "${eventName}":`, err);
435
+ }
436
+ });
437
+ return true;
421
438
  }
422
439
  // ---------------------------------------------------------------------------
423
440
  // Cleanup
@@ -450,17 +467,32 @@ bridgeGlobals.__VN_handleEvent = NativeBridge.handleNativeEvent.bind(NativeBridg
450
467
  bridgeGlobals.__VN_resolveCallback = NativeBridge.resolveCallback.bind(NativeBridge);
451
468
  bridgeGlobals.__VN_handleGlobalEvent = NativeBridge.handleGlobalEvent.bind(NativeBridge);
452
469
  bridgeGlobals.__VN_teardown = () => {
470
+ teardownMountedApps();
453
471
  NativeBridge.reset();
454
472
  resetNodeId();
455
473
  };
456
474
 
457
475
  // src/renderer.ts
458
476
  function toEventName(key) {
459
- return key.slice(2).toLowerCase();
477
+ const name = key.slice(2);
478
+ return name.charAt(0).toLowerCase() + name.slice(1);
460
479
  }
461
480
  function isEventHandler(value) {
462
481
  return typeof value === "function";
463
482
  }
483
+ function getEventHandler(value) {
484
+ if (isEventHandler(value)) {
485
+ return value;
486
+ }
487
+ if (Array.isArray(value) && value.length > 0 && value.every(isEventHandler)) {
488
+ return (payload) => {
489
+ for (const handler of value) {
490
+ handler(payload);
491
+ }
492
+ };
493
+ }
494
+ return null;
495
+ }
464
496
  function patchStyle(nodeId, prevStyle, nextStyle) {
465
497
  try {
466
498
  const prev = typeof prevStyle === "object" && prevStyle !== null ? prevStyle : {};
@@ -486,6 +518,14 @@ function patchStyle(nodeId, prevStyle, nextStyle) {
486
518
  console.error(`[VueNative] Error patching style on node ${nodeId}:`, err);
487
519
  }
488
520
  }
521
+ function releaseSubtree(node) {
522
+ const children = node.children.splice(0);
523
+ for (const child of children) {
524
+ child.parent = null;
525
+ releaseSubtree(child);
526
+ }
527
+ releaseNodeId(node.id);
528
+ }
489
529
  var nodeOps = {
490
530
  /**
491
531
  * Create a native element node.
@@ -522,8 +562,12 @@ var nodeOps = {
522
562
  * Set the text content of an element, replacing all its children.
523
563
  */
524
564
  setElementText(node, text) {
525
- for (const child of node.children) {
565
+ for (const child of [...node.children]) {
566
+ if (child.type !== "__COMMENT__") {
567
+ NativeBridge.removeChild(node.id, child.id);
568
+ }
526
569
  child.parent = null;
570
+ releaseSubtree(child);
527
571
  }
528
572
  node.children = [];
529
573
  NativeBridge.setElementText(node.id, text);
@@ -537,13 +581,15 @@ var nodeOps = {
537
581
  */
538
582
  patchProp(el, key, prevValue, nextValue) {
539
583
  try {
540
- if (key.startsWith("on") && key.length > 2 && key[2] === key[2].toUpperCase()) {
584
+ const previousHandler = getEventHandler(prevValue);
585
+ const nextHandler = getEventHandler(nextValue);
586
+ if (/^on[A-Z]/.test(key) && (previousHandler || nextHandler)) {
541
587
  const eventName = toEventName(key);
542
- if (prevValue) {
588
+ if (previousHandler) {
543
589
  NativeBridge.removeEventListener(el.id, eventName);
544
590
  }
545
- if (isEventHandler(nextValue)) {
546
- NativeBridge.addEventListener(el.id, eventName, nextValue);
591
+ if (nextHandler) {
592
+ NativeBridge.addEventListener(el.id, eventName, nextHandler);
547
593
  }
548
594
  return;
549
595
  }
@@ -618,7 +664,7 @@ var nodeOps = {
618
664
  } catch (err) {
619
665
  console.error(`[VueNative] Error removing node ${child.id}:`, err);
620
666
  }
621
- releaseNodeId(child.id);
667
+ releaseSubtree(child);
622
668
  }
623
669
  },
624
670
  /**
@@ -712,6 +758,10 @@ import { defineComponent as defineComponent3, h as h3 } from "@vue/runtime-core"
712
758
  var VButton = defineComponent3({
713
759
  name: "VButton",
714
760
  props: {
761
+ /** Convenience label rendered as VText when no default slot is provided. */
762
+ title: String,
763
+ /** Text styling for the title shorthand. */
764
+ titleStyle: Object,
715
765
  style: Object,
716
766
  disabled: {
717
767
  type: Boolean,
@@ -729,15 +779,25 @@ var VButton = defineComponent3({
729
779
  accessibilityState: Object
730
780
  },
731
781
  setup(props, { slots }) {
732
- return () => h3(
733
- "VButton",
734
- {
735
- ...props,
736
- onPress: props.disabled ? void 0 : props.onPress,
737
- onLongPress: props.disabled ? void 0 : props.onLongPress
738
- },
739
- slots.default?.()
740
- );
782
+ return () => {
783
+ const slotContent = slots.default?.();
784
+ const content = slotContent?.length ? slotContent : props.title !== void 0 ? [h3(VText, { style: props.titleStyle }, () => props.title)] : [];
785
+ return h3(
786
+ "VButton",
787
+ {
788
+ style: props.style,
789
+ disabled: props.disabled,
790
+ activeOpacity: props.activeOpacity,
791
+ onPress: props.disabled ? void 0 : props.onPress,
792
+ onLongPress: props.disabled ? void 0 : props.onLongPress,
793
+ accessibilityLabel: props.accessibilityLabel,
794
+ accessibilityRole: props.accessibilityRole,
795
+ accessibilityHint: props.accessibilityHint,
796
+ accessibilityState: props.accessibilityState
797
+ },
798
+ content
799
+ );
800
+ };
741
801
  }
742
802
  });
743
803
 
@@ -1072,6 +1132,10 @@ var VSafeArea = defineComponent10({
1072
1132
 
1073
1133
  // src/components/VSlider.ts
1074
1134
  import { defineComponent as defineComponent11, h as h11 } from "@vue/runtime-core";
1135
+ function getSliderValue(payload) {
1136
+ const value = typeof payload === "object" && payload !== null && "value" in payload ? payload.value : payload;
1137
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1138
+ }
1075
1139
  var VSlider = defineComponent11({
1076
1140
  name: "VSlider",
1077
1141
  props: {
@@ -1095,9 +1159,11 @@ var VSlider = defineComponent11({
1095
1159
  accessibilityRole: props.accessibilityRole,
1096
1160
  accessibilityHint: props.accessibilityHint,
1097
1161
  accessibilityState: props.accessibilityState,
1098
- onChange: (val) => {
1099
- emit("update:modelValue", val);
1100
- emit("change", val);
1162
+ onChange: (payload) => {
1163
+ const value = getSliderValue(payload);
1164
+ if (value === null) return;
1165
+ emit("update:modelValue", value);
1166
+ emit("change", value);
1101
1167
  }
1102
1168
  });
1103
1169
  }
@@ -1349,13 +1415,15 @@ var VAlertDialog = defineComponent14({
1349
1415
  }
1350
1416
  }
1351
1417
  return h14("VAlertDialog", {
1352
- visible: debouncedVisible.value,
1353
1418
  title: props.title,
1354
1419
  message: props.message,
1355
1420
  buttons: resolvedButtons,
1356
1421
  onConfirm: (event) => emit("confirm", event),
1357
1422
  onCancel: () => emit("cancel"),
1358
- onAction: (event) => emit("action", event)
1423
+ onAction: (event) => emit("action", event),
1424
+ // Native factories present synchronously when this becomes true. Keep
1425
+ // it last so content and handlers are installed before presentation.
1426
+ visible: debouncedVisible.value
1359
1427
  });
1360
1428
  };
1361
1429
  }
@@ -1401,9 +1469,12 @@ var VWebView = defineComponent16({
1401
1469
  return source;
1402
1470
  });
1403
1471
  return () => h16("VWebView", {
1472
+ // Native WKWebView factories begin navigation as soon as `source` is
1473
+ // applied. Keep this first so the initial navigation observes the
1474
+ // requested JavaScript policy as well as subsequent navigations.
1475
+ javaScriptEnabled: props.javaScriptEnabled,
1404
1476
  source: sanitizedSource.value,
1405
1477
  style: props.style,
1406
- javaScriptEnabled: props.javaScriptEnabled,
1407
1478
  onLoad: (event) => emit("load", event),
1408
1479
  onError: (event) => emit("error", event),
1409
1480
  onMessage: (event) => emit("message", event)
@@ -1497,12 +1568,14 @@ var VActionSheet = defineComponent20({
1497
1568
  emits: ["action", "cancel"],
1498
1569
  setup(props, { emit }) {
1499
1570
  return () => h20("VActionSheet", {
1500
- visible: props.visible,
1501
1571
  title: props.title,
1502
1572
  message: props.message,
1503
1573
  actions: props.actions,
1504
1574
  onAction: (e) => emit("action", e),
1505
- onCancel: () => emit("cancel")
1575
+ onCancel: () => emit("cancel"),
1576
+ // Presentation is triggered by the visible update on native. Apply it
1577
+ // only after content and event handlers have reached the bridge.
1578
+ visible: props.visible
1506
1579
  });
1507
1580
  }
1508
1581
  });
@@ -1837,12 +1910,14 @@ var VVideo = defineComponent27({
1837
1910
  loop: { type: Boolean, default: false },
1838
1911
  muted: { type: Boolean, default: false },
1839
1912
  paused: { type: Boolean, default: false },
1913
+ /** Reserved for native transport controls; currently has no effect. */
1840
1914
  controls: { type: Boolean, default: true },
1841
1915
  volume: { type: Number, default: 1 },
1842
1916
  resizeMode: {
1843
1917
  type: String,
1844
1918
  default: "cover"
1845
1919
  },
1920
+ /** Reserved for native poster rendering; currently has no effect. */
1846
1921
  poster: String,
1847
1922
  style: Object,
1848
1923
  testID: String,
@@ -1851,7 +1926,21 @@ var VVideo = defineComponent27({
1851
1926
  emits: ["ready", "play", "pause", "end", "error", "progress"],
1852
1927
  setup(props, { emit }) {
1853
1928
  return () => h27("VVideo", {
1854
- ...props,
1929
+ // Playback intent must reach native before a source can become ready.
1930
+ // In particular, the default paused=false must not override
1931
+ // autoplay=false by eagerly starting a newly-created player.
1932
+ autoplay: props.autoplay,
1933
+ paused: props.paused,
1934
+ source: props.source,
1935
+ loop: props.loop,
1936
+ volume: props.volume,
1937
+ muted: props.muted,
1938
+ controls: props.controls,
1939
+ resizeMode: props.resizeMode,
1940
+ poster: props.poster,
1941
+ style: props.style,
1942
+ testID: props.testID,
1943
+ accessibilityLabel: props.accessibilityLabel,
1855
1944
  onReady: (event) => emit("ready", event),
1856
1945
  onPlay: () => emit("play"),
1857
1946
  onPause: () => emit("pause"),
@@ -2058,23 +2147,35 @@ var VTabBar = defineComponent29({
2058
2147
  /** Currently active tab ID */
2059
2148
  activeTab: {
2060
2149
  type: String,
2061
- required: true
2150
+ default: void 0
2151
+ },
2152
+ /** Currently active tab name/id for v-model */
2153
+ modelValue: {
2154
+ type: String,
2155
+ default: void 0
2062
2156
  },
2063
2157
  /** Position: 'top' | 'bottom' */
2064
2158
  position: {
2065
2159
  type: String,
2066
2160
  default: "bottom"
2067
- }
2161
+ },
2162
+ activeColor: { type: String, default: "#007AFF" },
2163
+ inactiveColor: { type: String, default: "#8E8E93" },
2164
+ backgroundColor: { type: String, default: "#fff" }
2068
2165
  },
2069
- emits: ["change"],
2166
+ emits: ["change", "update:modelValue"],
2070
2167
  setup(props, { emit }) {
2071
- const activeTab = ref6(props.activeTab);
2168
+ const activeTab = ref6(props.modelValue ?? props.activeTab ?? "");
2072
2169
  watch4(() => props.activeTab, (newVal) => {
2073
- activeTab.value = newVal;
2170
+ if (newVal !== void 0) activeTab.value = newVal;
2171
+ });
2172
+ watch4(() => props.modelValue, (newVal) => {
2173
+ if (newVal !== void 0) activeTab.value = newVal;
2074
2174
  });
2075
2175
  const switchTab = (tabId) => {
2076
2176
  activeTab.value = tabId;
2077
2177
  emit("change", tabId);
2178
+ emit("update:modelValue", tabId);
2078
2179
  };
2079
2180
  return () => h29(VView, {
2080
2181
  style: {
@@ -2082,35 +2183,36 @@ var VTabBar = defineComponent29({
2082
2183
  [props.position]: 0,
2083
2184
  left: 0,
2084
2185
  right: 0,
2085
- backgroundColor: "#fff",
2086
- borderTopWidth: 1,
2087
- borderTopColor: "#e0e0e0",
2186
+ backgroundColor: props.backgroundColor,
2187
+ ...props.position === "top" ? { borderBottomWidth: 1, borderBottomColor: "#e0e0e0" } : { borderTopWidth: 1, borderTopColor: "#e0e0e0" },
2088
2188
  flexDirection: "row",
2089
2189
  height: 60
2090
2190
  }
2091
2191
  }, () => props.tabs.map((tab) => {
2092
- const isActive = activeTab.value === tab.id;
2192
+ const tabId = tab.name ?? tab.id;
2193
+ if (tabId === void 0 || tabId === "") return null;
2194
+ const isActive = activeTab.value === tabId;
2093
2195
  return h29(VPressable, {
2094
- key: tab.id,
2196
+ key: tabId,
2095
2197
  style: {
2096
2198
  flex: 1,
2097
2199
  justifyContent: "center",
2098
2200
  alignItems: "center"
2099
2201
  },
2100
- onPress: () => switchTab(tab.id),
2202
+ onPress: () => switchTab(tabId),
2101
2203
  accessibilityLabel: tab.label,
2102
2204
  accessibilityRole: "tab",
2103
2205
  accessibilityState: { selected: isActive }
2104
2206
  }, () => [
2105
- tab.icon ? h29(VText, { style: { fontSize: 24, marginBottom: 4 } }, () => tab.icon) : null,
2207
+ tab.icon ? h29(VText, { style: { fontSize: 24, marginBottom: 4, color: isActive ? props.activeColor : props.inactiveColor } }, () => tab.icon) : null,
2106
2208
  h29(VText, {
2107
2209
  style: {
2108
2210
  fontSize: 12,
2109
2211
  fontWeight: isActive ? "600" : "400",
2110
- color: isActive ? "#007AFF" : "#8E8E93"
2212
+ color: isActive ? props.activeColor : props.inactiveColor
2111
2213
  }
2112
2214
  }, () => tab.label),
2113
- tab.badge ? h29(VView, {
2215
+ tab.badge !== void 0 && tab.badge !== null && tab.badge !== "" ? h29(VView, {
2114
2216
  style: {
2115
2217
  position: "absolute",
2116
2218
  top: 8,
@@ -2135,10 +2237,82 @@ var VTabBar = defineComponent29({
2135
2237
  }
2136
2238
  });
2137
2239
 
2240
+ // src/components/VToolbar.ts
2241
+ import { defineComponent as defineComponent30, h as h30 } from "@vue/runtime-core";
2242
+ var VToolbar = defineComponent30({
2243
+ name: "VToolbar",
2244
+ props: {
2245
+ items: { type: Array, required: true },
2246
+ displayMode: {
2247
+ type: String,
2248
+ default: "iconAndLabel"
2249
+ },
2250
+ showsBaselineSeparator: { type: Boolean, default: true },
2251
+ style: Object
2252
+ },
2253
+ emits: ["itemClick"],
2254
+ setup(props, { emit }) {
2255
+ return () => h30("VToolbar", {
2256
+ ...props,
2257
+ onItemClick: (e) => emit("itemClick", e)
2258
+ });
2259
+ }
2260
+ });
2261
+
2262
+ // src/components/VSplitView.ts
2263
+ import { defineComponent as defineComponent31, h as h31 } from "@vue/runtime-core";
2264
+ var VSplitView = defineComponent31({
2265
+ name: "VSplitView",
2266
+ props: {
2267
+ direction: {
2268
+ type: String,
2269
+ default: "horizontal"
2270
+ },
2271
+ dividerStyle: {
2272
+ type: String,
2273
+ default: "thin"
2274
+ },
2275
+ dividerColor: String,
2276
+ dividerPosition: Number,
2277
+ style: Object
2278
+ },
2279
+ emits: ["resize"],
2280
+ setup(props, { emit, slots }) {
2281
+ return () => h31("VSplitView", {
2282
+ ...props,
2283
+ onResize: (e) => emit("resize", e)
2284
+ }, slots.default?.());
2285
+ }
2286
+ });
2287
+
2288
+ // src/components/VOutlineView.ts
2289
+ import { defineComponent as defineComponent32, h as h32 } from "@vue/runtime-core";
2290
+ var VOutlineView = defineComponent32({
2291
+ name: "VOutlineView",
2292
+ props: {
2293
+ data: { type: Array, required: true },
2294
+ expandAll: { type: Boolean, default: false },
2295
+ selectionMode: {
2296
+ type: String,
2297
+ default: "single"
2298
+ },
2299
+ style: Object
2300
+ },
2301
+ emits: ["select", "expand", "collapse"],
2302
+ setup(props, { emit }) {
2303
+ return () => h32("VOutlineView", {
2304
+ ...props,
2305
+ onSelect: (e) => emit("select", e),
2306
+ onExpand: (e) => emit("expand", e),
2307
+ onCollapse: (e) => emit("collapse", e)
2308
+ });
2309
+ }
2310
+ });
2311
+
2138
2312
  // src/components/VDrawer.ts
2139
- import { defineComponent as defineComponent30, h as h30, inject, provide, ref as ref7, watch as watch5 } from "@vue/runtime-core";
2313
+ import { defineComponent as defineComponent33, h as h33, inject, provide, ref as ref7, watch as watch5 } from "@vue/runtime-core";
2140
2314
  var drawerContextKey = /* @__PURE__ */ Symbol("VDrawerContext");
2141
- var VDrawer = defineComponent30({
2315
+ var VDrawer = defineComponent33({
2142
2316
  name: "VDrawer",
2143
2317
  props: {
2144
2318
  /** Whether the drawer is open */
@@ -2156,19 +2330,32 @@ var VDrawer = defineComponent30({
2156
2330
  type: Number,
2157
2331
  default: 280
2158
2332
  },
2333
+ /** Backdrop color shown behind the drawer */
2334
+ overlayColor: {
2335
+ type: String,
2336
+ default: "rgba(0, 0, 0, 0.5)"
2337
+ },
2159
2338
  /** Close on item press */
2160
2339
  closeOnPress: {
2161
2340
  type: Boolean,
2162
2341
  default: true
2342
+ },
2343
+ /** Close when the backdrop is pressed */
2344
+ closeOnPressOutside: {
2345
+ type: Boolean,
2346
+ default: true
2163
2347
  }
2164
2348
  },
2165
- emits: ["update:open", "close"],
2349
+ emits: ["update:open", "open", "close"],
2166
2350
  setup(props, { attrs, slots, emit }) {
2167
2351
  const isOpen = ref7(props.open);
2168
2352
  watch5(() => props.open, (value) => {
2353
+ if (isOpen.value === value) return;
2169
2354
  isOpen.value = value;
2355
+ emit(value ? "open" : "close");
2170
2356
  });
2171
2357
  const closeDrawer = () => {
2358
+ if (!isOpen.value) return;
2172
2359
  isOpen.value = false;
2173
2360
  emit("update:open", false);
2174
2361
  emit("close");
@@ -2187,7 +2374,7 @@ var VDrawer = defineComponent30({
2187
2374
  left: 0,
2188
2375
  right: 0,
2189
2376
  bottom: 0,
2190
- backgroundColor: "rgba(0, 0, 0, 0.5)",
2377
+ backgroundColor: props.overlayColor,
2191
2378
  opacity: isOpen.value ? 1 : 0,
2192
2379
  zIndex: 999
2193
2380
  };
@@ -2217,20 +2404,24 @@ var VDrawer = defineComponent30({
2217
2404
  if (slots.default) {
2218
2405
  drawerChildren.push(...slots.default({ close: closeDrawer }) ?? []);
2219
2406
  }
2407
+ if (slots.footer) {
2408
+ drawerChildren.push(...slots.footer());
2409
+ }
2220
2410
  return [
2221
2411
  // Overlay
2222
- isOpen.value ? h30(VPressable, {
2412
+ isOpen.value ? h33(VPressable, {
2223
2413
  style: overlayStyle,
2224
- onPress: closeDrawer,
2225
- accessibilityLabel: "Close menu"
2414
+ onPress: props.closeOnPressOutside ? closeDrawer : void 0,
2415
+ accessibilityLabel: "Close menu",
2416
+ accessibilityState: { disabled: !props.closeOnPressOutside }
2226
2417
  }) : null,
2227
2418
  // Drawer
2228
- h30(VView, drawerProps, () => drawerChildren)
2419
+ h33(VView, drawerProps, () => drawerChildren)
2229
2420
  ];
2230
2421
  };
2231
2422
  }
2232
2423
  });
2233
- var VDrawerItem = defineComponent30({
2424
+ var VDrawerItem = defineComponent33({
2234
2425
  name: "VDrawerItem",
2235
2426
  props: {
2236
2427
  /** Icon (emoji or icon name) */
@@ -2243,6 +2434,11 @@ var VDrawerItem = defineComponent30({
2243
2434
  type: String,
2244
2435
  required: true
2245
2436
  },
2437
+ /** Whether this item represents the current destination */
2438
+ active: {
2439
+ type: Boolean,
2440
+ default: false
2441
+ },
2246
2442
  /** Badge count */
2247
2443
  badge: {
2248
2444
  type: [Number, String],
@@ -2264,25 +2460,26 @@ var VDrawerItem = defineComponent30({
2264
2460
  drawer.close();
2265
2461
  }
2266
2462
  };
2267
- return () => h30(VPressable, {
2463
+ return () => h33(VPressable, {
2268
2464
  style: {
2269
2465
  flexDirection: "row",
2270
2466
  alignItems: "center",
2271
2467
  padding: 16,
2272
2468
  borderBottomWidth: 1,
2273
2469
  borderBottomColor: "#f0f0f0",
2470
+ backgroundColor: props.active ? "#EAF3FF" : "transparent",
2274
2471
  opacity: props.disabled ? 0.5 : 1
2275
2472
  },
2276
2473
  onPress: handlePress,
2277
2474
  disabled: props.disabled,
2278
2475
  accessibilityLabel: props.label,
2279
2476
  accessibilityRole: "menuitem",
2280
- accessibilityState: { disabled: props.disabled }
2477
+ accessibilityState: { disabled: props.disabled, selected: props.active }
2281
2478
  }, () => {
2282
2479
  const children = [];
2283
2480
  if (props.icon) {
2284
2481
  children.push(
2285
- h30(VText, {
2482
+ h33(VText, {
2286
2483
  style: {
2287
2484
  fontSize: 24,
2288
2485
  marginRight: 16,
@@ -2293,17 +2490,17 @@ var VDrawerItem = defineComponent30({
2293
2490
  );
2294
2491
  }
2295
2492
  children.push(
2296
- h30(VText, {
2493
+ h33(VText, {
2297
2494
  style: {
2298
2495
  flex: 1,
2299
2496
  fontSize: 16,
2300
- color: props.disabled ? "#999" : "#333"
2497
+ color: props.disabled ? "#999" : props.active ? "#007AFF" : "#333"
2301
2498
  }
2302
2499
  }, () => props.label)
2303
2500
  );
2304
- if (props.badge) {
2501
+ if (props.badge !== null && props.badge !== void 0 && props.badge !== "") {
2305
2502
  children.push(
2306
- h30(VView, {
2503
+ h33(VView, {
2307
2504
  style: {
2308
2505
  backgroundColor: "#007AFF",
2309
2506
  borderRadius: 12,
@@ -2313,7 +2510,7 @@ var VDrawerItem = defineComponent30({
2313
2510
  alignItems: "center",
2314
2511
  paddingHorizontal: 8
2315
2512
  }
2316
- }, () => h30(VText, {
2513
+ }, () => h33(VText, {
2317
2514
  style: {
2318
2515
  color: "#fff",
2319
2516
  fontSize: 12,
@@ -2329,7 +2526,7 @@ var VDrawerItem = defineComponent30({
2329
2526
  });
2330
2527
  }
2331
2528
  });
2332
- var VDrawerSection = defineComponent30({
2529
+ var VDrawerSection = defineComponent33({
2333
2530
  name: "VDrawerSection",
2334
2531
  props: {
2335
2532
  /** Section title */
@@ -2339,7 +2536,7 @@ var VDrawerSection = defineComponent30({
2339
2536
  }
2340
2537
  },
2341
2538
  setup(props, { slots }) {
2342
- return () => h30(VView, {
2539
+ return () => h33(VView, {
2343
2540
  style: {
2344
2541
  paddingVertical: 8,
2345
2542
  paddingHorizontal: 16,
@@ -2351,7 +2548,7 @@ var VDrawerSection = defineComponent30({
2351
2548
  const children = [];
2352
2549
  if (props.title) {
2353
2550
  children.push(
2354
- h30(VText, {
2551
+ h33(VText, {
2355
2552
  style: {
2356
2553
  fontSize: 13,
2357
2554
  fontWeight: "600",
@@ -2373,7 +2570,7 @@ VDrawer.Item = VDrawerItem;
2373
2570
  VDrawer.Section = VDrawerSection;
2374
2571
 
2375
2572
  // src/components/VTransition.ts
2376
- import { defineComponent as defineComponent31, h as h31, ref as ref8 } from "@vue/runtime-core";
2573
+ import { BaseTransition, defineComponent as defineComponent34, h as h34, ref as ref8 } from "@vue/runtime-core";
2377
2574
 
2378
2575
  // src/composables/useAnimation.ts
2379
2576
  var Easing = {
@@ -2453,9 +2650,23 @@ function resolveAnimationTarget(el) {
2453
2650
  if (typeof el === "object" && "id" in el) return el.id;
2454
2651
  return void 0;
2455
2652
  }
2456
- var VTransition = defineComponent31({
2653
+ function getElementFromVNode(vnode) {
2654
+ try {
2655
+ const el = vnode.el;
2656
+ if (!el) return void 0;
2657
+ return resolveAnimationTarget(el);
2658
+ } catch {
2659
+ return void 0;
2660
+ }
2661
+ }
2662
+ function resolveTransitionTarget(el) {
2663
+ return resolveAnimationTarget(el) ?? getElementFromVNode(el);
2664
+ }
2665
+ var VTransition = defineComponent34({
2457
2666
  name: "VTransition",
2667
+ inheritAttrs: false,
2458
2668
  props: {
2669
+ show: { type: Boolean, default: true },
2459
2670
  name: { type: String, default: "" },
2460
2671
  appear: { type: Boolean, default: false },
2461
2672
  persist: { type: Boolean, default: false },
@@ -2473,31 +2684,56 @@ var VTransition = defineComponent31({
2473
2684
  appearClass: { type: String, default: "" },
2474
2685
  appearActiveClass: { type: String, default: "" },
2475
2686
  appearToClass: { type: String, default: "" },
2476
- duration: [Number, Object]
2687
+ duration: [Number, Object],
2688
+ enterFrom: Object,
2689
+ enterTo: Object,
2690
+ leaveFrom: Object,
2691
+ leaveTo: Object,
2692
+ easing: { type: String, default: "ease" }
2477
2693
  },
2478
- setup(transitionProps, { slots, expose }) {
2694
+ setup(transitionProps, { slots, expose, attrs }) {
2479
2695
  const { timing } = useAnimation();
2480
2696
  const isAppearing = ref8(false);
2481
2697
  const isLeaving = ref8(false);
2482
2698
  const hasEntered = ref8(!transitionProps.appear);
2483
- function getElementFromVNode(vnode) {
2484
- try {
2485
- const el = vnode.el;
2486
- if (!el) return void 0;
2487
- return resolveAnimationTarget(el);
2488
- } catch {
2489
- return void 0;
2699
+ function presetStyles() {
2700
+ if (transitionProps.name === "slide") {
2701
+ return {
2702
+ enterFrom: { opacity: 0, translateX: -30 },
2703
+ enterTo: { opacity: 1, translateX: 0 },
2704
+ leaveFrom: { opacity: 1, translateX: 0 },
2705
+ leaveTo: { opacity: 0, translateX: -30 }
2706
+ };
2707
+ }
2708
+ return {
2709
+ enterFrom: { opacity: 0 },
2710
+ enterTo: { opacity: 1 },
2711
+ leaveFrom: { opacity: 1 },
2712
+ leaveTo: { opacity: 0 }
2713
+ };
2714
+ }
2715
+ function callListener(name, ...args) {
2716
+ const listener = attrs[name];
2717
+ if (Array.isArray(listener)) {
2718
+ listener.forEach((fn) => typeof fn === "function" && fn(...args));
2719
+ } else if (typeof listener === "function") {
2720
+ listener(...args);
2490
2721
  }
2491
2722
  }
2492
2723
  async function doEnter(el) {
2493
- const viewId = getElementFromVNode(el);
2494
- if (!viewId) return;
2724
+ const viewId = resolveTransitionTarget(el);
2725
+ if (viewId == null) return;
2495
2726
  isAppearing.value = true;
2496
2727
  const enterDuration = typeof transitionProps.duration === "object" ? transitionProps.duration.enter ?? DefaultDuration : transitionProps.duration ?? DefaultDuration;
2497
- const enterStyles = { opacity: 1 };
2728
+ const presets = presetStyles();
2729
+ const enterFrom = transitionProps.enterFrom ?? presets.enterFrom;
2730
+ const enterTo = transitionProps.enterTo ?? presets.enterTo;
2498
2731
  try {
2499
- await timing(viewId, { opacity: 0 }, { duration: 0 });
2500
- await timing(viewId, enterStyles, { duration: enterDuration, easing: "easeOut" });
2732
+ await timing(viewId, enterFrom, { duration: 0 });
2733
+ await timing(viewId, enterTo, {
2734
+ duration: enterDuration,
2735
+ easing: transitionProps.easing
2736
+ });
2501
2737
  isAppearing.value = false;
2502
2738
  hasEntered.value = true;
2503
2739
  } catch (e) {
@@ -2507,47 +2743,60 @@ var VTransition = defineComponent31({
2507
2743
  }
2508
2744
  }
2509
2745
  async function doLeave(el) {
2510
- const viewId = getElementFromVNode(el);
2511
- if (!viewId) return;
2746
+ const viewId = resolveTransitionTarget(el);
2747
+ if (viewId == null) return;
2512
2748
  isLeaving.value = true;
2513
2749
  const leaveDuration = typeof transitionProps.duration === "object" ? transitionProps.duration.leave ?? DefaultDuration : transitionProps.duration ?? DefaultDuration;
2514
2750
  try {
2515
- await timing(viewId, { opacity: 0 }, { duration: leaveDuration, easing: "easeIn" });
2751
+ const presets = presetStyles();
2752
+ await timing(viewId, transitionProps.leaveFrom ?? presets.leaveFrom, { duration: 0 });
2753
+ await timing(viewId, transitionProps.leaveTo ?? presets.leaveTo, {
2754
+ duration: leaveDuration,
2755
+ easing: transitionProps.easing
2756
+ });
2516
2757
  } catch (e) {
2517
2758
  console.warn("[VueNative Transition] leave animation failed:", e);
2518
2759
  } finally {
2519
2760
  isLeaving.value = false;
2520
2761
  }
2521
2762
  }
2522
- function onEnter(_el, done) {
2523
- if (!hasEntered.value || transitionProps.appear) {
2524
- doEnter(_el).then(() => done());
2525
- } else {
2526
- done();
2527
- }
2763
+ function onBeforeEnter(el) {
2764
+ callListener("onBeforeEnter", el);
2765
+ }
2766
+ function onEnter(el, done) {
2767
+ callListener("onEnter", el);
2768
+ doEnter(el).then(() => done());
2528
2769
  }
2529
2770
  function onLeave(el, done) {
2771
+ callListener("onLeave", el);
2530
2772
  doLeave(el).then(() => done());
2531
2773
  }
2532
2774
  function onAfterEnter() {
2775
+ callListener("onAfterEnter");
2533
2776
  }
2534
2777
  function onAfterLeave() {
2778
+ callListener("onAfterLeave");
2535
2779
  }
2536
2780
  function onEnterCancelled() {
2537
2781
  isAppearing.value = false;
2782
+ callListener("onEnterCancelled");
2538
2783
  }
2539
2784
  function onLeaveCancelled() {
2540
2785
  isLeaving.value = false;
2786
+ callListener("onLeaveCancelled");
2541
2787
  }
2542
2788
  function onAppear(el, done) {
2543
2789
  isAppearing.value = true;
2790
+ callListener("onAppear", el);
2544
2791
  doEnter(el).then(() => done());
2545
2792
  }
2546
2793
  function onAfterAppear() {
2547
2794
  isAppearing.value = false;
2795
+ callListener("onAfterAppear");
2548
2796
  }
2549
2797
  expose({
2550
2798
  onEnter,
2799
+ onBeforeEnter,
2551
2800
  onLeave,
2552
2801
  onAfterEnter,
2553
2802
  onAfterLeave,
@@ -2560,30 +2809,15 @@ var VTransition = defineComponent31({
2560
2809
  hasEntered
2561
2810
  });
2562
2811
  return () => {
2563
- const children = slots.default?.() ?? [];
2564
- const hasDefault = children.length > 0;
2565
- if (!hasDefault) {
2566
- return h31("", {}, []);
2567
- }
2568
- let finalChildren = children;
2569
- if (transitionProps.mode === "out-in") {
2570
- if (isLeaving.value) {
2571
- finalChildren = [children[children.length - 1]];
2572
- } else if (!hasEntered.value) {
2573
- finalChildren = [];
2574
- }
2575
- }
2576
- if (transitionProps.mode === "in-out") {
2577
- if (isAppearing.value && children.length > 1) {
2578
- finalChildren = [children[0]];
2579
- }
2580
- }
2581
- return h31("Transition", {
2812
+ const finalChildren = transitionProps.show ? slots.default?.() ?? [] : [];
2813
+ return h34(BaseTransition, {
2582
2814
  name: transitionProps.name || "v",
2583
2815
  appear: transitionProps.appear,
2584
2816
  persist: transitionProps.persist || transitionProps.name === "persist",
2817
+ mode: transitionProps.mode === "default" ? void 0 : transitionProps.mode,
2585
2818
  css: transitionProps.css,
2586
2819
  type: transitionProps.type,
2820
+ onBeforeEnter,
2587
2821
  onEnter,
2588
2822
  onLeave,
2589
2823
  onAfterEnter,
@@ -2596,7 +2830,7 @@ var VTransition = defineComponent31({
2596
2830
  };
2597
2831
  }
2598
2832
  });
2599
- var VTransitionGroup = defineComponent31({
2833
+ var VTransitionGroup = defineComponent34({
2600
2834
  name: "VTransitionGroup",
2601
2835
  props: {
2602
2836
  tag: { type: String, default: void 0 },
@@ -2610,185 +2844,61 @@ var VTransitionGroup = defineComponent31({
2610
2844
  const { timing } = useAnimation();
2611
2845
  function onMove(_el) {
2612
2846
  }
2847
+ function onBeforeEnter(el) {
2848
+ const viewId = resolveTransitionTarget(el);
2849
+ if (viewId == null) return;
2850
+ timing(viewId, { opacity: 0 }, { duration: 0 }).catch(() => {
2851
+ });
2852
+ }
2613
2853
  function onEnter(el, done) {
2614
- const viewId = resolveAnimationTarget(el);
2615
- if (!viewId) {
2854
+ const viewId = resolveTransitionTarget(el);
2855
+ if (viewId == null) {
2616
2856
  done();
2617
2857
  return;
2618
2858
  }
2619
2859
  timing(viewId, { opacity: 1 }, { duration: groupProps.duration ?? 300, easing: "easeOut" }).then(() => done()).catch(() => done());
2620
2860
  }
2621
2861
  function onLeave(el, done) {
2622
- const viewId = resolveAnimationTarget(el);
2623
- if (!viewId) {
2862
+ const viewId = resolveTransitionTarget(el);
2863
+ if (viewId == null) {
2624
2864
  done();
2625
2865
  return;
2626
2866
  }
2627
2867
  timing(viewId, { opacity: 0 }, { duration: groupProps.duration ?? 300, easing: "easeIn" }).then(() => done()).catch(() => done());
2628
2868
  }
2629
2869
  expose({
2870
+ onBeforeEnter,
2630
2871
  onEnter,
2631
2872
  onLeave,
2632
2873
  onMove
2633
2874
  });
2634
2875
  return () => {
2635
2876
  const children = slots.default?.() ?? [];
2636
- return h31("TransitionGroup", {
2637
- tag: groupProps.tag,
2877
+ return h34(groupProps.tag || "VView", {}, children.map((child, index) => h34(BaseTransition, {
2878
+ key: child.key ?? index,
2638
2879
  name: groupProps.name,
2639
2880
  appear: groupProps.appear,
2640
2881
  persist: groupProps.persist,
2641
- moveClass: groupProps.moveClass,
2882
+ css: false,
2883
+ onBeforeEnter,
2642
2884
  onEnter,
2643
- onLeave,
2644
- onMove
2645
- }, () => children);
2885
+ onLeave
2886
+ }, () => [child])));
2646
2887
  };
2647
2888
  }
2648
2889
  });
2649
2890
 
2650
2891
  // src/components/KeepAlive.ts
2651
- import {
2652
- defineComponent as defineComponent32,
2653
- onUnmounted as onUnmounted3,
2654
- watch as watch6
2655
- } from "@vue/runtime-core";
2656
- function matches(pattern, name) {
2657
- if (!pattern) return false;
2658
- if (typeof pattern === "string") {
2659
- return pattern === name;
2660
- }
2661
- if (pattern instanceof RegExp) {
2662
- return pattern.test(name);
2663
- }
2664
- if (Array.isArray(pattern)) {
2665
- return pattern.includes(name);
2666
- }
2667
- return false;
2668
- }
2669
- function getComponentName(vnode) {
2670
- const component = vnode.type;
2671
- if (typeof component === "object" && component !== null && "name" in component) {
2672
- return component.name;
2673
- }
2674
- if (typeof component === "function") {
2675
- return component.name;
2676
- }
2677
- return void 0;
2678
- }
2679
- var KeepAlive = defineComponent32({
2680
- name: "KeepAlive",
2681
- props: {
2682
- include: [String, RegExp, Array],
2683
- exclude: [String, RegExp, Array],
2684
- max: [Number, String]
2685
- },
2686
- setup(props, { slots }) {
2687
- const cache = /* @__PURE__ */ new Map();
2688
- const keys = /* @__PURE__ */ new Set();
2689
- const maxCacheSize = typeof props.max === "string" ? parseInt(props.max, 10) : props.max;
2690
- function pruneCache(filter) {
2691
- cache.forEach((_, key) => {
2692
- if (!filter(key)) {
2693
- cache.delete(key);
2694
- keys.delete(key);
2695
- }
2696
- });
2697
- }
2698
- function pruneCacheEntry(key) {
2699
- cache.delete(key);
2700
- keys.delete(key);
2701
- }
2702
- watch6(
2703
- () => [props.include, props.exclude],
2704
- () => {
2705
- if (!props.include && !props.exclude) return;
2706
- pruneCache((name) => {
2707
- if (props.include && !matches(props.include, name)) return false;
2708
- if (props.exclude && matches(props.exclude, name)) return false;
2709
- return true;
2710
- });
2711
- }
2712
- );
2713
- onUnmounted3(() => {
2714
- cache.clear();
2715
- keys.clear();
2716
- });
2717
- return () => {
2718
- const children = slots.default?.() ?? [];
2719
- if (!children.length) return children;
2720
- const vnode = children[0];
2721
- const name = getComponentName(vnode) ?? String(vnode.type);
2722
- const key = name;
2723
- if (props.include && !matches(props.include, name)) {
2724
- return vnode;
2725
- }
2726
- if (props.exclude && matches(props.exclude, name)) {
2727
- return vnode;
2728
- }
2729
- const cached = cache.get(key);
2730
- if (cached) {
2731
- keys.delete(key);
2732
- keys.add(key);
2733
- return cached.vnode;
2734
- }
2735
- if (maxCacheSize && cache.size >= maxCacheSize) {
2736
- const firstKey = keys.values().next().value;
2737
- if (firstKey) {
2738
- pruneCacheEntry(firstKey);
2739
- }
2740
- }
2741
- cache.set(key, { vnode, key });
2742
- keys.add(key);
2743
- return vnode;
2744
- };
2745
- }
2746
- });
2747
- KeepAlive.isKeepAlive = true;
2892
+ import { KeepAlive as VueKeepAlive } from "@vue/runtime-core";
2893
+ var KeepAlive = VueKeepAlive;
2748
2894
 
2749
2895
  // src/components/VSuspense.ts
2750
2896
  import {
2751
- defineComponent as defineComponent33,
2752
- h as h32,
2753
- ref as ref9,
2754
- onMounted,
2755
- shallowRef,
2756
- watch as watch7,
2757
- provide as provide2,
2758
- inject as inject2
2897
+ Suspense,
2898
+ defineAsyncComponent as vueDefineAsyncComponent
2759
2899
  } from "@vue/runtime-core";
2760
- var suspenseContextKey = /* @__PURE__ */ Symbol("suspense");
2761
- var VSuspense = defineComponent33({
2762
- name: "Suspense",
2763
- props: {
2764
- timeout: { type: Number, default: 3e4 }
2765
- },
2766
- setup(_props, { slots }) {
2767
- const hasError = ref9(false);
2768
- const error = shallowRef(null);
2769
- const pendingCount = ref9(0);
2770
- const context = {
2771
- hasError,
2772
- error,
2773
- pendingCount,
2774
- resolve: () => {
2775
- pendingCount.value--;
2776
- },
2777
- reject: (err) => {
2778
- hasError.value = true;
2779
- error.value = err;
2780
- }
2781
- };
2782
- provide2(suspenseContextKey, context);
2783
- return () => {
2784
- if (hasError.value) {
2785
- return slots.fallback?.({ error: error.value }) ?? null;
2786
- }
2787
- const defaultSlots = slots.default?.() ?? [];
2788
- return defaultSlots;
2789
- };
2790
- }
2791
- });
2900
+ var VSuspense = Suspense;
2901
+ var defineAsyncComponent = vueDefineAsyncComponent;
2792
2902
 
2793
2903
  // src/components/index.ts
2794
2904
  var builtInComponents = {
@@ -2821,18 +2931,24 @@ var builtInComponents = {
2821
2931
  VVideo,
2822
2932
  VFlatList,
2823
2933
  VTabBar,
2934
+ VToolbar,
2935
+ VSplitView,
2936
+ VOutlineView,
2824
2937
  VDrawer,
2825
2938
  VDrawerItem,
2826
2939
  VDrawerSection,
2827
2940
  VTransition,
2828
2941
  VTransitionGroup,
2829
2942
  KeepAlive,
2943
+ // Vue types Suspense separately from ordinary components even though it is
2944
+ // valid in the global component registry and handled specially by the
2945
+ // renderer at runtime.
2830
2946
  VSuspense
2831
2947
  };
2832
2948
 
2833
2949
  // src/errorBoundary.ts
2834
- import { defineComponent as defineComponent34, ref as ref10, watch as watch8, onErrorCaptured } from "@vue/runtime-core";
2835
- var ErrorBoundary = defineComponent34({
2950
+ import { defineComponent as defineComponent35, ref as ref9, watch as watch6, onErrorCaptured } from "@vue/runtime-core";
2951
+ var ErrorBoundary = defineComponent35({
2836
2952
  name: "ErrorBoundary",
2837
2953
  props: {
2838
2954
  onError: Function,
@@ -2842,8 +2958,8 @@ var ErrorBoundary = defineComponent34({
2842
2958
  }
2843
2959
  },
2844
2960
  setup(props, { slots }) {
2845
- const error = ref10(null);
2846
- const errorInfo = ref10("");
2961
+ const error = ref9(null);
2962
+ const errorInfo = ref9("");
2847
2963
  onErrorCaptured((err, _instance, info) => {
2848
2964
  const normalizedError = err instanceof Error ? err : new Error(String(err));
2849
2965
  error.value = normalizedError;
@@ -2857,7 +2973,7 @@ var ErrorBoundary = defineComponent34({
2857
2973
  error.value = null;
2858
2974
  errorInfo.value = "";
2859
2975
  }
2860
- watch8(
2976
+ watch6(
2861
2977
  () => props.resetKeys,
2862
2978
  () => {
2863
2979
  if (error.value) {
@@ -3118,9 +3234,9 @@ function useAsyncStorage() {
3118
3234
  }
3119
3235
 
3120
3236
  // src/composables/useClipboard.ts
3121
- import { ref as ref11 } from "@vue/runtime-core";
3237
+ import { ref as ref10 } from "@vue/runtime-core";
3122
3238
  function useClipboard() {
3123
- const content = ref11("");
3239
+ const content = ref10("");
3124
3240
  function copy(text) {
3125
3241
  return NativeBridge.invokeNativeModule("Clipboard", "copy", [text]).then(() => void 0);
3126
3242
  }
@@ -3134,29 +3250,29 @@ function useClipboard() {
3134
3250
  }
3135
3251
 
3136
3252
  // src/composables/useDeviceInfo.ts
3137
- import { ref as ref12, onMounted as onMounted2 } from "@vue/runtime-core";
3253
+ import { ref as ref11, onMounted } from "@vue/runtime-core";
3138
3254
  function useDeviceInfo() {
3139
- const model = ref12("");
3140
- const systemVersion = ref12("");
3141
- const systemName = ref12("");
3142
- const name = ref12("");
3143
- const screenWidth = ref12(0);
3144
- const screenHeight = ref12(0);
3145
- const scale = ref12(1);
3146
- const isLoaded = ref12(false);
3255
+ const model = ref11("");
3256
+ const systemVersion = ref11("");
3257
+ const systemName = ref11("");
3258
+ const name = ref11("");
3259
+ const screenWidth = ref11(0);
3260
+ const screenHeight = ref11(0);
3261
+ const scale = ref11(1);
3262
+ const isLoaded = ref11(false);
3147
3263
  async function fetchInfo() {
3148
3264
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
3149
3265
  model.value = info.model ?? "";
3150
3266
  systemVersion.value = info.systemVersion ?? "";
3151
3267
  systemName.value = info.systemName ?? "";
3152
- name.value = info.name ?? "";
3268
+ name.value = info.name ?? info.deviceName ?? "";
3153
3269
  screenWidth.value = info.screenWidth ?? 0;
3154
3270
  screenHeight.value = info.screenHeight ?? 0;
3155
- scale.value = info.scale ?? 1;
3271
+ scale.value = info.scale ?? info.screenScale ?? 1;
3156
3272
  isLoaded.value = true;
3157
3273
  }
3158
- onMounted2(() => {
3159
- fetchInfo();
3274
+ onMounted(() => {
3275
+ void fetchInfo().catch(() => void 0);
3160
3276
  });
3161
3277
  return {
3162
3278
  model,
@@ -3172,10 +3288,10 @@ function useDeviceInfo() {
3172
3288
  }
3173
3289
 
3174
3290
  // src/composables/useKeyboard.ts
3175
- import { ref as ref13 } from "@vue/runtime-core";
3291
+ import { ref as ref12 } from "@vue/runtime-core";
3176
3292
  function useKeyboard() {
3177
- const isVisible = ref13(false);
3178
- const height = ref13(0);
3293
+ const isVisible = ref12(false);
3294
+ const height = ref12(0);
3179
3295
  function dismiss() {
3180
3296
  return NativeBridge.invokeNativeModule("Keyboard", "dismiss", []).then(() => void 0);
3181
3297
  }
@@ -3189,10 +3305,10 @@ function useKeyboard() {
3189
3305
  }
3190
3306
 
3191
3307
  // src/composables/useNetwork.ts
3192
- import { ref as ref14, onUnmounted as onUnmounted4 } from "@vue/runtime-core";
3308
+ import { ref as ref13, onUnmounted as onUnmounted3 } from "@vue/runtime-core";
3193
3309
  function useNetwork() {
3194
- const isConnected = ref14(true);
3195
- const connectionType = ref14("unknown");
3310
+ const isConnected = ref13(true);
3311
+ const connectionType = ref13("unknown");
3196
3312
  let lastEventTime = 0;
3197
3313
  const unsubscribe = NativeBridge.onGlobalEvent("network:change", (payload) => {
3198
3314
  lastEventTime = Date.now();
@@ -3208,14 +3324,14 @@ function useNetwork() {
3208
3324
  }).catch((err) => {
3209
3325
  if (__DEV__) console.warn("[vue-native] Network.getStatus failed:", err);
3210
3326
  });
3211
- onUnmounted4(unsubscribe);
3327
+ onUnmounted3(unsubscribe);
3212
3328
  return { isConnected, connectionType };
3213
3329
  }
3214
3330
 
3215
3331
  // src/composables/useAppState.ts
3216
- import { ref as ref15, onUnmounted as onUnmounted5 } from "@vue/runtime-core";
3332
+ import { ref as ref14, onUnmounted as onUnmounted4 } from "@vue/runtime-core";
3217
3333
  function useAppState() {
3218
- const state = ref15("active");
3334
+ const state = ref14("active");
3219
3335
  NativeBridge.invokeNativeModule("AppState", "getState").then((s) => {
3220
3336
  state.value = s;
3221
3337
  }).catch((err) => {
@@ -3224,7 +3340,7 @@ function useAppState() {
3224
3340
  const unsubscribe = NativeBridge.onGlobalEvent("appState:change", (payload) => {
3225
3341
  state.value = payload.state;
3226
3342
  });
3227
- onUnmounted5(unsubscribe);
3343
+ onUnmounted4(unsubscribe);
3228
3344
  return { state };
3229
3345
  }
3230
3346
 
@@ -3259,11 +3375,31 @@ function usePermissions() {
3259
3375
  }
3260
3376
 
3261
3377
  // src/composables/useGeolocation.ts
3262
- import { ref as ref16, onUnmounted as onUnmounted6 } from "@vue/runtime-core";
3378
+ import { getCurrentInstance, ref as ref15, onUnmounted as onUnmounted5 } from "@vue/runtime-core";
3263
3379
  function useGeolocation() {
3264
- const coords = ref16(null);
3265
- const error = ref16(null);
3380
+ const coords = ref15(null);
3381
+ const error = ref15(null);
3266
3382
  let watchId = null;
3383
+ let unsubscribePosition = null;
3384
+ let unsubscribeError = null;
3385
+ let isDisposed = false;
3386
+ function removeWatchListeners() {
3387
+ unsubscribePosition?.();
3388
+ unsubscribeError?.();
3389
+ unsubscribePosition = null;
3390
+ unsubscribeError = null;
3391
+ }
3392
+ if (getCurrentInstance()) {
3393
+ onUnmounted5(() => {
3394
+ isDisposed = true;
3395
+ removeWatchListeners();
3396
+ const activeWatchId = watchId;
3397
+ watchId = null;
3398
+ if (activeWatchId !== null) {
3399
+ void NativeBridge.invokeNativeModule("Geolocation", "clearWatch", [activeWatchId]).catch(() => void 0);
3400
+ }
3401
+ });
3402
+ }
3267
3403
  async function getCurrentPosition() {
3268
3404
  try {
3269
3405
  error.value = null;
@@ -3279,19 +3415,22 @@ function useGeolocation() {
3279
3415
  async function watchPosition() {
3280
3416
  try {
3281
3417
  error.value = null;
3418
+ if (watchId !== null) {
3419
+ await clearWatch(watchId);
3420
+ }
3421
+ removeWatchListeners();
3282
3422
  const id = await NativeBridge.invokeNativeModule("Geolocation", "watchPosition");
3423
+ if (isDisposed) {
3424
+ await NativeBridge.invokeNativeModule("Geolocation", "clearWatch", [id]);
3425
+ return id;
3426
+ }
3283
3427
  watchId = id;
3284
- const unsubscribe = NativeBridge.onGlobalEvent("location:update", (payload) => {
3428
+ unsubscribePosition = NativeBridge.onGlobalEvent("location:update", (payload) => {
3285
3429
  coords.value = payload;
3286
3430
  });
3287
- const unsubscribeError = NativeBridge.onGlobalEvent("location:error", (payload) => {
3431
+ unsubscribeError = NativeBridge.onGlobalEvent("location:error", (payload) => {
3288
3432
  error.value = payload.message;
3289
3433
  });
3290
- onUnmounted6(() => {
3291
- unsubscribe();
3292
- unsubscribeError();
3293
- if (watchId !== null) clearWatch(watchId);
3294
- });
3295
3434
  return id;
3296
3435
  } catch (e) {
3297
3436
  const msg = e instanceof Error ? e.message : String(e);
@@ -3301,13 +3440,16 @@ function useGeolocation() {
3301
3440
  }
3302
3441
  async function clearWatch(id) {
3303
3442
  await NativeBridge.invokeNativeModule("Geolocation", "clearWatch", [id]);
3304
- watchId = null;
3443
+ if (watchId === id) {
3444
+ watchId = null;
3445
+ removeWatchListeners();
3446
+ }
3305
3447
  }
3306
3448
  return { coords, error, getCurrentPosition, watchPosition, clearWatch };
3307
3449
  }
3308
3450
 
3309
3451
  // src/composables/useCamera.ts
3310
- import { onUnmounted as onUnmounted7 } from "@vue/runtime-core";
3452
+ import { onUnmounted as onUnmounted6 } from "@vue/runtime-core";
3311
3453
  function useCamera() {
3312
3454
  const qrCleanups = [];
3313
3455
  async function launchCamera(options = {}) {
@@ -3330,7 +3472,7 @@ function useCamera() {
3330
3472
  qrCleanups.push(unsubscribe);
3331
3473
  return unsubscribe;
3332
3474
  }
3333
- onUnmounted7(() => {
3475
+ onUnmounted6(() => {
3334
3476
  NativeBridge.invokeNativeModule("Camera", "stopQRScan").catch((err) => {
3335
3477
  if (__DEV__) console.warn("[vue-native] Camera.stopQRScan failed:", err);
3336
3478
  });
@@ -3341,10 +3483,10 @@ function useCamera() {
3341
3483
  }
3342
3484
 
3343
3485
  // src/composables/useNotifications.ts
3344
- import { ref as ref17, onUnmounted as onUnmounted8 } from "@vue/runtime-core";
3486
+ import { ref as ref16, onUnmounted as onUnmounted7 } from "@vue/runtime-core";
3345
3487
  function useNotifications() {
3346
- const isGranted = ref17(false);
3347
- const pushToken = ref17(null);
3488
+ const isGranted = ref16(false);
3489
+ const pushToken = ref16(null);
3348
3490
  async function requestPermission() {
3349
3491
  const granted = await NativeBridge.invokeNativeModule("Notifications", "requestPermission");
3350
3492
  isGranted.value = granted;
@@ -3364,7 +3506,7 @@ function useNotifications() {
3364
3506
  }
3365
3507
  function onNotification(handler) {
3366
3508
  const unsubscribe = NativeBridge.onGlobalEvent("notification:received", handler);
3367
- onUnmounted8(unsubscribe);
3509
+ onUnmounted7(unsubscribe);
3368
3510
  return unsubscribe;
3369
3511
  }
3370
3512
  async function registerForPush() {
@@ -3378,12 +3520,17 @@ function useNotifications() {
3378
3520
  pushToken.value = payload.token;
3379
3521
  handler(payload.token);
3380
3522
  });
3381
- onUnmounted8(unsubscribe);
3523
+ onUnmounted7(unsubscribe);
3382
3524
  return unsubscribe;
3383
3525
  }
3384
3526
  function onPushReceived(handler) {
3385
3527
  const unsubscribe = NativeBridge.onGlobalEvent("push:received", handler);
3386
- onUnmounted8(unsubscribe);
3528
+ onUnmounted7(unsubscribe);
3529
+ return unsubscribe;
3530
+ }
3531
+ function onPushError(handler) {
3532
+ const unsubscribe = NativeBridge.onGlobalEvent("push:error", handler);
3533
+ onUnmounted7(unsubscribe);
3387
3534
  return unsubscribe;
3388
3535
  }
3389
3536
  return {
@@ -3400,7 +3547,8 @@ function useNotifications() {
3400
3547
  registerForPush,
3401
3548
  getToken,
3402
3549
  onPushToken,
3403
- onPushReceived
3550
+ onPushReceived,
3551
+ onPushError
3404
3552
  };
3405
3553
  }
3406
3554
 
@@ -3419,23 +3567,39 @@ function useBiometry() {
3419
3567
  }
3420
3568
 
3421
3569
  // src/composables/useHttp.ts
3422
- import { ref as ref18, onUnmounted as onUnmounted9 } from "@vue/runtime-core";
3570
+ import { ref as ref17, onUnmounted as onUnmounted8 } from "@vue/runtime-core";
3423
3571
  function isQueryRequestOptions(value) {
3424
3572
  return "params" in value || "headers" in value;
3425
3573
  }
3574
+ function getHeader(headers, name) {
3575
+ const expected = name.toLowerCase();
3576
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === expected)?.[1];
3577
+ }
3578
+ async function parseResponseBody(response, headers) {
3579
+ if (response.status === 204 || response.status === 205) return void 0;
3580
+ const contentType = getHeader(headers, "content-type")?.toLowerCase() ?? "";
3581
+ const expectsJson = contentType === "" || contentType.includes("json") || contentType.includes("+json");
3582
+ if (!expectsJson && typeof response.text === "function") {
3583
+ return await response.text();
3584
+ }
3585
+ return await response.json();
3586
+ }
3426
3587
  function useHttp(config = {}) {
3588
+ let configurePinsPromise;
3427
3589
  if (config.pins && Object.keys(config.pins).length > 0) {
3428
3590
  const configurePins = globalThis.__VN_configurePins;
3429
3591
  if (typeof configurePins === "function") {
3430
3592
  configurePins(JSON.stringify(config.pins));
3593
+ configurePinsPromise = Promise.resolve();
3431
3594
  } else {
3432
- NativeBridge.invokeNativeModule("Http", "configurePins", [config.pins]);
3595
+ configurePinsPromise = NativeBridge.invokeNativeModule("Http", "configurePins", [config.pins]);
3433
3596
  }
3434
3597
  }
3435
- const loading = ref18(false);
3436
- const error = ref18(null);
3598
+ const loading = ref17(false);
3599
+ const error = ref17(null);
3600
+ let activeRequestCount = 0;
3437
3601
  let isMounted = true;
3438
- onUnmounted9(() => {
3602
+ onUnmounted8(() => {
3439
3603
  isMounted = false;
3440
3604
  });
3441
3605
  const BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
@@ -3458,21 +3622,23 @@ function useHttp(config = {}) {
3458
3622
  }
3459
3623
  async function request(method, url, options = {}) {
3460
3624
  const fullUrl = config.baseURL ? `${config.baseURL}${url}` : url;
3625
+ activeRequestCount++;
3461
3626
  loading.value = true;
3462
3627
  error.value = null;
3463
- let controller;
3628
+ const AbortControllerCtor = globalThis.AbortController;
3629
+ const controller = config.timeout && config.timeout > 0 && typeof AbortControllerCtor === "function" ? new AbortControllerCtor() : void 0;
3464
3630
  let timeoutId;
3465
- if (config.timeout && config.timeout > 0 && typeof AbortController !== "undefined") {
3466
- controller = new AbortController();
3467
- timeoutId = setTimeout(() => controller.abort(), config.timeout);
3468
- }
3469
3631
  try {
3632
+ if (configurePinsPromise) {
3633
+ await configurePinsPromise;
3634
+ }
3470
3635
  const upperMethod = method.toUpperCase();
3471
3636
  const mergedHeaders = {
3472
3637
  ...config.headers ?? {},
3473
3638
  ...options.headers ?? {}
3474
3639
  };
3475
- if (BODY_METHODS.has(upperMethod) && !mergedHeaders["Content-Type"]) {
3640
+ const hasContentType = getHeader(mergedHeaders, "content-type") !== void 0;
3641
+ if (BODY_METHODS.has(upperMethod) && !hasContentType && (options.body === void 0 || typeof options.body !== "string")) {
3476
3642
  mergedHeaders["Content-Type"] = "application/json";
3477
3643
  }
3478
3644
  const fetchOptions = {
@@ -3483,11 +3649,20 @@ function useHttp(config = {}) {
3483
3649
  fetchOptions.signal = controller.signal;
3484
3650
  }
3485
3651
  if (options.body !== void 0) {
3486
- fetchOptions.body = JSON.stringify(options.body);
3652
+ fetchOptions.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
3487
3653
  }
3488
- const response = await fetch(fullUrl, fetchOptions);
3489
- const data = await response.json();
3654
+ const fetchPromise = fetch(fullUrl, fetchOptions);
3655
+ const response = config.timeout && config.timeout > 0 ? await Promise.race([
3656
+ fetchPromise,
3657
+ new Promise((_resolve, reject) => {
3658
+ timeoutId = setTimeout(() => {
3659
+ controller?.abort();
3660
+ reject(new Error(`[VueNative] ${upperMethod} ${fullUrl} timed out after ${config.timeout}ms`));
3661
+ }, config.timeout);
3662
+ })
3663
+ ]) : await fetchPromise;
3490
3664
  const responseHeaders = parseResponseHeaders(response);
3665
+ const data = await parseResponseBody(response, responseHeaders);
3491
3666
  if (!isMounted) {
3492
3667
  return { data, status: response.status, ok: response.ok, headers: responseHeaders };
3493
3668
  }
@@ -3507,8 +3682,9 @@ function useHttp(config = {}) {
3507
3682
  if (timeoutId !== void 0) {
3508
3683
  clearTimeout(timeoutId);
3509
3684
  }
3685
+ activeRequestCount = Math.max(0, activeRequestCount - 1);
3510
3686
  if (isMounted) {
3511
- loading.value = false;
3687
+ loading.value = activeRequestCount > 0;
3512
3688
  }
3513
3689
  }
3514
3690
  }
@@ -3535,23 +3711,41 @@ function useHttp(config = {}) {
3535
3711
  }
3536
3712
 
3537
3713
  // src/composables/useColorScheme.ts
3538
- import { ref as ref19, onUnmounted as onUnmounted10 } from "@vue/runtime-core";
3714
+ import { ref as ref18, onMounted as onMounted2, onUnmounted as onUnmounted9 } from "@vue/runtime-core";
3539
3715
  function useColorScheme() {
3540
- const colorScheme = ref19("light");
3541
- const isDark = ref19(false);
3716
+ const colorScheme = ref18("light");
3717
+ const isDark = ref18(false);
3718
+ let eventRevision = 0;
3719
+ let isActive = true;
3720
+ const applyColorScheme = (value) => {
3721
+ if (value !== "light" && value !== "dark") return;
3722
+ colorScheme.value = value;
3723
+ isDark.value = value === "dark";
3724
+ };
3725
+ onMounted2(() => {
3726
+ const revisionAtRequest = eventRevision;
3727
+ void NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []).then((info) => {
3728
+ if (isActive && eventRevision === revisionAtRequest) {
3729
+ applyColorScheme(info?.colorScheme);
3730
+ }
3731
+ }).catch(() => void 0);
3732
+ });
3542
3733
  const unsubscribe = NativeBridge.onGlobalEvent(
3543
3734
  "colorScheme:change",
3544
3735
  (payload) => {
3545
- colorScheme.value = payload.colorScheme;
3546
- isDark.value = payload.colorScheme === "dark";
3736
+ eventRevision++;
3737
+ applyColorScheme(payload?.colorScheme);
3547
3738
  }
3548
3739
  );
3549
- onUnmounted10(unsubscribe);
3740
+ onUnmounted9(() => {
3741
+ isActive = false;
3742
+ unsubscribe();
3743
+ });
3550
3744
  return { colorScheme, isDark };
3551
3745
  }
3552
3746
 
3553
3747
  // src/composables/useBackHandler.ts
3554
- import { onMounted as onMounted3, onUnmounted as onUnmounted11 } from "@vue/runtime-core";
3748
+ import { onMounted as onMounted3, onUnmounted as onUnmounted10 } from "@vue/runtime-core";
3555
3749
  function useBackHandler(handler) {
3556
3750
  let unsubscribe = null;
3557
3751
  onMounted3(() => {
@@ -3564,7 +3758,7 @@ function useBackHandler(handler) {
3564
3758
  }
3565
3759
  });
3566
3760
  });
3567
- onUnmounted11(() => {
3761
+ onUnmounted10(() => {
3568
3762
  unsubscribe?.();
3569
3763
  unsubscribe = null;
3570
3764
  });
@@ -3588,14 +3782,18 @@ function useSecureStorage() {
3588
3782
  }
3589
3783
 
3590
3784
  // src/composables/useI18n.ts
3591
- import { ref as ref20, onMounted as onMounted4 } from "@vue/runtime-core";
3785
+ import { ref as ref19, onMounted as onMounted4 } from "@vue/runtime-core";
3786
+ function normalizeLocale(value) {
3787
+ if (typeof value !== "string" || value.trim().length === 0) return "en";
3788
+ return value.trim();
3789
+ }
3592
3790
  function useI18n() {
3593
- const isRTL = ref20(false);
3594
- const locale = ref20("en");
3791
+ const isRTL = ref19(false);
3792
+ const locale = ref19("en");
3595
3793
  onMounted4(async () => {
3596
3794
  try {
3597
- const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getDeviceInfo", []);
3598
- locale.value = info?.locale || "en";
3795
+ const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
3796
+ locale.value = normalizeLocale(info?.locale);
3599
3797
  isRTL.value = ["ar", "he", "fa", "ur"].some((l) => locale.value.startsWith(l));
3600
3798
  } catch {
3601
3799
  }
@@ -3604,31 +3802,40 @@ function useI18n() {
3604
3802
  }
3605
3803
 
3606
3804
  // src/composables/useDimensions.ts
3607
- import { ref as ref21, onMounted as onMounted5, onUnmounted as onUnmounted12 } from "@vue/runtime-core";
3805
+ import { ref as ref20, onMounted as onMounted5, onUnmounted as onUnmounted11 } from "@vue/runtime-core";
3608
3806
  function useDimensions() {
3609
- const width = ref21(0);
3610
- const height = ref21(0);
3611
- const scale = ref21(1);
3807
+ const width = ref20(0);
3808
+ const height = ref20(0);
3809
+ const scale = ref20(1);
3810
+ let eventRevision = 0;
3811
+ let isActive = true;
3612
3812
  onMounted5(async () => {
3813
+ const revisionAtRequest = eventRevision;
3613
3814
  try {
3614
3815
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
3615
- width.value = info?.screenWidth || 0;
3616
- height.value = info?.screenHeight || 0;
3617
- scale.value = info?.scale || 1;
3816
+ if (isActive && eventRevision === revisionAtRequest) {
3817
+ width.value = info?.screenWidth || 0;
3818
+ height.value = info?.screenHeight || 0;
3819
+ scale.value = info?.scale ?? info?.screenScale ?? 1;
3820
+ }
3618
3821
  } catch {
3619
3822
  }
3620
3823
  });
3621
3824
  const cleanup = NativeBridge.onGlobalEvent("dimensionsChange", (payload) => {
3825
+ eventRevision++;
3622
3826
  if (payload.width != null) width.value = payload.width;
3623
3827
  if (payload.height != null) height.value = payload.height;
3624
3828
  if (payload.scale != null) scale.value = payload.scale;
3625
3829
  });
3626
- onUnmounted12(cleanup);
3830
+ onUnmounted11(() => {
3831
+ isActive = false;
3832
+ cleanup();
3833
+ });
3627
3834
  return { width, height, scale };
3628
3835
  }
3629
3836
 
3630
3837
  // src/composables/useWebSocket.ts
3631
- import { ref as ref22, onUnmounted as onUnmounted13 } from "@vue/runtime-core";
3838
+ import { ref as ref21, onUnmounted as onUnmounted12 } from "@vue/runtime-core";
3632
3839
  var connectionCounter = 0;
3633
3840
  function useWebSocket(url, options = {}) {
3634
3841
  const {
@@ -3638,9 +3845,9 @@ function useWebSocket(url, options = {}) {
3638
3845
  reconnectInterval = 1e3
3639
3846
  } = options;
3640
3847
  const connectionId = `ws_${++connectionCounter}_${Date.now()}`;
3641
- const status = ref22("CLOSED");
3642
- const lastMessage = ref22(null);
3643
- const error = ref22(null);
3848
+ const status = ref21("CLOSED");
3849
+ const lastMessage = ref21(null);
3850
+ const error = ref21(null);
3644
3851
  let reconnectAttempts = 0;
3645
3852
  let reconnectTimer = null;
3646
3853
  const MAX_PENDING_MESSAGES = 100;
@@ -3725,7 +3932,7 @@ function useWebSocket(url, options = {}) {
3725
3932
  if (autoConnect) {
3726
3933
  open();
3727
3934
  }
3728
- onUnmounted13(() => {
3935
+ onUnmounted12(() => {
3729
3936
  if (reconnectTimer) {
3730
3937
  clearTimeout(reconnectTimer);
3731
3938
  }
@@ -3795,12 +4002,12 @@ function useFileSystem() {
3795
4002
  }
3796
4003
 
3797
4004
  // src/composables/useSensors.ts
3798
- import { ref as ref23, onUnmounted as onUnmounted14 } from "@vue/runtime-core";
4005
+ import { ref as ref22, onUnmounted as onUnmounted13 } from "@vue/runtime-core";
3799
4006
  function useAccelerometer(options = {}) {
3800
- const x = ref23(0);
3801
- const y = ref23(0);
3802
- const z = ref23(0);
3803
- const isAvailable = ref23(false);
4007
+ const x = ref22(0);
4008
+ const y = ref22(0);
4009
+ const z = ref22(0);
4010
+ const isAvailable = ref22(false);
3804
4011
  let running = false;
3805
4012
  let unsubscribe = null;
3806
4013
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["accelerometer"]).then((result) => {
@@ -3832,16 +4039,16 @@ function useAccelerometer(options = {}) {
3832
4039
  if (__DEV__) console.warn("[vue-native] Sensors.stopAccelerometer failed:", err);
3833
4040
  });
3834
4041
  }
3835
- onUnmounted14(() => {
4042
+ onUnmounted13(() => {
3836
4043
  stop();
3837
4044
  });
3838
4045
  return { x, y, z, isAvailable, start, stop };
3839
4046
  }
3840
4047
  function useGyroscope(options = {}) {
3841
- const x = ref23(0);
3842
- const y = ref23(0);
3843
- const z = ref23(0);
3844
- const isAvailable = ref23(false);
4048
+ const x = ref22(0);
4049
+ const y = ref22(0);
4050
+ const z = ref22(0);
4051
+ const isAvailable = ref22(false);
3845
4052
  let running = false;
3846
4053
  let unsubscribe = null;
3847
4054
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["gyroscope"]).then((result) => {
@@ -3873,14 +4080,14 @@ function useGyroscope(options = {}) {
3873
4080
  if (__DEV__) console.warn("[vue-native] Sensors.stopGyroscope failed:", err);
3874
4081
  });
3875
4082
  }
3876
- onUnmounted14(() => {
4083
+ onUnmounted13(() => {
3877
4084
  stop();
3878
4085
  });
3879
4086
  return { x, y, z, isAvailable, start, stop };
3880
4087
  }
3881
4088
 
3882
4089
  // src/composables/useAudio.ts
3883
- import { ref as ref24, onUnmounted as onUnmounted15 } from "@vue/runtime-core";
4090
+ import { ref as ref23, onUnmounted as onUnmounted14 } from "@vue/runtime-core";
3884
4091
  function asRecord(value) {
3885
4092
  return typeof value === "object" && value !== null ? value : null;
3886
4093
  }
@@ -3893,11 +4100,11 @@ function getStringProp(record, key) {
3893
4100
  return typeof value === "string" ? value : void 0;
3894
4101
  }
3895
4102
  function useAudio() {
3896
- const duration = ref24(0);
3897
- const position = ref24(0);
3898
- const isPlaying = ref24(false);
3899
- const isRecording = ref24(false);
3900
- const error = ref24(null);
4103
+ const duration = ref23(0);
4104
+ const position = ref23(0);
4105
+ const isPlaying = ref23(false);
4106
+ const isRecording = ref23(false);
4107
+ const error = ref23(null);
3901
4108
  const unsubProgress = NativeBridge.onGlobalEvent("audio:progress", (payload) => {
3902
4109
  position.value = payload.currentTime ?? 0;
3903
4110
  duration.value = payload.duration ?? 0;
@@ -3910,7 +4117,7 @@ function useAudio() {
3910
4117
  error.value = payload.message ?? "Unknown audio error";
3911
4118
  isPlaying.value = false;
3912
4119
  });
3913
- onUnmounted15(() => {
4120
+ onUnmounted14(() => {
3914
4121
  unsubProgress();
3915
4122
  unsubComplete();
3916
4123
  unsubError();
@@ -3995,9 +4202,9 @@ function useAudio() {
3995
4202
  }
3996
4203
 
3997
4204
  // src/composables/useDatabase.ts
3998
- import { ref as ref25, onUnmounted as onUnmounted16 } from "@vue/runtime-core";
4205
+ import { ref as ref24, onUnmounted as onUnmounted15 } from "@vue/runtime-core";
3999
4206
  function useDatabase(name = "default") {
4000
- const isOpen = ref25(false);
4207
+ const isOpen = ref24(false);
4001
4208
  let opened = false;
4002
4209
  async function ensureOpen() {
4003
4210
  if (opened) return;
@@ -4040,7 +4247,7 @@ function useDatabase(name = "default") {
4040
4247
  opened = false;
4041
4248
  isOpen.value = false;
4042
4249
  }
4043
- onUnmounted16(() => {
4250
+ onUnmounted15(() => {
4044
4251
  if (opened) {
4045
4252
  NativeBridge.invokeNativeModule("Database", "close", [name]).catch((err) => {
4046
4253
  if (__DEV__) console.warn("[vue-native] Database.close failed:", err);
@@ -4053,12 +4260,12 @@ function useDatabase(name = "default") {
4053
4260
  }
4054
4261
 
4055
4262
  // src/composables/usePerformance.ts
4056
- import { ref as ref26, onUnmounted as onUnmounted17 } from "@vue/runtime-core";
4263
+ import { ref as ref25, onUnmounted as onUnmounted16 } from "@vue/runtime-core";
4057
4264
  function usePerformance() {
4058
- const isProfiling = ref26(false);
4059
- const fps = ref26(0);
4060
- const memoryMB = ref26(0);
4061
- const bridgeOps = ref26(0);
4265
+ const isProfiling = ref25(false);
4266
+ const fps = ref25(0);
4267
+ const memoryMB = ref25(0);
4268
+ const bridgeOps = ref25(0);
4062
4269
  let unsubscribe = null;
4063
4270
  function handleMetrics(payload) {
4064
4271
  fps.value = payload.fps ?? 0;
@@ -4083,7 +4290,7 @@ function usePerformance() {
4083
4290
  async function getMetrics() {
4084
4291
  return NativeBridge.invokeNativeModule("Performance", "getMetrics", []);
4085
4292
  }
4086
- onUnmounted17(() => {
4293
+ onUnmounted16(() => {
4087
4294
  if (isProfiling.value) {
4088
4295
  NativeBridge.invokeNativeModule("Performance", "stopProfiling", []).catch((err) => {
4089
4296
  if (__DEV__) console.warn("[vue-native] Performance.stopProfiling failed:", err);
@@ -4107,10 +4314,10 @@ function usePerformance() {
4107
4314
  }
4108
4315
 
4109
4316
  // src/composables/useSharedElementTransition.ts
4110
- import { ref as ref27, onUnmounted as onUnmounted18 } from "@vue/runtime-core";
4317
+ import { ref as ref26, onUnmounted as onUnmounted17 } from "@vue/runtime-core";
4111
4318
  var sharedElementRegistry = /* @__PURE__ */ new Map();
4112
4319
  function useSharedElementTransition(elementId) {
4113
- const viewId = ref27(null);
4320
+ const viewId = ref26(null);
4114
4321
  function register(nativeViewId) {
4115
4322
  viewId.value = nativeViewId;
4116
4323
  sharedElementRegistry.set(elementId, nativeViewId);
@@ -4119,7 +4326,7 @@ function useSharedElementTransition(elementId) {
4119
4326
  viewId.value = null;
4120
4327
  sharedElementRegistry.delete(elementId);
4121
4328
  }
4122
- onUnmounted18(() => {
4329
+ onUnmounted17(() => {
4123
4330
  unregister();
4124
4331
  });
4125
4332
  return {
@@ -4143,7 +4350,7 @@ function clearSharedElementRegistry() {
4143
4350
  }
4144
4351
 
4145
4352
  // src/composables/useIAP.ts
4146
- import { ref as ref28, onUnmounted as onUnmounted19 } from "@vue/runtime-core";
4353
+ import { ref as ref27, onUnmounted as onUnmounted18 } from "@vue/runtime-core";
4147
4354
  function getErrorMessage(error) {
4148
4355
  if (error instanceof Error) return error.message;
4149
4356
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4153,9 +4360,9 @@ function getErrorMessage(error) {
4153
4360
  return String(error);
4154
4361
  }
4155
4362
  function useIAP() {
4156
- const products = ref28([]);
4157
- const isReady = ref28(false);
4158
- const error = ref28(null);
4363
+ const products = ref27([]);
4364
+ const isReady = ref27(false);
4365
+ const error = ref27(null);
4159
4366
  const cleanups = [];
4160
4367
  const unsubscribe = NativeBridge.onGlobalEvent("iap:transactionUpdate", (payload) => {
4161
4368
  if (payload.state === "failed" && payload.error) {
@@ -4212,7 +4419,7 @@ function useIAP() {
4212
4419
  cleanups.push(unsub);
4213
4420
  return unsub;
4214
4421
  }
4215
- onUnmounted19(() => {
4422
+ onUnmounted18(() => {
4216
4423
  cleanups.forEach((fn) => fn());
4217
4424
  cleanups.length = 0;
4218
4425
  });
@@ -4229,7 +4436,7 @@ function useIAP() {
4229
4436
  }
4230
4437
 
4231
4438
  // src/composables/useAppleSignIn.ts
4232
- import { ref as ref29, onUnmounted as onUnmounted20 } from "@vue/runtime-core";
4439
+ import { ref as ref28, onUnmounted as onUnmounted19 } from "@vue/runtime-core";
4233
4440
  function getErrorMessage2(error) {
4234
4441
  if (error instanceof Error) return error.message;
4235
4442
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4252,9 +4459,9 @@ function normalizeSocialUser(value, provider) {
4252
4459
  };
4253
4460
  }
4254
4461
  function useAppleSignIn() {
4255
- const user = ref29(null);
4256
- const isAuthenticated = ref29(false);
4257
- const error = ref29(null);
4462
+ const user = ref28(null);
4463
+ const isAuthenticated = ref28(false);
4464
+ const error = ref28(null);
4258
4465
  const cleanups = [];
4259
4466
  const unsubscribe = NativeBridge.onGlobalEvent("auth:appleCredentialRevoked", () => {
4260
4467
  user.value = null;
@@ -4297,7 +4504,7 @@ function useAppleSignIn() {
4297
4504
  error.value = getErrorMessage2(err);
4298
4505
  }
4299
4506
  }
4300
- onUnmounted20(() => {
4507
+ onUnmounted19(() => {
4301
4508
  cleanups.forEach((fn) => fn());
4302
4509
  cleanups.length = 0;
4303
4510
  });
@@ -4305,7 +4512,7 @@ function useAppleSignIn() {
4305
4512
  }
4306
4513
 
4307
4514
  // src/composables/useGoogleSignIn.ts
4308
- import { ref as ref30, onUnmounted as onUnmounted21 } from "@vue/runtime-core";
4515
+ import { ref as ref29, onUnmounted as onUnmounted20 } from "@vue/runtime-core";
4309
4516
  function getErrorMessage3(error) {
4310
4517
  if (error instanceof Error) return error.message;
4311
4518
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4328,9 +4535,9 @@ function normalizeSocialUser2(value) {
4328
4535
  };
4329
4536
  }
4330
4537
  function useGoogleSignIn(clientId) {
4331
- const user = ref30(null);
4332
- const isAuthenticated = ref30(false);
4333
- const error = ref30(null);
4538
+ const user = ref29(null);
4539
+ const isAuthenticated = ref29(false);
4540
+ const error = ref29(null);
4334
4541
  const cleanups = [];
4335
4542
  NativeBridge.invokeNativeModule("SocialAuth", "getCurrentUser", ["google"]).then((result) => {
4336
4543
  const currentUser = normalizeSocialUser2(result);
@@ -4368,7 +4575,7 @@ function useGoogleSignIn(clientId) {
4368
4575
  error.value = getErrorMessage3(err);
4369
4576
  }
4370
4577
  }
4371
- onUnmounted21(() => {
4578
+ onUnmounted20(() => {
4372
4579
  cleanups.forEach((fn) => fn());
4373
4580
  cleanups.length = 0;
4374
4581
  });
@@ -4376,17 +4583,17 @@ function useGoogleSignIn(clientId) {
4376
4583
  }
4377
4584
 
4378
4585
  // src/composables/useBackgroundTask.ts
4379
- import { ref as ref31, onUnmounted as onUnmounted22 } from "@vue/runtime-core";
4586
+ import { ref as ref30, onUnmounted as onUnmounted21 } from "@vue/runtime-core";
4380
4587
  function useBackgroundTask() {
4381
4588
  const taskHandlers = /* @__PURE__ */ new Map();
4382
- const defaultHandler = ref31(null);
4589
+ const defaultHandler = ref30(null);
4383
4590
  const unsubscribe = NativeBridge.onGlobalEvent("background:taskExecute", (payload) => {
4384
4591
  const handler = taskHandlers.get(payload.taskId) || defaultHandler.value;
4385
4592
  if (handler) {
4386
4593
  handler(payload.taskId);
4387
4594
  }
4388
4595
  });
4389
- onUnmounted22(unsubscribe);
4596
+ onUnmounted21(unsubscribe);
4390
4597
  function registerTask(taskId) {
4391
4598
  return NativeBridge.invokeNativeModule("BackgroundTask", "registerTask", [taskId]).then(() => void 0);
4392
4599
  }
@@ -4425,7 +4632,8 @@ function useBackgroundTask() {
4425
4632
  }
4426
4633
 
4427
4634
  // src/composables/useOTAUpdate.ts
4428
- import { ref as ref32, onUnmounted as onUnmounted23 } from "@vue/runtime-core";
4635
+ import { ref as ref31, onUnmounted as onUnmounted22 } from "@vue/runtime-core";
4636
+ var SHA256_HEX_PATTERN = /^[a-f\d]{64}$/i;
4429
4637
  function getErrorMessage4(error) {
4430
4638
  if (error instanceof Error) return error.message;
4431
4639
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4435,18 +4643,18 @@ function getErrorMessage4(error) {
4435
4643
  return String(error);
4436
4644
  }
4437
4645
  function useOTAUpdate(serverUrl) {
4438
- const currentVersion = ref32("embedded");
4439
- const availableVersion = ref32(null);
4440
- const downloadProgress = ref32(0);
4441
- const isChecking = ref32(false);
4442
- const isDownloading = ref32(false);
4443
- const status = ref32("idle");
4444
- const error = ref32(null);
4646
+ const currentVersion = ref31("embedded");
4647
+ const availableVersion = ref31(null);
4648
+ const downloadProgress = ref31(0);
4649
+ const isChecking = ref31(false);
4650
+ const isDownloading = ref31(false);
4651
+ const status = ref31("idle");
4652
+ const error = ref31(null);
4445
4653
  let lastUpdateInfo = null;
4446
4654
  const unsubscribe = NativeBridge.onGlobalEvent("ota:downloadProgress", (payload) => {
4447
4655
  downloadProgress.value = payload.progress;
4448
4656
  });
4449
- onUnmounted23(unsubscribe);
4657
+ onUnmounted22(unsubscribe);
4450
4658
  NativeBridge.invokeNativeModule("OTA", "getCurrentVersion", []).then((info) => {
4451
4659
  currentVersion.value = info.version;
4452
4660
  }).catch((err) => {
@@ -4458,13 +4666,18 @@ function useOTAUpdate(serverUrl) {
4458
4666
  error.value = null;
4459
4667
  try {
4460
4668
  const info = await NativeBridge.invokeNativeModule("OTA", "checkForUpdate", [serverUrl]);
4669
+ if (info.updateAvailable) {
4670
+ if (!info.version || !info.downloadUrl || !SHA256_HEX_PATTERN.test(info.hash)) {
4671
+ throw new Error("Update server returned incomplete or invalid update metadata.");
4672
+ }
4673
+ }
4461
4674
  lastUpdateInfo = info;
4462
4675
  if (info.updateAvailable) {
4463
4676
  availableVersion.value = info.version;
4464
4677
  } else {
4465
4678
  availableVersion.value = null;
4466
4679
  }
4467
- status.value = info.updateAvailable ? "idle" : "idle";
4680
+ status.value = "idle";
4468
4681
  return info;
4469
4682
  } catch (err) {
4470
4683
  error.value = getErrorMessage4(err);
@@ -4474,21 +4687,34 @@ function useOTAUpdate(serverUrl) {
4474
4687
  isChecking.value = false;
4475
4688
  }
4476
4689
  }
4477
- async function downloadUpdate(url, hash) {
4690
+ async function downloadUpdate(url, hash, version) {
4478
4691
  const downloadUrl = url || lastUpdateInfo?.downloadUrl;
4479
4692
  const expectedHash = hash || lastUpdateInfo?.hash;
4693
+ const offeredVersion = version || lastUpdateInfo?.version;
4480
4694
  if (!downloadUrl) {
4481
4695
  const msg = "No download URL. Call checkForUpdate() first or provide a URL.";
4482
4696
  error.value = msg;
4483
4697
  status.value = "error";
4484
4698
  throw new Error(msg);
4485
4699
  }
4700
+ if (!expectedHash || !SHA256_HEX_PATTERN.test(expectedHash)) {
4701
+ const msg = "A valid 64-character SHA-256 hash is required for OTA updates.";
4702
+ error.value = msg;
4703
+ status.value = "error";
4704
+ throw new Error(msg);
4705
+ }
4706
+ if (!offeredVersion) {
4707
+ const msg = "No update version. Call checkForUpdate() first or provide a version.";
4708
+ error.value = msg;
4709
+ status.value = "error";
4710
+ throw new Error(msg);
4711
+ }
4486
4712
  isDownloading.value = true;
4487
4713
  downloadProgress.value = 0;
4488
4714
  status.value = "downloading";
4489
4715
  error.value = null;
4490
4716
  try {
4491
- await NativeBridge.invokeNativeModule("OTA", "downloadUpdate", [downloadUrl, expectedHash || ""]);
4717
+ await NativeBridge.invokeNativeModule("OTA", "downloadUpdate", [downloadUrl, expectedHash, offeredVersion]);
4492
4718
  status.value = "ready";
4493
4719
  } catch (err) {
4494
4720
  await NativeBridge.invokeNativeModule("OTA", "cleanupPartialDownload", []).catch((err2) => {
@@ -4509,6 +4735,9 @@ function useOTAUpdate(serverUrl) {
4509
4735
  try {
4510
4736
  await NativeBridge.invokeNativeModule("OTA", "verifyBundle", []);
4511
4737
  } catch (err) {
4738
+ await NativeBridge.invokeNativeModule("OTA", "cleanupPartialDownload", []).catch((cleanupError) => {
4739
+ if (__DEV__) console.warn("[vue-native] OTA.cleanupPartialDownload failed:", cleanupError);
4740
+ });
4512
4741
  status.value = "error";
4513
4742
  error.value = "Bundle verification failed: " + getErrorMessage4(err);
4514
4743
  throw err;
@@ -4560,7 +4789,7 @@ function useOTAUpdate(serverUrl) {
4560
4789
  }
4561
4790
 
4562
4791
  // src/composables/useBluetooth.ts
4563
- import { ref as ref33, onUnmounted as onUnmounted24 } from "@vue/runtime-core";
4792
+ import { ref as ref32, onUnmounted as onUnmounted23 } from "@vue/runtime-core";
4564
4793
  function getErrorMessage5(error) {
4565
4794
  if (error instanceof Error) return error.message;
4566
4795
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4570,11 +4799,11 @@ function getErrorMessage5(error) {
4570
4799
  return String(error);
4571
4800
  }
4572
4801
  function useBluetooth() {
4573
- const devices = ref33([]);
4574
- const connectedDevice = ref33(null);
4575
- const isScanning = ref33(false);
4576
- const isAvailable = ref33(false);
4577
- const error = ref33(null);
4802
+ const devices = ref32([]);
4803
+ const connectedDevice = ref32(null);
4804
+ const isScanning = ref32(false);
4805
+ const isAvailable = ref32(false);
4806
+ const error = ref32(null);
4578
4807
  const cleanups = [];
4579
4808
  NativeBridge.invokeNativeModule("Bluetooth", "getState").then((state) => {
4580
4809
  isAvailable.value = state === "poweredOn";
@@ -4653,7 +4882,7 @@ function useBluetooth() {
4653
4882
  ]);
4654
4883
  };
4655
4884
  }
4656
- onUnmounted24(() => {
4885
+ onUnmounted23(() => {
4657
4886
  if (isScanning.value) {
4658
4887
  NativeBridge.invokeNativeModule("Bluetooth", "stopScan").catch((err) => {
4659
4888
  if (__DEV__) console.warn("[vue-native] Bluetooth.stopScan failed:", err);
@@ -4679,7 +4908,7 @@ function useBluetooth() {
4679
4908
  }
4680
4909
 
4681
4910
  // src/composables/useCalendar.ts
4682
- import { ref as ref34 } from "@vue/runtime-core";
4911
+ import { ref as ref33 } from "@vue/runtime-core";
4683
4912
  function getErrorMessage6(error) {
4684
4913
  if (error instanceof Error) return error.message;
4685
4914
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4689,8 +4918,8 @@ function getErrorMessage6(error) {
4689
4918
  return String(error);
4690
4919
  }
4691
4920
  function useCalendar() {
4692
- const hasAccess = ref34(false);
4693
- const error = ref34(null);
4921
+ const hasAccess = ref33(false);
4922
+ const error = ref33(null);
4694
4923
  async function requestAccess() {
4695
4924
  try {
4696
4925
  const result = await NativeBridge.invokeNativeModule("Calendar", "requestAccess");
@@ -4723,7 +4952,7 @@ function useCalendar() {
4723
4952
  }
4724
4953
 
4725
4954
  // src/composables/useContacts.ts
4726
- import { ref as ref35 } from "@vue/runtime-core";
4955
+ import { ref as ref34 } from "@vue/runtime-core";
4727
4956
  function getErrorMessage7(error) {
4728
4957
  if (error instanceof Error) return error.message;
4729
4958
  if (typeof error === "object" && error !== null && "message" in error) {
@@ -4733,8 +4962,8 @@ function getErrorMessage7(error) {
4733
4962
  return String(error);
4734
4963
  }
4735
4964
  function useContacts() {
4736
- const hasAccess = ref35(false);
4737
- const error = ref35(null);
4965
+ const hasAccess = ref34(false);
4966
+ const error = ref34(null);
4738
4967
  async function requestAccess() {
4739
4968
  try {
4740
4969
  const result = await NativeBridge.invokeNativeModule("Contacts", "requestAccess");
@@ -4834,10 +5063,10 @@ function useFileDialog() {
4834
5063
  }
4835
5064
 
4836
5065
  // src/composables/useDragDrop.ts
4837
- import { ref as ref36, readonly } from "@vue/runtime-core";
5066
+ import { ref as ref35, readonly } from "@vue/runtime-core";
4838
5067
  function useDragDrop() {
4839
5068
  const { isMacOS } = usePlatform();
4840
- const isDragging = ref36(false);
5069
+ const isDragging = ref35(false);
4841
5070
  async function enableDropZone() {
4842
5071
  if (!isMacOS) return;
4843
5072
  await NativeBridge.invokeNativeModule("DragDrop", "enableDropZone", []);
@@ -4878,7 +5107,7 @@ function useTeleport(target) {
4878
5107
  }
4879
5108
 
4880
5109
  // src/composables/useGesture.ts
4881
- import { ref as ref37, onUnmounted as onUnmounted25 } from "@vue/runtime-core";
5110
+ import { ref as ref36, onUnmounted as onUnmounted24 } from "@vue/runtime-core";
4882
5111
  function hasViewId2(value) {
4883
5112
  return typeof value === "object" && value !== null && "id" in value && typeof value.id === "number";
4884
5113
  }
@@ -4937,21 +5166,21 @@ var GestureManager = class {
4937
5166
  }
4938
5167
  };
4939
5168
  function useGesture(target, options = {}) {
4940
- const pan = ref37(null);
4941
- const pinch = ref37(null);
4942
- const rotate = ref37(null);
4943
- const swipeLeft = ref37(null);
4944
- const swipeRight = ref37(null);
4945
- const swipeUp = ref37(null);
4946
- const swipeDown = ref37(null);
4947
- const press = ref37(null);
4948
- const longPress = ref37(null);
4949
- const doubleTap = ref37(null);
4950
- const forceTouch = ref37(null);
4951
- const hover = ref37(null);
4952
- const gestureState = ref37(null);
4953
- const activeGesture = ref37(null);
4954
- const isGesturing = ref37(false);
5169
+ const pan = ref36(null);
5170
+ const pinch = ref36(null);
5171
+ const rotate = ref36(null);
5172
+ const swipeLeft = ref36(null);
5173
+ const swipeRight = ref36(null);
5174
+ const swipeUp = ref36(null);
5175
+ const swipeDown = ref36(null);
5176
+ const press = ref36(null);
5177
+ const longPress = ref36(null);
5178
+ const doubleTap = ref36(null);
5179
+ const forceTouch = ref36(null);
5180
+ const hover = ref36(null);
5181
+ const gestureState = ref36(null);
5182
+ const activeGesture = ref36(null);
5183
+ const isGesturing = ref36(false);
4955
5184
  const manager = new GestureManager();
4956
5185
  const cleanupFns = [];
4957
5186
  function attach(t) {
@@ -5106,7 +5335,7 @@ function useGesture(target, options = {}) {
5106
5335
  cleanupFns.push(dispose);
5107
5336
  }
5108
5337
  }
5109
- onUnmounted25(() => {
5338
+ onUnmounted24(() => {
5110
5339
  detach();
5111
5340
  });
5112
5341
  if (target !== void 0) {
@@ -5134,14 +5363,14 @@ function useGesture(target, options = {}) {
5134
5363
  };
5135
5364
  }
5136
5365
  function useComposedGestures(target, options = {}) {
5137
- const pan = ref37(null);
5138
- const pinch = ref37(null);
5139
- const rotate = ref37(null);
5140
- const gestureState = ref37(null);
5141
- const activeGesture = ref37(null);
5142
- const isGesturing = ref37(false);
5143
- const isPinchingAndRotating = ref37(false);
5144
- const isPanningAndPinching = ref37(false);
5366
+ const pan = ref36(null);
5367
+ const pinch = ref36(null);
5368
+ const rotate = ref36(null);
5369
+ const gestureState = ref36(null);
5370
+ const activeGesture = ref36(null);
5371
+ const isGesturing = ref36(false);
5372
+ const isPinchingAndRotating = ref36(false);
5373
+ const isPanningAndPinching = ref36(false);
5145
5374
  const manager = new GestureManager();
5146
5375
  manager.attach(target);
5147
5376
  const panConfig = typeof options.pan === "object" ? options.pan : {};
@@ -5176,7 +5405,7 @@ function useComposedGestures(target, options = {}) {
5176
5405
  isPinchingAndRotating.value = pinch.value !== null && (state.state === "began" || state.state === "changed");
5177
5406
  }, rotateConfig));
5178
5407
  }
5179
- onUnmounted25(() => {
5408
+ onUnmounted24(() => {
5180
5409
  for (const fn of cleanupFns) fn();
5181
5410
  manager.detach();
5182
5411
  });
@@ -5194,15 +5423,15 @@ function useComposedGestures(target, options = {}) {
5194
5423
 
5195
5424
  // src/theme.ts
5196
5425
  import {
5197
- inject as inject3,
5198
- provide as provide3,
5426
+ inject as inject2,
5427
+ provide as provide2,
5199
5428
  computed as computed3,
5200
- defineComponent as defineComponent35,
5201
- ref as ref38
5429
+ defineComponent as defineComponent36,
5430
+ ref as ref37
5202
5431
  } from "@vue/runtime-core";
5203
5432
  function createTheme(definition) {
5204
5433
  const key = /* @__PURE__ */ Symbol("vue-native-theme");
5205
- const ThemeProvider = defineComponent35({
5434
+ const ThemeProvider = defineComponent36({
5206
5435
  name: "ThemeProvider",
5207
5436
  props: {
5208
5437
  initialColorScheme: {
@@ -5211,7 +5440,7 @@ function createTheme(definition) {
5211
5440
  }
5212
5441
  },
5213
5442
  setup(props, { slots }) {
5214
- const colorScheme = ref38(props.initialColorScheme);
5443
+ const colorScheme = ref37(props.initialColorScheme);
5215
5444
  const theme = computed3(() => {
5216
5445
  return colorScheme.value === "dark" ? definition.dark : definition.light;
5217
5446
  });
@@ -5225,12 +5454,12 @@ function createTheme(definition) {
5225
5454
  colorScheme.value = scheme;
5226
5455
  }
5227
5456
  };
5228
- provide3(key, ctx);
5457
+ provide2(key, ctx);
5229
5458
  return () => slots.default?.();
5230
5459
  }
5231
5460
  });
5232
5461
  function useTheme() {
5233
- const ctx = inject3(key);
5462
+ const ctx = inject2(key);
5234
5463
  if (!ctx) {
5235
5464
  throw new Error(
5236
5465
  "[Vue Native] useTheme() was called outside of a <ThemeProvider>. Wrap your app root with <ThemeProvider> to provide theme context."
@@ -5275,19 +5504,64 @@ function createApp(rootComponent, rootProps) {
5275
5504
  console.warn(`[VueNative] Warning in ${componentName}: ${msg}`);
5276
5505
  };
5277
5506
  }
5507
+ let mountedRoot = null;
5508
+ let unregisterTeardown = null;
5509
+ let hasUnmounted = false;
5510
+ const unmount = app.unmount.bind(app);
5511
+ app.unmount = () => {
5512
+ const root = mountedRoot;
5513
+ const wasMounted = root !== null || app._instance !== null;
5514
+ if (!wasMounted) return;
5515
+ hasUnmounted = true;
5516
+ unregisterTeardown?.();
5517
+ unregisterTeardown = null;
5518
+ mountedRoot = null;
5519
+ unmount();
5520
+ if (root) {
5521
+ NativeBridge.removeChild(0, root.id);
5522
+ releaseNodeId(root.id);
5523
+ }
5524
+ };
5278
5525
  app.start = () => {
5526
+ if (mountedRoot) {
5527
+ return mountedRoot;
5528
+ }
5529
+ if (hasUnmounted) {
5530
+ throw new Error("[VueNative] This app has been unmounted and cannot be restarted. Create a new app instance.");
5531
+ }
5532
+ if (app._instance) {
5533
+ throw new Error("[VueNative] This app is already mounted. Use either app.mount() or app.start(), not both.");
5534
+ }
5279
5535
  const root = createNativeNode("__ROOT__");
5280
5536
  NativeBridge.createNode(root.id, "__ROOT__");
5281
5537
  NativeBridge.setRootView(root.id);
5282
- const vnode = createVNode(rootComponent, rootProps);
5283
- vnode.appContext = app._context;
5284
- render(vnode, root);
5538
+ mountedRoot = root;
5539
+ try {
5540
+ app.mount(root);
5541
+ } catch (error) {
5542
+ mountedRoot = null;
5543
+ hasUnmounted = true;
5544
+ if (app._instance) {
5545
+ try {
5546
+ unmount();
5547
+ } catch {
5548
+ }
5549
+ }
5550
+ NativeBridge.removeChild(0, root.id);
5551
+ releaseNodeId(root.id);
5552
+ throw error;
5553
+ }
5554
+ if (mountedRoot === root) {
5555
+ unregisterTeardown = registerAppTeardown(() => app.unmount());
5556
+ }
5285
5557
  return root;
5286
5558
  };
5287
5559
  return app;
5288
5560
  }
5289
5561
  export {
5562
+ Easing,
5290
5563
  ErrorBoundary,
5564
+ KeepAlive,
5291
5565
  NativeBridge,
5292
5566
  VActionSheet,
5293
5567
  VActivityIndicator,
@@ -5298,12 +5572,14 @@ export {
5298
5572
  VDrawerItem,
5299
5573
  VDrawerSection,
5300
5574
  VDropdown,
5575
+ ErrorBoundary as VErrorBoundary,
5301
5576
  VFlatList,
5302
5577
  VImage,
5303
5578
  VInput,
5304
5579
  VKeyboardAvoiding,
5305
5580
  VList,
5306
5581
  VModal,
5582
+ VOutlineView,
5307
5583
  VPicker,
5308
5584
  VPressable,
5309
5585
  VProgressBar,
@@ -5314,10 +5590,15 @@ export {
5314
5590
  VSectionList,
5315
5591
  VSegmentedControl,
5316
5592
  VSlider,
5593
+ VSplitView,
5317
5594
  VStatusBar,
5595
+ VSuspense,
5318
5596
  VSwitch,
5319
5597
  VTabBar,
5320
5598
  VText,
5599
+ VToolbar,
5600
+ VTransition,
5601
+ VTransitionGroup,
5321
5602
  VVideo,
5322
5603
  VView,
5323
5604
  VWebView,
@@ -5330,6 +5611,7 @@ export {
5330
5611
  createStyleSheet,
5331
5612
  createTextNode,
5332
5613
  createTheme,
5614
+ defineAsyncComponent,
5333
5615
  getRegisteredSharedElements,
5334
5616
  getSharedElementViewId,
5335
5617
  measureViewFrame,