@flamingo-stack/openframe-frontend-core 0.0.720 → 0.0.722

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/{chunk-3HZ6XSZC.js → chunk-2TXGR7IK.js} +136 -59
  2. package/dist/{chunk-WSDT7LYL.js → chunk-3UBWDR65.js} +1 -1
  3. package/dist/{chunk-CMZ3QTFQ.js → chunk-4I3K6LEH.js} +24 -0
  4. package/dist/{chunk-4SIZCHVZ.cjs → chunk-4LY6R4TN.cjs} +126 -49
  5. package/dist/{chunk-2NASBO7O.js → chunk-5D2K7TFT.js} +3 -3
  6. package/dist/{chunk-HFEYEPOG.cjs → chunk-5K6YKRP5.cjs} +29 -29
  7. package/dist/{chunk-7HYEQKN6.js → chunk-65C4WHG4.js} +6 -6
  8. package/dist/{chunk-DOPOHO6H.cjs → chunk-6HRQLM6T.cjs} +20 -20
  9. package/dist/{chunk-B2N4YX66.js → chunk-6YBUQAOH.js} +1 -1
  10. package/dist/{chunk-GANIUMRU.js → chunk-7UZ2JZJY.js} +3 -3
  11. package/dist/{chunk-4HJRHEXD.cjs → chunk-ABSWD4PQ.cjs} +27 -27
  12. package/dist/{chunk-VYVPII4W.js → chunk-AWWLEYAU.js} +6 -6
  13. package/dist/{chunk-V6B2FBZK.js → chunk-BCU6UUNQ.js} +8 -8
  14. package/dist/{chunk-LO3KCLEF.js → chunk-BUDZWXRG.js} +4 -4
  15. package/dist/{chunk-WBNAEYTQ.cjs → chunk-BYUIFT7Q.cjs} +6 -6
  16. package/dist/{chunk-KSGMZHVV.cjs → chunk-DFM572VH.cjs} +2 -2
  17. package/dist/{chunk-YDPKUAP4.js → chunk-DJ4IFQNQ.js} +2 -0
  18. package/dist/{chunk-DYDDAA6O.cjs → chunk-DWSBTGKT.cjs} +816 -798
  19. package/dist/{chunk-PSMJYZMV.js → chunk-DX3W7DPC.js} +2 -2
  20. package/dist/{chunk-PXAKSPLZ.js → chunk-E7NIZXDT.js} +20 -8
  21. package/dist/{chunk-6W46LVK4.js → chunk-ETQVXIYW.js} +6 -6
  22. package/dist/{chunk-742BGXNX.js → chunk-EWED6ISQ.js} +1 -1
  23. package/dist/{chunk-Q5327DXK.js → chunk-F33RMJOL.js} +11 -11
  24. package/dist/{chunk-RZQGIWK3.cjs → chunk-FDWYBJYD.cjs} +8 -8
  25. package/dist/{chunk-QF464PPL.cjs → chunk-GAFK3WNT.cjs} +62 -62
  26. package/dist/{chunk-6BLN5WDS.cjs → chunk-GYSTZD6X.cjs} +40 -40
  27. package/dist/{chunk-TORERAXD.js → chunk-HA2ASGJM.js} +6 -6
  28. package/dist/{chunk-KT3U3ZRP.cjs → chunk-HP4UUBJC.cjs} +16 -16
  29. package/dist/{chunk-DFQYAYBH.cjs → chunk-HQIOMFJH.cjs} +2 -0
  30. package/dist/{chunk-AZRI4XBP.js → chunk-I3UUMRO7.js} +1 -1
  31. package/dist/{chunk-VYZCKY5K.js → chunk-IF72N4PE.js} +7 -7
  32. package/dist/{chunk-RFUOIQSG.cjs → chunk-IITZ6JSC.cjs} +37 -37
  33. package/dist/{chunk-7LPR4RZ3.cjs → chunk-JQ55FFIK.cjs} +31 -31
  34. package/dist/{chunk-GQ7JJUIT.cjs → chunk-K2JUJ37D.cjs} +25 -1
  35. package/dist/{chunk-URW23QUM.js → chunk-K76ACBR3.js} +2 -2
  36. package/dist/{chunk-7IOU6BJE.js → chunk-KBPGSPXQ.js} +2387 -2369
  37. package/dist/{chunk-ILNN6QRW.js → chunk-KS3YLYVL.js} +4 -4
  38. package/dist/{chunk-2C72EWQR.cjs → chunk-KWBH63GV.cjs} +26 -26
  39. package/dist/{chunk-IYVFSLWJ.js → chunk-KZQO5EJQ.js} +4 -0
  40. package/dist/{chunk-IMTY3GRR.cjs → chunk-LV6UJD2K.cjs} +2 -2
  41. package/dist/{chunk-UWJMDFDO.cjs → chunk-MJ4SXOG2.cjs} +28 -28
  42. package/dist/{chunk-VF2CJXZ2.cjs → chunk-NFABAOMD.cjs} +77 -79
  43. package/dist/{chunk-B5LNX4GZ.js → chunk-NMGWNUKB.js} +3 -3
  44. package/dist/{chunk-RDP5UZXQ.cjs → chunk-P7Y626YJ.cjs} +370 -358
  45. package/dist/{chunk-P3JMAMHP.js → chunk-REETLEL5.js} +1 -1
  46. package/dist/{chunk-FFT56TYB.cjs → chunk-RUZIICXT.cjs} +2 -2
  47. package/dist/{chunk-ISCZ6UNF.cjs → chunk-RVQV6J5K.cjs} +75 -75
  48. package/dist/{chunk-VBHGJFA3.js → chunk-SZ3CBFSH.js} +13 -13
  49. package/dist/{chunk-O3MKJU6A.js → chunk-SZF7ENIW.js} +4 -4
  50. package/dist/{chunk-F5BPGWGT.cjs → chunk-TKFTWM3V.cjs} +5 -1
  51. package/dist/{chunk-YK3X74SF.js → chunk-UYMTAFHG.js} +6 -8
  52. package/dist/{chunk-DHNJSQGX.cjs → chunk-VC7COEWF.cjs} +105 -105
  53. package/dist/{chunk-XW3Q623Q.js → chunk-VSYQJCNR.js} +2 -2
  54. package/dist/{chunk-IL3ZWNM2.cjs → chunk-VU7RU3LL.cjs} +7 -7
  55. package/dist/{chunk-XSM46FHD.cjs → chunk-VV5INIQB.cjs} +3 -3
  56. package/dist/{chunk-HTS46FFY.cjs → chunk-VYHCN5NU.cjs} +16 -16
  57. package/dist/{chunk-ZPHOQDAE.js → chunk-WY4SLTMZ.js} +3 -3
  58. package/dist/{chunk-5XDCCQ4A.cjs → chunk-XJYAUIOL.cjs} +13 -13
  59. package/dist/{chunk-ZHD3F2TH.cjs → chunk-XR7NVJXS.cjs} +13 -13
  60. package/dist/{chunk-EVMM3JE6.cjs → chunk-ZHC6MDGF.cjs} +43 -43
  61. package/dist/{chunk-K2BYVPGR.js → chunk-ZMHP4Y6M.js} +5 -5
  62. package/dist/{chunk-NYSMDM4J.cjs → chunk-ZSOFLPQN.cjs} +7 -7
  63. package/dist/{cloud-tenants-RKQRSDNE.js → cloud-tenants-4Z36KWSX.js} +14 -14
  64. package/dist/{cloud-tenants-UQZASXUB.cjs → cloud-tenants-5NIC5VXK.cjs} +15 -15
  65. package/dist/components/calendar.cjs +4 -4
  66. package/dist/components/calendar.js +1 -1
  67. package/dist/components/case-studies/index.cjs +29 -29
  68. package/dist/components/case-studies/index.js +15 -15
  69. package/dist/components/chat/index.cjs +10 -10
  70. package/dist/components/chat/index.js +9 -9
  71. package/dist/components/contact/index.cjs +16 -16
  72. package/dist/components/contact/index.js +15 -15
  73. package/dist/components/docs/index.cjs +19 -19
  74. package/dist/components/docs/index.js +18 -18
  75. package/dist/components/embeds/index.cjs +16 -16
  76. package/dist/components/embeds/index.js +15 -15
  77. package/dist/components/faq/index.cjs +16 -16
  78. package/dist/components/faq/index.js +15 -15
  79. package/dist/components/features/index.cjs +15 -15
  80. package/dist/components/features/index.js +14 -14
  81. package/dist/components/features/remote-session/remote-access-request-card.d.ts.map +1 -1
  82. package/dist/components/help-center-pages/download-apps-page.d.ts +68 -0
  83. package/dist/components/help-center-pages/download-apps-page.d.ts.map +1 -0
  84. package/dist/components/help-center-pages/index.cjs +347 -117
  85. package/dist/components/help-center-pages/index.d.ts +1 -0
  86. package/dist/components/help-center-pages/index.d.ts.map +1 -1
  87. package/dist/components/help-center-pages/index.js +260 -30
  88. package/dist/components/index.cjs +87 -85
  89. package/dist/components/index.js +30 -28
  90. package/dist/components/meeting-scheduler/index.cjs +67 -67
  91. package/dist/components/meeting-scheduler/index.js +16 -16
  92. package/dist/components/navigation/index.cjs +18 -18
  93. package/dist/components/navigation/index.js +17 -17
  94. package/dist/components/onboarding-guides/index.cjs +18 -18
  95. package/dist/components/onboarding-guides/index.js +17 -17
  96. package/dist/components/product-demo/index.cjs +8 -8
  97. package/dist/components/product-demo/index.js +8 -8
  98. package/dist/components/related-content/index.cjs +7 -7
  99. package/dist/components/related-content/index.js +6 -6
  100. package/dist/components/tickets/index.cjs +20 -20
  101. package/dist/components/tickets/index.js +19 -19
  102. package/dist/components/ui/button/button.d.ts.map +1 -1
  103. package/dist/components/ui/file-manager/index.cjs +11 -11
  104. package/dist/components/ui/file-manager/index.js +1 -1
  105. package/dist/components/ui/index.cjs +17 -15
  106. package/dist/components/ui/index.d.ts +1 -0
  107. package/dist/components/ui/index.d.ts.map +1 -1
  108. package/dist/components/ui/index.js +16 -14
  109. package/dist/components/ui/mobile-app-qr.d.ts +21 -0
  110. package/dist/components/ui/mobile-app-qr.d.ts.map +1 -0
  111. package/dist/components/ui/store-badges.d.ts +7 -4
  112. package/dist/components/ui/store-badges.d.ts.map +1 -1
  113. package/dist/{devices-U3Y4IIW4.js → devices-2TK3LAKM.js} +14 -14
  114. package/dist/{devices-7Q2EUM7B.cjs → devices-7IKT72DK.cjs} +16 -16
  115. package/dist/hooks/index.cjs +9 -3
  116. package/dist/hooks/index.d.ts +1 -0
  117. package/dist/hooks/index.d.ts.map +1 -1
  118. package/dist/hooks/index.js +8 -2
  119. package/dist/hooks/use-downloads.d.ts +41 -0
  120. package/dist/hooks/use-downloads.d.ts.map +1 -0
  121. package/dist/index.cjs +52 -20
  122. package/dist/index.js +50 -18
  123. package/dist/{knowledge-NST53EXE.js → knowledge-3GJ4MU3V.js} +14 -14
  124. package/dist/{knowledge-UYIZ74MA.cjs → knowledge-HYSEV35H.cjs} +16 -16
  125. package/dist/{logs-JVTRG232.js → logs-TATVM62H.js} +14 -14
  126. package/dist/{logs-HTH7X7KV.cjs → logs-ZX3NADQC.cjs} +24 -24
  127. package/dist/{policies-MXK2HB4W.cjs → policies-J4FSDKPI.cjs} +19 -19
  128. package/dist/{policies-DTXTRK56.js → policies-MRRKLF4Q.js} +14 -14
  129. package/dist/{remote-session-LAJZKQBN.js → remote-session-WCLR47RN.js} +6 -6
  130. package/dist/{remote-session-SKKF4PBV.cjs → remote-session-Z2YCUPHU.cjs} +8 -8
  131. package/dist/{software-update-P6BSM6HX.cjs → software-update-CXUBWYD2.cjs} +19 -19
  132. package/dist/{software-update-6RHMPM3W.js → software-update-KM46WUVQ.js} +14 -14
  133. package/dist/{tickets-board-BHLPAVNJ.cjs → tickets-board-R5OQSEEC.cjs} +10 -10
  134. package/dist/{tickets-board-PYDNELSK.js → tickets-board-U3RF3AO4.js} +5 -5
  135. package/dist/types/downloads.d.ts +83 -0
  136. package/dist/types/downloads.d.ts.map +1 -0
  137. package/dist/types/index.cjs +24 -0
  138. package/dist/types/index.d.ts +1 -0
  139. package/dist/types/index.d.ts.map +1 -1
  140. package/dist/types/index.js +16 -1
  141. package/dist/utils/format.d.ts +11 -0
  142. package/dist/utils/format.d.ts.map +1 -1
  143. package/dist/utils/index.cjs +15 -0
  144. package/dist/utils/index.d.ts +2 -1
  145. package/dist/utils/index.d.ts.map +1 -1
  146. package/dist/utils/index.js +13 -1
  147. package/dist/utils/mobile-app.cjs +37 -0
  148. package/dist/utils/mobile-app.d.ts +23 -0
  149. package/dist/utils/mobile-app.d.ts.map +1 -0
  150. package/dist/utils/mobile-app.js +34 -0
  151. package/package.json +7 -1
  152. package/src/components/features/remote-session/remote-access-request-card.tsx +6 -6
  153. package/src/components/help-center-pages/download-apps-page.tsx +388 -0
  154. package/src/components/help-center-pages/index.ts +1 -0
  155. package/src/components/ui/button/button.tsx +3 -0
  156. package/src/components/ui/index.ts +1 -0
  157. package/src/components/ui/mobile-app-qr.tsx +35 -0
  158. package/src/components/ui/store-badges.tsx +17 -9
  159. package/src/hooks/index.ts +1 -0
  160. package/src/hooks/use-downloads.ts +138 -0
  161. package/src/types/downloads.ts +99 -0
  162. package/src/types/index.ts +1 -0
  163. package/src/utils/format.ts +15 -0
  164. package/src/utils/index.ts +3 -1
  165. package/src/utils/mobile-app.ts +34 -0
