@thelacanians/vue-native-runtime 0.4.14 → 0.6.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
@@ -8,16 +8,25 @@ import { createRenderer } from "@vue/runtime-core";
8
8
  import { markRaw } from "@vue/reactivity";
9
9
  var nextNodeId = 1;
10
10
  var MAX_NODE_ID = 2147483647;
11
+ var activeNodeIds = /* @__PURE__ */ new Set();
11
12
  function resetNodeId() {
12
13
  nextNodeId = 1;
14
+ activeNodeIds.clear();
15
+ }
16
+ function releaseNodeId(id) {
17
+ activeNodeIds.delete(id);
13
18
  }
14
19
  function getNextNodeId() {
15
20
  const id = nextNodeId;
16
21
  if (nextNodeId >= MAX_NODE_ID) {
17
22
  nextNodeId = 1;
23
+ while (activeNodeIds.has(nextNodeId) && nextNodeId < MAX_NODE_ID) {
24
+ nextNodeId++;
25
+ }
18
26
  } else {
19
27
  nextNodeId++;
20
28
  }
29
+ activeNodeIds.add(id);
21
30
  return id;
22
31
  }
23
32
  function createNativeNode(type) {
@@ -56,6 +65,7 @@ function createCommentNode(_text) {
56
65
  }
57
66
 
58
67
  // src/bridge.ts
68
+ var bridgeGlobals = globalThis;
59
69
  var _NativeBridgeImpl = class _NativeBridgeImpl {
60
70
  constructor() {
61
71
  /** Pending operations waiting to be flushed to native */
@@ -97,7 +107,7 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
97
107
  const ops = this.pendingOps;
98
108
  this.pendingOps = [];
99
109
  const json = JSON.stringify(ops);
100
- const flushFn = globalThis.__VN_flushOperations;
110
+ const flushFn = bridgeGlobals.__VN_flushOperations;
101
111
  if (typeof flushFn === "function") {
102
112
  try {
103
113
  flushFn(json);
@@ -177,6 +187,16 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
177
187
  updateStyle(nodeId, key, value) {
178
188
  this.enqueue("updateStyle", [nodeId, { [key]: value }]);
179
189
  }
190
+ /**
191
+ * Update multiple style properties on a native view in a single bridge op.
192
+ * Swift/Kotlin handler: handleUpdateStyle(args: [nodeId, { key1: val1, key2: val2, ... }])
193
+ *
194
+ * More efficient than calling updateStyle() per property — sends one op
195
+ * instead of N ops, reducing JSON overhead and bridge dispatch.
196
+ */
197
+ updateStyles(nodeId, styles) {
198
+ this.enqueue("updateStyle", [nodeId, styles]);
199
+ }
180
200
  // ---------------------------------------------------------------------------
181
201
  // Tree mutations
182
202
  // ---------------------------------------------------------------------------
@@ -264,6 +284,8 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
264
284
  * a crash or unregistered module), the Promise rejects with a clear error instead
265
285
  * of hanging forever.
266
286
  */
287
+ // Native module results are intentionally dynamic; callers often narrow them
288
+ // ad hoc based on the module contract rather than a shared generated type.
267
289
  invokeNativeModule(moduleName, methodName, args = [], timeoutMs = 3e4) {
268
290
  return new Promise((resolve, reject) => {
269
291
  const callbackId = this.nextCallbackId;
@@ -291,7 +313,11 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
291
313
  }
292
314
  }
293
315
  }
294
- this.pendingCallbacks.set(callbackId, { resolve, reject, timeoutId });
316
+ this.pendingCallbacks.set(callbackId, {
317
+ resolve: (result) => resolve(result),
318
+ reject: (error) => reject(error),
319
+ timeoutId
320
+ });
295
321
  this.enqueue("invokeNativeModule", [moduleName, methodName, args, callbackId]);
296
322
  });
297
323
  }
@@ -323,6 +349,28 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
323
349
  pending.resolve(result);
324
350
  }
325
351
  }
352
+ /**
353
+ * Create teleport markers in native.
354
+ * Used for Teleport component to render content outside parent hierarchy.
355
+ */
356
+ createTeleport(parentId, startId, endId) {
357
+ this.enqueue("createTeleport", [parentId, startId, endId]);
358
+ }
359
+ /**
360
+ * Remove teleport markers from native.
361
+ * Cleans up teleport containers and markers.
362
+ */
363
+ removeTeleport(parentId, startId, endId) {
364
+ this.enqueue("removeTeleport", [parentId, startId, endId]);
365
+ }
366
+ /**
367
+ * Move a node to a teleport target.
368
+ * @param target - Teleport target name ('modal', 'root', etc.)
369
+ * @param nodeId - Node ID to teleport
370
+ */
371
+ teleportTo(target, nodeId) {
372
+ this.enqueue("teleportTo", [target, nodeId]);
373
+ }
326
374
  // ---------------------------------------------------------------------------
327
375
  // Global push events
328
376
  // ---------------------------------------------------------------------------
@@ -343,7 +391,7 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
343
391
  * Called from Swift via globalThis.__VN_handleGlobalEvent when a push event fires.
344
392
  */
