@thelacanians/vue-native-runtime 0.4.13 → 0.4.15

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) {
@@ -177,6 +186,16 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
177
186
  updateStyle(nodeId, key, value) {
178
187
  this.enqueue("updateStyle", [nodeId, { [key]: value }]);
179
188
  }
189
+ /**
190
+ * Update multiple style properties on a native view in a single bridge op.
191
+ * Swift/Kotlin handler: handleUpdateStyle(args: [nodeId, { key1: val1, key2: val2, ... }])
192
+ *
193
+ * More efficient than calling updateStyle() per property — sends one op
194
+ * instead of N ops, reducing JSON overhead and bridge dispatch.
195
+ */
196
+ updateStyles(nodeId, styles) {
197
+ this.enqueue("updateStyle", [nodeId, styles]);
198
+ }
180
199
  // ---------------------------------------------------------------------------
181
200
  // Tree mutations
182
201
  // ---------------------------------------------------------------------------
@@ -351,9 +370,9 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
351
370
  }
352
371
  const handlers = this.globalEventHandlers.get(eventName);
353
372
  if (handlers) {
354
- handlers.forEach((h28) => {
373
+ handlers.forEach((h29) => {
355
374
  try {
356
- h28(payload);
375
+ h29(payload);
357
376
  } catch (err) {
358
377
  console.error(`[VueNative] Error in global event handler "${eventName}":`, err);
359
378
  }
@@ -370,6 +389,9 @@ var _NativeBridgeImpl = class _NativeBridgeImpl {
370
389
  this.pendingOps = [];
371
390
  this.flushScheduled = false;
372
391
  this.eventHandlers.clear();
392
+ for (const pending of this.pendingCallbacks.values()) {
393
+ clearTimeout(pending.timeoutId);
394
+ }
373
395
  this.pendingCallbacks.clear();
374
396
  this.nextCallbackId = 1;
375
397
  this.globalEventHandlers.clear();
@@ -401,16 +423,23 @@ function patchStyle(nodeId, prevStyle, nextStyle) {
401
423
  try {
402
424
  const prev = prevStyle || {};
403
425
  const next = nextStyle || {};
426
+ const changes = {};
427
+ let hasChanges = false;
404
428
  for (const key in next) {
405
429
  if (next[key] !== prev[key]) {
406
- NativeBridge.updateStyle(nodeId, key, next[key]);
430
+ changes[key] = next[key];
431
+ hasChanges = true;
407
432
  }
408
433
  }
409
434
  for (const key in prev) {
410
435
  if (!(key in next)) {
411
- NativeBridge.updateStyle(nodeId, key, null);
436
+ changes[key] = null;
437
+ hasChanges = true;
412
438
  }
413
439
  }
440
+ if (hasChanges) {
441
+ NativeBridge.updateStyles(nodeId, changes);
442
+ }
414
443
  } catch (err) {
415
444
  console.error(`[VueNative] Error patching style on node ${nodeId}:`, err);
416
445
  }
@@ -547,6 +576,7 @@ var nodeOps = {
547
576
  } catch (err) {
548
577
  console.error(`[VueNative] Error removing node ${child.id}:`, err);
549
578
  }
579
+ releaseNodeId(child.id);
550
580
  }
551
581
  },
552
582
  /**
@@ -1700,20 +1730,201 @@ var VVideo = defineComponent27({
1700
1730
  }
1701
1731
  });
1702
1732
 
1733
+ // src/components/VFlatList.ts
1734
+ import { defineComponent as defineComponent28, h as h28, ref as ref5, computed as computed2 } from "@vue/runtime-core";
1735
+ var VFlatList = defineComponent28({
1736
+ name: "VFlatList",
1737
+ props: {
1738
+ /** Array of data items to render. */
1739
+ data: {
1740
+ type: Array,
1741
+ required: true
1742
+ },
1743
+ /**
1744
+ * Render function for each item. Receives { item, index } and returns a VNode.
1745
+ * If not provided, the `#item` slot is used instead.
1746
+ */
1747
+ renderItem: {
1748
+ type: Function,
1749
+ default: void 0
1750
+ },
1751
+ /** Extract a unique key from each item. Defaults to item.id, item.key, or index. */
1752
+ keyExtractor: {
1753
+ type: Function,
1754
+ default: (item, index) => item?.id ?? item?.key ?? index
1755
+ },
1756
+ /** Fixed height for each item in points. Required for virtualization math. */
1757
+ itemHeight: {
1758
+ type: Number,
1759
+ required: true
1760
+ },
1761
+ /**
1762
+ * Number of viewport-heights to render above and below the visible area.
1763
+ * Higher values reduce blank flashes during fast scrolling but use more memory.
1764
+ * Default: 3 (3 viewports above + 3 below = 7 total viewports of items).
1765
+ */
1766
+ windowSize: {
1767
+ type: Number,
1768
+ default: 3
1769
+ },
1770
+ /** Style for the outer scroll container. */
1771
+ style: {
1772
+ type: Object,
1773
+ default: () => ({})
1774
+ },
1775
+ /** Show vertical scroll indicator. Default: true */
1776
+ showsScrollIndicator: {
1777
+ type: Boolean,
1778
+ default: true
1779
+ },
1780
+ /** Enable bounce at scroll boundaries. Default: true */
1781
+ bounces: {
1782
+ type: Boolean,
1783
+ default: true
1784
+ },
1785
+ /** Height of the header slot in points. Used to offset items below the header. */
1786
+ headerHeight: {
1787
+ type: Number,
1788
+ default: 0
1789
+ },
1790
+ /**
1791
+ * How far from the end (in viewport fractions) to trigger endReached.
1792
+ * Default: 0.5 (trigger when within 50% of a viewport from the bottom).
1793
+ */
1794
+ endReachedThreshold: {
1795
+ type: Number,
1796
+ default: 0.5
1797
+ }
1798
+ },
1799
+ emits: ["scroll", "endReached"],
1800
+ setup(props, { slots, emit }) {
1801
+ const scrollOffset = ref5(0);
1802
+ const viewportHeight = ref5(0);
1803
+ let endReachedFired = false;
1804
+ const hasHeader = computed2(() => !!slots.header);
1805
+ const totalHeight = computed2(() => {
1806
+ const itemsHeight = (props.data?.length ?? 0) * props.itemHeight;
1807
+ return itemsHeight + (hasHeader.value ? props.headerHeight : 0);
1808
+ });
1809
+ const visibleRange = computed2(() => {
1810
+ const vh = viewportHeight.value || props.itemHeight * 20;
1811
+ const buffer = vh * props.windowSize;
1812
+ const startPx = Math.max(0, scrollOffset.value - buffer);
1813
+ const endPx = scrollOffset.value + vh + buffer;
1814
+ const startIdx = Math.floor(startPx / props.itemHeight);
1815
+ const endIdx = Math.min(
1816
+ Math.ceil(endPx / props.itemHeight),
1817
+ props.data?.length ?? 0
1818
+ );
1819
+ return { start: startIdx, end: endIdx };
1820
+ });
1821
+ function onScroll(event) {
1822
+ scrollOffset.value = event.y ?? 0;
1823
+ if (event.layoutHeight && event.layoutHeight > 0) {
1824
+ viewportHeight.value = event.layoutHeight;
1825
+ }
1826
+ emit("scroll", event);
1827
+ const contentLength = totalHeight.value;
1828
+ const offset = scrollOffset.value;
1829
+ const vh = viewportHeight.value || props.itemHeight * 20;
1830
+ const distanceFromEnd = contentLength - vh - offset;
1831
+ const threshold = vh * props.endReachedThreshold;
1832
+ if (distanceFromEnd < threshold && !endReachedFired) {
1833
+ endReachedFired = true;
1834
+ emit("endReached");
1835
+ } else if (distanceFromEnd >= threshold) {
1836
+ endReachedFired = false;
1837
+ }
1838
+ }
1839
+ return () => {
1840
+ const items = props.data ?? [];
1841
+ const { start, end } = visibleRange.value;
1842
+ const children = [];
1843
+ for (let i = start; i < end; i++) {
1844
+ const item = items[i];
1845
+ if (item === void 0) continue;
1846
+ const key = props.keyExtractor(item, i);
1847
+ const itemContent = props.renderItem ? [props.renderItem({ item, index: i })] : slots.item?.({ item, index: i }) ?? [];
1848
+ children.push(
1849
+ h28(
1850
+ "VView",
1851
+ {
1852
+ key,
1853
+ style: {
1854
+ position: "absolute",
1855
+ top: (hasHeader.value ? props.headerHeight : 0) + i * props.itemHeight,
1856
+ left: 0,
1857
+ right: 0,
1858
+ height: props.itemHeight
1859
+ }
1860
+ },
1861
+ itemContent
1862
+ )
1863
+ );
1864
+ }
1865
+ if (slots.header) {
1866
+ children.unshift(
1867
+ h28("VView", { key: "__vfl_header__", style: { position: "absolute", top: 0, left: 0, right: 0 } }, slots.header())
1868
+ );
1869
+ }
1870
+ if (items.length === 0 && slots.empty) {
1871
+ return h28(
1872
+ "VScrollView",
1873
+ {
1874
+ style: { ...props.style, flex: props.style?.flex ?? 1 },
1875
+ showsVerticalScrollIndicator: props.showsScrollIndicator,
1876
+ bounces: props.bounces
1877
+ },
1878
+ [h28("VView", { style: { flex: 1 } }, slots.empty())]
1879
+ );
1880
+ }
1881
+ const innerContainer = h28(
1882
+ "VView",
1883
+ {
1884
+ key: "__vfl_container__",
1885
+ style: {
1886
+ height: totalHeight.value,
1887
+ width: "100%"
1888
+ }
1889
+ },
1890
+ children
1891
+ );
1892
+ return h28(
1893
+ "VScrollView",
1894
+ {
1895
+ style: { ...props.style, flex: props.style?.flex ?? 1 },
1896
+ showsVerticalScrollIndicator: props.showsScrollIndicator,
1897
+ bounces: props.bounces,
1898
+ onScroll
1899
+ },
1900
+ [innerContainer]
1901
+ );
1902
+ };
1903
+ }
1904
+ });
1905
+
1703
1906
  // src/directives/vShow.ts
1704
1907
  var vShow = {
1705
1908
  beforeMount(el, { value }) {
1706
- NativeBridge.updateProp(el.id, "hidden", !value);
1909
+ try {
1910
+ NativeBridge.updateProp(el.id, "hidden", !value);
1911
+ } catch (err) {
1912
+ console.error("[VueNative] v-show beforeMount error:", err);
1913
+ }
1707
1914
  },
1708
1915
  updated(el, { value, oldValue }) {
1709
1916
  if (value === oldValue) return;
1710
- NativeBridge.updateProp(el.id, "hidden", !value);
1917
+ try {
1918
+ NativeBridge.updateProp(el.id, "hidden", !value);
1919
+ } catch (err) {
1920
+ console.error("[VueNative] v-show updated error:", err);
1921
+ }
1711
1922
  }
1712
1923
  };
1713
1924
 
1714
1925
  // src/errorBoundary.ts
1715
- import { defineComponent as defineComponent28, ref as ref5, watch as watch4, onErrorCaptured } from "@vue/runtime-core";
1716
- var ErrorBoundary = defineComponent28({
1926
+ import { defineComponent as defineComponent29, ref as ref6, watch as watch4, onErrorCaptured } from "@vue/runtime-core";
1927
+ var ErrorBoundary = defineComponent29({
1717
1928
  name: "ErrorBoundary",
1718
1929
  props: {
1719
1930
  onError: Function,
@@ -1723,8 +1934,8 @@ var ErrorBoundary = defineComponent28({
1723
1934
  }
1724
1935
  },
1725
1936
  setup(props, { slots }) {
1726
- const error = ref5(null);
1727
- const errorInfo = ref5("");
1937
+ const error = ref6(null);
1938
+ const errorInfo = ref6("");
1728
1939
  onErrorCaptured((err, _instance, info) => {
1729
1940
  const normalizedError = err instanceof Error ? err : new Error(String(err));
1730
1941
  error.value = normalizedError;
@@ -1932,9 +2143,9 @@ function useAsyncStorage() {
1932
2143
  }
1933
2144
 
1934
2145
  // src/composables/useClipboard.ts
1935
- import { ref as ref6 } from "@vue/runtime-core";
2146
+ import { ref as ref7 } from "@vue/runtime-core";
1936
2147
  function useClipboard() {
1937
- const content = ref6("");
2148
+ const content = ref7("");
1938
2149
  function copy(text) {
1939
2150
  return NativeBridge.invokeNativeModule("Clipboard", "copy", [text]).then(() => void 0);
1940
2151
  }
@@ -1948,16 +2159,16 @@ function useClipboard() {
1948
2159
  }
1949
2160
 
1950
2161
  // src/composables/useDeviceInfo.ts
1951
- import { ref as ref7, onMounted } from "@vue/runtime-core";
2162
+ import { ref as ref8, onMounted } from "@vue/runtime-core";
1952
2163
  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);
2164
+ const model = ref8("");
2165
+ const systemVersion = ref8("");
2166
+ const systemName = ref8("");
2167
+ const name = ref8("");
2168
+ const screenWidth = ref8(0);
2169
+ const screenHeight = ref8(0);
2170
+ const scale = ref8(1);
2171
+ const isLoaded = ref8(false);
1961
2172
  async function fetchInfo() {
1962
2173
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
1963
2174
  model.value = info.model ?? "";
@@ -1986,10 +2197,10 @@ function useDeviceInfo() {
1986
2197
  }
1987
2198
 
1988
2199
  // src/composables/useKeyboard.ts
1989
- import { ref as ref8 } from "@vue/runtime-core";
2200
+ import { ref as ref9 } from "@vue/runtime-core";
1990
2201
  function useKeyboard() {
1991
- const isVisible = ref8(false);
1992
- const height = ref8(0);
2202
+ const isVisible = ref9(false);
2203
+ const height = ref9(0);
1993
2204
  function dismiss() {
1994
2205
  return NativeBridge.invokeNativeModule("Keyboard", "dismiss", []).then(() => void 0);
1995
2206
  }
@@ -2010,15 +2221,25 @@ var Easing = {
2010
2221
  easeOut: "easeOut",
2011
2222
  easeInOut: "easeInOut"
2012
2223
  };
2224
+ function resolveViewId(target) {
2225
+ if (typeof target === "number") return target;
2226
+ if (target && typeof target === "object" && "value" in target) {
2227
+ const val = target.value;
2228
+ if (val && typeof val.id === "number") return val.id;
2229
+ throw new Error("[VueNative] Animation target ref has no .value.id \u2014 is the ref attached to a component?");
2230
+ }
2231
+ if (target && typeof target.id === "number") return target.id;
2232
+ throw new Error("[VueNative] Invalid animation target. Pass a number, template ref, or NativeNode.");
2233
+ }
2013
2234
  function useAnimation() {
2014
- function timing(viewId, toStyles, config = {}) {
2015
- return NativeBridge.invokeNativeModule("Animation", "timing", [viewId, toStyles, config]);
2235
+ function timing(target, toStyles, config = {}) {
2236
+ return NativeBridge.invokeNativeModule("Animation", "timing", [resolveViewId(target), toStyles, config]);
2016
2237
  }
2017
- function spring(viewId, toStyles, config = {}) {
2018
- return NativeBridge.invokeNativeModule("Animation", "spring", [viewId, toStyles, config]);
2238
+ function spring(target, toStyles, config = {}) {
2239
+ return NativeBridge.invokeNativeModule("Animation", "spring", [resolveViewId(target), toStyles, config]);
2019
2240
  }
2020
- function keyframe(viewId, steps, config = {}) {
2021
- return NativeBridge.invokeNativeModule("Animation", "keyframe", [viewId, steps, config]);
2241
+ function keyframe(target, steps, config = {}) {
2242
+ return NativeBridge.invokeNativeModule("Animation", "keyframe", [resolveViewId(target), steps, config]);
2022
2243
  }
2023
2244
  function sequence(animations) {
2024
2245
  return NativeBridge.invokeNativeModule("Animation", "sequence", [animations]);
@@ -2026,17 +2247,20 @@ function useAnimation() {
2026
2247
  function parallel(animations) {
2027
2248
  return NativeBridge.invokeNativeModule("Animation", "parallel", [animations]);
2028
2249
  }
2029
- function fadeOut(viewId, duration = 300) {
2030
- return timing(viewId, { opacity: 0 }, { duration });
2250
+ function fadeOut(target, duration = 300) {
2251
+ return timing(target, { opacity: 0 }, { duration });
2252
+ }
2253
+ function fadeIn(target, duration = 300) {
2254
+ return timing(target, { opacity: 1 }, { duration });
2031
2255
  }
2032
- function fadeIn(viewId, duration = 300) {
2033
- return timing(viewId, { opacity: 1 }, { duration });
2256
+ function slideInFromRight(target, duration = 300) {
2257
+ return timing(target, { translateX: 0 }, { duration, easing: "easeOut" });
2034
2258
  }
2035
- function slideInFromRight(viewId, duration = 300) {
2036
- return timing(viewId, { translateX: 0 }, { duration, easing: "easeOut" });
2259
+ function slideOutToRight(target, duration = 300) {
2260
+ return timing(target, { translateX: 400 }, { duration, easing: "easeIn" });
2037
2261
  }
2038
- function slideOutToRight(viewId, duration = 300) {
2039
- return timing(viewId, { translateX: 400 }, { duration, easing: "easeIn" });
2262
+ function resolveId(target) {
2263
+ return resolveViewId(target);
2040
2264
  }
2041
2265
  return {
2042
2266
  timing,
@@ -2048,15 +2272,16 @@ function useAnimation() {
2048
2272
  fadeOut,
2049
2273
  slideInFromRight,
2050
2274
  slideOutToRight,
2275
+ resolveId,
2051
2276
  Easing
2052
2277
  };
2053
2278
  }
2054
2279
 
2055
2280
  // src/composables/useNetwork.ts
2056
- import { ref as ref9, onUnmounted as onUnmounted3 } from "@vue/runtime-core";
2281
+ import { ref as ref10, onUnmounted as onUnmounted3 } from "@vue/runtime-core";
2057
2282
  function useNetwork() {
2058
- const isConnected = ref9(true);
2059
- const connectionType = ref9("unknown");
2283
+ const isConnected = ref10(true);
2284
+ const connectionType = ref10("unknown");
2060
2285
  let lastEventTime = 0;
2061
2286
  const unsubscribe = NativeBridge.onGlobalEvent("network:change", (payload) => {
2062
2287
  lastEventTime = Date.now();
@@ -2076,9 +2301,9 @@ function useNetwork() {
2076
2301
  }
2077
2302
 
2078
2303
  // src/composables/useAppState.ts
2079
- import { ref as ref10, onUnmounted as onUnmounted4 } from "@vue/runtime-core";
2304
+ import { ref as ref11, onUnmounted as onUnmounted4 } from "@vue/runtime-core";
2080
2305
  function useAppState() {
2081
- const state = ref10("active");
2306
+ const state = ref11("active");
2082
2307
  NativeBridge.invokeNativeModule("AppState", "getState").then((s) => {
2083
2308
  state.value = s;
2084
2309
  }).catch(() => {
@@ -2121,10 +2346,10 @@ function usePermissions() {
2121
2346
  }
2122
2347
 
2123
2348
  // src/composables/useGeolocation.ts
2124
- import { ref as ref11, onUnmounted as onUnmounted5 } from "@vue/runtime-core";
2349
+ import { ref as ref12, onUnmounted as onUnmounted5 } from "@vue/runtime-core";
2125
2350
  function useGeolocation() {
2126
- const coords = ref11(null);
2127
- const error = ref11(null);
2351
+ const coords = ref12(null);
2352
+ const error = ref12(null);
2128
2353
  let watchId = null;
2129
2354
  async function getCurrentPosition() {
2130
2355
  try {
@@ -2202,10 +2427,10 @@ function useCamera() {
2202
2427
  }
2203
2428
 
2204
2429
  // src/composables/useNotifications.ts
2205
- import { ref as ref12, onUnmounted as onUnmounted7 } from "@vue/runtime-core";
2430
+ import { ref as ref13, onUnmounted as onUnmounted7 } from "@vue/runtime-core";
2206
2431
  function useNotifications() {
2207
- const isGranted = ref12(false);
2208
- const pushToken = ref12(null);
2432
+ const isGranted = ref13(false);
2433
+ const pushToken = ref13(null);
2209
2434
  async function requestPermission() {
2210
2435
  const granted = await NativeBridge.invokeNativeModule("Notifications", "requestPermission");
2211
2436
  isGranted.value = granted;
@@ -2280,7 +2505,7 @@ function useBiometry() {
2280
2505
  }
2281
2506
 
2282
2507
  // src/composables/useHttp.ts
2283
- import { ref as ref13, onUnmounted as onUnmounted8 } from "@vue/runtime-core";
2508
+ import { ref as ref14, onUnmounted as onUnmounted8 } from "@vue/runtime-core";
2284
2509
  function useHttp(config = {}) {
2285
2510
  if (config.pins && Object.keys(config.pins).length > 0) {
2286
2511
  const configurePins = globalThis.__VN_configurePins;
@@ -2290,39 +2515,70 @@ function useHttp(config = {}) {
2290
2515
  NativeBridge.invokeNativeModule("Http", "configurePins", [config.pins]);
2291
2516
  }
2292
2517
  }
2293
- const loading = ref13(false);
2294
- const error = ref13(null);
2518
+ const loading = ref14(false);
2519
+ const error = ref14(null);
2295
2520
  let isMounted = true;
2296
2521
  onUnmounted8(() => {
2297
2522
  isMounted = false;
2298
2523
  });
2524
+ const BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
2525
+ function parseResponseHeaders(response) {
2526
+ const result = {};
2527
+ if (!response.headers) return result;
2528
+ try {
2529
+ if (typeof response.headers.forEach === "function") {
2530
+ response.headers.forEach((value, key) => {
2531
+ result[key] = value;
2532
+ });
2533
+ } else if (typeof response.headers.entries === "function") {
2534
+ for (const [key, value] of response.headers.entries()) {
2535
+ result[key] = value;
2536
+ }
2537
+ }
2538
+ } catch {
2539
+ }
2540
+ return result;
2541
+ }
2299
2542
  async function request(method, url, options = {}) {
2300
2543
  const fullUrl = config.baseURL ? `${config.baseURL}${url}` : url;
2301
2544
  loading.value = true;
2302
2545
  error.value = null;
2546
+ let controller;
2547
+ let timeoutId;
2548
+ if (config.timeout && config.timeout > 0 && typeof AbortController !== "undefined") {
2549
+ controller = new AbortController();
2550
+ timeoutId = setTimeout(() => controller.abort(), config.timeout);
2551
+ }
2303
2552
  try {
2553
+ const upperMethod = method.toUpperCase();
2304
2554
  const mergedHeaders = {
2305
- "Content-Type": "application/json",
2306
2555
  ...config.headers ?? {},
2307
2556
  ...options.headers ?? {}
2308
2557
  };
2558
+ if (BODY_METHODS.has(upperMethod) && !mergedHeaders["Content-Type"]) {
2559
+ mergedHeaders["Content-Type"] = "application/json";
2560
+ }
2309
2561
  const fetchOptions = {
2310
- method: method.toUpperCase(),
2562
+ method: upperMethod,
2311
2563
  headers: mergedHeaders
2312
2564
  };
2565
+ if (controller) {
2566
+ fetchOptions.signal = controller.signal;
2567
+ }
2313
2568
  if (options.body !== void 0) {
2314
2569
  fetchOptions.body = JSON.stringify(options.body);
2315
2570
  }
2316
2571
  const response = await fetch(fullUrl, fetchOptions);
2317
2572
  const data = await response.json();
2573
+ const responseHeaders = parseResponseHeaders(response);
2318
2574
  if (!isMounted) {
2319
- return { data, status: response.status, ok: response.ok, headers: {} };
2575
+ return { data, status: response.status, ok: response.ok, headers: responseHeaders };
2320
2576
  }
2321
2577
  return {
2322
2578
  data,
2323
2579
  status: response.status,
2324
2580
  ok: response.ok,
2325
- headers: {}
2581
+ headers: responseHeaders
2326
2582
  };
2327
2583
  } catch (e) {
2328
2584
  const msg = e instanceof Error ? e.message : String(e);
@@ -2331,6 +2587,9 @@ function useHttp(config = {}) {
2331
2587
  }
2332
2588
  throw e;
2333
2589
  } finally {
2590
+ if (timeoutId !== void 0) {
2591
+ clearTimeout(timeoutId);
2592
+ }
2334
2593
  if (isMounted) {
2335
2594
  loading.value = false;
2336
2595
  }
@@ -2365,10 +2624,10 @@ function useHttp(config = {}) {
2365
2624
  }
2366
2625
 
2367
2626
  // src/composables/useColorScheme.ts
2368
- import { ref as ref14, onUnmounted as onUnmounted9 } from "@vue/runtime-core";
2627
+ import { ref as ref15, onUnmounted as onUnmounted9 } from "@vue/runtime-core";
2369
2628
  function useColorScheme() {
2370
- const colorScheme = ref14("light");
2371
- const isDark = ref14(false);
2629
+ const colorScheme = ref15("light");
2630
+ const isDark = ref15(false);
2372
2631
  const unsubscribe = NativeBridge.onGlobalEvent(
2373
2632
  "colorScheme:change",
2374
2633
  (payload) => {
@@ -2417,10 +2676,10 @@ function useSecureStorage() {
2417
2676
  }
2418
2677
 
2419
2678
  // src/composables/useI18n.ts
2420
- import { ref as ref15, onMounted as onMounted3 } from "@vue/runtime-core";
2679
+ import { ref as ref16, onMounted as onMounted3 } from "@vue/runtime-core";
2421
2680
  function useI18n() {
2422
- const isRTL = ref15(false);
2423
- const locale = ref15("en");
2681
+ const isRTL = ref16(false);
2682
+ const locale = ref16("en");
2424
2683
  onMounted3(async () => {
2425
2684
  try {
2426
2685
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getDeviceInfo", []);
@@ -2441,11 +2700,11 @@ function usePlatform() {
2441
2700
  }
2442
2701
 
2443
2702
  // src/composables/useDimensions.ts
2444
- import { ref as ref16, onMounted as onMounted4, onUnmounted as onUnmounted11 } from "@vue/runtime-core";
2703
+ import { ref as ref17, onMounted as onMounted4, onUnmounted as onUnmounted11 } from "@vue/runtime-core";
2445
2704
  function useDimensions() {
2446
- const width = ref16(0);
2447
- const height = ref16(0);
2448
- const scale = ref16(1);
2705
+ const width = ref17(0);
2706
+ const height = ref17(0);
2707
+ const scale = ref17(1);
2449
2708
  onMounted4(async () => {
2450
2709
  try {
2451
2710
  const info = await NativeBridge.invokeNativeModule("DeviceInfo", "getInfo", []);
@@ -2465,7 +2724,7 @@ function useDimensions() {
2465
2724
  }
2466
2725
 
2467
2726
  // src/composables/useWebSocket.ts
2468
- import { ref as ref17, onUnmounted as onUnmounted12 } from "@vue/runtime-core";
2727
+ import { ref as ref18, onUnmounted as onUnmounted12 } from "@vue/runtime-core";
2469
2728
  var connectionCounter = 0;
2470
2729
  function useWebSocket(url, options = {}) {
2471
2730
  const {
@@ -2475,9 +2734,9 @@ function useWebSocket(url, options = {}) {
2475
2734
  reconnectInterval = 1e3
2476
2735
  } = options;
2477
2736
  const connectionId = `ws_${++connectionCounter}_${Date.now()}`;
2478
- const status = ref17("CLOSED");
2479
- const lastMessage = ref17(null);
2480
- const error = ref17(null);
2737
+ const status = ref18("CLOSED");
2738
+ const lastMessage = ref18(null);
2739
+ const error = ref18(null);
2481
2740
  let reconnectAttempts = 0;
2482
2741
  let reconnectTimer = null;
2483
2742
  const MAX_PENDING_MESSAGES = 100;
@@ -2631,12 +2890,12 @@ function useFileSystem() {
2631
2890
  }
2632
2891
 
2633
2892
  // src/composables/useSensors.ts
2634
- import { ref as ref18, onUnmounted as onUnmounted13 } from "@vue/runtime-core";
2893
+ import { ref as ref19, onUnmounted as onUnmounted13 } from "@vue/runtime-core";
2635
2894
  function useAccelerometer(options = {}) {
2636
- const x = ref18(0);
2637
- const y = ref18(0);
2638
- const z = ref18(0);
2639
- const isAvailable = ref18(false);
2895
+ const x = ref19(0);
2896
+ const y = ref19(0);
2897
+ const z = ref19(0);
2898
+ const isAvailable = ref19(false);
2640
2899
  let running = false;
2641
2900
  let unsubscribe = null;
2642
2901
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["accelerometer"]).then((result) => {
@@ -2670,10 +2929,10 @@ function useAccelerometer(options = {}) {
2670
2929
  return { x, y, z, isAvailable, start, stop };
2671
2930
  }
2672
2931
  function useGyroscope(options = {}) {
2673
- const x = ref18(0);
2674
- const y = ref18(0);
2675
- const z = ref18(0);
2676
- const isAvailable = ref18(false);
2932
+ const x = ref19(0);
2933
+ const y = ref19(0);
2934
+ const z = ref19(0);
2935
+ const isAvailable = ref19(false);
2677
2936
  let running = false;
2678
2937
  let unsubscribe = null;
2679
2938
  NativeBridge.invokeNativeModule("Sensors", "isAvailable", ["gyroscope"]).then((result) => {
@@ -2708,13 +2967,13 @@ function useGyroscope(options = {}) {
2708
2967
  }
2709
2968
 
2710
2969
  // src/composables/useAudio.ts
2711
- import { ref as ref19, onUnmounted as onUnmounted14 } from "@vue/runtime-core";
2970
+ import { ref as ref20, onUnmounted as onUnmounted14 } from "@vue/runtime-core";
2712
2971
  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);
2972
+ const duration = ref20(0);
2973
+ const position = ref20(0);
2974
+ const isPlaying = ref20(false);
2975
+ const isRecording = ref20(false);
2976
+ const error = ref20(null);
2718
2977
  const unsubProgress = NativeBridge.onGlobalEvent("audio:progress", (payload) => {
2719
2978
  position.value = payload.currentTime ?? 0;
2720
2979
  duration.value = payload.duration ?? 0;
@@ -2806,9 +3065,9 @@ function useAudio() {
2806
3065
  }
2807
3066
 
2808
3067
  // src/composables/useDatabase.ts
2809
- import { ref as ref20, onUnmounted as onUnmounted15 } from "@vue/runtime-core";
3068
+ import { ref as ref21, onUnmounted as onUnmounted15 } from "@vue/runtime-core";
2810
3069
  function useDatabase(name = "default") {
2811
- const isOpen = ref20(false);
3070
+ const isOpen = ref21(false);
2812
3071
  let opened = false;
2813
3072
  async function ensureOpen() {
2814
3073
  if (opened) return;
@@ -2862,12 +3121,12 @@ function useDatabase(name = "default") {
2862
3121
  }
2863
3122
 
2864
3123
  // src/composables/usePerformance.ts
2865
- import { ref as ref21, onUnmounted as onUnmounted16 } from "@vue/runtime-core";
3124
+ import { ref as ref22, onUnmounted as onUnmounted16 } from "@vue/runtime-core";
2866
3125
  function usePerformance() {
2867
- const isProfiling = ref21(false);
2868
- const fps = ref21(0);
2869
- const memoryMB = ref21(0);
2870
- const bridgeOps = ref21(0);
3126
+ const isProfiling = ref22(false);
3127
+ const fps = ref22(0);
3128
+ const memoryMB = ref22(0);
3129
+ const bridgeOps = ref22(0);
2871
3130
  let unsubscribe = null;
2872
3131
  function handleMetrics(payload) {
2873
3132
  fps.value = payload.fps ?? 0;
@@ -2915,10 +3174,10 @@ function usePerformance() {
2915
3174
  }
2916
3175
 
2917
3176
  // src/composables/useSharedElementTransition.ts
2918
- import { ref as ref22, onUnmounted as onUnmounted17 } from "@vue/runtime-core";
3177
+ import { ref as ref23, onUnmounted as onUnmounted17 } from "@vue/runtime-core";
2919
3178
  var sharedElementRegistry = /* @__PURE__ */ new Map();
2920
3179
  function useSharedElementTransition(elementId) {
2921
- const viewId = ref22(null);
3180
+ const viewId = ref23(null);
2922
3181
  function register(nativeViewId) {
2923
3182
  viewId.value = nativeViewId;
2924
3183
  sharedElementRegistry.set(elementId, nativeViewId);
@@ -2951,11 +3210,11 @@ function clearSharedElementRegistry() {
2951
3210
  }
2952
3211
 
2953
3212
  // src/composables/useIAP.ts
2954
- import { ref as ref23, onUnmounted as onUnmounted18 } from "@vue/runtime-core";
3213
+ import { ref as ref24, onUnmounted as onUnmounted18 } from "@vue/runtime-core";
2955
3214
  function useIAP() {
2956
- const products = ref23([]);
2957
- const isReady = ref23(false);
2958
- const error = ref23(null);
3215
+ const products = ref24([]);
3216
+ const isReady = ref24(false);
3217
+ const error = ref24(null);
2959
3218
  const cleanups = [];
2960
3219
  const unsubscribe = NativeBridge.onGlobalEvent("iap:transactionUpdate", (payload) => {
2961
3220
  if (payload.state === "failed" && payload.error) {
@@ -3029,11 +3288,11 @@ function useIAP() {
3029
3288
  }
3030
3289
 
3031
3290
  // src/composables/useAppleSignIn.ts
3032
- import { ref as ref24, onUnmounted as onUnmounted19 } from "@vue/runtime-core";
3291
+ import { ref as ref25, onUnmounted as onUnmounted19 } from "@vue/runtime-core";
3033
3292
  function useAppleSignIn() {
3034
- const user = ref24(null);
3035
- const isAuthenticated = ref24(false);
3036
- const error = ref24(null);
3293
+ const user = ref25(null);
3294
+ const isAuthenticated = ref25(false);
3295
+ const error = ref25(null);
3037
3296
  const cleanups = [];
3038
3297
  const unsubscribe = NativeBridge.onGlobalEvent("auth:appleCredentialRevoked", () => {
3039
3298
  user.value = null;
@@ -3079,11 +3338,11 @@ function useAppleSignIn() {
3079
3338
  }
3080
3339
 
3081
3340
  // src/composables/useGoogleSignIn.ts
3082
- import { ref as ref25, onUnmounted as onUnmounted20 } from "@vue/runtime-core";
3341
+ import { ref as ref26, onUnmounted as onUnmounted20 } from "@vue/runtime-core";
3083
3342
  function useGoogleSignIn(clientId) {
3084
- const user = ref25(null);
3085
- const isAuthenticated = ref25(false);
3086
- const error = ref25(null);
3343
+ const user = ref26(null);
3344
+ const isAuthenticated = ref26(false);
3345
+ const error = ref26(null);
3087
3346
  const cleanups = [];
3088
3347
  NativeBridge.invokeNativeModule("SocialAuth", "getCurrentUser", ["google"]).then((result) => {
3089
3348
  if (result && result.userId) {
@@ -3124,10 +3383,10 @@ function useGoogleSignIn(clientId) {
3124
3383
  }
3125
3384
 
3126
3385
  // src/composables/useBackgroundTask.ts
3127
- import { ref as ref26, onUnmounted as onUnmounted21 } from "@vue/runtime-core";
3386
+ import { ref as ref27, onUnmounted as onUnmounted21 } from "@vue/runtime-core";
3128
3387
  function useBackgroundTask() {
3129
3388
  const taskHandlers = /* @__PURE__ */ new Map();
3130
- const defaultHandler = ref26(null);
3389
+ const defaultHandler = ref27(null);
3131
3390
  const unsubscribe = NativeBridge.onGlobalEvent("background:taskExecute", (payload) => {
3132
3391
  const handler = taskHandlers.get(payload.taskId) || defaultHandler.value;
3133
3392
  if (handler) {
@@ -3173,15 +3432,15 @@ function useBackgroundTask() {
3173
3432
  }
3174
3433
 
3175
3434
  // src/composables/useOTAUpdate.ts
3176
- import { ref as ref27, onUnmounted as onUnmounted22 } from "@vue/runtime-core";
3435
+ import { ref as ref28, onUnmounted as onUnmounted22 } from "@vue/runtime-core";
3177
3436
  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);
3437
+ const currentVersion = ref28("embedded");
3438
+ const availableVersion = ref28(null);
3439
+ const downloadProgress = ref28(0);
3440
+ const isChecking = ref28(false);
3441
+ const isDownloading = ref28(false);
3442
+ const status = ref28("idle");
3443
+ const error = ref28(null);
3185
3444
  let lastUpdateInfo = null;
3186
3445
  const unsubscribe = NativeBridge.onGlobalEvent("ota:downloadProgress", (payload) => {
3187
3446
  downloadProgress.value = payload.progress;
@@ -3298,13 +3557,13 @@ function useOTAUpdate(serverUrl) {
3298
3557
  }
3299
3558
 
3300
3559
  // src/composables/useBluetooth.ts
3301
- import { ref as ref28, onUnmounted as onUnmounted23 } from "@vue/runtime-core";
3560
+ import { ref as ref29, onUnmounted as onUnmounted23 } from "@vue/runtime-core";
3302
3561
  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);
3562
+ const devices = ref29([]);
3563
+ const connectedDevice = ref29(null);
3564
+ const isScanning = ref29(false);
3565
+ const isAvailable = ref29(false);
3566
+ const error = ref29(null);
3308
3567
  const cleanups = [];
3309
3568
  NativeBridge.invokeNativeModule("Bluetooth", "getState").then((state) => {
3310
3569
  isAvailable.value = state === "poweredOn";
@@ -3408,10 +3667,10 @@ function useBluetooth() {
3408
3667
  }
3409
3668
 
3410
3669
  // src/composables/useCalendar.ts
3411
- import { ref as ref29 } from "@vue/runtime-core";
3670
+ import { ref as ref30 } from "@vue/runtime-core";
3412
3671
  function useCalendar() {
3413
- const hasAccess = ref29(false);
3414
- const error = ref29(null);
3672
+ const hasAccess = ref30(false);
3673
+ const error = ref30(null);
3415
3674
  async function requestAccess() {
3416
3675
  try {
3417
3676
  const result = await NativeBridge.invokeNativeModule("Calendar", "requestAccess");
@@ -3444,10 +3703,10 @@ function useCalendar() {
3444
3703
  }
3445
3704
 
3446
3705
  // src/composables/useContacts.ts
3447
- import { ref as ref30 } from "@vue/runtime-core";
3706
+ import { ref as ref31 } from "@vue/runtime-core";
3448
3707
  function useContacts() {
3449
- const hasAccess = ref30(false);
3450
- const error = ref30(null);
3708
+ const hasAccess = ref31(false);
3709
+ const error = ref31(null);
3451
3710
  async function requestAccess() {
3452
3711
  try {
3453
3712
  const result = await NativeBridge.invokeNativeModule("Contacts", "requestAccess");
@@ -3473,6 +3732,58 @@ function useContacts() {
3473
3732
  return { requestAccess, getContacts, getContact, createContact, deleteContact, hasAccess, error };
3474
3733
  }
3475
3734
 
3735
+ // src/theme.ts
3736
+ import {
3737
+ inject,
3738
+ provide,
3739
+ computed as computed3,
3740
+ defineComponent as defineComponent30,
3741
+ ref as ref32
3742
+ } from "@vue/runtime-core";
3743
+ function createTheme(definition) {
3744
+ const key = /* @__PURE__ */ Symbol("vue-native-theme");
3745
+ const ThemeProvider = defineComponent30({
3746
+ name: "ThemeProvider",
3747
+ props: {
3748
+ initialColorScheme: {
3749
+ type: String,
3750
+ default: "light"
3751
+ }
3752
+ },
3753
+ setup(props, { slots }) {
3754
+ const colorScheme = ref32(props.initialColorScheme);
3755
+ const theme = computed3(() => {
3756
+ return colorScheme.value === "dark" ? definition.dark : definition.light;
3757
+ });
3758
+ const ctx = {
3759
+ theme,
3760
+ colorScheme,
3761
+ toggleColorScheme: () => {
3762
+ colorScheme.value = colorScheme.value === "light" ? "dark" : "light";
3763
+ },
3764
+ setColorScheme: (scheme) => {
3765
+ colorScheme.value = scheme;
3766
+ }
3767
+ };
3768
+ provide(key, ctx);
3769
+ return () => slots.default?.();
3770
+ }
3771
+ });
3772
+ function useTheme() {
3773
+ const ctx = inject(key);
3774
+ if (!ctx) {
3775
+ throw new Error(
3776
+ "[Vue Native] useTheme() was called outside of a <ThemeProvider>. Wrap your app root with <ThemeProvider> to provide theme context."
3777
+ );
3778
+ }
3779
+ return ctx;
3780
+ }
3781
+ return { ThemeProvider, useTheme };
3782
+ }
3783
+ function createDynamicStyleSheet(theme, factory) {
3784
+ return computed3(() => createStyleSheet(factory(theme.value)));
3785
+ }
3786
+
3476
3787
  // src/index.ts
3477
3788
  function createApp(rootComponent, rootProps) {
3478
3789
  const app = baseCreateApp(rootComponent, rootProps);
@@ -3503,6 +3814,7 @@ function createApp(rootComponent, rootProps) {
3503
3814
  app.component("VRadio", VRadio);
3504
3815
  app.component("VDropdown", VDropdown);
3505
3816
  app.component("VVideo", VVideo);
3817
+ app.component("VFlatList", VFlatList);
3506
3818
  app.component("ErrorBoundary", ErrorBoundary);
3507
3819
  app.component("VErrorBoundary", ErrorBoundary);
3508
3820
  app.directive("show", vShow);
@@ -3547,6 +3859,7 @@ export {
3547
3859
  VButton,
3548
3860
  VCheckbox,
3549
3861
  VDropdown,
3862
+ VFlatList,
3550
3863
  VImage,
3551
3864
  VInput,
3552
3865
  VKeyboardAvoiding,
@@ -3571,9 +3884,11 @@ export {
3571
3884
  clearSharedElementRegistry,
3572
3885
  createApp,
3573
3886
  createCommentNode,
3887
+ createDynamicStyleSheet,
3574
3888
  createNativeNode,
3575
3889
  createStyleSheet,
3576
3890
  createTextNode,
3891
+ createTheme,
3577
3892
  getRegisteredSharedElements,
3578
3893
  getSharedElementViewId,
3579
3894
  measureViewFrame,