@@ -7,6 +7,7 @@ import {
7
7
  useRequiredEndpointsRuntime
8
8
  } from "./chunk-HFL2F4RM.js";
9
9
  import {
10
+ DOWNLOADS_API_PATH,
10
11
  ELAPSED_MS_FIELD,
11
12
  FORM_RESCUE_ATTEMPT_FIELD,
12
13
  FORM_RESCUE_DEBOUNCE_MS,
@@ -17,6 +18,7 @@ import {
17
18
  LOCATION_HASH_SYNC_EVENT,
18
19
  captureFormRescueEvent,
19
20
  consumeAccessCode,
21
+ desktopOsOf,
20
22
  filledRescueFields,
21
23
  getHashTargetElement,
22
24
  isFormAttemptId,
@@ -32,9 +34,10 @@ import {
32
34
  transformPlatformConfigsToOptions,
33
35
  useAutoplay,
34
36
  useDebounce,
37
+ useVisitorOs,
35
38
  validateAccessCode,
36
39
  validateAndConsumeAccessCode
37
- } from "./chunk-CMZ3QTFQ.js";
40
+ } from "./chunk-4I3K6LEH.js";
38
41
  import {
39
42
  ToolIcon,
40
43
  apiErrorMessage
@@ -1202,8 +1205,79 @@ function useCopyToClipboard({
1202
1205
  return { copy, copied };
1203
1206
  }
1204
1207
 
1208
+ // src/hooks/use-downloads.ts
1209
+ import { useCallback as useCallback10, useEffect as useEffect12, useSyncExternalStore } from "react";
1210
+ var IDLE = { data: null, isLoading: false, error: false };
1211
+ var entries = /* @__PURE__ */ new Map();
1212
+ function entryFor(endpoint) {
1213
+ let entry = entries.get(endpoint);
1214
+ if (!entry) {
1215
+ entry = { state: IDLE, request: null, listeners: /* @__PURE__ */ new Set() };
1216
+ entries.set(endpoint, entry);
1217
+ }
1218
+ return entry;
1219
+ }
1220
+ function publish(endpoint, state) {
1221
+ const entry = entryFor(endpoint);
1222
+ entry.state = state;
1223
+ for (const listener of entry.listeners) listener();
1224
+ }
1225
+ function load(endpoint, force = false) {
1226
+ const entry = entryFor(endpoint);
1227
+ if (entry.request || entry.state.data && !force) return;
1228
+ publish(endpoint, { data: entry.state.data, isLoading: true, error: false });
1229
+ entry.request = contentFetch(endpoint).then(async (response) => {
1230
+ if (!response.ok) throw new Error(`Request failed (${response.status})`);
1231
+ publish(endpoint, { data: await response.json(), isLoading: false, error: false });
1232
+ }).catch(() => publish(endpoint, { data: entry.state.data, isLoading: false, error: true })).finally(() => {
1233
+ entry.request = null;
1234
+ });
1235
+ }
1236
+ function resetDownloadsStore() {
1237
+ entries.clear();
1238
+ }
1239
+ function useDownloads({ endpoint = DOWNLOADS_API_PATH, initialData, enabled = true } = {}) {
1240
+ const entry = entryFor(endpoint);
1241
+ if (initialData && entry.state.data !== initialData && !entry.request) {
1242
+ entry.state = { data: initialData, isLoading: false, error: false };
1243
+ }
1244
+ useEffect12(() => {
1245
+ if (!initialData) return;
1246
+ for (const listener of entry.listeners) listener();
1247
+ }, [entry, initialData]);
1248
+ const subscribe2 = useCallback10(
1249
+ (listener) => {
1250
+ entry.listeners.add(listener);
1251
+ return () => {
1252
+ entry.listeners.delete(listener);
1253
+ };
1254
+ },
1255
+ [entry]
1256
+ );
1257
+ const state = useSyncExternalStore(
1258
+ subscribe2,
1259
+ () => entry.state,
1260
+ () => entry.state
1261
+ );
1262
+ useEffect12(() => {
1263
+ if (enabled) load(endpoint);
1264
+ }, [enabled, endpoint]);
1265
+ const reload = useCallback10(() => load(endpoint, true), [endpoint]);
1266
+ const isLoading = state.isLoading || enabled && !state.data && !state.error;
1267
+ return { data: state.data, isLoading, error: state.error, reload };
1268
+ }
1269
+ function useVisitorDesktopDownload(options = {}) {
1270
+ const enabled = options.enabled ?? true;
1271
+ const visitor = useVisitorOs();
1272
+ const { data, error } = useDownloads(options);
1273
+ if (!enabled || !visitor.known || !data && !error) return { known: false, download: null };
1274
+ const os = desktopOsOf(visitor.os);
1275
+ const download = data?.desktop.find((row) => row.kind === "binary" && row.url && row.os === os) ?? null;
1276
+ return { known: true, download };
1277
+ }
1278
+
1205
1279
  // src/hooks/use-batch-images.ts
1206
- import { useEffect as useEffect12, useMemo as useMemo3, useState as useState13, useRef as useRef6 } from "react";
1280
+ import { useEffect as useEffect13, useMemo as useMemo3, useState as useState13, useRef as useRef6 } from "react";
1207
1281
  var globalBatchImageConfig = {};
1208
1282
  function configureBatchImageFetch(config) {
1209
1283
  globalBatchImageConfig = { ...globalBatchImageConfig, ...config };
@@ -1283,10 +1357,10 @@ function useBatchImages(imageUrls, config) {
1283
1357
  );
1284
1358
  const requestedUrls = useRef6(/* @__PURE__ */ new Set());
1285
1359
  const fetchedImagesRef = useRef6(fetchedImages);
1286
- useEffect12(() => {
1360
+ useEffect13(() => {
1287
1361
  fetchedImagesRef.current = fetchedImages;
1288
1362
  }, [fetchedImages]);
1289
- useEffect12(
1363
+ useEffect13(
1290
1364
  () => () => {
1291
1365
  Object.values(fetchedImagesRef.current).forEach((blobUrl) => {
1292
1366
  if (blobUrl && blobUrl.startsWith("blob:")) {
@@ -1296,7 +1370,7 @@ function useBatchImages(imageUrls, config) {
1296
1370
  },
1297
1371
  []
1298
1372
  );
1299
- useEffect12(() => {
1373
+ useEffect13(() => {
1300
1374
  const urlsToFetch = uniqueUrls.filter((url) => !requestedUrls.current.has(url));
1301
1375
  if (urlsToFetch.length === 0) {
1302
1376
  return;
@@ -1314,7 +1388,7 @@ function useBatchImages(imageUrls, config) {
1314
1388
  }
1315
1389
 
1316
1390
  // src/hooks/use-authenticated-image.ts
1317
- import { useEffect as useEffect13, useState as useState14, useRef as useRef7 } from "react";
1391
+ import { useEffect as useEffect14, useState as useState14, useRef as useRef7 } from "react";
1318
1392
  var globalImageConfig = {};
1319
1393
  var imageCache = /* @__PURE__ */ new Map();
1320
1394
  var pendingRequests = /* @__PURE__ */ new Map();
@@ -1362,7 +1436,7 @@ function useAuthenticatedImage(imageUrl, refreshKey, config) {
1362
1436
  }
1363
1437
  }
1364
1438
  const cacheKey = fullImageUrl === null ? null : refreshKey ? `${fullImageUrl}?v=${refreshKey}` : fullImageUrl;
1365
- useEffect13(() => {
1439
+ useEffect14(() => {
1366
1440
  if (cacheKey === null) {
1367
1441
  if (currentCacheKeyRef.current) {
1368
1442
  const entry = imageCache.get(currentCacheKeyRef.current);
@@ -1439,7 +1513,7 @@ function useAuthenticatedImage(imageUrl, refreshKey, config) {
1439
1513
  });
1440
1514
  pendingRequests.set(cacheKey, fetchPromise2);
1441
1515
  }, [cacheKey, fullImageUrl, refreshKey, enableDevMode, accessTokenKey]);
1442
- useEffect13(() => {
1516
+ useEffect14(() => {
1443
1517
  return () => {
1444
1518
  if (currentCacheKeyRef.current) {
1445
1519
  const entry = imageCache.get(currentCacheKeyRef.current);
@@ -1460,7 +1534,7 @@ function useAuthenticatedImage(imageUrl, refreshKey, config) {
1460
1534
  }
1461
1535
 
1462
1536
  // src/hooks/state/use-query-params.ts
1463
- import { useEffect as useEffect14, useState as useState15, useMemo as useMemo4, useCallback as useCallback10, useRef as useRef8 } from "react";
1537
+ import { useEffect as useEffect15, useState as useState15, useMemo as useMemo4, useCallback as useCallback11, useRef as useRef8 } from "react";
1464
1538
 
1465
1539
  // src/hooks/state/flatten-schema.ts
1466
1540
  function flattenQueryVariables(queryVariables, introspector2) {
@@ -1923,14 +1997,14 @@ function useQueryParams(query, options = {}) {
1923
1997
  paramMapping: options.paramMapping,
1924
1998
  defaultValues
1925
1999
  });
1926
- useEffect14(() => {
2000
+ useEffect15(() => {
1927
2001
  initSourceRef.current = {
1928
2002
  introspectionHeaders: options.introspectionHeaders,
1929
2003
  paramMapping: options.paramMapping,
1930
2004
  defaultValues
1931
2005
  };
1932
2006
  });
1933
- useEffect14(() => {
2007
+ useEffect15(() => {
1934
2008
  let cancelled = false;
1935
2009
  async function initialize() {
1936
2010
  try {
@@ -2007,7 +2081,7 @@ function useQueryParams(query, options = {}) {
2007
2081
  });
2008
2082
  return result;
2009
2083
  }, [searchParams]);
2010
- const updateUrl = useCallback10(
2084
+ const updateUrl = useCallback11(
2011
2085
  (newParams) => {
2012
2086
  const url = newParams.toString() ? `?${newParams.toString()}` : window.location.pathname;
2013
2087
  if (debug) {
@@ -2017,7 +2091,7 @@ function useQueryParams(query, options = {}) {
2017
2091
  },
2018
2092
  [router, debug]
2019
2093
  );
2020
- const setParam = useCallback10(
2094
+ const setParam = useCallback11(
2021
2095
  (key, value) => {
2022
2096
  if (!isReady) {
2023
2097
  console.warn("[useQueryParams] Schema not ready, cannot set param");
@@ -2033,7 +2107,7 @@ function useQueryParams(query, options = {}) {
2033
2107
  },
2034
2108
  [variables, schema, isReady, updateUrl]
2035
2109
  );
2036
- const setParams = useCallback10(
2110
+ const setParams = useCallback11(
2037
2111
  (updates) => {
2038
2112
  if (!isReady) {
2039
2113
  console.warn("[useQueryParams] Schema not ready, cannot set params");
@@ -2049,7 +2123,7 @@ function useQueryParams(query, options = {}) {
2049
2123
  },
2050
2124
  [variables, schema, isReady, updateUrl]
2051
2125
  );
2052
- const clearParamsHandler = useCallback10(
2126
+ const clearParamsHandler = useCallback11(
2053
2127
  (keys) => {
2054
2128
  if (!isReady) {
2055
2129
  console.warn("[useQueryParams] Schema not ready, cannot clear params");
@@ -2065,7 +2139,7 @@ function useQueryParams(query, options = {}) {
2065
2139
  },
2066
2140
  [variables, schema, isReady, updateUrl]
2067
2141
  );
2068
- const resetParams = useCallback10(() => {
2142
+ const resetParams = useCallback11(() => {
2069
2143
  if (debug) {
2070
2144
  console.log("[useQueryParams] Resetting params");
2071
2145
  }
@@ -2100,7 +2174,7 @@ function applyParamMapping(schema, mapping) {
2100
2174
  }
2101
2175
 
2102
2176
  // src/hooks/state/use-api-params.ts
2103
- import { useCallback as useCallback11, useEffect as useEffect15, useMemo as useMemo5, useRef as useRef9, useState as useState16 } from "react";
2177
+ import { useCallback as useCallback12, useEffect as useEffect16, useMemo as useMemo5, useRef as useRef9, useState as useState16 } from "react";
2104
2178
  function useContentStable(value, key) {
2105
2179
  const [cached, setCached] = useState16({ value, key });
2106
2180
  if (cached.key !== key) setCached({ value, key });
@@ -2131,7 +2205,7 @@ function useApiParams(schema, options = {}) {
2131
2205
  const searchString = searchParamsLive.toString();
2132
2206
  const pendingRef = useRef9([]);
2133
2207
  const [pendingTail, setPendingTail] = useState16(void 0);
2134
- const syncPendingTail = useCallback11(() => {
2208
+ const syncPendingTail = useCallback12(() => {
2135
2209
  setPendingTail(pendingRef.current[pendingRef.current.length - 1]);
2136
2210
  }, []);
2137
2211
  const schemaKey = useMemo5(() => JSON.stringify(schema), [schema]);
@@ -2166,7 +2240,7 @@ function useApiParams(schema, options = {}) {
2166
2240
  setParamsCache({ src: rawParams, value: stableParams });
2167
2241
  }
2168
2242
  const params = stableParams;
2169
- const addParamToSearchParams = useCallback11((searchParams, key, value) => {
2243
+ const addParamToSearchParams = useCallback12((searchParams, key, value) => {
2170
2244
  for (const [k, v] of createSearchParams({ [key]: value })) searchParams.append(k, v);
2171
2245
  }, []);
2172
2246
  const urlSearchParams = useMemo5(() => {
@@ -2181,7 +2255,7 @@ function useApiParams(schema, options = {}) {
2181
2255
  }
2182
2256
  return newParams;
2183
2257
  }, [params, flattenedSchema, addParamToSearchParams, stableSchema]);
2184
- const updateUrl = useCallback11(
2258
+ const updateUrl = useCallback12(
2185
2259
  (newParams, keysToRemove = []) => {
2186
2260
  const base = pendingRef.current[pendingRef.current.length - 1] ?? searchString;
2187
2261
  const finalParams = new URLSearchParams(base);
@@ -2216,14 +2290,14 @@ function useApiParams(schema, options = {}) {
2216
2290
  },
2217
2291
  [router, debug, searchString, stableSchema, syncPendingTail]
2218
2292
  );
2219
- useEffect15(() => {
2293
+ useEffect16(() => {
2220
2294
  const queue = pendingRef.current;
2221
2295
  if (queue.length === 0) return;
2222
2296
  const hit = queue.indexOf(searchString);
2223
2297
  pendingRef.current = hit === -1 ? [] : queue.slice(hit + 1);
2224
2298
  syncPendingTail();
2225
2299
  }, [searchString, syncPendingTail]);
2226
- const setParam = useCallback11(
2300
+ const setParam = useCallback12(
2227
2301
  (key, value) => {
2228
2302
  const config = stableSchema[key];
2229
2303
  if (!config) {
@@ -2240,7 +2314,7 @@ function useApiParams(schema, options = {}) {
2240
2314
  },
2241
2315
  [updateUrl, addParamToSearchParams, stableSchema]
2242
2316
  );
2243
- const setParams = useCallback11(
2317
+ const setParams = useCallback12(
2244
2318
  (updates) => {
2245
2319
  const newParams = new URLSearchParams();
2246
2320
  const keysToRemove = [];
@@ -2260,14 +2334,14 @@ function useApiParams(schema, options = {}) {
2260
2334
  },
2261
2335
  [updateUrl, addParamToSearchParams, stableSchema, flattenedSchema]
2262
2336
  );
2263
- const clearParams2 = useCallback11(
2337
+ const clearParams2 = useCallback12(
2264
2338
  (keys) => {
2265
2339
  const newParams = new URLSearchParams();
2266
2340
  updateUrl(newParams, keys);
2267
2341
  },
2268
2342
  [updateUrl]
2269
2343
  );
2270
- const resetParams = useCallback11(() => {
2344
+ const resetParams = useCallback12(() => {
2271
2345
  if (debug) {
2272
2346
  console.log("[useApiParams] Resetting params");
2273
2347
  }
@@ -2291,7 +2365,7 @@ function useApiParams(schema, options = {}) {
2291
2365
  }
2292
2366
 
2293
2367
  // src/hooks/state/use-cursor-pagination-state.ts
2294
- import { useCallback as useCallback12, useEffect as useEffect16, useRef as useRef10, useState as useState17 } from "react";
2368
+ import { useCallback as useCallback13, useEffect as useEffect17, useRef as useRef10, useState as useState17 } from "react";
2295
2369
  var urlSchema = {
2296
2370
  search: { type: "string", default: "" },
2297
2371
  cursor: { type: "string", default: "" }
@@ -2308,12 +2382,12 @@ function useCursorPaginationState(options) {
2308
2382
  const searchInputRef = useRef10(searchInput);
2309
2383
  const onInitialLoadRef = useRef10(onInitialLoad);
2310
2384
  const paramsRef = useRef10(params);
2311
- useEffect16(() => {
2385
+ useEffect17(() => {
2312
2386
  searchInputRef.current = searchInput;
2313
2387
  onInitialLoadRef.current = onInitialLoad;
2314
2388
  paramsRef.current = params;
2315
2389
  });
2316
- useEffect16(() => {
2390
+ useEffect17(() => {
2317
2391
  if (isInitialLoadInProgress.current) return;
2318
2392
  const urlSearch = params.search || "";
2319
2393
  if (urlSearch !== searchInputRef.current) {
@@ -2324,7 +2398,7 @@ function useCursorPaginationState(options) {
2324
2398
  }, 0);
2325
2399
  }
2326
2400
  }, [params.search, initialLoadCount]);
2327
- useEffect16(() => {
2401
+ useEffect17(() => {
2328
2402
  if (isInitialLoadInProgress.current) return;
2329
2403
  if (isSyncingFromUrl.current) return;
2330
2404
  if (searchInput !== params.search) {
@@ -2335,7 +2409,7 @@ function useCursorPaginationState(options) {
2335
2409
  });
2336
2410
  }
2337
2411
  }, [searchInput, params.search, setParams, initialLoadCount]);
2338
- useEffect16(() => {
2412
+ useEffect17(() => {
2339
2413
  if (initialLoadCount === 0) {
2340
2414
  const cursor = paramsRef.current.cursor || null;
2341
2415
  const search = paramsRef.current.search || "";
@@ -2351,7 +2425,7 @@ function useCursorPaginationState(options) {
2351
2425
  });
2352
2426
  }
2353
2427
  }, [initialLoadCount]);
2354
- useEffect16(() => {
2428
+ useEffect17(() => {
2355
2429
  if (isInitialLoadInProgress.current) return;
2356
2430
  if (initialLoadCount === 0) return;
2357
2431
  const currentSearch = params.search || "";
@@ -2363,7 +2437,7 @@ function useCursorPaginationState(options) {
2363
2437
  });
2364
2438
  }
2365
2439
  }, [params.search, onSearchChange, initialLoadCount]);
2366
- const handleNextPage = useCallback12(
2440
+ const handleNextPage = useCallback13(
2367
2441
  async (endCursor, fetchFn) => {
2368
2442
  setParam("cursor", endCursor);
2369
2443
  await fetchFn();
@@ -2371,7 +2445,7 @@ function useCursorPaginationState(options) {
2371
2445
  },
2372
2446
  [setParam]
2373
2447
  );
2374
- const handleResetToFirstPage = useCallback12(
2448
+ const handleResetToFirstPage = useCallback13(
2375
2449
  async (fetchFn) => {
2376
2450
  setParam("cursor", "");
2377
2451
  await fetchFn();
@@ -2393,7 +2467,7 @@ function useCursorPaginationState(options) {
2393
2467
  }
2394
2468
 
2395
2469
  // src/hooks/nats/use-nats-client.ts
2396
- import { useEffect as useEffect17, useMemo as useMemo6, useState as useState18 } from "react";
2470
+ import { useEffect as useEffect18, useMemo as useMemo6, useState as useState18 } from "react";
2397
2471
  function useNatsClient(clientOptions, options = {}) {
2398
2472
  const { autoConnect = true } = options;
2399
2473
  const client = useMemo6(() => {
@@ -2408,7 +2482,7 @@ function useNatsClient(clientOptions, options = {}) {
2408
2482
  setStatus("disconnected");
2409
2483
  setLastError(null);
2410
2484
  }
2411
- useEffect17(() => {
2485
+ useEffect18(() => {
2412
2486
  if (!client) return void 0;
2413
2487
  const off = client.onStatus((event) => {
2414
2488
  setStatus(event.status);
@@ -2435,7 +2509,7 @@ function useNatsClient(clientOptions, options = {}) {
2435
2509
  }
2436
2510
 
2437
2511
  // src/hooks/use-scenario-player.ts
2438
- import { useCallback as useCallback13, useEffect as useEffect18, useRef as useRef11, useState as useState19 } from "react";
2512
+ import { useCallback as useCallback14, useEffect as useEffect19, useRef as useRef11, useState as useState19 } from "react";
2439
2513
  var START_MS = 300;
2440
2514
  function useScenarioPlayer({
2441
2515
  scenarioCount,
@@ -2455,7 +2529,7 @@ function useScenarioPlayer({
2455
2529
  const playing = auto.playing && scenarioCount > 0;
2456
2530
  const starting = useRef11(false);
2457
2531
  const wasPlaying = useRef11(false);
2458
- useEffect18(() => {
2532
+ useEffect19(() => {
2459
2533
  if (playing && !wasPlaying.current) starting.current = true;
2460
2534
  wasPlaying.current = playing;
2461
2535
  if (!playing) return void 0;
@@ -2475,14 +2549,14 @@ function useScenarioPlayer({
2475
2549
  );
2476
2550
  return () => clearTimeout(timer);
2477
2551
  }, [playing, rawStep, lastStep, stepMs, holdMs, scenario, scenarioCount]);
2478
- const setStep = useCallback13(
2552
+ const setStep = useCallback14(
2479
2553
  (next) => {
2480
2554
  setRawStep(Math.max(0, Math.min(lastStep, next)));
2481
2555
  setPaused(false);
2482
2556
  },
2483
2557
  [lastStep, setPaused]
2484
2558
  );
2485
- const go = useCallback13(
2559
+ const go = useCallback14(
2486
2560
  (next) => {
2487
2561
  setScenario(next);
2488
2562
  setRawStep(0);
@@ -2537,7 +2611,7 @@ function useAccessCodeIntegration() {
2537
2611
  }
2538
2612
 
2539
2613
  // src/hooks/use-claude-mirror-src.ts
2540
- import { useEffect as useEffect19, useState as useState20 } from "react";
2614
+ import { useEffect as useEffect20, useState as useState20 } from "react";
2541
2615
  async function revalidateMirror(mirrorPath) {
2542
2616
  try {
2543
2617
  const res = await fetch(`${mirrorPath}?revalidate=1`);
@@ -2553,7 +2627,7 @@ function useClaudeMirrorSrc(url) {
2553
2627
  const mirrorPath = toClaudeMirrorPath(url, endpoints?.storageViewBaseUrl);
2554
2628
  const [resolved, setResolved] = useState20(null);
2555
2629
  const state = resolved && resolved.key === mirrorPath ? resolved.value : { src: null, status: mirrorPath ? "probing" : "absent" };
2556
- useEffect19(() => {
2630
+ useEffect20(() => {
2557
2631
  if (!mirrorPath) return void 0;
2558
2632
  let cancelled = false;
2559
2633
  const settle = (value) => {
@@ -2580,7 +2654,7 @@ function useClaudeMirrorSrc(url) {
2580
2654
  }
2581
2655
 
2582
2656
  // src/hooks/use-location-hash.ts
2583
- import { useSyncExternalStore } from "react";
2657
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
2584
2658
  var HASH_EVENTS = ["hashchange", "popstate", LOCATION_HASH_SYNC_EVENT];
2585
2659
  function subscribe(onChange) {
2586
2660
  for (const event of HASH_EVENTS) window.addEventListener(event, onChange);
@@ -2597,15 +2671,15 @@ function readHash() {
2597
2671
  }
2598
2672
  }
2599
2673
  function useLocationHash() {
2600
- return useSyncExternalStore(subscribe, readHash, () => "");
2674
+ return useSyncExternalStore2(subscribe, readHash, () => "");
2601
2675
  }
2602
2676
 
2603
2677
  // src/hooks/use-scroll-to-hash.ts
2604
- import { useEffect as useEffect20 } from "react";
2678
+ import { useEffect as useEffect21 } from "react";
2605
2679
  var MAX_POLL_FRAMES = 60;
2606
2680
  function useScrollToHash(readyDep = true, options) {
2607
2681
  const headerOffset = options?.headerOffset ?? 0;
2608
- useEffect20(() => {
2682
+ useEffect21(() => {
2609
2683
  if (typeof window === "undefined") return void 0;
2610
2684
  if (readyDep === null || readyDep === false) return void 0;
2611
2685
  let rafId = null;
@@ -2646,19 +2720,19 @@ function useScrollToHash(readyDep = true, options) {
2646
2720
  }
2647
2721
 
2648
2722
  // src/hooks/use-humanity-signals.ts
2649
- import { useCallback as useCallback14, useRef as useRef12, useState as useState21 } from "react";
2723
+ import { useCallback as useCallback15, useRef as useRef12, useState as useState21 } from "react";
2650
2724
  function useHumanitySignals() {
2651
2725
  const ref = useRef12(null);
2652
2726
  const [mountedAt] = useState21(() => typeof performance !== "undefined" ? performance.now() : 0);
2653
2727
  const startedAtRef = useRef12(mountedAt);
2654
- const getSignals = useCallback14(
2728
+ const getSignals = useCallback15(
2655
2729
  () => ({
2656
2730
  [HONEYPOT_FIELD]: ref.current?.value ?? "",
2657
2731
  [ELAPSED_MS_FIELD]: typeof performance !== "undefined" ? Math.round(performance.now() - startedAtRef.current) : 0
2658
2732
  }),
2659
2733
  []
2660
2734
  );
2661
- const resetSignals = useCallback14(() => {
2735
+ const resetSignals = useCallback15(() => {
2662
2736
  if (ref.current) ref.current.value = "";
2663
2737
  if (typeof performance !== "undefined") startedAtRef.current = performance.now();
2664
2738
  }, []);
@@ -2666,7 +2740,7 @@ function useHumanitySignals() {
2666
2740
  }
2667
2741
 
2668
2742
  // src/hooks/use-form-rescue.ts
2669
- import { useCallback as useCallback15, useEffect as useEffect21, useMemo as useMemo7, useRef as useRef13 } from "react";
2743
+ import { useCallback as useCallback16, useEffect as useEffect22, useMemo as useMemo7, useRef as useRef13 } from "react";
2670
2744
  var LOCAL_DRAFT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
2671
2745
  var STORAGE_PREFIX = "form-rescue:v1:";
2672
2746
  var UTM_KEYS = ["source", "medium", "campaign", "content", "term"];
@@ -2739,16 +2813,16 @@ function useFormRescue({ form, fieldNames, onRestore, getSignals }) {
2739
2813
  const fieldNamesRef = useRef13(fieldNames);
2740
2814
  const onRestoreRef = useRef13(onRestore);
2741
2815
  const getSignalsRef = useRef13(getSignals);
2742
- useEffect21(() => {
2816
+ useEffect22(() => {
2743
2817
  fieldNamesRef.current = fieldNames;
2744
2818
  onRestoreRef.current = onRestore;
2745
2819
  getSignalsRef.current = getSignals;
2746
2820
  });
2747
- const attemptId = useCallback15(() => {
2821
+ const attemptId = useCallback16(() => {
2748
2822
  if (!attemptIdRef.current) attemptIdRef.current = newAttemptId();
2749
2823
  return attemptIdRef.current;
2750
2824
  }, []);
2751
- const send = useCallback15(
2825
+ const send = useCallback16(
2752
2826
  (keepalive) => {
2753
2827
  if (!formId || !draftsUrl || !dirtyRef.current || completedRef.current || !latestRef.current) return;
2754
2828
  dirtyRef.current = false;
@@ -2779,7 +2853,7 @@ function useFormRescue({ form, fieldNames, onRestore, getSignals }) {
2779
2853
  },
2780
2854
  [attemptId, draftsUrl, formId]
2781
2855
  );
2782
- useEffect21(() => {
2856
+ useEffect22(() => {
2783
2857
  if (!formId || typeof window === "undefined") return void 0;
2784
2858
  let cancelled = false;
2785
2859
  const token = new URLSearchParams(window.location.search).get(FORM_RESCUE_RESUME_PARAM);
@@ -2809,7 +2883,7 @@ function useFormRescue({ form, fieldNames, onRestore, getSignals }) {
2809
2883
  cancelled = true;
2810
2884
  };
2811
2885
  }, [attemptId, draftsUrl, formId]);
2812
- useEffect21(() => {
2886
+ useEffect22(() => {
2813
2887
  if (!formId || typeof document === "undefined") return void 0;
2814
2888
  const onHide = () => {
2815
2889
  if (completedRef.current || !startedRef.current) return;
@@ -2836,7 +2910,7 @@ function useFormRescue({ form, fieldNames, onRestore, getSignals }) {
2836
2910
  if (timerRef.current) clearTimeout(timerRef.current);
2837
2911
  };
2838
2912
  }, [attemptId, formId, send]);
2839
- const track = useCallback15(
2913
+ const track = useCallback16(
2840
2914
  (values, lastField) => {
2841
2915
  if (!formId || completedRef.current) return;
2842
2916
  const names = fieldNamesRef.current;
@@ -2869,14 +2943,14 @@ function useFormRescue({ form, fieldNames, onRestore, getSignals }) {
2869
2943
  },
2870
2944
  [attemptId, formId, send]
2871
2945
  );
2872
- const submitFields = useCallback15(() => {
2946
+ const submitFields = useCallback16(() => {
2873
2947
  if (!formId) return {};
2874
2948
  return {
2875
2949
  [FORM_RESCUE_ATTEMPT_FIELD]: attemptId(),
2876
2950
  ...resumeTokenRef.current ? { [FORM_RESCUE_RESUME_FIELD]: resumeTokenRef.current } : {}
2877
2951
  };
2878
2952
  }, [attemptId, formId]);
2879
- const complete = useCallback15(() => {
2953
+ const complete = useCallback16(() => {
2880
2954
  if (!formId) return;
2881
2955
  if (timerRef.current) {
2882
2956
  clearTimeout(timerRef.current);
@@ -2894,7 +2968,7 @@ function useFormRescue({ form, fieldNames, onRestore, getSignals }) {
2894
2968
  }
2895
2969
 
2896
2970
  // src/hooks/use-rescued-form.ts
2897
- import { useEffect as useEffect22 } from "react";
2971
+ import { useEffect as useEffect23 } from "react";
2898
2972
  function flattenOneLevel(values) {
2899
2973
  const flat = {};
2900
2974
  for (const [key, value] of Object.entries(values)) {
@@ -2918,7 +2992,7 @@ function useRescuedForm(form, { rescue, fields, getSignals, fieldPath }) {
2918
2992
  }
2919
2993
  }
2920
2994
  });
2921
- useEffect22(() => {
2995
+ useEffect23(() => {
2922
2996
  const subscription = watch((values, { name }) => {
2923
2997
  handle.track(flattenOneLevel(values), name ? name.split(".").pop() ?? null : null);
2924
2998
  });
@@ -2956,6 +3030,9 @@ export {
2956
3030
  useToast,
2957
3031
  useContactSubmission,
2958
3032
  useCopyToClipboard,
3033
+ resetDownloadsStore,
3034
+ useDownloads,
3035
+ useVisitorDesktopDownload,
2959
3036
  configureBatchImageFetch,
2960
3037
  batchFetchAuthenticatedImages,
2961
3038
  useBatchImages,
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  Button
4
- } from "./chunk-YDPKUAP4.js";
4
+ } from "./chunk-DJ4IFQNQ.js";
5
5
  import {
6
6
  cn
7
7
  } from "./chunk-7RXWKTAP.js";
@@ -429,6 +429,21 @@ function getPlatformColorClass(platformName) {
429
429
  return getPlatformBrandClasses(platformName).accentBg;
430
430
  }
431
431
 
432
+ // src/types/downloads.ts
433
+ var DOWNLOADS_API_PATH = "/api/downloads";
434
+ var DOWNLOADS_CACHE_SECONDS = 300;
435
+ var DOWNLOADS_TITLE = "Download OpenFrame";
436
+ var DOWNLOADS_TAGLINE = "Install it on the computer you manage IT from, and on your phone. One account everywhere.";
437
+ var DOWNLOADS_MOBILE_TITLE = "Get the OpenFrame app";
438
+ var DOWNLOADS_MOBILE_TAGLINE = "Alerts and tickets on your phone, signed in with the account you already have.";
439
+ var APP_DOWNLOAD_KINDS = ["binary", "command"];
440
+ var DESKTOP_OS_LABELS = { mac: "Mac", windows: "Windows", linux: "Linux" };
441
+ var DOWNLOAD_ARCHITECTURE_LABELS = {
442
+ universal: "Apple Silicon and Intel",
443
+ x64: "x64",
444
+ arm64: "ARM"
445
+ };
446
+
432
447
  // src/utils/access-code-client.ts
433
448
  async function validateAccessCode(email, code, endpoints) {
434
449
  try {
@@ -1138,6 +1153,15 @@ export {
1138
1153
  getPlatformIcon,
1139
1154
  getSmallPlatformIcon,
1140
1155
  getPlatformIconComponent,
1156
+ DOWNLOADS_API_PATH,
1157
+ DOWNLOADS_CACHE_SECONDS,
1158
+ DOWNLOADS_TITLE,
1159
+ DOWNLOADS_TAGLINE,
1160
+ DOWNLOADS_MOBILE_TITLE,
1161
+ DOWNLOADS_MOBILE_TAGLINE,
1162
+ APP_DOWNLOAD_KINDS,
1163
+ DESKTOP_OS_LABELS,
1164
+ DOWNLOAD_ARCHITECTURE_LABELS,
1141
1165
  validateAccessCode,
1142
1166
  consumeAccessCode,
1143
1167
  validateAndConsumeAccessCode,