345
393
  handleGlobalEvent(eventName, payloadJSON) {
346
- let payload;
394
+ let payload = {};
347
395
  try {
348
396
  payload = JSON.parse(payloadJSON);
349
397
  } catch {
@@ -351,9 +399,9 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
351
399
  }
352
400
  const handlers = this.globalEventHandlers.get(eventName);
353
401
  if (handlers) {
354
- handlers.forEach((h28) => {
402
+ handlers.forEach((h33) => {
355
403
  try {
356
- h28(payload);
404
+ h33(payload);
357
405
  } catch (err) {
358
406
  console.error(`[VueNative] Error in global event handler "${eventName}":`, err);
359
407
  }
@@ -370,6 +418,9 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
370
418
  this.pendingOps = [];
371
419
  this.flushScheduled = false;
372
420
  this.eventHandlers.clear();
421
+ for (const pending of this.pendingCallbacks.values()) {
422
+ clearTimeout(pending.timeoutId);
423
+ }
373
424
  this.pendingCallbacks.clear();
374
425
  this.nextCallbackId = 1;
375
426
  this.globalEventHandlers.clear();
@@ -380,15 +431,14 @@ _NativeBridgeImpl.MAX_CALLBACK_ID = 2147483647;
380
431
  /** Maximum number of pending callbacks before evicting the oldest */
381
432
  _NativeBridgeImpl.MAX_PENDING_CALLBACKS = 1e3;
382
433
  var NativeBridgeImpl = _NativeBridgeImpl;
383
- if (typeof globalThis.__DEV__ === "undefined") {
384
- ;
385
- globalThis.__DEV__ = true;
434
+ if (typeof bridgeGlobals.__DEV__ === "undefined") {
435
+ bridgeGlobals.__DEV__ = true;
386
436
  }
387
437
  var NativeBridge = new NativeBridgeImpl();
388
- globalThis.__VN_handleEvent = NativeBridge.handleNativeEvent.bind(NativeBridge);
389
- globalThis.__VN_resolveCallback = NativeBridge.resolveCallback.bind(NativeBridge);
390
- globalThis.__VN_handleGlobalEvent = NativeBridge.handleGlobalEvent.bind(NativeBridge);
391
- globalThis.__VN_teardown = () => {
438
+ bridgeGlobals.__VN_handleEvent = NativeBridge.handleNativeEvent.bind(NativeBridge);
439
+ bridgeGlobals.__VN_resolveCallback = NativeBridge.resolveCallback.bind(NativeBridge);
440
+ bridgeGlobals.__VN_handleGlobalEvent = NativeBridge.handleGlobalEvent.bind(NativeBridge);
441
+ bridgeGlobals.__VN_teardown = () => {
392
442
  NativeBridge.reset();
393
443
  resetNodeId();
394
444
  };
@@ -397,20 +447,30 @@ globalThis.__VN_teardown = () => {
397
447
  function toEventName(key) {
398
448
  return key.slice(2).toLowerCase();
399
449
  }
450
+ function isEventHandler(value) {
451
+ return typeof value === "function";
452
+ }
400
453
  function patchStyle(nodeId, prevStyle, nextStyle) {
401
454
  try {
402
- const prev = prevStyle || {};
403
- const next = nextStyle || {};
455
+ const prev = typeof prevStyle === "object" && prevStyle !== null ? prevStyle : {};
456
+ const next = typeof nextStyle === "object" && nextStyle !== null ? nextStyle : {};
457
+ const changes = {};
458
+ let hasChanges = false;
404
459
  for (const key in next) {
405
460
  if (next[key] !== prev[key]) {
406
- NativeBridge.updateStyle(nodeId, key, next[key]);
461
+ changes[key] = next[key];
462
+ hasChanges = true;
407
463
  }
408
464
  }
409
465
  for (const key in prev) {
410
466
  if (!(key in next)) {
411
- NativeBridge.updateStyle(nodeId, key, null);
467
+ changes[key] = null;
468
+ hasChanges = true;
412
469
  }
413
470
  }
471
+ if (hasChanges) {
472
+ NativeBridge.updateStyles(nodeId, changes);
473
+ }
414
474
  } catch (err) {
415
475
  console.error(`[VueNative] Error patching style on node ${nodeId}:`, err);
416
476
  }
@@ -471,7 +531,7 @@ var nodeOps = {
471
531
  if (prevValue) {
472
532
  NativeBridge.removeEventListener(el.id, eventName);
473
533
  }
474
- if (nextValue) {
534
+ if (isEventHandler(nextValue)) {
475
535
  NativeBridge.addEventListener(el.id, eventName, nextValue);
476
536
  }
477
537
  return;
@@ -547,6 +607,7 @@ var nodeOps = {
547
607
  } catch (err) {
548
608
  console.error(`[VueNative] Error removing node ${child.id}:`, err);
549
609
  }
610
+ releaseNodeId(child.id);
550
611
  }
551
612
  },
552
613
  /**
@@ -564,6 +625,25 @@ var nodeOps = {
564
625
  const idx = parent.children.indexOf(node);
565
626
  if (idx === -1 || idx >= parent.children.length - 1) return null;
566
627
  return parent.children[idx + 1];
628
+ },
629
+ /**
630
+ * Insert static content (for Teleport).
631
+ * Creates teleport boundary markers.
632
+ */
633
+ insertStaticContent(content, parent, anchor, _namespace, _start, _end) {
634
+ const startNode = createNativeNode("__TELEPORT_START__");
635
+ const endNode = createNativeNode("__TELEPORT_END__");
636
+ if (anchor) {
637
+ const idx = parent.children.indexOf(anchor);
638
+ if (idx !== -1) {
639
+ parent.children.splice(idx, 0, startNode);
640
+ parent.children.splice(idx + 1, 0, endNode);
641
+ }
642
+ } else {
643
+ parent.children.push(startNode, endNode);
644
+ }
645
+ NativeBridge.createTeleport(parent.id, startNode.id, endNode.id);
646
+ return [startNode, endNode];
567
647
  }
568
648
  };
569
649
  function findNextNonComment(parent, anchor) {
@@ -652,6 +732,16 @@ var VButton = defineComponent3({
652
732
 
653
733
  // src/components/VInput.ts
654
734
  import { defineComponent as defineComponent4, h as h4, ref } from "@vue/runtime-core";
735
+ function extractText(payload) {
736
+ if (typeof payload === "string") {
737
+ return payload;
738
+ }
739
+ if (typeof payload === "object" && payload !== null && "text" in payload) {
740
+ const text = payload.text;
741
+ return typeof text === "string" ? text : "";
742
+ }
743
+ return "";
744
+ }
655
745
  var VInput = defineComponent4({
656
746
  name: "VInput",
657
747
  props: {
@@ -699,13 +789,11 @@ var VInput = defineComponent4({
699
789
  };
700
790
  const onCompositionend = (payload) => {
701
791
  isComposing.value = false;
702
- const text = typeof payload === "string" ? payload : payload?.text ?? "";
703
- emit("update:modelValue", text);
792
+ emit("update:modelValue", extractText(payload));
704
793
  };
705
794
  const onChangetext = (payload) => {
706
795
  if (isComposing.value) return;
707
- const text = typeof payload === "string" ? payload : payload?.text ?? "";
708
- emit("update:modelValue", text);
796
+ emit("update:modelValue", extractText(payload));
709
797
  };
710
798
  const onFocus = (payload) => {
711
799
  emit("focus", payload);
@@ -765,7 +853,8 @@ var VSwitch = defineComponent5({
765
853
  emits: ["update:modelValue", "change"],
766
854
  setup(props, { emit }) {
767
855
  const onChange = (payload) => {
768
- const value = typeof payload === "boolean" ? payload : !!(payload?.value ?? payload);
856
+ const nextValue = typeof payload === "object" && payload !== null && "value" in payload ? payload.value : payload;
857
+ const value = typeof nextValue === "boolean" ? nextValue : Boolean(nextValue);
769
858
  emit("update:modelValue", value);
770
859
  emit("change", value);
771
860
  };
@@ -929,9 +1018,9 @@ var VImage = defineComponent8({
929
1018
  loading.value = false;
930
1019
  emit("load");
931
1020
  };
932
- const onError = (e) => {
1021
+ const onError = (event) => {
933
1022
  loading.value = false;
934
- emit("error", e);
1023
+ emit("error", event);
935
1024
  };
936
1025
  expose({ loading });
937
1026
  return () => h8(
@@ -1005,6 +1094,20 @@ var VSlider = defineComponent11({
1005
1094
 
1006
1095
  // src/components/VList.ts
1007
1096
  import { defineComponent as defineComponent12, h as h12 } from "@vue/runtime-core";
1097
+
1098
+ // src/composables/usePlatform.ts
1099
+ function usePlatform() {
1100
+ const platform = typeof __PLATFORM__ !== "undefined" ? __PLATFORM__ : "ios";
1101
+ const isIOS = platform === "ios";
1102
+ const isAndroid = platform === "android";
1103
+ const isMacOS = platform === "macos";
1104
+ const isApple = isIOS || isMacOS;
1105
+ const isDesktop = isMacOS;
1106
+ const isMobile = isIOS || isAndroid;
1107
+ return { platform, isIOS, isAndroid, isMacOS, isApple, isDesktop, isMobile };
1108
+ }
1109
+
1110
+ // src/components/VList.ts
1008
1111
  var VList = defineComponent12({
1009
1112
  name: "VList",
1010
1113
  props: {
@@ -1045,13 +1148,27 @@ var VList = defineComponent12({
1045
1148
  },
1046
1149
  emits: ["scroll", "endReached"],
1047
1150
  setup(props, { slots, emit }) {
1151
+ const { isAndroid } = usePlatform();
1048
1152
  let lastScrollEmit = 0;
1153
+ let endReachedFired = false;
1049
1154
  const onScroll = (e) => {
1050
1155
  const now = Date.now();
1051
1156
  if (now - lastScrollEmit >= 16) {
1052
1157
  lastScrollEmit = now;
1053
1158
  emit("scroll", e);
1054
1159
  }
1160
+ if (props.horizontal && !isAndroid) {
1161
+ const contentWidth = e.contentWidth ?? 0;
1162
+ const layoutWidth = e.layoutWidth ?? 0;
1163
+ const distanceFromEnd = contentWidth - layoutWidth - (e.x ?? 0);
1164
+ const threshold = layoutWidth * 0.2;
1165
+ if (contentWidth > layoutWidth && distanceFromEnd < threshold && !endReachedFired) {
1166
+ endReachedFired = true;
1167
+ emit("endReached");
1168
+ } else if (distanceFromEnd >= threshold) {
1169
+ endReachedFired = false;
1170
+ }
1171
+ }
1055
1172
  };
1056
1173
  return () => {
1057
1174
  const items = props.data ?? [];
@@ -1097,6 +1214,31 @@ var VList = defineComponent12({
1097
1214
  h12("VView", { key: "__footer__", style: { flexShrink: 0 } }, slots.footer())
1098
1215
  );
1099
1216
  }
1217
+ if (props.horizontal && !isAndroid) {
1218
+ return h12(
1219
+ "VScrollView",
1220
+ {
1221
+ style: props.style,
1222
+ horizontal: true,
1223
+ showsVerticalScrollIndicator: false,
1224
+ showsHorizontalScrollIndicator: props.showsScrollIndicator,
1225
+ bounces: props.bounces,
1226
+ onScroll
1227
+ },
1228
+ [
1229
+ h12(
1230
+ "VView",
1231
+ {
1232
+ style: {
1233
+ flexDirection: "row",
1234
+ alignItems: "stretch"
1235
+ }
1236
+ },
1237
+ children
1238
+ )
1239
+ ]
1240
+ );
1241
+ }
1100
1242
  return h12(
1101
1243
  "VList",
1102
1244
  {
@@ -1200,9 +1342,9 @@ var VAlertDialog = defineComponent14({
1200
1342
  title: props.title,
1201
1343
  message: props.message,
1202
1344
  buttons: resolvedButtons,
1203
- onConfirm: (e) => emit("confirm", e),
1345
+ onConfirm: (event) => emit("confirm", event),
1204
1346
  onCancel: () => emit("cancel"),
1205
- onAction: (e) => emit("action", e)
1347
+ onAction: (event) => emit("action", event)
1206
1348
  });
1207
1349
  };
1208
1350
  }
@@ -1251,9 +1393,9 @@ var VWebView = defineComponent16({
1251
1393
  source: sanitizedSource.value,
1252
1394
  style: props.style,
1253
1395
  javaScriptEnabled: props.javaScriptEnabled,
1254
- onLoad: (e) => emit("load", e),
1255
- onError: (e) => emit("error", e),
1256
- onMessage: (e) => emit("message", e)
1396
+ onLoad: (event) => emit("load", event),
1397
+ onError: (event) => emit("error", event),
1398
+ onMessage: (event) => emit("message", event)
1257
1399
  });
1258
1400
  }
1259
1401
  });
@@ -1562,7 +1704,8 @@ var VCheckbox = defineComponent24({
1562
1704
  emits: ["update:modelValue", "change"],
1563
1705
  setup(props, { emit }) {
1564
1706
  const onChange = (payload) => {
1565
- const value = typeof payload === "boolean" ? payload : !!(payload?.value ?? payload);
1707
+ const nextValue = typeof payload === "object" && payload !== null && "value" in payload ? payload.value : payload;
1708
+ const value = typeof nextValue === "boolean" ? nextValue : Boolean(nextValue);
1566
1709
  emit("update:modelValue", value);
1567
1710
  emit("change", value);
1568
1711
  };
@@ -1582,6 +1725,10 @@ var VCheckbox = defineComponent24({
1582
1725
 
1583
1726
  // src/components/VRadio.ts
1584
1727
  import { defineComponent as defineComponent25, h as h25 } from "@vue/runtime-core";
1728
+ function extractRadioValue(payload) {
1729
+ const nextValue = typeof payload === "object" && payload !== null && "value" in payload ? payload.value : payload;
1730
+ return typeof nextValue === "string" ? nextValue : void 0;
1731
+ }
1585
1732
  var VRadio = defineComponent25({
1586
1733
  name: "VRadio",
1587
1734
  props: {
@@ -1604,7 +1751,7 @@ var VRadio = defineComponent25({
1604
1751
  emits: ["update:modelValue", "change"],
1605
1752
  setup(props, { emit }) {
1606
1753
  const onChange = (payload) => {
1607
- const value = payload?.value ?? payload;
1754
+ const value = extractRadioValue(payload);
1608
1755
  emit("update:modelValue", value);
1609
1756
  emit("change", value);
1610
1757
  };
@@ -1622,6 +1769,10 @@ var VRadio = defineComponent25({
1622
1769
 
1623
1770
  // src/components/VDropdown.ts
1624
1771
  import { defineComponent as defineComponent26, h as h26 } from "@vue/runtime-core";
1772
+ function extractDropdownValue(payload) {
1773
+ const nextValue = typeof payload === "object" && payload !== null && "value" in payload ? payload.value : payload;
1774
+ return typeof nextValue === "string" ? nextValue : void 0;
1775
+ }
1625
1776
  var VDropdown = defineComponent26({
1626
1777
  name: "VDropdown",
1627
1778
  props: {
@@ -1648,7 +1799,7 @@ var VDropdown = defineComponent26({
1648
1799
  emits: ["update:modelValue", "change"],
1649
1800
  setup(props, { emit }) {
1650
1801
  const onChange = (payload) => {
1651
- const value = payload?.value ?? payload;
1802
+ const value = extractDropdownValue(payload);
1652
1803
  emit("update:modelValue", value);
1653
1804
  emit("change", value);
1654
1805
  };
@@ -1690,85 +1841,1042 @@ var VVideo = defineComponent27({
1690
1841
  setup(props, { emit }) {
1691
1842
  return () => h27("VVideo", {
1692
1843
  ...props,
1693
- onReady: (e) => emit("ready", e),
1844
+ onReady: (event) => emit("ready", event),
1694
1845
  onPlay: () => emit("play"),
1695
1846
  onPause: () => emit("pause"),
1696
1847
  onEnd: () => emit("end"),
1697
- onError: (e) => emit("error", e),
1698
- onProgress: (e) => emit("progress", e)
1848
+ onError: (event) => emit("error", event),
1849
+ onProgress: (event) => emit("progress", event)
1699
1850
  });
1700
1851
  }
1701
1852
  });
1702
1853
 
1703
- // src/directives/vShow.ts
1704
- var vShow = {
1705
- beforeMount(el, { value }) {
1706
- NativeBridge.updateProp(el.id, "hidden", !value);
1854
+ // src/components/VFlatList.ts
1855
+ import { defineComponent as defineComponent28, h as h28, ref as ref5, computed as computed2 } from "@vue/runtime-core";
1856
+ function getDefaultItemKey(item, index) {
1857
+ if (typeof item === "object" && item !== null) {
1858
+ const keyedItem = item;
1859
+ return keyedItem.id ?? keyedItem.key ?? index;
1860
+ }
1861
+ return index;
1862
+ }
1863
+ function resolveFlexValue(style) {
1864
+ return typeof style?.flex === "number" ? style.flex : 1;
1865
+ }
1866
+ var VFlatList = defineComponent28({
1867
+ name: "VFlatList",
1868
+ props: {
1869
+ /** Array of data items to render. */
1870
+ data: {
1871
+ type: Array,
1872
+ required: true
1873
+ },
1874
+ /**
1875
+ * Render function for each item. Receives { item, index } and returns a VNode.
1876
+ * If not provided, the `#item` slot is used instead.
1877
+ */
1878
+ renderItem: {
1879
+ type: Function,
1880
+ default: void 0
1881
+ },
1882
+ /** Extract a unique key from each item. Defaults to item.id, item.key, or index. */
1883
+ keyExtractor: {
1884
+ type: Function,
1885
+ default: getDefaultItemKey
1886
+ },
1887
+ /** Fixed height for each item in points. Required for virtualization math. */
1888
+ itemHeight: {
1889
+ type: Number,
1890
+ required: true
1891
+ },
1892
+ /**
1893
+ * Number of viewport-heights to render above and below the visible area.
1894
+ * Higher values reduce blank flashes during fast scrolling but use more memory.
1895
+ * Default: 3 (3 viewports above + 3 below = 7 total viewports of items).
1896
+ */
1897
+ windowSize: {
1898
+ type: Number,
1899
+ default: 3
1900
+ },
1901
+ /** Style for the outer scroll container. */
1902
+ style: {
1903
+ type: Object,
1904
+ default: () => ({})
1905
+ },
1906
+ /** Show vertical scroll indicator. Default: true */
1907
+ showsScrollIndicator: {
1908
+ type: Boolean,
1909
+ default: true
1910
+ },
1911
+ /** Enable bounce at scroll boundaries. Default: true */
1912
+ bounces: {
1913
+ type: Boolean,
1914
+ default: true
1915
+ },
1916
+ /** Height of the header slot in points. Used to offset items below the header. */
1917
+ headerHeight: {
1918
+ type: Number,
1919
+ default: 0
1920
+ },
1921
+ /**
1922
+ * How far from the end (in viewport fractions) to trigger endReached.
1923
+ * Default: 0.5 (trigger when within 50% of a viewport from the bottom).
1924
+ */
1925
+ endReachedThreshold: {
1926
+ type: Number,
1927
+ default: 0.5
1928
+ }
1707
1929
  },
1708
- updated(el, { value, oldValue }) {
1709
- if (value === oldValue) return;
1710
- NativeBridge.updateProp(el.id, "hidden", !value);
1930
+ emits: ["scroll", "endReached"],
1931
+ setup(props, { slots, emit }) {
1932
+ const scrollOffset = ref5(0);
1933
+ const viewportHeight = ref5(0);
1934
+ let endReachedFired = false;
1935
+ const hasHeader = computed2(() => !!slots.header);
1936
+ const totalHeight = computed2(() => {
1937
+ const itemsHeight = (props.data?.length ?? 0) * props.itemHeight;
1938
+ return itemsHeight + (hasHeader.value ? props.headerHeight : 0);
1939
+ });
1940
+ const visibleRange = computed2(() => {
1941
+ const vh = viewportHeight.value || props.itemHeight * 20;
1942
+ const buffer = vh * props.windowSize;
1943
+ const startPx = Math.max(0, scrollOffset.value - buffer);
1944
+ const endPx = scrollOffset.value + vh + buffer;
1945
+ const startIdx = Math.floor(startPx / props.itemHeight);
1946
+ const endIdx = Math.min(
1947
+ Math.ceil(endPx / props.itemHeight),
1948
+ props.data?.length ?? 0
1949
+ );
1950
+ return { start: startIdx, end: endIdx };
1951
+ });
1952
+ function onScroll(event) {
1953
+ scrollOffset.value = event.y ?? 0;
1954
+ if (event.layoutHeight && event.layoutHeight > 0) {
1955
+ viewportHeight.value = event.layoutHeight;
1956
+ }
1957
+ emit("scroll", event);
1958
+ const contentLength = totalHeight.value;
1959
+ const offset = scrollOffset.value;
1960
+ const vh = viewportHeight.value || props.itemHeight * 20;
1961
+ const distanceFromEnd = contentLength - vh - offset;
1962
+ const threshold = vh * props.endReachedThreshold;
1963
+ if (distanceFromEnd < threshold && !endReachedFired) {
1964
+ endReachedFired = true;
1965
+ emit("endReached");
1966
+ } else if (distanceFromEnd >= threshold) {
1967
+ endReachedFired = false;
1968
+ }
1969
+ }
1970
+ return () => {
1971
+ const items = props.data ?? [];
1972
+ const { start, end } = visibleRange.value;
1973
+ const children = [];
1974
+ for (let i = start; i < end; i++) {
1975
+ const item = items[i];
1976
+ if (item === void 0) continue;
1977
+ const key = props.keyExtractor(item, i);
1978
+ const itemContent = props.renderItem ? [props.renderItem({ item, index: i })] : slots.item?.({ item, index: i }) ?? [];
1979
+ children.push(
1980
+ h28(
1981
+ "VView",
1982
+ {
1983
+ key,
1984
+ style: {
1985
+ position: "absolute",
1986
+ top: (hasHeader.value ? props.headerHeight : 0) + i * props.itemHeight,
1987
+ left: 0,
1988
+ right: 0,
1989
+ height: props.itemHeight
1990
+ }
1991
+ },
1992
+ itemContent
1993
+ )
1994
+ );
1995
+ }
1996
+ if (slots.header) {
1997
+ children.unshift(
1998
+ h28("VView", { key: "__vfl_header__", style: { position: "absolute", top: 0, left: 0, right: 0 } }, slots.header())
1999
+ );
2000
+ }
2001
+ if (items.length === 0 && slots.empty) {
2002
+ return h28(
2003
+ "VScrollView",
2004
+ {
2005
+ style: { ...props.style, flex: resolveFlexValue(props.style) },
2006
+ showsVerticalScrollIndicator: props.showsScrollIndicator,
2007
+ bounces: props.bounces
2008
+ },
2009
+ [h28("VView", { style: { flex: 1 } }, slots.empty())]
2010
+ );
2011
+ }
2012
+ const innerContainer = h28(
2013
+ "VView",
2014
+ {
2015
+ key: "__vfl_container__",
2016
+ style: {
2017
+ height: totalHeight.value,
2018
+ width: "100%"
2019
+ }
2020
+ },
2021
+ children
2022
+ );
2023
+ return h28(
2024
+ "VScrollView",
2025
+ {
2026
+ style: { ...props.style, flex: resolveFlexValue(props.style) },
2027
+ showsVerticalScrollIndicator: props.showsScrollIndicator,
2028
+ bounces: props.bounces,
2029
+ onScroll
2030
+ },
2031
+ [innerContainer]
2032
+ );
2033
+ };
1711
2034
  }
1712
- };
2035
+ });
1713
2036
 
1714
- // src/errorBoundary.ts
1715
- import { defineComponent as defineComponent28, ref as ref5, watch as watch4, onErrorCaptured } from "@vue/runtime-core";
1716
- var ErrorBoundary = defineComponent28({
1717
- name: "ErrorBoundary",
2037
+ // src/components/VTabBar.ts
2038
+ import { defineComponent as defineComponent29, h as h29, ref as ref6, watch as watch4 } from "@vue/runtime-core";
2039
+ var VTabBar = defineComponent29({
2040
+ name: "VTabBar",
1718
2041
  props: {
1719
- onError: Function,
1720
- resetKeys: {
2042
+ /** Array of tab configurations */
2043
+ tabs: {
1721
2044
  type: Array,
1722
- default: () => []
2045
+ required: true
2046
+ },
2047
+ /** Currently active tab ID */
2048
+ activeTab: {
2049
+ type: String,
2050
+ required: true
2051
+ },
2052
+ /** Position: 'top' | 'bottom' */
2053
+ position: {
2054
+ type: String,
2055
+ default: "bottom"
1723
2056
  }
1724
2057
  },
1725
- setup(props, { slots }) {
1726
- const error = ref5(null);
1727
- const errorInfo = ref5("");
1728
- onErrorCaptured((err, _instance, info) => {
1729
- const normalizedError = err instanceof Error ? err : new Error(String(err));
1730
- error.value = normalizedError;
1731
- errorInfo.value = info;
1732
- if (props.onError) {
1733
- props.onError(normalizedError, info);
1734
- }
1735
- return false;
2058
+ emits: ["change"],
2059
+ setup(props, { emit }) {
2060
+ const activeTab = ref6(props.activeTab);
2061
+ watch4(() => props.activeTab, (newVal) => {
2062
+ activeTab.value = newVal;
1736
2063
  });
1737
- function reset() {
1738
- error.value = null;
1739
- errorInfo.value = "";
2064
+ const switchTab = (tabId) => {
2065
+ activeTab.value = tabId;
2066
+ emit("change", tabId);
2067
+ };
2068
+ return () => h29(VView, {
2069
+ style: {
2070
+ position: "absolute",
2071
+ [props.position]: 0,
2072
+ left: 0,
2073
+ right: 0,
2074
+ backgroundColor: "#fff",
2075
+ borderTopWidth: 1,
2076
+ borderTopColor: "#e0e0e0",
2077
+ flexDirection: "row",
2078
+ height: 60
2079
+ }
2080
+ }, () => props.tabs.map((tab) => {
2081
+ const isActive = activeTab.value === tab.id;
2082
+ return h29(VPressable, {
2083
+ key: tab.id,
2084
+ style: {
2085
+ flex: 1,
2086
+ justifyContent: "center",
2087
+ alignItems: "center"
2088
+ },
2089
+ onPress: () => switchTab(tab.id),
2090
+ accessibilityLabel: tab.label,
2091
+ accessibilityRole: "tab",
2092
+ accessibilityState: { selected: isActive }
2093
+ }, () => [
2094
+ tab.icon ? h29(VText, { style: { fontSize: 24, marginBottom: 4 } }, () => tab.icon) : null,
2095
+ h29(VText, {
2096
+ style: {
2097
+ fontSize: 12,
2098
+ fontWeight: isActive ? "600" : "400",
2099
+ color: isActive ? "#007AFF" : "#8E8E93"
2100
+ }
2101
+ }, () => tab.label),
2102
+ tab.badge ? h29(VView, {
2103
+ style: {
2104
+ position: "absolute",
2105
+ top: 8,
2106
+ right: "25%",
2107
+ backgroundColor: "#FF3B30",
2108
+ borderRadius: 10,
2109
+ minWidth: 20,
2110
+ height: 20,
2111
+ justifyContent: "center",
2112
+ alignItems: "center"
2113
+ }
2114
+ }, () => h29(VText, {
2115
+ style: {
2116
+ color: "#fff",
2117
+ fontSize: 12,
2118
+ fontWeight: "600",
2119
+ paddingHorizontal: 6
2120
+ }
2121
+ }, () => String(tab.badge))) : null
2122
+ ]);
2123
+ }));
2124
+ }
2125
+ });
2126
+
2127
+ // src/components/VDrawer.ts
2128
+ import { defineComponent as defineComponent30, h as h30, inject, provide, ref as ref7, watch as watch5 } from "@vue/runtime-core";
2129
+ var drawerContextKey = /* @__PURE__ */ Symbol("VDrawerContext");
2130
+ var VDrawer = defineComponent30({
2131
+ name: "VDrawer",
2132
+ props: {
2133
+ /** Whether the drawer is open */
2134
+ open: {
2135
+ type: Boolean,
2136
+ default: false
2137
+ },
2138
+ /** Drawer position: 'left' | 'right' */
2139
+ position: {
2140
+ type: String,
2141
+ default: "left"
2142
+ },
2143
+ /** Drawer width */
2144
+ width: {
2145
+ type: Number,
2146
+ default: 280
2147
+ },
2148
+ /** Close on item press */
2149
+ closeOnPress: {
2150
+ type: Boolean,
2151
+ default: true
1740
2152
  }
1741
- watch4(
1742
- () => props.resetKeys,
1743
- () => {
1744
- if (error.value) {
1745
- reset();
1746
- }
1747
- },
1748
- { deep: true }
1749
- );
2153
+ },
2154
+ emits: ["update:open", "close"],
2155
+ setup(props, { attrs, slots, emit }) {
2156
+ const isOpen = ref7(props.open);
2157
+ watch5(() => props.open, (value) => {
2158
+ isOpen.value = value;
2159
+ });
2160
+ const closeDrawer = () => {
2161
+ isOpen.value = false;
2162
+ emit("update:open", false);
2163
+ emit("close");
2164
+ };
2165
+ provide(drawerContextKey, {
2166
+ close: closeDrawer,
2167
+ shouldCloseOnPress: () => props.closeOnPress
2168
+ });
1750
2169
  return () => {
1751
- if (error.value && slots.fallback) {
1752
- return slots.fallback({ error: error.value, errorInfo: errorInfo.value, reset });
2170
+ if (!isOpen.value && !slots.default) {
2171
+ return null;
2172
+ }
2173
+ const overlayStyle = {
2174
+ position: "absolute",
2175
+ top: 0,
2176
+ left: 0,
2177
+ right: 0,
2178
+ bottom: 0,
2179
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
2180
+ opacity: isOpen.value ? 1 : 0,
2181
+ zIndex: 999
2182
+ };
2183
+ const drawerStyle = {
2184
+ position: "absolute",
2185
+ top: 0,
2186
+ [props.position]: 0,
2187
+ width: props.width,
2188
+ height: "100%",
2189
+ backgroundColor: "#fff",
2190
+ transform: [
2191
+ { translateX: isOpen.value ? 0 : props.position === "left" ? -props.width : props.width }
2192
+ ],
2193
+ zIndex: 1e3
2194
+ };
2195
+ const drawerProps = {
2196
+ ...attrs,
2197
+ style: {
2198
+ ...drawerStyle,
2199
+ ...attrs.style && typeof attrs.style === "object" && !Array.isArray(attrs.style) ? attrs.style : {}
2200
+ }
2201
+ };
2202
+ const drawerChildren = [];
2203
+ if (slots.header) {
2204
+ drawerChildren.push(...slots.header());
2205
+ }
2206
+ if (slots.default) {
2207
+ drawerChildren.push(...slots.default({ close: closeDrawer }) ?? []);
2208
+ }
2209
+ return [
2210
+ // Overlay
2211
+ isOpen.value ? h30(VPressable, {
2212
+ style: overlayStyle,
2213
+ onPress: closeDrawer,
2214
+ accessibilityLabel: "Close menu"
2215
+ }) : null,
2216
+ // Drawer
2217
+ h30(VView, drawerProps, () => drawerChildren)
2218
+ ];
2219
+ };
2220
+ }
2221
+ });
2222
+ var VDrawerItem = defineComponent30({
2223
+ name: "VDrawerItem",
2224
+ props: {
2225
+ /** Icon (emoji or icon name) */
2226
+ icon: {
2227
+ type: String,
2228
+ default: ""
2229
+ },
2230
+ /** Label text */
2231
+ label: {
2232
+ type: String,
2233
+ required: true
2234
+ },
2235
+ /** Badge count */
2236
+ badge: {
2237
+ type: [Number, String],
2238
+ default: null
2239
+ },
2240
+ /** Disabled state */
2241
+ disabled: {
2242
+ type: Boolean,
2243
+ default: false
2244
+ }
2245
+ },
2246
+ emits: ["press"],
2247
+ setup(props, { slots, emit }) {
2248
+ const drawer = inject(drawerContextKey, null);
2249
+ const handlePress = () => {
2250
+ if (props.disabled) return;
2251
+ emit("press");
2252
+ if (drawer?.shouldCloseOnPress()) {
2253
+ drawer.close();
1753
2254
  }
1754
- return slots.default?.();
1755
2255
  };
2256
+ return () => h30(VPressable, {
2257
+ style: {
2258
+ flexDirection: "row",
2259
+ alignItems: "center",
2260
+ padding: 16,
2261
+ borderBottomWidth: 1,
2262
+ borderBottomColor: "#f0f0f0",
2263
+ opacity: props.disabled ? 0.5 : 1
2264
+ },
2265
+ onPress: handlePress,
2266
+ disabled: props.disabled,
2267
+ accessibilityLabel: props.label,
2268
+ accessibilityRole: "menuitem",
2269
+ accessibilityState: { disabled: props.disabled }
2270
+ }, () => {
2271
+ const children = [];
2272
+ if (props.icon) {
2273
+ children.push(
2274
+ h30(VText, {
2275
+ style: {
2276
+ fontSize: 24,
2277
+ marginRight: 16,
2278
+ width: 32,
2279
+ textAlign: "center"
2280
+ }
2281
+ }, () => props.icon)
2282
+ );
2283
+ }
2284
+ children.push(
2285
+ h30(VText, {
2286
+ style: {
2287
+ flex: 1,
2288
+ fontSize: 16,
2289
+ color: props.disabled ? "#999" : "#333"
2290
+ }
2291
+ }, () => props.label)
2292
+ );
2293
+ if (props.badge) {
2294
+ children.push(
2295
+ h30(VView, {
2296
+ style: {
2297
+ backgroundColor: "#007AFF",
2298
+ borderRadius: 12,
2299
+ minWidth: 24,
2300
+ height: 24,
2301
+ justifyContent: "center",
2302
+ alignItems: "center",
2303
+ paddingHorizontal: 8
2304
+ }
2305
+ }, () => h30(VText, {
2306
+ style: {
2307
+ color: "#fff",
2308
+ fontSize: 12,
2309
+ fontWeight: "600"
2310
+ }
2311
+ }, () => String(props.badge)))
2312
+ );
2313
+ }
2314
+ if (slots.default) {
2315
+ children.push(...slots.default() ?? []);
2316
+ }
2317
+ return children;
2318
+ });
2319
+ }
2320
+ });
2321
+ var VDrawerSection = defineComponent30({
2322
+ name: "VDrawerSection",
2323
+ props: {
2324
+ /** Section title */
2325
+ title: {
2326
+ type: String,
2327
+ default: ""
2328
+ }
2329
+ },
2330
+ setup(props, { slots }) {
2331
+ return () => h30(VView, {
2332
+ style: {
2333
+ paddingVertical: 8,
2334
+ paddingHorizontal: 16,
2335
+ backgroundColor: "#f9f9f9",
2336
+ borderBottomWidth: 1,
2337
+ borderBottomColor: "#e0e0e0"
2338
+ }
2339
+ }, () => {
2340
+ const children = [];
2341
+ if (props.title) {
2342
+ children.push(
2343
+ h30(VText, {
2344
+ style: {
2345
+ fontSize: 13,
2346
+ fontWeight: "600",
2347
+ color: "#666",
2348
+ textTransform: "uppercase",
2349
+ letterSpacing: 0.5
2350
+ }
2351
+ }, () => props.title)
2352
+ );
2353
+ }
2354
+ if (slots.default) {
2355
+ children.push(...slots.default() ?? []);
2356
+ }
2357
+ return children;
2358
+ });
1756
2359
  }
1757
2360
  });
2361
+ VDrawer.Item = VDrawerItem;
2362
+ VDrawer.Section = VDrawerSection;
1758
2363
 
1759
- // src/index.ts
1760
- export * from "@vue/runtime-core";
2364
+ // src/components/VTransition.ts
2365
+ import { defineComponent as defineComponent31, h as h31, ref as ref8 } from "@vue/runtime-core";
1761
2366
 
1762
- // src/stylesheet.ts
1763
- var validStyleProperties = /* @__PURE__ */ new Set([
1764
- // Layout (Yoga / Flexbox)
1765
- "flex",
1766
- "flexDirection",
1767
- "flexWrap",
1768
- "flexGrow",
1769
- "flexShrink",
1770
- "flexBasis",
1771
- "justifyContent",
2367
+ // src/composables/useAnimation.ts
2368
+ var Easing = {
2369
+ linear: "linear",
2370
+ ease: "ease",
2371
+ easeIn: "easeIn",
2372
+ easeOut: "easeOut",
2373
+ easeInOut: "easeInOut"
2374
+ };
2375
+ function hasViewId(value) {
2376
+ return typeof value === "object" && value !== null && "id" in value && typeof value.id === "number";
2377
+ }
2378
+ function isAnimationRef(target) {
2379
+ return typeof target === "object" && target !== null && "value" in target;
2380
+ }
2381
+ function resolveViewId(target) {
2382
+ if (typeof target === "number") return target;
2383
+ if (isAnimationRef(target)) {
2384
+ const val = target.value;
2385
+ if (hasViewId(val)) return val.id;
2386
+ throw new Error("[VueNative] Animation target ref has no .value.id \u2014 is the ref attached to a component?");
2387
+ }
2388
+ if (hasViewId(target)) return target.id;
2389
+ throw new Error("[VueNative] Invalid animation target. Pass a number, template ref, or NativeNode.");
2390
+ }
2391
+ function useAnimation() {
2392
+ function timing(target, toStyles, config = {}) {
2393
+ return NativeBridge.invokeNativeModule("Animation", "timing", [resolveViewId(target), toStyles, config]);
2394
+ }
2395
+ function spring(target, toStyles, config = {}) {
2396
+ return NativeBridge.invokeNativeModule("Animation", "spring", [resolveViewId(target), toStyles, config]);
2397
+ }
2398
+ function keyframe(target, steps, config = {}) {
2399
+ return NativeBridge.invokeNativeModule("Animation", "keyframe", [resolveViewId(target), steps, config]);
2400
+ }
2401
+ function sequence(animations) {
2402
+ return NativeBridge.invokeNativeModule("Animation", "sequence", [animations]);
2403
+ }
2404
+ function parallel(animations) {
2405
+ return NativeBridge.invokeNativeModule("Animation", "parallel", [animations]);
2406
+ }
2407
+ function fadeOut(target, duration = 300) {
2408
+ return timing(target, { opacity: 0 }, { duration });
2409
+ }
2410
+ function fadeIn(target, duration = 300) {
2411
+ return timing(target, { opacity: 1 }, { duration });
2412
+ }
2413
+ function slideInFromRight(target, duration = 300) {
2414
+ return timing(target, { translateX: 0 }, { duration, easing: "easeOut" });
2415
+ }
2416
+ function slideOutToRight(target, duration = 300) {
2417
+ return timing(target, { translateX: 400 }, { duration, easing: "easeIn" });
2418
+ }
2419
+ function resolveId(target) {
2420
+ return resolveViewId(target);
2421
+ }
2422
+ return {
2423
+ timing,
2424
+ spring,
2425
+ keyframe,
2426
+ sequence,
2427
+ parallel,
2428
+ fadeIn,
2429
+ fadeOut,
2430
+ slideInFromRight,
2431
+ slideOutToRight,
2432
+ resolveId,
2433
+ Easing
2434
+ };
2435
+ }
2436
+
2437
+ // src/components/VTransition.ts
2438
+ var DefaultDuration = 300;
2439
+ function resolveAnimationTarget(el) {
2440
+ if (!el) return void 0;
2441
+ if (typeof el === "number") return el;
2442
+ if (typeof el === "object" && "id" in el) return el.id;
2443
+ return void 0;
2444
+ }
2445
+ var VTransition = defineComponent31({
2446
+ name: "VTransition",
2447
+ props: {
2448
+ name: { type: String, default: "" },
2449
+ appear: { type: Boolean, default: false },
2450
+ persist: { type: Boolean, default: false },
2451
+ mode: { type: String, default: "default" },
2452
+ css: { type: Boolean, default: true },
2453
+ type: { type: String, default: "transition" },
2454
+ enterClass: { type: String, default: "" },
2455
+ leaveClass: { type: String, default: "" },
2456
+ enterActiveClass: { type: String, default: "" },
2457
+ leaveActiveClass: { type: String, default: "" },
2458
+ enterToClass: { type: String, default: "" },
2459
+ leaveToClass: { type: String, default: "" },
2460
+ enterFromClass: { type: String, default: "" },
2461
+ leaveFromClass: { type: String, default: "" },
2462
+ appearClass: { type: String, default: "" },
2463
+ appearActiveClass: { type: String, default: "" },
2464
+ appearToClass: { type: String, default: "" },
2465
+ duration: [Number, Object]
2466
+ },
2467
+ setup(transitionProps, { slots, expose }) {
2468
+ const { timing } = useAnimation();
2469
+ const isAppearing = ref8(false);
2470
+ const isLeaving = ref8(false);
2471
+ const hasEntered = ref8(!transitionProps.appear);
2472
+ function getElementFromVNode(vnode) {
2473
+ try {
2474
+ const el = vnode.el;
2475
+ if (!el) return void 0;
2476
+ return resolveAnimationTarget(el);
2477
+ } catch {
2478
+ return void 0;
2479
+ }
2480
+ }
2481
+ async function doEnter(el) {
2482
+ const viewId = getElementFromVNode(el);
2483
+ if (!viewId) return;
2484
+ isAppearing.value = true;
2485
+ const enterDuration = typeof transitionProps.duration === "object" ? transitionProps.duration.enter ?? DefaultDuration : transitionProps.duration ?? DefaultDuration;
2486
+ const enterStyles = { opacity: 1 };
2487
+ try {
2488
+ await timing(viewId, { opacity: 0 }, { duration: 0 });
2489
+ await timing(viewId, enterStyles, { duration: enterDuration, easing: "easeOut" });
2490
+ isAppearing.value = false;
2491
+ hasEntered.value = true;
2492
+ } catch (e) {
2493
+ console.warn("[VueNative Transition] enter animation failed:", e);
2494
+ isAppearing.value = false;
2495
+ hasEntered.value = true;
2496
+ }
2497
+ }
2498
+ async function doLeave(el) {
2499
+ const viewId = getElementFromVNode(el);
2500
+ if (!viewId) return;
2501
+ isLeaving.value = true;
2502
+ const leaveDuration = typeof transitionProps.duration === "object" ? transitionProps.duration.leave ?? DefaultDuration : transitionProps.duration ?? DefaultDuration;
2503
+ try {
2504
+ await timing(viewId, { opacity: 0 }, { duration: leaveDuration, easing: "easeIn" });
2505
+ } catch (e) {
2506
+ console.warn("[VueNative Transition] leave animation failed:", e);
2507
+ } finally {
2508
+ isLeaving.value = false;
2509
+ }
2510
+ }
2511
+ function onEnter(_el, done) {
2512
+ if (!hasEntered.value || transitionProps.appear) {
2513
+ doEnter(_el).then(() => done());
2514
+ } else {
2515
+ done();
2516
+ }
2517
+ }
2518
+ function onLeave(el, done) {
2519
+ doLeave(el).then(() => done());
2520
+ }
2521
+ function onAfterEnter() {
2522
+ }
2523
+ function onAfterLeave() {
2524
+ }
2525
+ function onEnterCancelled() {
2526
+ isAppearing.value = false;
2527
+ }
2528
+ function onLeaveCancelled() {
2529
+ isLeaving.value = false;
2530
+ }
2531
+ function onAppear(el, done) {
2532
+ isAppearing.value = true;
2533
+ doEnter(el).then(() => done());
2534
+ }
2535
+ function onAfterAppear() {
2536
+ isAppearing.value = false;
2537
+ }
2538
+ expose({
2539
+ onEnter,
2540
+ onLeave,
2541
+ onAfterEnter,
2542
+ onAfterLeave,
2543
+ onEnterCancelled,
2544
+ onLeaveCancelled,
2545
+ onAppear,
2546
+ onAfterAppear,
2547
+ isAppearing,
2548
+ isLeaving,
2549
+ hasEntered
2550
+ });
2551
+ return () => {
2552
+ const children = slots.default?.() ?? [];
2553
+ const hasDefault = children.length > 0;
2554
+ if (!hasDefault) {
2555
+ return h31("", {}, []);
2556
+ }
2557
+ let finalChildren = children;
2558
+ if (transitionProps.mode === "out-in") {
2559
+ if (isLeaving.value) {
2560
+ finalChildren = [children[children.length - 1]];
2561
+ } else if (!hasEntered.value) {
2562
+ finalChildren = [];
2563
+ }
2564
+ }
2565
+ if (transitionProps.mode === "in-out") {
2566
+ if (isAppearing.value && children.length > 1) {
2567
+ finalChildren = [children[0]];
2568
+ }
2569
+ }
2570
+ return h31("Transition", {
2571
+ name: transitionProps.name || "v",
2572
+ appear: transitionProps.appear,
2573
+ persist: transitionProps.persist || transitionProps.name === "persist",
2574
+ css: transitionProps.css,
2575
+ type: transitionProps.type,
2576
+ onEnter,
2577
+ onLeave,
2578
+ onAfterEnter,
2579
+ onAfterLeave,
2580
+ onEnterCancelled,
2581
+ onLeaveCancelled,
2582
+ onAppear,
2583
+ onAfterAppear
2584
+ }, () => finalChildren);
2585
+ };
2586
+ }
2587
+ });
2588
+ var VTransitionGroup = defineComponent31({
2589
+ name: "VTransitionGroup",
2590
+ props: {
2591
+ tag: { type: String, default: void 0 },
2592
+ name: { type: String, default: "v" },
2593
+ appear: { type: Boolean, default: false },
2594
+ persist: { type: Boolean, default: false },
2595
+ moveClass: { type: String, default: "" },
2596
+ duration: { type: Number, default: void 0 }
2597
+ },
2598
+ setup(groupProps, { slots, expose }) {
2599
+ const { timing } = useAnimation();
2600
+ function onMove(_el) {
2601
+ }
2602
+ function onEnter(el, done) {
2603
+ const viewId = resolveAnimationTarget(el);
2604
+ if (!viewId) {
2605
+ done();
2606
+ return;
2607
+ }
2608
+ timing(viewId, { opacity: 1 }, { duration: groupProps.duration ?? 300, easing: "easeOut" }).then(() => done()).catch(() => done());
2609
+ }
2610
+ function onLeave(el, done) {
2611
+ const viewId = resolveAnimationTarget(el);
2612
+ if (!viewId) {
2613
+ done();
2614
+ return;
2615
+ }
2616
+ timing(viewId, { opacity: 0 }, { duration: groupProps.duration ?? 300, easing: "easeIn" }).then(() => done()).catch(() => done());
2617
+ }
2618
+ expose({
2619
+ onEnter,
2620
+ onLeave,
2621
+ onMove
2622
+ });
2623
+ return () => {
2624
+ const children = slots.default?.() ?? [];
2625
+ return h31("TransitionGroup", {
2626
+ tag: groupProps.tag,
2627
+ name: groupProps.name,
2628
+ appear: groupProps.appear,
2629
+ persist: groupProps.persist,
2630
+ moveClass: groupProps.moveClass,
2631
+ onEnter,
2632
+ onLeave,
2633
+ onMove
2634
+ }, () => children);
2635
+ };
2636
+ }
2637
+ });
2638
+
2639
+ // src/components/KeepAlive.ts
2640
+ import {
2641
+ defineComponent as defineComponent32,
2642
+ onUnmounted as onUnmounted3,
2643
+ watch as watch6
2644
+ } from "@vue/runtime-core";
2645
+ function matches(pattern, name) {
2646
+ if (!pattern) return false;
2647
+ if (typeof pattern === "string") {
2648
+ return pattern === name;
2649
+ }
2650
+ if (pattern instanceof RegExp) {
2651
+ return pattern.test(name);
2652
+ }
2653
+ if (Array.isArray(pattern)) {
2654
+ return pattern.includes(name);
2655
+ }
2656
+ return false;
2657
+ }
2658
+ function getComponentName(vnode) {
2659
+ const component = vnode.type;
2660
+ if (typeof component === "object" && component !== null && "name" in component) {
2661
+ return component.name;
2662
+ }
2663
+ if (typeof component === "function") {
2664
+ return component.name;
2665
+ }
2666
+ return void 0;
2667
+ }
2668
+ var KeepAlive = defineComponent32({
2669
+ name: "KeepAlive",
2670
+ props: {
2671
+ include: [String, RegExp, Array],
2672
+ exclude: [String, RegExp, Array],
2673
+ max: [Number, String]
2674
+ },
2675
+ setup(props, { slots }) {
2676
+ const cache = /* @__PURE__ */ new Map();
2677
+ const keys = /* @__PURE__ */ new Set();
2678
+ const maxCacheSize = typeof props.max === "string" ? parseInt(props.max, 10) : props.max;
2679
+ function pruneCache(filter) {
2680
+ cache.forEach((_, key) => {
2681
+ if (!filter(key)) {
2682
+ cache.delete(key);
2683
+ keys.delete(key);
2684
+ }
2685
+ });
2686
+ }
2687
+ function pruneCacheEntry(key) {
2688
+ cache.delete(key);
2689
+ keys.delete(key);
2690
+ }
2691
+ watch6(
2692
+ () => [props.include, props.exclude],
2693
+ () => {
2694
+ if (!props.include && !props.exclude) return;
2695
+ pruneCache((name) => {
2696
+ if (props.include && !matches(props.include, name)) return false;
2697
+ if (props.exclude && matches(props.exclude, name)) return false;
2698
+ return true;
2699
+ });
2700
+ }
2701
+ );
2702
+ onUnmounted3(() => {
2703
+ cache.clear();
2704
+ keys.clear();
2705
+ });
2706
+ return () => {
2707
+ const children = slots.default?.() ?? [];
2708
+ if (!children.length) return children;
2709
+ const vnode = children[0];
2710
+ const name = getComponentName(vnode) ?? String(vnode.type);
2711
+ const key = name;
2712
+ if (props.include && !matches(props.include, name)) {
2713
+ return vnode;
2714
+ }
2715
+ if (props.exclude && matches(props.exclude, name)) {
2716
+ return vnode;
2717
+ }
2718
+ const cached = cache.get(key);
2719
+ if (cached) {
2720
+ keys.delete(key);
2721
+ keys.add(key);
2722
+ return cached.vnode;
2723
+ }
2724
+ if (maxCacheSize && cache.size >= maxCacheSize) {
2725
+ const firstKey = keys.values().next().value;
2726
+ if (firstKey) {
2727
+ pruneCacheEntry(firstKey);
2728
+ }
2729
+ }
2730
+ cache.set(key, { vnode, key });
2731
+ keys.add(key);
2732
+ return vnode;
2733
+ };
2734
+ }
2735
+ });
2736
+ KeepAlive.isKeepAlive = true;
2737
+
2738
+ // src/components/VSuspense.ts
2739
+ import {
2740
+ defineComponent as defineComponent33,
2741
+ h as h32,
2742
+ ref as ref9,
2743
+ onMounted,
2744
+ shallowRef,
2745
+ watch as watch7,
2746
+ provide as provide2,
2747
+ inject as inject2
2748
+ } from "@vue/runtime-core";
2749
+ var suspenseContextKey = /* @__PURE__ */ Symbol("suspense");
2750
+ var VSuspense = defineComponent33({
2751
+ name: "Suspense",
2752
+ props: {
2753
+ timeout: { type: Number, default: 3e4 }
2754
+ },
2755
+ setup(_props, { slots }) {
2756
+ const hasError = ref9(false);
2757
+ const error = shallowRef(null);
2758
+ const pendingCount = ref9(0);
2759
+ const context = {
2760
+ hasError,
2761
+ error,
2762
+ pendingCount,
2763
+ resolve: () => {
2764
+ pendingCount.value--;
2765
+ },
2766
+ reject: (err) => {
2767
+ hasError.value = true;
2768
+ error.value = err;
2769
+ }
2770
+ };
2771
+ provide2(suspenseContextKey, context);
2772
+ return () => {
2773
+ if (hasError.value) {
2774
+ return slots.fallback?.({ error: error.value }) ?? null;
2775
+ }
2776
+ const defaultSlots = slots.default?.() ?? [];
2777
+ return defaultSlots;
2778
+ };
2779
+ }
2780
+ });
2781
+
2782
+ // src/components/index.ts
2783
+ var builtInComponents = {
2784
+ VView,
2785
+ VText,
2786
+ VButton,
2787
+ VInput,
2788
+ VSwitch,
2789
+ VActivityIndicator,
2790
+ VScrollView,
2791
+ VImage,
2792
+ VKeyboardAvoiding,
2793
+ VSafeArea,
2794
+ VSlider,
2795
+ VList,
2796
+ VModal,
2797
+ VAlertDialog,
2798
+ VStatusBar,
2799
+ VWebView,
2800
+ VProgressBar,
2801
+ VPicker,
2802
+ VSegmentedControl,
2803
+ VActionSheet,
2804
+ VRefreshControl,
2805
+ VPressable,
2806
+ VSectionList,
2807
+ VCheckbox,
2808
+ VRadio,
2809
+ VDropdown,
2810
+ VVideo,
2811
+ VFlatList,
2812
+ VTabBar,
2813
+ VDrawer,
2814
+ VDrawerItem,
2815
+ VDrawerSection,
2816
+ VTransition,
2817
+ VTransitionGroup,
2818
+ KeepAlive,
2819
+ VSuspense
2820
+ };
2821
+
2822
+ // src/errorBoundary.ts
2823
+ import { defineComponent as defineComponent34, ref as ref10, watch as watch8, onErrorCaptured } from "@vue/runtime-core";
2824
+ var ErrorBoundary = defineComponent34({
2825
+ name: "ErrorBoundary",
2826
+ props: {
2827
+ onError: Function,
2828
+ resetKeys: {
2829
+ type: Array,
2830
+ default: () => []
2831
+ }
2832
+ },
2833
+ setup(props, { slots }) {
2834
+ const error = ref10(null);
2835
+ const errorInfo = ref10("");
2836
+ onErrorCaptured((err, _instance, info) => {
2837
+ const normalizedError = err instanceof Error ? err : new Error(String(err));
2838
+ error.value = normalizedError;
2839
+ errorInfo.value = info;
2840
+ if (props.onError) {
2841
+ props.onError(normalizedError, info);
2842
+ }
2843
+ return false;
2844
+ });
2845
+ function reset() {
2846
+ error.value = null;
2847
+ errorInfo.value = "";
2848
+ }
2849
+ watch8(
2850
+ () => props.resetKeys,
2851
+ () => {
2852
+ if (error.value) {
2853
+ reset();
2854
+ }
2855
+ },
2856
+ { deep: true }
2857
+ );
2858
+ return () => {
2859
+ if (error.value && slots.fallback) {
2860
+ return slots.fallback({ error: error.value, errorInfo: errorInfo.value, reset });
2861
+ }
2862
+ return slots.default?.();
2863
+ };
2864
+ }
2865
+ });
2866
+
2867
+ // src/index.ts
2868
+ export * from "@vue/runtime-core";
2869
+
2870
+ // src/stylesheet.ts
2871
+ var validStyleProperties = /* @__PURE__ */ new Set([
2872
+ // Layout (Yoga / Flexbox)
2873
+ "flex",
2874
+ "flexDirection",
2875
+ "flexWrap",
2876
+ "flexGrow",
2877
+ "flexShrink",
2878
+ "flexBasis",
2879
+ "justifyContent",
1772
2880
  "alignItems",
1773
2881
  "alignSelf",
1774
2882
  "alignContent",
@@ -1874,11 +2982,78 @@ function createStyleSheet(styles) {
1874
2982
  }
1875
2983
  const result = {};
1876
2984
  for (const key in styles) {
1877
- result[key] = Object.freeze({ ...styles[key] });
2985
+ const styleKey = key;
2986
+ result[styleKey] = Object.freeze({ ...styles[styleKey] });
1878
2987
  }
1879
2988
  return Object.freeze(result);
1880
2989
  }
1881
2990
 
2991
+ // src/directives/vShow.ts
2992
+ var vShow = {
2993
+ beforeMount(el, { value }) {
2994
+ try {
2995
+ NativeBridge.updateProp(el.id, "hidden", !value);
2996
+ } catch (err) {
2997
+ console.error("[VueNative] v-show beforeMount error:", err);
2998
+ }
2999
+ },
3000
+ updated(el, { value, oldValue }) {
3001
+ if (value === oldValue) return;
3002
+ try {
3003
+ NativeBridge.updateProp(el.id, "hidden", !value);
3004
+ } catch (err) {
3005
+ console.error("[VueNative] v-show updated error:", err);
3006
+ }
3007
+ }
3008
+ };
3009
+
3010
+ // src/directives/vModel.ts
3011
+ function getModelValue(event) {
3012
+ const modelEvent = event;
3013
+ return modelEvent?.value ?? modelEvent?.target?.value ?? event;
3014
+ }
3015
+ var vModel = {
3016
+ beforeMount(el, binding, vnode) {
3017
+ const { value, modifiers } = binding;
3018
+ const { lazy, number, trim } = modifiers || {};
3019
+ NativeBridge.updateProp(el.id, "value", value);
3020
+ const assign = vnode.dirs?.[0]?.value;
3021
+ if (typeof assign !== "function") {
3022
+ console.warn(
3023
+ "[VueNative] v-model directive requires the vnode to have an assign function. This usually happens when using v-model on native elements rendered by Vue, not custom components with modelValue props."
3024
+ );
3025
+ return;
3026
+ }
3027
+ const eventName = lazy ? "change" : "input";
3028
+ NativeBridge.addEventListener(el.id, eventName, (event) => {
3029
+ let newValue = getModelValue(event);
3030
+ if (trim && typeof newValue === "string") {
3031
+ newValue = newValue.trim();
3032
+ }
3033
+ if (number) {
3034
+ newValue = Number(newValue);
3035
+ }
3036
+ assign(newValue);
3037
+ });
3038
+ },
3039
+ updated(el, { value, oldValue, modifiers }, vnode) {
3040
+ if (value === oldValue) return;
3041
+ const _assign = vnode.dirs?.[0]?.value;
3042
+ let newValue = value;
3043
+ if (modifiers?.trim && typeof value === "string") {
3044
+ newValue = value.trim();
3045
+ }
3046
+ if (modifiers?.number) {
3047
+ newValue = Number(value);
3048
+ }
3049
+ NativeBridge.updateProp(el.id, "value", newValue);
3050
+ },
3051
+ beforeUnmount(el, _binding, _vnode) {
3052
+ NativeBridge.removeEventListener(el.id, "input");
3053
+ NativeBridge.removeEventListener(el.id, "change");
3054
+ }
3055
+ };
3056
+
1882
3057
  // src/composables/useHaptics.ts
1883
3058
  function useHaptics() {
1884
3059
  function vibrate(style = "medium") {
@@ -1932,9 +3107,9 @@ function useAsyncStorage() {
1932
3107
  }
1933
3108
 
1934
3109
  // src/composables/useClipboard.ts
1935
- import { ref as ref6 } from "@vue/runtime-core";
3110
+ import { ref as ref11 } from "@vue/runtime-core";
1936
3111
  function useClipboard() {
1937
- const content = ref6("");
3112
+ const content = ref11("");
1938
3113
  function copy(text) {
1939
3114
  return NativeBridge.invokeNativeModule("Clipboard", "copy", [text]).then(() => void 0);
1940
3115
  }
@@ -1948,16 +3123,16 @@ function useClipboard() {
1948
3123
  }
1949
3124
 
1950
3125
  // src/composables/useDeviceInfo.ts
1951
- import { ref as ref7, onMounted } from "@vue/runtime-core";
3126
+ import { ref as ref12, onMounted as onMounted2 } from "@vue/runtime-core";
1952
3127
  function useDeviceInfo() {
1953
- const model = ref7("");
1954
- const systemVersion = ref7("");
1955
- const systemName = ref7("");
1956
- const name = ref7("");
1957
- const screenWidth = ref7(0);
1958
- const screenHeight = ref7(0);
1959
- const scale = ref7(1);
1960
- const isLoaded = ref7(false);
3128
+ const model = ref12("");
3129
+ const systemVersion = ref12("");
3130
+ const systemName = ref12("");
3131
+ const name = ref12("");
3132
+ const screenWidth = ref12(0);
3133
+ const screenHeight = ref12(0);
3134
+ const scale = ref12(1);
3135
+ const isLoaded = ref12(false);
1961
3136
  async function fetchInfo() {
1962
3137
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
1963
3138
  model.value = info.model ?? "";
@@ -1969,7 +3144,7 @@ function useDeviceInfo() {
1969
3144
  scale.value = info.scale ?? 1;
1970
3145
  isLoaded.value = true;
1971
3146
  }
1972
- onMounted(() => {
3147
+ onMounted2(() => {
1973
3148
  fetchInfo();
1974
3149
  });
1975
3150
  return {
@@ -1986,10 +3161,10 @@ function useDeviceInfo() {
1986
3161
  }
1987
3162
 
1988
3163
  // src/composables/useKeyboard.ts
1989
- import { ref as ref8 } from "@vue/runtime-core";
3164
+ import { ref as ref13 } from "@vue/runtime-core";
1990
3165
  function useKeyboard() {
1991
- const isVisible = ref8(false);
1992
- const height = ref8(0);
3166
+ const isVisible = ref13(false);
3167
+ const height = ref13(0);
1993
3168
  function dismiss() {
1994
3169
  return NativeBridge.invokeNativeModule("Keyboard", "dismiss", []).then(() => void 0);
1995
3170
  }
@@ -2002,61 +3177,11 @@ function useKeyboard() {
2002
3177
  return { isVisible, height, dismiss, getHeight };
2003
3178
  }
2004
3179
 
2005
- // src/composables/useAnimation.ts
2006
- var Easing = {
2007
- linear: "linear",
2008
- ease: "ease",
2009
- easeIn: "easeIn",
2010
- easeOut: "easeOut",
2011
- easeInOut: "easeInOut"
2012
- };
2013
- function useAnimation() {
2014
- function timing(viewId, toStyles, config = {}) {
2015
- return NativeBridge.invokeNativeModule("Animation", "timing", [viewId, toStyles, config]);
2016
- }
2017
- function spring(viewId, toStyles, config = {}) {
2018
- return NativeBridge.invokeNativeModule("Animation", "spring", [viewId, toStyles, config]);
2019
- }
2020
- function keyframe(viewId, steps, config = {}) {
2021
- return NativeBridge.invokeNativeModule("Animation", "keyframe", [viewId, steps, config]);
2022
- }
2023
- function sequence(animations) {
2024
- return NativeBridge.invokeNativeModule("Animation", "sequence", [animations]);
2025
- }
2026
- function parallel(animations) {
2027
- return NativeBridge.invokeNativeModule("Animation", "parallel", [animations]);
2028
- }
2029
- function fadeOut(viewId, duration = 300) {
2030
- return timing(viewId, { opacity: 0 }, { duration });
2031
- }
2032
- function fadeIn(viewId, duration = 300) {
2033
- return timing(viewId, { opacity: 1 }, { duration });
2034
- }
2035
- function slideInFromRight(viewId, duration = 300) {
2036
- return timing(viewId, { translateX: 0 }, { duration, easing: "easeOut" });
2037
- }
2038
- function slideOutToRight(viewId, duration = 300) {
2039
- return timing(viewId, { translateX: 400 }, { duration, easing: "easeIn" });
2040
- }
2041
- return {
2042
- timing,
2043
- spring,
2044
- keyframe,
2045
- sequence,
2046
- parallel,
2047
- fadeIn,
2048
- fadeOut,
2049
- slideInFromRight,
2050
- slideOutToRight,
2051
- Easing
2052
- };
2053
- }
2054
-
2055
3180
  // src/composables/useNetwork.ts
2056
- import { ref as ref9, onUnmounted as onUnmounted3 } from "@vue/runtime-core";
3181
+ import { ref as ref14, onUnmounted as onUnmounted4 } from "@vue/runtime-core";
2057
3182
  function useNetwork() {
2058
- const isConnected = ref9(true);
2059
- const connectionType = ref9("unknown");
3183
+ const isConnected = ref14(true);
3184
+ const connectionType = ref14("unknown");
2060
3185
  let lastEventTime = 0;
2061
3186
  const unsubscribe = NativeBridge.onGlobalEvent("network:change", (payload) => {
2062
3187
  lastEventTime = Date.now();
@@ -2071,14 +3196,14 @@ function useNetwork() {
2071
3196
  }
2072
3197
  }).catch(() => {
2073
3198
  });
2074
- onUnmounted3(unsubscribe);
3199
+ onUnmounted4(unsubscribe);
2075
3200
  return { isConnected, connectionType };
2076
3201
  }
2077
3202
 
2078
3203
  // src/composables/useAppState.ts
2079
- import { ref as ref10, onUnmounted as onUnmounted4 } from "@vue/runtime-core";
3204
+ import { ref as ref15, onUnmounted as onUnmounted5 } from "@vue/runtime-core";
2080
3205
  function useAppState() {
2081
- const state = ref10("active");
3206
+ const state = ref15("active");
2082
3207
  NativeBridge.invokeNativeModule("AppState", "getState").then((s) => {
2083
3208
  state.value = s;
2084
3209
  }).catch(() => {
@@ -2086,7 +3211,7 @@ function useAppState() {
2086
3211
  const unsubscribe = NativeBridge.onGlobalEvent("appState:change", (payload) => {
2087
3212
  state.value = payload.state;
2088
3213
  });
2089
- onUnmounted4(unsubscribe);
3214
+ onUnmounted5(unsubscribe);
2090
3215
  return { state };
2091
3216
  }
2092
3217
 
@@ -2121,10 +3246,10 @@ function usePermissions() {
2121
3246
  }
2122
3247
 
2123
3248
  // src/composables/useGeolocation.ts
2124
- import { ref as ref11, onUnmounted as onUnmounted5 } from "@vue/runtime-core";
3249
+ import { ref as ref16, onUnmounted as onUnmounted6 } from "@vue/runtime-core";
2125
3250
  function useGeolocation() {
2126
- const coords = ref11(null);
2127
- const error = ref11(null);
3251
+ const coords = ref16(null);
3252
+ const error = ref16(null);
2128
3253
  let watchId = null;
2129
3254
  async function getCurrentPosition() {
2130
3255
  try {
@@ -2149,7 +3274,7 @@ function useGeolocation() {
2149
3274
  const unsubscribeError = NativeBridge.onGlobalEvent("location:error", (payload) => {
2150
3275
  error.value = payload.message;
2151
3276
  });
2152
- onUnmounted5(() => {
3277
+ onUnmounted6(() => {
2153
3278
  unsubscribe();
2154
3279
  unsubscribeError();
2155
3280
  if (watchId !== null) clearWatch(watchId);
@@ -2169,7 +3294,7 @@ function useGeolocation() {
2169
3294
  }
2170
3295
 
2171
3296
  // src/composables/useCamera.ts
2172
- import { onUnmounted as onUnmounted6 } from "@vue/runtime-core";
3297
+ import { onUnmounted as onUnmounted7 } from "@vue/runtime-core";
2173
3298
  function useCamera() {
2174
3299
  const qrCleanups = [];
2175
3300
  async function launchCamera(options = {}) {
@@ -2192,7 +3317,7 @@ function useCamera() {
2192
3317
  qrCleanups.push(unsubscribe);
2193
3318
  return unsubscribe;
2194
3319
  }
2195
- onUnmounted6(() => {
3320
+ onUnmounted7(() => {
2196
3321
  NativeBridge.invokeNativeModule("Camera", "stopQRScan").catch(() => {
2197
3322
  });
2198
3323
  qrCleanups.forEach((fn) => fn());
@@ -2202,10 +3327,10 @@ function useCamera() {
2202
3327
  }
2203
3328
 
2204
3329
  // src/composables/useNotifications.ts
2205
- import { ref as ref12, onUnmounted as onUnmounted7 } from "@vue/runtime-core";
3330
+ import { ref as ref17, onUnmounted as onUnmounted8 } from "@vue/runtime-core";
2206
3331
  function useNotifications() {
2207
- const isGranted = ref12(false);
2208
- const pushToken = ref12(null);
3332
+ const isGranted = ref17(false);
3333
+ const pushToken = ref17(null);
2209
3334
  async function requestPermission() {
2210
3335
  const granted = await NativeBridge.invokeNativeModule("Notifications", "requestPermission");
2211
3336
  isGranted.value = granted;
@@ -2225,7 +3350,7 @@ function useNotifications() {
2225
3350
  }
2226
3351
  function onNotification(handler) {
2227
3352
  const unsubscribe = NativeBridge.onGlobalEvent("notification:received", handler);
2228
- onUnmounted7(unsubscribe);
3353
+ onUnmounted8(unsubscribe);
2229
3354
  return unsubscribe;
2230
3355
  }
2231
3356
  async function registerForPush() {
@@ -2239,12 +3364,12 @@ function useNotifications() {
2239
3364
  pushToken.value = payload.token;
2240
3365
  handler(payload.token);
2241
3366
  });
2242
- onUnmounted7(unsubscribe);
3367
+ onUnmounted8(unsubscribe);
2243
3368
  return unsubscribe;
2244
3369
  }
2245
3370
  function onPushReceived(handler) {
2246
3371
  const unsubscribe = NativeBridge.onGlobalEvent("push:received", handler);
2247
- onUnmounted7(unsubscribe);
3372
+ onUnmounted8(unsubscribe);
2248
3373
  return unsubscribe;
2249
3374
  }
2250
3375
  return {
@@ -2280,7 +3405,10 @@ function useBiometry() {
2280
3405
  }
2281
3406
 
2282
3407
  // src/composables/useHttp.ts
2283
- import { ref as ref13, onUnmounted as onUnmounted8 } from "@vue/runtime-core";
3408
+ import { ref as ref18, onUnmounted as onUnmounted9 } from "@vue/runtime-core";
3409
+ function isQueryRequestOptions(value) {
3410
+ return "params" in value || "headers" in value;
3411
+ }
2284
3412
  function useHttp(config = {}) {
2285
3413
  if (config.pins && Object.keys(config.pins).length > 0) {
2286
3414
  const configurePins = globalThis.__VN_configurePins;
@@ -2290,39 +3418,70 @@ function useHttp(config = {}) {
2290
3418
  NativeBridge.invokeNativeModule("Http", "configurePins", [config.pins]);
2291
3419
  }
2292
3420
  }
2293
- const loading = ref13(false);
2294
- const error = ref13(null);
3421
+ const loading = ref18(false);
3422
+ const error = ref18(null);
2295
3423
  let isMounted = true;
2296
- onUnmounted8(() => {
3424
+ onUnmounted9(() => {
2297
3425
  isMounted = false;
2298
3426
  });
3427
+ const BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
3428
+ function parseResponseHeaders(response) {
3429
+ const result = {};
3430
+ if (!response.headers) return result;
3431
+ try {
3432
+ if (typeof response.headers.forEach === "function") {
3433
+ response.headers.forEach((value, key) => {
3434
+ result[key] = value;
3435
+ });
3436
+ } else if (typeof response.headers.entries === "function") {
3437
+ for (const [key, value] of response.headers.entries()) {
3438
+ result[key] = value;
3439
+ }
3440
+ }
3441
+ } catch {
3442
+ }
3443
+ return result;
3444
+ }
2299
3445
  async function request(method, url, options = {}) {
2300
3446
  const fullUrl = config.baseURL ? `${config.baseURL}${url}` : url;
2301
3447
  loading.value = true;
2302
3448
  error.value = null;
3449
+ let controller;
3450
+ let timeoutId;
3451
+ if (config.timeout && config.timeout > 0 && typeof AbortController !== "undefined") {
3452
+ controller = new AbortController();
3453
+ timeoutId = setTimeout(() => controller.abort(), config.timeout);
3454
+ }
2303
3455
  try {
3456
+ const upperMethod = method.toUpperCase();
2304
3457
  const mergedHeaders = {
2305
- "Content-Type": "application/json",
2306
3458
  ...config.headers ?? {},
2307
3459
  ...options.headers ?? {}
2308
3460
  };
3461
+ if (BODY_METHODS.has(upperMethod) && !mergedHeaders["Content-Type"]) {
3462
+ mergedHeaders["Content-Type"] = "application/json";
3463
+ }
2309
3464
  const fetchOptions = {
2310
- method: method.toUpperCase(),
3465
+ method: upperMethod,
2311
3466
  headers: mergedHeaders
2312
3467
  };
3468
+ if (controller) {
3469
+ fetchOptions.signal = controller.signal;
3470
+ }
2313
3471
  if (options.body !== void 0) {
2314
3472
  fetchOptions.body = JSON.stringify(options.body);
2315
3473
  }
2316
3474
  const response = await fetch(fullUrl, fetchOptions);
2317
3475
  const data = await response.json();
3476
+ const responseHeaders = parseResponseHeaders(response);
2318
3477
  if (!isMounted) {
2319
- return { data, status: response.status, ok: response.ok, headers: {} };
3478
+ return { data, status: response.status, ok: response.ok, headers: responseHeaders };
2320
3479
  }
2321
3480
  return {
2322
3481
  data,
2323
3482
  status: response.status,
2324
3483
  ok: response.ok,
2325
- headers: {}
3484
+ headers: responseHeaders
2326
3485
  };
2327
3486
  } catch (e) {
2328
3487
  const msg = e instanceof Error ? e.message : String(e);
@@ -2331,6 +3490,9 @@ function useHttp(config = {}) {
2331
3490
  }
2332
3491
  throw e;
2333
3492
  } finally {
3493
+ if (timeoutId !== void 0) {
3494
+ clearTimeout(timeoutId);
3495
+ }
2334
3496
  if (isMounted) {
2335
3497
  loading.value = false;
2336
3498
  }
@@ -2345,30 +3507,24 @@ function useHttp(config = {}) {
2345
3507
  loading,
2346
3508
  error,
2347
3509
  get: (url, options) => {
2348
- if (options && !("params" in options) && !("headers" in options)) {
2349
- return request("GET", url, { headers: options });
2350
- }
2351
- const opts = options;
2352
- return request("GET", buildUrl(url, opts?.params), { headers: opts?.headers });
3510
+ const normalizedOptions = options ? isQueryRequestOptions(options) ? options : { headers: options } : void 0;
3511
+ return request("GET", buildUrl(url, normalizedOptions?.params), { headers: normalizedOptions?.headers });
2353
3512
  },
2354
3513
  post: (url, body, headers) => request("POST", url, { body, headers }),
2355
3514
  put: (url, body, headers) => request("PUT", url, { body, headers }),
2356
3515
  patch: (url, body, headers) => request("PATCH", url, { body, headers }),
2357
3516
  delete: (url, options) => {
2358
- if (options && !("params" in options) && !("headers" in options)) {
2359
- return request("DELETE", url, { headers: options });
2360
- }
2361
- const opts = options;
2362
- return request("DELETE", buildUrl(url, opts?.params), { headers: opts?.headers });
3517
+ const normalizedOptions = options ? isQueryRequestOptions(options) ? options : { headers: options } : void 0;
3518
+ return request("DELETE", buildUrl(url, normalizedOptions?.params), { headers: normalizedOptions?.headers });
2363
3519
  }
2364
3520
  };
2365
3521
  }
2366
3522
 
2367
3523
  // src/composables/useColorScheme.ts
2368
- import { ref as ref14, onUnmounted as onUnmounted9 } from "@vue/runtime-core";
3524
+ import { ref as ref19, onUnmounted as onUnmounted10 } from "@vue/runtime-core";
2369
3525
  function useColorScheme() {
2370
- const colorScheme = ref14("light");
2371
- const isDark = ref14(false);
3526
+ const colorScheme = ref19("light");
3527
+ const isDark = ref19(false);
2372
3528
  const unsubscribe = NativeBridge.onGlobalEvent(
2373
3529
  "colorScheme:change",
2374
3530
  (payload) => {
@@ -2376,15 +3532,15 @@ function useColorScheme() {
2376
3532
  isDark.value = payload.colorScheme === "dark";
2377
3533
  }
2378
3534
  );
2379
- onUnmounted9(unsubscribe);
3535
+ onUnmounted10(unsubscribe);
2380
3536
  return { colorScheme, isDark };
2381
3537
  }
2382
3538
 
2383
3539
  // src/composables/useBackHandler.ts
2384
- import { onMounted as onMounted2, onUnmounted as onUnmounted10 } from "@vue/runtime-core";
3540
+ import { onMounted as onMounted3, onUnmounted as onUnmounted11 } from "@vue/runtime-core";
2385
3541
  function useBackHandler(handler) {
2386
3542
  let unsubscribe = null;
2387
- onMounted2(() => {
3543
+ onMounted3(() => {
2388
3544
  unsubscribe = NativeBridge.onGlobalEvent("hardware:backPress", () => {
2389
3545
  const handled = handler();
2390
3546
  if (!handled) {
@@ -2393,7 +3549,7 @@ function useBackHandler(handler) {
2393
3549
  }
2394
3550
  });
2395
3551
  });
2396
- onUnmounted10(() => {
3552
+ onUnmounted11(() => {
2397
3553
  unsubscribe?.();
2398
3554
  unsubscribe = null;
2399
3555
  });
@@ -2417,11 +3573,11 @@ function useSecureStorage() {
2417
3573
  }
2418
3574
 
2419
3575
  // src/composables/useI18n.ts
2420
- import { ref as ref15, onMounted as onMounted3 } from "@vue/runtime-core";
3576
+ import { ref as ref20, onMounted as onMounted4 } from "@vue/runtime-core";
2421
3577
  function useI18n() {
2422
- const isRTL = ref15(false);
2423
- const locale = ref15("en");
2424
- onMounted3(async () => {
3578
+ const isRTL = ref20(false);
3579
+ const locale = ref20("en");
3580
+ onMounted4(async () => {
2425
3581
  try {
2426
3582
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getDeviceInfo", []);
2427
3583
  locale.value = info?.locale || "en";
@@ -2432,21 +3588,13 @@ function useI18n() {
2432
3588
  return { isRTL, locale };
2433
3589
  }
2434
3590
 
2435
- // src/composables/usePlatform.ts
2436
- function usePlatform() {
2437
- const platform = typeof __PLATFORM__ !== "undefined" ? __PLATFORM__ : "ios";
2438
- const isIOS = platform === "ios";
2439
- const isAndroid = platform === "android";
2440
- return { platform, isIOS, isAndroid };
2441
- }
2442
-
2443
3591
  // src/composables/useDimensions.ts
2444
- import { ref as ref16, onMounted as onMounted4, onUnmounted as onUnmounted11 } from "@vue/runtime-core";
3592
+ import { ref as ref21, onMounted as onMounted5, onUnmounted as onUnmounted12 } from "@vue/runtime-core";
2445
3593
  function useDimensions() {
2446
- const width = ref16(0);
2447
- const height = ref16(0);
2448
- const scale = ref16(1);
2449
- onMounted4(async () => {
3594
+ const width = ref21(0);
3595
+ const height = ref21(0);
3596
+ const scale = ref21(1);
3597
+ onMounted5(async () => {
2450
3598
  try {
2451
3599
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
2452
3600
  width.value = info?.screenWidth || 0;
@@ -2460,12 +3608,12 @@ function useDimensions() {
2460
3608
  if (payload.height != null) height.value = payload.height;
2461
3609
  if (payload.scale != null) scale.value = payload.scale;
2462
3610
  });
2463
- onUnmounted11(cleanup);
3611
+ onUnmounted12(cleanup);
2464
3612
  return { width, height, scale };
2465
3613
  }
2466
3614
 
2467
3615
  // src/composables/useWebSocket.ts
2468
- import { ref as ref17, onUnmounted as onUnmounted12 } from "@vue/runtime-core";
3616
+ import { ref as ref22, onUnmounted as onUnmounted13 } from "@vue/runtime-core";
2469
3617
  var connectionCounter = 0;
2470
3618
  function useWebSocket(url, options = {}) {
2471
3619
  const {
@@ -2475,9 +3623,9 @@ function useWebSocket(url, options = {}) {
2475
3623
  reconnectInterval = 1e3
2476
3624
  } = options;
2477
3625
  const connectionId = `ws_${++connectionCounter}_${Date.now()}`;
2478
- const status = ref17("CLOSED");
2479
- const lastMessage = ref17(null);
2480
- const error = ref17(null);
3626
+ const status = ref22("CLOSED");
3627
+ const lastMessage = ref22(null);
3628
+ const error = ref22(null);
2481
3629
  let reconnectAttempts = 0;
2482
3630
  let reconnectTimer = null;
2483
3631
  const MAX_PENDING_MESSAGES = 100;
@@ -2562,7 +3710,7 @@ function useWebSocket(url, options = {}) {
2562
3710
  if (autoConnect) {
2563
3711
  open();
2564
3712
  }
2565
- onUnmounted12(() => {
3713
+ onUnmounted13(() => {
2566
3714
  if (reconnectTimer) {
2567
3715
  clearTimeout(reconnectTimer);
2568
3716
  }
@@ -2631,12 +3779,12 @@ function useFileSystem() {
2631
3779
  }
2632
3780
 
2633
3781
  // src/composables/useSensors.ts
2634
- import { ref as ref18, onUnmounted as onUnmounted13 } from "@vue/runtime-core";
3782
+ import { ref as ref23, onUnmounted as onUnmounted14 } from "@vue/runtime-core";
2635
3783
  function useAccelerometer(options = {}) {
2636
- const x = ref18(0);
2637
- const y = ref18(0);
2638
- const z = ref18(0);
2639
- const isAvailable = ref18(false);
3784
+ const x = ref23(0);
3785
+ const y = ref23(0);
3786
+ const z = ref23(0);
3787
+ const isAvailable = ref23(false);
2640
3788
  let running = false;
2641
3789
  let unsubscribe = null;
2642
3790
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["accelerometer"]).then((result) => {
@@ -2664,16 +3812,16 @@ function useAccelerometer(options = {}) {
2664
3812
  NativeBridge.invokeNativeModule("Sensors", "stopAccelerometer").catch(() => {
2665
3813
  });
2666
3814
  }
2667
- onUnmounted13(() => {
3815
+ onUnmounted14(() => {
2668
3816
  stop();
2669
3817
  });
2670
3818
  return { x, y, z, isAvailable, start, stop };
2671
3819
  }
2672
3820
  function useGyroscope(options = {}) {
2673
- const x = ref18(0);
2674
- const y = ref18(0);
2675
- const z = ref18(0);
2676
- const isAvailable = ref18(false);
3821
+ const x = ref23(0);
3822
+ const y = ref23(0);
3823
+ const z = ref23(0);
3824
+ const isAvailable = ref23(false);
2677
3825
  let running = false;
2678
3826
  let unsubscribe = null;
2679
3827
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["gyroscope"]).then((result) => {
@@ -2701,20 +3849,31 @@ function useGyroscope(options = {}) {
2701
3849
  NativeBridge.invokeNativeModule("Sensors", "stopGyroscope").catch(() => {
2702
3850
  });
2703
3851
  }
2704
- onUnmounted13(() => {
3852
+ onUnmounted14(() => {
2705
3853
  stop();
2706
3854
  });
2707
3855
  return { x, y, z, isAvailable, start, stop };
2708
3856
  }
2709
3857
 
2710
3858
  // src/composables/useAudio.ts
2711
- import { ref as ref19, onUnmounted as onUnmounted14 } from "@vue/runtime-core";
3859
+ import { ref as ref24, onUnmounted as onUnmounted15 } from "@vue/runtime-core";
3860
+ function asRecord(value) {
3861
+ return typeof value === "object" && value !== null ? value : null;
3862
+ }
3863
+ function getNumberProp(record, key) {
3864
+ const value = record?.[key];
3865
+ return typeof value === "number" ? value : void 0;
3866
+ }
3867
+ function getStringProp(record, key) {
3868
+ const value = record?.[key];
3869
+ return typeof value === "string" ? value : void 0;
3870
+ }
2712
3871
  function useAudio() {
2713
- const duration = ref19(0);
2714
- const position = ref19(0);
2715
- const isPlaying = ref19(false);
2716
- const isRecording = ref19(false);
2717
- const error = ref19(null);
3872
+ const duration = ref24(0);
3873
+ const position = ref24(0);
3874
+ const isPlaying = ref24(false);
3875
+ const isRecording = ref24(false);
3876
+ const error = ref24(null);
2718
3877
  const unsubProgress = NativeBridge.onGlobalEvent("audio:progress", (payload) => {
2719
3878
  position.value = payload.currentTime ?? 0;
2720
3879
  duration.value = payload.duration ?? 0;
@@ -2727,7 +3886,7 @@ function useAudio() {
2727
3886
  error.value = payload.message ?? "Unknown audio error";
2728
3887
  isPlaying.value = false;
2729
3888
  });
2730
- onUnmounted14(() => {
3889
+ onUnmounted15(() => {
2731
3890
  unsubProgress();
2732
3891
  unsubComplete();
2733
3892
  unsubError();
@@ -2740,9 +3899,10 @@ function useAudio() {
2740
3899
  });
2741
3900
  async function play(uri, options = {}) {
2742
3901
  error.value = null;
2743
- const result = await NativeBridge.invokeNativeModule("Audio", "play", [uri, options]);
2744
- if (result?.duration != null) {
2745
- duration.value = result.duration;
3902
+ const result = asRecord(await NativeBridge.invokeNativeModule("Audio", "play", [uri, options]));
3903
+ const audioDuration = getNumberProp(result, "duration");
3904
+ if (audioDuration !== void 0) {
3905
+ duration.value = audioDuration;
2746
3906
  }
2747
3907
  isPlaying.value = true;
2748
3908
  }
@@ -2768,14 +3928,17 @@ function useAudio() {
2768
3928
  }
2769
3929
  async function startRecording(options = {}) {
2770
3930
  error.value = null;
2771
- const result = await NativeBridge.invokeNativeModule("Audio", "startRecording", [options]);
3931
+ const result = asRecord(await NativeBridge.invokeNativeModule("Audio", "startRecording", [options]));
2772
3932
  isRecording.value = true;
2773
- return result?.uri ?? "";
3933
+ return getStringProp(result, "uri") ?? "";
2774
3934
  }
2775
3935
  async function stopRecording() {
2776
- const result = await NativeBridge.invokeNativeModule("Audio", "stopRecording", []);
3936
+ const result = asRecord(await NativeBridge.invokeNativeModule("Audio", "stopRecording", []));
2777
3937
  isRecording.value = false;
2778
- return { uri: result?.uri ?? "", duration: result?.duration ?? 0 };
3938
+ return {
3939
+ uri: getStringProp(result, "uri") ?? "",
3940
+ duration: getNumberProp(result, "duration") ?? 0
3941
+ };
2779
3942
  }
2780
3943
  async function pauseRecording() {
2781
3944
  await NativeBridge.invokeNativeModule("Audio", "pauseRecording", []);
@@ -2806,9 +3969,9 @@ function useAudio() {
2806
3969
  }
2807
3970
 
2808
3971
  // src/composables/useDatabase.ts
2809
- import { ref as ref20, onUnmounted as onUnmounted15 } from "@vue/runtime-core";
3972
+ import { ref as ref25, onUnmounted as onUnmounted16 } from "@vue/runtime-core";
2810
3973
  function useDatabase(name = "default") {
2811
- const isOpen = ref20(false);
3974
+ const isOpen = ref25(false);
2812
3975
  let opened = false;
2813
3976
  async function ensureOpen() {
2814
3977
  if (opened) return;
@@ -2850,7 +4013,7 @@ function useDatabase(name = "default") {
2850
4013
  opened = false;
2851
4014
  isOpen.value = false;
2852
4015
  }
2853
- onUnmounted15(() => {
4016
+ onUnmounted16(() => {
2854
4017
  if (opened) {
2855
4018
  NativeBridge.invokeNativeModule("Database", "close", [name]).catch(() => {
2856
4019
  });
@@ -2862,12 +4025,12 @@ function useDatabase(name = "default") {
2862
4025
  }
2863
4026
 
2864
4027
  // src/composables/usePerformance.ts
2865
- import { ref as ref21, onUnmounted as onUnmounted16 } from "@vue/runtime-core";
4028
+ import { ref as ref26, onUnmounted as onUnmounted17 } from "@vue/runtime-core";
2866
4029
  function usePerformance() {
2867
- const isProfiling = ref21(false);
2868
- const fps = ref21(0);
2869
- const memoryMB = ref21(0);
2870
- const bridgeOps = ref21(0);
4030
+ const isProfiling = ref26(false);
4031
+ const fps = ref26(0);
4032
+ const memoryMB = ref26(0);
4033
+ const bridgeOps = ref26(0);
2871
4034
  let unsubscribe = null;
2872
4035
  function handleMetrics(payload) {
2873
4036
  fps.value = payload.fps ?? 0;
@@ -2892,7 +4055,7 @@ function usePerformance() {
2892
4055
  async function getMetrics() {
2893
4056
  return NativeBridge.invokeNativeModule("Performance", "getMetrics", []);
2894
4057
  }
2895
- onUnmounted16(() => {
4058
+ onUnmounted17(() => {
2896
4059
  if (isProfiling.value) {
2897
4060
  NativeBridge.invokeNativeModule("Performance", "stopProfiling", []).catch(() => {
2898
4061
  });
@@ -2915,10 +4078,10 @@ function usePerformance() {
2915
4078
  }
2916
4079
 
2917
4080
  // src/composables/useSharedElementTransition.ts
2918
- import { ref as ref22, onUnmounted as onUnmounted17 } from "@vue/runtime-core";
4081
+ import { ref as ref27, onUnmounted as onUnmounted18 } from "@vue/runtime-core";
2919
4082
  var sharedElementRegistry = /* @__PURE__ */ new Map();
2920
4083
  function useSharedElementTransition(elementId) {
2921
- const viewId = ref22(null);
4084
+ const viewId = ref27(null);
2922
4085
  function register(nativeViewId) {
2923
4086
  viewId.value = nativeViewId;
2924
4087
  sharedElementRegistry.set(elementId, nativeViewId);
@@ -2927,7 +4090,7 @@ function useSharedElementTransition(elementId) {
2927
4090
  viewId.value = null;
2928
4091
  sharedElementRegistry.delete(elementId);
2929
4092
  }
2930
- onUnmounted17(() => {
4093
+ onUnmounted18(() => {
2931
4094
  unregister();
2932
4095
  });
2933
4096
  return {
@@ -2951,11 +4114,19 @@ function clearSharedElementRegistry() {
2951
4114
  }
2952
4115
 
2953
4116
  // src/composables/useIAP.ts
2954
- import { ref as ref23, onUnmounted as onUnmounted18 } from "@vue/runtime-core";
4117
+ import { ref as ref28, onUnmounted as onUnmounted19 } from "@vue/runtime-core";
4118
+ function getErrorMessage(error) {
4119
+ if (error instanceof Error) return error.message;
4120
+ if (typeof error === "object" && error !== null && "message" in error) {
4121
+ const message = error.message;
4122
+ if (typeof message === "string") return message;
4123
+ }
4124
+ return String(error);
4125
+ }
2955
4126
  function useIAP() {
2956
- const products = ref23([]);
2957
- const isReady = ref23(false);
2958
- const error = ref23(null);
4127
+ const products = ref28([]);
4128
+ const isReady = ref28(false);
4129
+ const error = ref28(null);
2959
4130
  const cleanups = [];
2960
4131
  const unsubscribe = NativeBridge.onGlobalEvent("iap:transactionUpdate", (payload) => {
2961
4132
  if (payload.state === "failed" && payload.error) {
@@ -2966,7 +4137,7 @@ function useIAP() {
2966
4137
  NativeBridge.invokeNativeModule("IAP", "initialize").then(() => {
2967
4138
  isReady.value = true;
2968
4139
  }).catch((err) => {
2969
- error.value = String(err);
4140
+ error.value = getErrorMessage(err);
2970
4141
  });
2971
4142
  async function getProducts(skus) {
2972
4143
  error.value = null;
@@ -2976,7 +4147,7 @@ function useIAP() {
2976
4147
  products.value = productList;
2977
4148
  return productList;
2978
4149
  } catch (err) {
2979
- error.value = String(err);
4150
+ error.value = getErrorMessage(err);
2980
4151
  return [];
2981
4152
  }
2982
4153
  }
@@ -2985,7 +4156,7 @@ function useIAP() {
2985
4156
  try {
2986
4157
  return await NativeBridge.invokeNativeModule("IAP", "purchase", [sku]);
2987
4158
  } catch (err) {
2988
- error.value = String(err);
4159
+ error.value = getErrorMessage(err);
2989
4160
  return null;
2990
4161
  }
2991
4162
  }
@@ -2994,7 +4165,7 @@ function useIAP() {
2994
4165
  try {
2995
4166
  return await NativeBridge.invokeNativeModule("IAP", "restorePurchases");
2996
4167
  } catch (err) {
2997
- error.value = String(err);
4168
+ error.value = getErrorMessage(err);
2998
4169
  return [];
2999
4170
  }
3000
4171
  }
@@ -3003,7 +4174,7 @@ function useIAP() {
3003
4174
  try {
3004
4175
  return await NativeBridge.invokeNativeModule("IAP", "getActiveSubscriptions");
3005
4176
  } catch (err) {
3006
- error.value = String(err);
4177
+ error.value = getErrorMessage(err);
3007
4178
  return [];
3008
4179
  }
3009
4180
  }
@@ -3012,7 +4183,7 @@ function useIAP() {
3012
4183
  cleanups.push(unsub);
3013
4184
  return unsub;
3014
4185
  }
3015
- onUnmounted18(() => {
4186
+ onUnmounted19(() => {
3016
4187
  cleanups.forEach((fn) => fn());
3017
4188
  cleanups.length = 0;
3018
4189
  });
@@ -3029,11 +4200,32 @@ function useIAP() {
3029
4200
  }
3030
4201
 
3031
4202
  // src/composables/useAppleSignIn.ts
3032
- import { ref as ref24, onUnmounted as onUnmounted19 } from "@vue/runtime-core";
4203
+ import { ref as ref29, onUnmounted as onUnmounted20 } from "@vue/runtime-core";
4204
+ function getErrorMessage2(error) {
4205
+ if (error instanceof Error) return error.message;
4206
+ if (typeof error === "object" && error !== null && "message" in error) {
4207
+ const message = error.message;
4208
+ if (typeof message === "string") return message;
4209
+ }
4210
+ return String(error);
4211
+ }
4212
+ function normalizeSocialUser(value, provider) {
4213
+ if (typeof value !== "object" || value === null) return null;
4214
+ const payload = value;
4215
+ if (typeof payload.userId !== "string") return null;
4216
+ return {
4217
+ userId: payload.userId,
4218
+ email: typeof payload.email === "string" ? payload.email : void 0,
4219
+ fullName: typeof payload.fullName === "string" ? payload.fullName : void 0,
4220
+ identityToken: typeof payload.identityToken === "string" ? payload.identityToken : void 0,
4221
+ authorizationCode: typeof payload.authorizationCode === "string" ? payload.authorizationCode : void 0,
4222
+ provider
4223
+ };
4224
+ }
3033
4225
  function useAppleSignIn() {
3034
- const user = ref24(null);
3035
- const isAuthenticated = ref24(false);
3036
- const error = ref24(null);
4226
+ const user = ref29(null);
4227
+ const isAuthenticated = ref29(false);
4228
+ const error = ref29(null);
3037
4229
  const cleanups = [];
3038
4230
  const unsubscribe = NativeBridge.onGlobalEvent("auth:appleCredentialRevoked", () => {
3039
4231
  user.value = null;
@@ -3041,8 +4233,9 @@ function useAppleSignIn() {
3041
4233
  });
3042
4234
  cleanups.push(unsubscribe);
3043
4235
  NativeBridge.invokeNativeModule("SocialAuth", "getCurrentUser", ["apple"]).then((result) => {
3044
- if (result && result.userId) {
3045
- user.value = { ...result, provider: "apple" };
4236
+ const currentUser = normalizeSocialUser(result, "apple");
4237
+ if (currentUser) {
4238
+ user.value = currentUser;
3046
4239
  isAuthenticated.value = true;
3047
4240
  }
3048
4241
  }).catch(() => {
@@ -3051,12 +4244,15 @@ function useAppleSignIn() {
3051
4244
  error.value = null;
3052
4245
  try {
3053
4246
  const result = await NativeBridge.invokeNativeModule("SocialAuth", "signInWithApple");
3054
- const socialUser = { ...result, provider: "apple" };
4247
+ const socialUser = normalizeSocialUser(result, "apple");
4248
+ if (!socialUser) {
4249
+ throw new Error("Invalid Apple Sign In response.");
4250
+ }
3055
4251
  user.value = socialUser;
3056
4252
  isAuthenticated.value = true;
3057
4253
  return { success: true, user: socialUser };
3058
4254
  } catch (err) {
3059
- const message = String(err);
4255
+ const message = getErrorMessage2(err);
3060
4256
  error.value = message;
3061
4257
  return { success: false, error: message };
3062
4258
  }
@@ -3068,10 +4264,10 @@ function useAppleSignIn() {
3068
4264
  user.value = null;
3069
4265
  isAuthenticated.value = false;
3070
4266
  } catch (err) {
3071
- error.value = String(err);
4267
+ error.value = getErrorMessage2(err);
3072
4268
  }
3073
4269
  }
3074
- onUnmounted19(() => {
4270
+ onUnmounted20(() => {
3075
4271
  cleanups.forEach((fn) => fn());
3076
4272
  cleanups.length = 0;
3077
4273
  });
@@ -3079,15 +4275,37 @@ function useAppleSignIn() {
3079
4275
  }
3080
4276
 
3081
4277
  // src/composables/useGoogleSignIn.ts
3082
- import { ref as ref25, onUnmounted as onUnmounted20 } from "@vue/runtime-core";
4278
+ import { ref as ref30, onUnmounted as onUnmounted21 } from "@vue/runtime-core";
4279
+ function getErrorMessage3(error) {
4280
+ if (error instanceof Error) return error.message;
4281
+ if (typeof error === "object" && error !== null && "message" in error) {
4282
+ const message = error.message;
4283
+ if (typeof message === "string") return message;
4284
+ }
4285
+ return String(error);
4286
+ }
4287
+ function normalizeSocialUser2(value) {
4288
+ if (typeof value !== "object" || value === null) return null;
4289
+ const payload = value;
4290
+ if (typeof payload.userId !== "string") return null;
4291
+ return {
4292
+ userId: payload.userId,
4293
+ email: typeof payload.email === "string" ? payload.email : void 0,
4294
+ fullName: typeof payload.fullName === "string" ? payload.fullName : void 0,
4295
+ identityToken: typeof payload.identityToken === "string" ? payload.identityToken : void 0,
4296
+ authorizationCode: typeof payload.authorizationCode === "string" ? payload.authorizationCode : void 0,
4297
+ provider: "google"
4298
+ };
4299
+ }
3083
4300
  function useGoogleSignIn(clientId) {
3084
- const user = ref25(null);
3085
- const isAuthenticated = ref25(false);
3086
- const error = ref25(null);
4301
+ const user = ref30(null);
4302
+ const isAuthenticated = ref30(false);
4303
+ const error = ref30(null);
3087
4304
  const cleanups = [];
3088
4305
  NativeBridge.invokeNativeModule("SocialAuth", "getCurrentUser", ["google"]).then((result) => {
3089
- if (result && result.userId) {
3090
- user.value = { ...result, provider: "google" };
4306
+ const currentUser = normalizeSocialUser2(result);
4307
+ if (currentUser) {
4308
+ user.value = currentUser;
3091
4309
  isAuthenticated.value = true;
3092
4310
  }
3093
4311
  }).catch(() => {
@@ -3096,12 +4314,15 @@ function useGoogleSignIn(clientId) {
3096
4314
  error.value = null;
3097
4315
  try {
3098
4316
  const result = await NativeBridge.invokeNativeModule("SocialAuth", "signInWithGoogle", [clientId]);
3099
- const socialUser = { ...result, provider: "google" };
4317
+ const socialUser = normalizeSocialUser2(result);
4318
+ if (!socialUser) {
4319
+ throw new Error("Invalid Google Sign In response.");
4320
+ }
3100
4321
  user.value = socialUser;
3101
4322
  isAuthenticated.value = true;
3102
4323
  return { success: true, user: socialUser };
3103
4324
  } catch (err) {
3104
- const message = String(err);
4325
+ const message = getErrorMessage3(err);
3105
4326
  error.value = message;
3106
4327
  return { success: false, error: message };
3107
4328
  }
@@ -3113,10 +4334,10 @@ function useGoogleSignIn(clientId) {
3113
4334
  user.value = null;
3114
4335
  isAuthenticated.value = false;
3115
4336
  } catch (err) {
3116
- error.value = String(err);
4337
+ error.value = getErrorMessage3(err);
3117
4338
  }
3118
4339
  }
3119
- onUnmounted20(() => {
4340
+ onUnmounted21(() => {
3120
4341
  cleanups.forEach((fn) => fn());
3121
4342
  cleanups.length = 0;
3122
4343
  });
@@ -3124,17 +4345,17 @@ function useGoogleSignIn(clientId) {
3124
4345
  }
3125
4346
 
3126
4347
  // src/composables/useBackgroundTask.ts
3127
- import { ref as ref26, onUnmounted as onUnmounted21 } from "@vue/runtime-core";
4348
+ import { ref as ref31, onUnmounted as onUnmounted22 } from "@vue/runtime-core";
3128
4349
  function useBackgroundTask() {
3129
4350
  const taskHandlers = /* @__PURE__ */ new Map();
3130
- const defaultHandler = ref26(null);
4351
+ const defaultHandler = ref31(null);
3131
4352
  const unsubscribe = NativeBridge.onGlobalEvent("background:taskExecute", (payload) => {
3132
4353
  const handler = taskHandlers.get(payload.taskId) || defaultHandler.value;
3133
4354
  if (handler) {
3134
4355
  handler(payload.taskId);
3135
4356
  }
3136
4357
  });
3137
- onUnmounted21(unsubscribe);
4358
+ onUnmounted22(unsubscribe);
3138
4359
  function registerTask(taskId) {
3139
4360
  return NativeBridge.invokeNativeModule("BackgroundTask", "registerTask", [taskId]).then(() => void 0);
3140
4361
  }
@@ -3173,20 +4394,28 @@ function useBackgroundTask() {
3173
4394
  }
3174
4395
 
3175
4396
  // src/composables/useOTAUpdate.ts
3176
- import { ref as ref27, onUnmounted as onUnmounted22 } from "@vue/runtime-core";
4397
+ import { ref as ref32, onUnmounted as onUnmounted23 } from "@vue/runtime-core";
4398
+ function getErrorMessage4(error) {
4399
+ if (error instanceof Error) return error.message;
4400
+ if (typeof error === "object" && error !== null && "message" in error) {
4401
+ const message = error.message;
4402
+ if (typeof message === "string") return message;
4403
+ }
4404
+ return String(error);
4405
+ }
3177
4406
  function useOTAUpdate(serverUrl) {
3178
- const currentVersion = ref27("embedded");
3179
- const availableVersion = ref27(null);
3180
- const downloadProgress = ref27(0);
3181
- const isChecking = ref27(false);
3182
- const isDownloading = ref27(false);
3183
- const status = ref27("idle");
3184
- const error = ref27(null);
4407
+ const currentVersion = ref32("embedded");
4408
+ const availableVersion = ref32(null);
4409
+ const downloadProgress = ref32(0);
4410
+ const isChecking = ref32(false);
4411
+ const isDownloading = ref32(false);
4412
+ const status = ref32("idle");
4413
+ const error = ref32(null);
3185
4414
  let lastUpdateInfo = null;
3186
4415
  const unsubscribe = NativeBridge.onGlobalEvent("ota:downloadProgress", (payload) => {
3187
4416
  downloadProgress.value = payload.progress;
3188
4417
  });
3189
- onUnmounted22(unsubscribe);
4418
+ onUnmounted23(unsubscribe);
3190
4419
  NativeBridge.invokeNativeModule("OTA", "getCurrentVersion", []).then((info) => {
3191
4420
  currentVersion.value = info.version;
3192
4421
  }).catch(() => {
@@ -3206,7 +4435,7 @@ function useOTAUpdate(serverUrl) {
3206
4435
  status.value = info.updateAvailable ? "idle" : "idle";
3207
4436
  return info;
3208
4437
  } catch (err) {
3209
- error.value = err?.message || String(err);
4438
+ error.value = getErrorMessage4(err);
3210
4439
  status.value = "error";
3211
4440
  throw err;
3212
4441
  } finally {
@@ -3232,7 +4461,7 @@ function useOTAUpdate(serverUrl) {
3232
4461
  } catch (err) {
3233
4462
  await NativeBridge.invokeNativeModule("OTA", "cleanupPartialDownload", []).catch(() => {
3234
4463
  });
3235
- error.value = err?.message || String(err);
4464
+ error.value = getErrorMessage4(err);
3236
4465
  status.value = "error";
3237
4466
  throw err;
3238
4467
  } finally {
@@ -3248,7 +4477,7 @@ function useOTAUpdate(serverUrl) {
3248
4477
  await NativeBridge.invokeNativeModule("OTA", "verifyBundle", []);
3249
4478
  } catch (err) {
3250
4479
  status.value = "error";
3251
- error.value = "Bundle verification failed: " + (err?.message || String(err));
4480
+ error.value = "Bundle verification failed: " + getErrorMessage4(err);
3252
4481
  throw err;
3253
4482
  }
3254
4483
  try {
@@ -3258,7 +4487,7 @@ function useOTAUpdate(serverUrl) {
3258
4487
  availableVersion.value = null;
3259
4488
  status.value = "idle";
3260
4489
  } catch (err) {
3261
- error.value = err?.message || String(err);
4490
+ error.value = getErrorMessage4(err);
3262
4491
  status.value = "error";
3263
4492
  throw err;
3264
4493
  }
@@ -3271,7 +4500,7 @@ function useOTAUpdate(serverUrl) {
3271
4500
  currentVersion.value = info.version;
3272
4501
  status.value = "idle";
3273
4502
  } catch (err) {
3274
- error.value = err?.message || String(err);
4503
+ error.value = getErrorMessage4(err);
3275
4504
  status.value = "error";
3276
4505
  throw err;
3277
4506
  }
@@ -3298,13 +4527,21 @@ function useOTAUpdate(serverUrl) {
3298
4527
  }
3299
4528
 
3300
4529
  // src/composables/useBluetooth.ts
3301
- import { ref as ref28, onUnmounted as onUnmounted23 } from "@vue/runtime-core";
4530
+ import { ref as ref33, onUnmounted as onUnmounted24 } from "@vue/runtime-core";
4531
+ function getErrorMessage5(error) {
4532
+ if (error instanceof Error) return error.message;
4533
+ if (typeof error === "object" && error !== null && "message" in error) {
4534
+ const message = error.message;
4535
+ if (typeof message === "string") return message;
4536
+ }
4537
+ return String(error);
4538
+ }
3302
4539
  function useBluetooth() {
3303
- const devices = ref28([]);
3304
- const connectedDevice = ref28(null);
3305
- const isScanning = ref28(false);
3306
- const isAvailable = ref28(false);
3307
- const error = ref28(null);
4540
+ const devices = ref33([]);
4541
+ const connectedDevice = ref33(null);
4542
+ const isScanning = ref33(false);
4543
+ const isAvailable = ref33(false);
4544
+ const error = ref33(null);
3308
4545
  const cleanups = [];
3309
4546
  NativeBridge.invokeNativeModule("Bluetooth", "getState").then((state) => {
3310
4547
  isAvailable.value = state === "poweredOn";
@@ -3343,7 +4580,7 @@ function useBluetooth() {
3343
4580
  try {
3344
4581
  await NativeBridge.invokeNativeModule("Bluetooth", "startScan", [serviceUUIDs]);
3345
4582
  } catch (e) {
3346
- error.value = e?.message || String(e);
4583
+ error.value = getErrorMessage5(e);
3347
4584
  isScanning.value = false;
3348
4585
  }
3349
4586
  }
@@ -3383,7 +4620,7 @@ function useBluetooth() {
3383
4620
  ]);
3384
4621
  };
3385
4622
  }
3386
- onUnmounted23(() => {
4623
+ onUnmounted24(() => {
3387
4624
  if (isScanning.value) {
3388
4625
  NativeBridge.invokeNativeModule("Bluetooth", "stopScan").catch(() => {
3389
4626
  });
@@ -3408,17 +4645,25 @@ function useBluetooth() {
3408
4645
  }
3409
4646
 
3410
4647
  // src/composables/useCalendar.ts
3411
- import { ref as ref29 } from "@vue/runtime-core";
4648
+ import { ref as ref34 } from "@vue/runtime-core";
4649
+ function getErrorMessage6(error) {
4650
+ if (error instanceof Error) return error.message;
4651
+ if (typeof error === "object" && error !== null && "message" in error) {
4652
+ const message = error.message;
4653
+ if (typeof message === "string") return message;
4654
+ }
4655
+ return String(error);
4656
+ }
3412
4657
  function useCalendar() {
3413
- const hasAccess = ref29(false);
3414
- const error = ref29(null);
4658
+ const hasAccess = ref34(false);
4659
+ const error = ref34(null);
3415
4660
  async function requestAccess() {
3416
4661
  try {
3417
4662
  const result = await NativeBridge.invokeNativeModule("Calendar", "requestAccess");
3418
4663
  hasAccess.value = result.granted;
3419
4664
  return result.granted;
3420
4665
  } catch (e) {
3421
- error.value = e?.message || String(e);
4666
+ error.value = getErrorMessage6(e);
3422
4667
  return false;
3423
4668
  }
3424
4669
  }
@@ -3444,17 +4689,25 @@ function useCalendar() {
3444
4689
  }
3445
4690
 
3446
4691
  // src/composables/useContacts.ts
3447
- import { ref as ref30 } from "@vue/runtime-core";
4692
+ import { ref as ref35 } from "@vue/runtime-core";
4693
+ function getErrorMessage7(error) {
4694
+ if (error instanceof Error) return error.message;
4695
+ if (typeof error === "object" && error !== null && "message" in error) {
4696
+ const message = error.message;
4697
+ if (typeof message === "string") return message;
4698
+ }
4699
+ return String(error);
4700
+ }
3448
4701
  function useContacts() {
3449
- const hasAccess = ref30(false);
3450
- const error = ref30(null);
4702
+ const hasAccess = ref35(false);
4703
+ const error = ref35(null);
3451
4704
  async function requestAccess() {
3452
4705
  try {
3453
4706
  const result = await NativeBridge.invokeNativeModule("Contacts", "requestAccess");
3454
4707
  hasAccess.value = result.granted;
3455
4708
  return result.granted;
3456
4709
  } catch (e) {
3457
- error.value = e?.message || String(e);
4710
+ error.value = getErrorMessage7(e);
3458
4711
  return false;
3459
4712
  }
3460
4713
  }
@@ -3473,39 +4726,500 @@ function useContacts() {
3473
4726
  return { requestAccess, getContacts, getContact, createContact, deleteContact, hasAccess, error };
3474
4727
  }
3475
4728
 
4729
+ // src/composables/useWindow.ts
4730
+ function useWindow() {
4731
+ const { isMacOS } = usePlatform();
4732
+ async function setTitle(title) {
4733
+ if (!isMacOS) return;
4734
+ await NativeBridge.invokeNativeModule("Window", "setTitle", [title]);
4735
+ }
4736
+ async function setSize(width, height) {
4737
+ if (!isMacOS) return;
4738
+ await NativeBridge.invokeNativeModule("Window", "setSize", [width, height]);
4739
+ }
4740
+ async function center() {
4741
+ if (!isMacOS) return;
4742
+ await NativeBridge.invokeNativeModule("Window", "center", []);
4743
+ }
4744
+ async function minimize() {
4745
+ if (!isMacOS) return;
4746
+ await NativeBridge.invokeNativeModule("Window", "minimize", []);
4747
+ }
4748
+ async function toggleFullScreen() {
4749
+ if (!isMacOS) return;
4750
+ await NativeBridge.invokeNativeModule("Window", "toggleFullScreen", []);
4751
+ }
4752
+ async function close() {
4753
+ if (!isMacOS) return;
4754
+ await NativeBridge.invokeNativeModule("Window", "close", []);
4755
+ }
4756
+ async function getInfo() {
4757
+ if (!isMacOS) return null;
4758
+ return await NativeBridge.invokeNativeModule("Window", "getInfo", []);
4759
+ }
4760
+ return { setTitle, setSize, center, minimize, toggleFullScreen, close, getInfo };
4761
+ }
4762
+
4763
+ // src/composables/useMenu.ts
4764
+ function useMenu() {
4765
+ const { isMacOS } = usePlatform();
4766
+ async function setAppMenu(sections) {
4767
+ if (!isMacOS) return;
4768
+ await NativeBridge.invokeNativeModule("Menu", "setAppMenu", [sections]);
4769
+ }
4770
+ async function showContextMenu(items) {
4771
+ if (!isMacOS) return;
4772
+ await NativeBridge.invokeNativeModule("Menu", "showContextMenu", [items]);
4773
+ }
4774
+ function onMenuItemClick(callback) {
4775
+ if (!isMacOS) return () => {
4776
+ };
4777
+ return NativeBridge.onGlobalEvent("menu:itemClick", (payload) => {
4778
+ callback(payload.id ?? "", payload.title ?? "");
4779
+ });
4780
+ }
4781
+ return { setAppMenu, showContextMenu, onMenuItemClick };
4782
+ }
4783
+
4784
+ // src/composables/useFileDialog.ts
4785
+ function useFileDialog() {
4786
+ const { isMacOS } = usePlatform();
4787
+ async function openFile(options) {
4788
+ if (!isMacOS) return null;
4789
+ return await NativeBridge.invokeNativeModule("FileDialog", "openFile", [options || {}]);
4790
+ }
4791
+ async function openDirectory(options) {
4792
+ if (!isMacOS) return null;
4793
+ return await NativeBridge.invokeNativeModule("FileDialog", "openDirectory", [options || {}]);
4794
+ }
4795
+ async function saveFile(options) {
4796
+ if (!isMacOS) return null;
4797
+ return await NativeBridge.invokeNativeModule("FileDialog", "saveFile", [options || {}]);
4798
+ }
4799
+ return { openFile, openDirectory, saveFile };
4800
+ }
4801
+
4802
+ // src/composables/useDragDrop.ts
4803
+ import { ref as ref36, readonly } from "@vue/runtime-core";
4804
+ function useDragDrop() {
4805
+ const { isMacOS } = usePlatform();
4806
+ const isDragging = ref36(false);
4807
+ async function enableDropZone() {
4808
+ if (!isMacOS) return;
4809
+ await NativeBridge.invokeNativeModule("DragDrop", "enableDropZone", []);
4810
+ }
4811
+ function onDrop(callback) {
4812
+ if (!isMacOS) return () => {
4813
+ };
4814
+ return NativeBridge.onGlobalEvent("dragdrop:drop", (payload) => {
4815
+ const files = Array.isArray(payload.files) ? payload.files.filter((file) => typeof file === "string") : [];
4816
+ callback(files);
4817
+ });
4818
+ }
4819
+ function onDragEnter(callback) {
4820
+ if (!isMacOS) return () => {
4821
+ };
4822
+ return NativeBridge.onGlobalEvent("dragdrop:enter", () => {
4823
+ isDragging.value = true;
4824
+ callback();
4825
+ });
4826
+ }
4827
+ function onDragLeave(callback) {
4828
+ if (!isMacOS) return () => {
4829
+ };
4830
+ return NativeBridge.onGlobalEvent("dragdrop:leave", () => {
4831
+ isDragging.value = false;
4832
+ callback();
4833
+ });
4834
+ }
4835
+ return { enableDropZone, onDrop, onDragEnter, onDragLeave, isDragging: readonly(isDragging) };
4836
+ }
4837
+
4838
+ // src/composables/useTeleport.ts
4839
+ function useTeleport(target) {
4840
+ const teleport = (node) => {
4841
+ NativeBridge.teleportTo(target, node.id);
4842
+ };
4843
+ return { teleport };
4844
+ }
4845
+
4846
+ // src/composables/useGesture.ts
4847
+ import { ref as ref37, onUnmounted as onUnmounted25 } from "@vue/runtime-core";
4848
+ function hasViewId2(value) {
4849
+ return typeof value === "object" && value !== null && "id" in value && typeof value.id === "number";
4850
+ }
4851
+ function isGestureRef(target) {
4852
+ return typeof target === "object" && target !== null && "value" in target;
4853
+ }
4854
+ function resolveViewId2(target) {
4855
+ if (typeof target === "number") return target;
4856
+ if (isGestureRef(target)) {
4857
+ const val = target.value;
4858
+ if (hasViewId2(val)) return val.id;
4859
+ throw new Error("[useGesture] Target ref has no .value.id \u2014 is the ref attached to a component?");
4860
+ }
4861
+ if (hasViewId2(target)) return target.id;
4862
+ throw new Error("[useGesture] Invalid target. Pass a number, template ref, or NativeNode.");
4863
+ }
4864
+ var GestureManager = class {
4865
+ constructor() {
4866
+ this.nodeId = null;
4867
+ this.handlers = /* @__PURE__ */ new Map();
4868
+ this.disposables = [];
4869
+ }
4870
+ attach(target) {
4871
+ this.nodeId = resolveViewId2(target);
4872
+ }
4873
+ on(event, callback, config) {
4874
+ if (!this.nodeId) {
4875
+ console.warn("[useGesture] Cannot add listener: not attached to a view. Call attach() first or use a ref.");
4876
+ return () => {
4877
+ };
4878
+ }
4879
+ if (!this.handlers.has(event)) {
4880
+ this.handlers.set(event, /* @__PURE__ */ new Set());
4881
+ }
4882
+ this.handlers.get(event).add(callback);
4883
+ NativeBridge.addEventListener(this.nodeId, event, (payload) => {
4884
+ if (config?.enabled === false) return;
4885
+ callback(payload);
4886
+ });
4887
+ const dispose = () => {
4888
+ this.handlers.get(event)?.delete(callback);
4889
+ if (this.nodeId) {
4890
+ NativeBridge.removeEventListener(this.nodeId, event);
4891
+ }
4892
+ };
4893
+ this.disposables.push(dispose);
4894
+ return dispose;
4895
+ }
4896
+ detach() {
4897
+ for (const dispose of this.disposables) {
4898
+ dispose();
4899
+ }
4900
+ this.disposables = [];
4901
+ this.handlers.clear();
4902
+ this.nodeId = null;
4903
+ }
4904
+ };
4905
+ function useGesture(target, options = {}) {
4906
+ const pan = ref37(null);
4907
+ const pinch = ref37(null);
4908
+ const rotate = ref37(null);
4909
+ const swipeLeft = ref37(null);
4910
+ const swipeRight = ref37(null);
4911
+ const swipeUp = ref37(null);
4912
+ const swipeDown = ref37(null);
4913
+ const press = ref37(null);
4914
+ const longPress = ref37(null);
4915
+ const doubleTap = ref37(null);
4916
+ const forceTouch = ref37(null);
4917
+ const hover = ref37(null);
4918
+ const gestureState = ref37(null);
4919
+ const activeGesture = ref37(null);
4920
+ const isGesturing = ref37(false);
4921
+ const manager = new GestureManager();
4922
+ const cleanupFns = [];
4923
+ function attach(t) {
4924
+ manager.attach(t);
4925
+ setupListeners();
4926
+ }
4927
+ function detach() {
4928
+ for (const fn of cleanupFns) fn();
4929
+ cleanupFns.length = 0;
4930
+ manager.detach();
4931
+ pan.value = null;
4932
+ pinch.value = null;
4933
+ rotate.value = null;
4934
+ swipeLeft.value = null;
4935
+ swipeRight.value = null;
4936
+ swipeUp.value = null;
4937
+ swipeDown.value = null;
4938
+ press.value = null;
4939
+ longPress.value = null;
4940
+ doubleTap.value = null;
4941
+ forceTouch.value = null;
4942
+ hover.value = null;
4943
+ gestureState.value = null;
4944
+ activeGesture.value = null;
4945
+ isGesturing.value = false;
4946
+ }
4947
+ function normalizeConfig(opt) {
4948
+ return typeof opt === "boolean" ? { enabled: opt } : opt ?? {};
4949
+ }
4950
+ function on(event, callback) {
4951
+ return manager.on(event, callback);
4952
+ }
4953
+ function setupListeners() {
4954
+ if (options.pan) {
4955
+ const cfg = normalizeConfig(options.pan);
4956
+ const dispose = manager.on("pan", (state) => {
4957
+ pan.value = state;
4958
+ gestureState.value = state;
4959
+ activeGesture.value = "pan";
4960
+ isGesturing.value = state.state === "began" || state.state === "changed";
4961
+ }, cfg);
4962
+ cleanupFns.push(dispose);
4963
+ }
4964
+ if (options.pinch) {
4965
+ const cfg = normalizeConfig(options.pinch);
4966
+ const dispose = manager.on("pinch", (state) => {
4967
+ pinch.value = state;
4968
+ gestureState.value = state;
4969
+ activeGesture.value = "pinch";
4970
+ isGesturing.value = state.state === "began" || state.state === "changed";
4971
+ }, cfg);
4972
+ cleanupFns.push(dispose);
4973
+ }
4974
+ if (options.rotate) {
4975
+ const cfg = normalizeConfig(options.rotate);
4976
+ const dispose = manager.on("rotate", (state) => {
4977
+ rotate.value = state;
4978
+ gestureState.value = state;
4979
+ activeGesture.value = "rotate";
4980
+ isGesturing.value = state.state === "began" || state.state === "changed";
4981
+ }, cfg);
4982
+ cleanupFns.push(dispose);
4983
+ }
4984
+ if (options.swipeLeft) {
4985
+ const cfg = normalizeConfig(options.swipeLeft);
4986
+ const dispose = manager.on("swipeLeft", (state) => {
4987
+ swipeLeft.value = state;
4988
+ gestureState.value = state;
4989
+ activeGesture.value = "swipeLeft";
4990
+ isGesturing.value = false;
4991
+ }, cfg);
4992
+ cleanupFns.push(dispose);
4993
+ }
4994
+ if (options.swipeRight) {
4995
+ const cfg = normalizeConfig(options.swipeRight);
4996
+ const dispose = manager.on("swipeRight", (state) => {
4997
+ swipeRight.value = state;
4998
+ gestureState.value = state;
4999
+ activeGesture.value = "swipeRight";
5000
+ isGesturing.value = false;
5001
+ }, cfg);
5002
+ cleanupFns.push(dispose);
5003
+ }
5004
+ if (options.swipeUp) {
5005
+ const cfg = normalizeConfig(options.swipeUp);
5006
+ const dispose = manager.on("swipeUp", (state) => {
5007
+ swipeUp.value = state;
5008
+ gestureState.value = state;
5009
+ activeGesture.value = "swipeUp";
5010
+ isGesturing.value = false;
5011
+ }, cfg);
5012
+ cleanupFns.push(dispose);
5013
+ }
5014
+ if (options.swipeDown) {
5015
+ const cfg = normalizeConfig(options.swipeDown);
5016
+ const dispose = manager.on("swipeDown", (state) => {
5017
+ swipeDown.value = state;
5018
+ gestureState.value = state;
5019
+ activeGesture.value = "swipeDown";
5020
+ isGesturing.value = false;
5021
+ }, cfg);
5022
+ cleanupFns.push(dispose);
5023
+ }
5024
+ if (options.press) {
5025
+ const cfg = normalizeConfig(options.press);
5026
+ const dispose = manager.on("press", (state) => {
5027
+ press.value = state;
5028
+ gestureState.value = state;
5029
+ activeGesture.value = "press";
5030
+ isGesturing.value = false;
5031
+ }, cfg);
5032
+ cleanupFns.push(dispose);
5033
+ }
5034
+ if (options.longPress) {
5035
+ const cfg = normalizeConfig(options.longPress);
5036
+ const dispose = manager.on("longPress", (state) => {
5037
+ longPress.value = state;
5038
+ gestureState.value = state;
5039
+ activeGesture.value = "longPress";
5040
+ isGesturing.value = false;
5041
+ }, cfg);
5042
+ cleanupFns.push(dispose);
5043
+ }
5044
+ if (options.doubleTap) {
5045
+ const cfg = normalizeConfig(options.doubleTap);
5046
+ const dispose = manager.on("doubleTap", (state) => {
5047
+ doubleTap.value = state;
5048
+ gestureState.value = state;
5049
+ activeGesture.value = "doubleTap";
5050
+ isGesturing.value = false;
5051
+ }, cfg);
5052
+ cleanupFns.push(dispose);
5053
+ }
5054
+ if (options.forceTouch) {
5055
+ const cfg = normalizeConfig(options.forceTouch);
5056
+ const dispose = manager.on("forceTouch", (state) => {
5057
+ forceTouch.value = state;
5058
+ gestureState.value = state;
5059
+ activeGesture.value = "forceTouch";
5060
+ isGesturing.value = state.stage > 0;
5061
+ }, cfg);
5062
+ cleanupFns.push(dispose);
5063
+ }
5064
+ if (options.hover) {
5065
+ const cfg = normalizeConfig(options.hover);
5066
+ const dispose = manager.on("hover", (state) => {
5067
+ hover.value = state;
5068
+ gestureState.value = state;
5069
+ activeGesture.value = "hover";
5070
+ isGesturing.value = state.state !== "exited";
5071
+ }, cfg);
5072
+ cleanupFns.push(dispose);
5073
+ }
5074
+ }
5075
+ onUnmounted25(() => {
5076
+ detach();
5077
+ });
5078
+ if (target !== void 0) {
5079
+ attach(target);
5080
+ }
5081
+ return {
5082
+ pan,
5083
+ pinch,
5084
+ rotate,
5085
+ swipeLeft,
5086
+ swipeRight,
5087
+ swipeUp,
5088
+ swipeDown,
5089
+ press,
5090
+ longPress,
5091
+ doubleTap,
5092
+ forceTouch,
5093
+ hover,
5094
+ gestureState,
5095
+ activeGesture,
5096
+ isGesturing,
5097
+ attach,
5098
+ detach,
5099
+ on
5100
+ };
5101
+ }
5102
+ function useComposedGestures(target, options = {}) {
5103
+ const pan = ref37(null);
5104
+ const pinch = ref37(null);
5105
+ const rotate = ref37(null);
5106
+ const gestureState = ref37(null);
5107
+ const activeGesture = ref37(null);
5108
+ const isGesturing = ref37(false);
5109
+ const isPinchingAndRotating = ref37(false);
5110
+ const isPanningAndPinching = ref37(false);
5111
+ const manager = new GestureManager();
5112
+ manager.attach(target);
5113
+ const panConfig = typeof options.pan === "object" ? options.pan : {};
5114
+ const pinchConfig = typeof options.pinch === "object" ? options.pinch : {};
5115
+ const rotateConfig = typeof options.rotate === "object" ? options.rotate : {};
5116
+ const cleanupFns = [];
5117
+ if (options.pan !== false) {
5118
+ cleanupFns.push(manager.on("pan", (state) => {
5119
+ pan.value = state;
5120
+ gestureState.value = state;
5121
+ activeGesture.value = "pan";
5122
+ isGesturing.value = state.state === "began" || state.state === "changed";
5123
+ isPanningAndPinching.value = pinch.value !== null && (state.state === "began" || state.state === "changed");
5124
+ }, panConfig));
5125
+ }
5126
+ if (options.pinch !== false) {
5127
+ cleanupFns.push(manager.on("pinch", (state) => {
5128
+ pinch.value = state;
5129
+ gestureState.value = state;
5130
+ activeGesture.value = "pinch";
5131
+ isGesturing.value = state.state === "began" || state.state === "changed";
5132
+ isPinchingAndRotating.value = rotate.value !== null && (state.state === "began" || state.state === "changed");
5133
+ isPanningAndPinching.value = pan.value !== null && (state.state === "began" || state.state === "changed");
5134
+ }, pinchConfig));
5135
+ }
5136
+ if (options.rotate !== false) {
5137
+ cleanupFns.push(manager.on("rotate", (state) => {
5138
+ rotate.value = state;
5139
+ gestureState.value = state;
5140
+ activeGesture.value = "rotate";
5141
+ isGesturing.value = state.state === "began" || state.state === "changed";
5142
+ isPinchingAndRotating.value = pinch.value !== null && (state.state === "began" || state.state === "changed");
5143
+ }, rotateConfig));
5144
+ }
5145
+ onUnmounted25(() => {
5146
+ for (const fn of cleanupFns) fn();
5147
+ manager.detach();
5148
+ });
5149
+ return {
5150
+ pan,
5151
+ pinch,
5152
+ rotate,
5153
+ gestureState,
5154
+ activeGesture,
5155
+ isGesturing,
5156
+ isPinchingAndRotating,
5157
+ isPanningAndPinching
5158
+ };
5159
+ }
5160
+
5161
+ // src/theme.ts
5162
+ import {
5163
+ inject as inject3,
5164
+ provide as provide3,
5165
+ computed as computed3,
5166
+ defineComponent as defineComponent35,
5167
+ ref as ref38
5168
+ } from "@vue/runtime-core";
5169
+ function createTheme(definition) {
5170
+ const key = /* @__PURE__ */ Symbol("vue-native-theme");
5171
+ const ThemeProvider = defineComponent35({
5172
+ name: "ThemeProvider",
5173
+ props: {
5174
+ initialColorScheme: {
5175
+ type: String,
5176
+ default: "light"
5177
+ }
5178
+ },
5179
+ setup(props, { slots }) {
5180
+ const colorScheme = ref38(props.initialColorScheme);
5181
+ const theme = computed3(() => {
5182
+ return colorScheme.value === "dark" ? definition.dark : definition.light;
5183
+ });
5184
+ const ctx = {
5185
+ theme,
5186
+ colorScheme,
5187
+ toggleColorScheme: () => {
5188
+ colorScheme.value = colorScheme.value === "light" ? "dark" : "light";
5189
+ },
5190
+ setColorScheme: (scheme) => {
5191
+ colorScheme.value = scheme;
5192
+ }
5193
+ };
5194
+ provide3(key, ctx);
5195
+ return () => slots.default?.();
5196
+ }
5197
+ });
5198
+ function useTheme() {
5199
+ const ctx = inject3(key);
5200
+ if (!ctx) {
5201
+ throw new Error(
5202
+ "[Vue Native] useTheme() was called outside of a <ThemeProvider>. Wrap your app root with <ThemeProvider> to provide theme context."
5203
+ );
5204
+ }
5205
+ return ctx;
5206
+ }
5207
+ return { ThemeProvider, useTheme };
5208
+ }
5209
+ function createDynamicStyleSheet(theme, factory) {
5210
+ return computed3(() => createStyleSheet(factory(theme.value)));
5211
+ }
5212
+
3476
5213
  // src/index.ts
3477
5214
  function createApp(rootComponent, rootProps) {
3478
5215
  const app = baseCreateApp(rootComponent, rootProps);
3479
- app.component("VView", VView);
3480
- app.component("VText", VText);
3481
- app.component("VButton", VButton);
3482
- app.component("VInput", VInput);
3483
- app.component("VSwitch", VSwitch);
3484
- app.component("VActivityIndicator", VActivityIndicator);
3485
- app.component("VScrollView", VScrollView);
3486
- app.component("VImage", VImage);
3487
- app.component("VKeyboardAvoiding", VKeyboardAvoiding);
3488
- app.component("VSafeArea", VSafeArea);
3489
- app.component("VSlider", VSlider);
3490
- app.component("VList", VList);
3491
- app.component("VModal", VModal);
3492
- app.component("VAlertDialog", VAlertDialog);
3493
- app.component("VStatusBar", VStatusBar);
3494
- app.component("VWebView", VWebView);
3495
- app.component("VProgressBar", VProgressBar);
3496
- app.component("VPicker", VPicker);
3497
- app.component("VSegmentedControl", VSegmentedControl);
3498
- app.component("VActionSheet", VActionSheet);
3499
- app.component("VRefreshControl", VRefreshControl);
3500
- app.component("VPressable", VPressable);
3501
- app.component("VSectionList", VSectionList);
3502
- app.component("VCheckbox", VCheckbox);
3503
- app.component("VRadio", VRadio);
3504
- app.component("VDropdown", VDropdown);
3505
- app.component("VVideo", VVideo);
5216
+ for (const [name, component] of Object.entries(builtInComponents)) {
5217
+ app.component(name, component);
5218
+ }
5219
+ app.component("VDrawer.Item", VDrawerItem);
5220
+ app.component("VDrawer.Section", VDrawerSection);
3506
5221
  app.component("ErrorBoundary", ErrorBoundary);
3507
5222
  app.component("VErrorBoundary", ErrorBoundary);
3508
- app.directive("show", vShow);
3509
5223
  app.config.errorHandler = (err, instance, info) => {
3510
5224
  const error = err instanceof Error ? err : new Error(String(err));
3511
5225
  const componentName = instance?.$options?.name || instance?.$.type?.name || "Anonymous";
@@ -3546,7 +5260,11 @@ export {
3546
5260
  VAlertDialog,
3547
5261
  VButton,
3548
5262
  VCheckbox,
5263
+ VDrawer,
5264
+ VDrawerItem,
5265
+ VDrawerSection,
3549
5266
  VDropdown,
5267
+ VFlatList,
3550
5268
  VImage,
3551
5269
  VInput,
3552
5270
  VKeyboardAvoiding,
@@ -3564,16 +5282,20 @@ export {
3564
5282
  VSlider,
3565
5283
  VStatusBar,
3566
5284
  VSwitch,
5285
+ VTabBar,
3567
5286
  VText,
3568
5287
  VVideo,
3569
5288
  VView,
3570
5289
  VWebView,
5290
+ builtInComponents,
3571
5291
  clearSharedElementRegistry,
3572
5292
  createApp,
3573
5293
  createCommentNode,
5294
+ createDynamicStyleSheet,
3574
5295
  createNativeNode,
3575
5296
  createStyleSheet,
3576
5297
  createTextNode,
5298
+ createTheme,
3577
5299
  getRegisteredSharedElements,
3578
5300
  getSharedElementViewId,
3579
5301
  measureViewFrame,
@@ -3593,12 +5315,16 @@ export {
3593
5315
  useCamera,
3594
5316
  useClipboard,
3595
5317
  useColorScheme,
5318
+ useComposedGestures,
3596
5319
  useContacts,
3597
5320
  useDatabase,
3598
5321
  useDeviceInfo,
3599
5322
  useDimensions,
5323
+ useDragDrop,
5324
+ useFileDialog,
3600
5325
  useFileSystem,
3601
5326
  useGeolocation,
5327
+ useGesture,
3602
5328
  useGoogleSignIn,
3603
5329
  useGyroscope,
3604
5330
  useHaptics,
@@ -3607,6 +5333,7 @@ export {
3607
5333
  useIAP,
3608
5334
  useKeyboard,
3609
5335
  useLinking,
5336
+ useMenu,
3610
5337
  useNetwork,
3611
5338
  useNotifications,
3612
5339
  useOTAUpdate,
@@ -3616,7 +5343,10 @@ export {
3616
5343
  useSecureStorage,
3617
5344
  useShare,
3618
5345
  useSharedElementTransition,
5346
+ useTeleport,
3619
5347
  useWebSocket,
5348
+ useWindow,
5349
+ vModel,
3620
5350
  vShow,
3621
5351
  validStyleProperties
3622
5352
  };