@flamingo-stack/openframe-frontend-core 0.0.296 → 0.0.297

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 (276) hide show
  1. package/README.md +9 -0
  2. package/dist/{chunk-7RIYT7ZH.js → chunk-2QG57XOJ.js} +1067 -205
  3. package/dist/chunk-2QG57XOJ.js.map +1 -0
  4. package/dist/{chunk-WHMATDVP.js → chunk-3JIQVE7T.js} +9 -15
  5. package/dist/{chunk-WHMATDVP.js.map → chunk-3JIQVE7T.js.map} +1 -1
  6. package/dist/{chunk-GLLDTKZK.cjs → chunk-4PSQS3SW.cjs} +7 -9
  7. package/dist/chunk-4PSQS3SW.cjs.map +1 -0
  8. package/dist/{chunk-OY7OF7E7.js → chunk-4TLE6VLU.js} +30 -24
  9. package/dist/chunk-4TLE6VLU.js.map +1 -0
  10. package/dist/{chunk-W6M2FLLT.cjs → chunk-53FUMSZ5.cjs} +40 -46
  11. package/dist/chunk-53FUMSZ5.cjs.map +1 -0
  12. package/dist/{chunk-D3LEFMOA.cjs → chunk-54KNMC2R.cjs} +3 -3
  13. package/dist/{chunk-D3LEFMOA.cjs.map → chunk-54KNMC2R.cjs.map} +1 -1
  14. package/dist/{chunk-EYEW6PTA.cjs → chunk-6C526VNN.cjs} +358 -118
  15. package/dist/chunk-6C526VNN.cjs.map +1 -0
  16. package/dist/{chunk-XREEV72C.cjs → chunk-7OVGB2DQ.cjs} +19 -25
  17. package/dist/chunk-7OVGB2DQ.cjs.map +1 -0
  18. package/dist/{chunk-6GCI7JOE.js → chunk-AD6C23QY.js} +8 -7
  19. package/dist/{chunk-6GCI7JOE.js.map → chunk-AD6C23QY.js.map} +1 -1
  20. package/dist/chunk-F5OB2YAL.cjs +144 -0
  21. package/dist/chunk-F5OB2YAL.cjs.map +1 -0
  22. package/dist/chunk-FBWXMMRB.cjs +2 -0
  23. package/dist/chunk-FBWXMMRB.cjs.map +1 -0
  24. package/dist/{chunk-YIGPRLQY.cjs → chunk-FCDQNTDG.cjs} +21 -20
  25. package/dist/chunk-FCDQNTDG.cjs.map +1 -0
  26. package/dist/{chunk-IE6OU3WQ.cjs → chunk-FQOTC3UU.cjs} +318 -16
  27. package/dist/chunk-FQOTC3UU.cjs.map +1 -0
  28. package/dist/{chunk-QHIXS3W2.cjs → chunk-GUTS7HGA.cjs} +11590 -2105
  29. package/dist/chunk-GUTS7HGA.cjs.map +1 -0
  30. package/dist/chunk-GZ4C3XW6.js +2 -0
  31. package/dist/chunk-GZ4C3XW6.js.map +1 -0
  32. package/dist/{chunk-5P3B2LZW.js → chunk-IL47XWV5.js} +8 -14
  33. package/dist/{chunk-5P3B2LZW.js.map → chunk-IL47XWV5.js.map} +1 -1
  34. package/dist/{chunk-LCNMR277.js → chunk-IZ7JSBFP.js} +1 -1
  35. package/dist/chunk-IZ7JSBFP.js.map +1 -0
  36. package/dist/{chunk-EL6QLAWX.js → chunk-JALO4TAZ.js} +357 -55
  37. package/dist/chunk-JALO4TAZ.js.map +1 -0
  38. package/dist/{chunk-AQOWFSMB.cjs → chunk-L6PSSIUQ.cjs} +1 -1
  39. package/dist/chunk-L6PSSIUQ.cjs.map +1 -0
  40. package/dist/{chunk-MBFWU2EM.js → chunk-L7ULJKG7.js} +6 -10
  41. package/dist/{chunk-MBFWU2EM.js.map → chunk-L7ULJKG7.js.map} +1 -1
  42. package/dist/{chunk-K2PFPBMF.js → chunk-PC746XCO.js} +15050 -5565
  43. package/dist/chunk-PC746XCO.js.map +1 -0
  44. package/dist/{chunk-3ZXUQQL4.js → chunk-PI4WSYQV.js} +2 -2
  45. package/dist/{chunk-E4XABBSU.js → chunk-PWQUAVA3.js} +338 -98
  46. package/dist/chunk-PWQUAVA3.js.map +1 -0
  47. package/dist/chunk-SA2WPJVO.js +144 -0
  48. package/dist/chunk-SA2WPJVO.js.map +1 -0
  49. package/dist/{chunk-X6BV7MB7.cjs → chunk-UNVE2SDJ.cjs} +37 -31
  50. package/dist/chunk-UNVE2SDJ.cjs.map +1 -0
  51. package/dist/{chunk-5E2HOSSH.cjs → chunk-WMSTJAZT.cjs} +913 -51
  52. package/dist/chunk-WMSTJAZT.cjs.map +1 -0
  53. package/dist/{chunk-ZP4AVIZP.js → chunk-X4DOXQRT.js} +4 -6
  54. package/dist/{chunk-ZP4AVIZP.js.map → chunk-X4DOXQRT.js.map} +1 -1
  55. package/dist/{chunk-X647HY3F.cjs → chunk-YBYI62OE.cjs} +33 -37
  56. package/dist/chunk-YBYI62OE.cjs.map +1 -0
  57. package/dist/components/case-studies/index.cjs +126 -0
  58. package/dist/components/case-studies/index.cjs.map +1 -0
  59. package/dist/components/case-studies/index.d.ts +2 -0
  60. package/dist/components/case-studies/index.d.ts.map +1 -0
  61. package/dist/components/case-studies/index.js +126 -0
  62. package/dist/components/case-studies/index.js.map +1 -0
  63. package/dist/components/case-studies/share-experience-section.d.ts +48 -0
  64. package/dist/components/case-studies/share-experience-section.d.ts.map +1 -0
  65. package/dist/components/chat/index.cjs +8 -18
  66. package/dist/components/chat/index.cjs.map +1 -1
  67. package/dist/components/chat/index.js +75 -85
  68. package/dist/components/contact/index.cjs +8 -15
  69. package/dist/components/contact/index.cjs.map +1 -1
  70. package/dist/components/contact/index.js +7 -14
  71. package/dist/components/docs/doc-viewer.d.ts +39 -2
  72. package/dist/components/docs/doc-viewer.d.ts.map +1 -1
  73. package/dist/components/docs/docs-hub-page.d.ts +46 -0
  74. package/dist/components/docs/docs-hub-page.d.ts.map +1 -0
  75. package/dist/components/docs/index.cjs +17 -9
  76. package/dist/components/docs/index.cjs.map +1 -1
  77. package/dist/components/docs/index.d.ts +4 -0
  78. package/dist/components/docs/index.d.ts.map +1 -1
  79. package/dist/components/docs/index.js +16 -8
  80. package/dist/components/docs/skeletons.d.ts +32 -0
  81. package/dist/components/docs/skeletons.d.ts.map +1 -0
  82. package/dist/components/docs/use-docs-resolve-link.d.ts +20 -0
  83. package/dist/components/docs/use-docs-resolve-link.d.ts.map +1 -0
  84. package/dist/components/docs/use-document-tree.d.ts.map +1 -1
  85. package/dist/components/embeds/embed-container.d.ts +37 -0
  86. package/dist/components/embeds/embed-container.d.ts.map +1 -0
  87. package/dist/components/embeds/embed-iframe.d.ts.map +1 -1
  88. package/dist/components/embeds/file-download-card.d.ts +18 -0
  89. package/dist/components/embeds/file-download-card.d.ts.map +1 -0
  90. package/dist/components/embeds/index.cjs +38 -15
  91. package/dist/components/embeds/index.cjs.map +1 -1
  92. package/dist/components/embeds/index.d.ts +8 -0
  93. package/dist/components/embeds/index.d.ts.map +1 -1
  94. package/dist/components/embeds/index.js +40 -17
  95. package/dist/components/embeds/linkedin-embed-client.d.ts +8 -0
  96. package/dist/components/embeds/linkedin-embed-client.d.ts.map +1 -0
  97. package/dist/components/embeds/markdown-image.d.ts +5 -0
  98. package/dist/components/embeds/markdown-image.d.ts.map +1 -0
  99. package/dist/components/embeds/reddit-embed-client.d.ts +7 -0
  100. package/dist/components/embeds/reddit-embed-client.d.ts.map +1 -0
  101. package/dist/components/embeds/rich-markdown-runtime.d.ts +46 -0
  102. package/dist/components/embeds/rich-markdown-runtime.d.ts.map +1 -0
  103. package/dist/components/embeds/twitter-embed-client.d.ts +8 -0
  104. package/dist/components/embeds/twitter-embed-client.d.ts.map +1 -0
  105. package/dist/components/faq/index.cjs +9 -16
  106. package/dist/components/faq/index.cjs.map +1 -1
  107. package/dist/components/faq/index.js +8 -15
  108. package/dist/components/features/index.cjs +8 -16
  109. package/dist/components/features/index.cjs.map +1 -1
  110. package/dist/components/features/index.js +24 -32
  111. package/dist/components/index.cjs +257 -452
  112. package/dist/components/index.cjs.map +1 -1
  113. package/dist/components/index.js +781 -976
  114. package/dist/components/index.js.map +1 -1
  115. package/dist/components/layout/page-header.d.ts +78 -0
  116. package/dist/components/layout/page-header.d.ts.map +1 -0
  117. package/dist/components/layout/page-layout.d.ts +10 -1
  118. package/dist/components/layout/page-layout.d.ts.map +1 -1
  119. package/dist/components/layout/page-with-header.d.ts +67 -0
  120. package/dist/components/layout/page-with-header.d.ts.map +1 -0
  121. package/dist/components/layout/title-block.d.ts +17 -1
  122. package/dist/components/layout/title-block.d.ts.map +1 -1
  123. package/dist/components/navigation/index.cjs +7 -15
  124. package/dist/components/navigation/index.cjs.map +1 -1
  125. package/dist/components/navigation/index.js +9 -17
  126. package/dist/components/onboarding-guides/index.cjs +35 -36
  127. package/dist/components/onboarding-guides/index.cjs.map +1 -1
  128. package/dist/components/onboarding-guides/index.js +13 -14
  129. package/dist/components/onboarding-guides/index.js.map +1 -1
  130. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts +1 -1
  131. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  132. package/dist/components/related-content/index.cjs +9 -16
  133. package/dist/components/related-content/index.cjs.map +1 -1
  134. package/dist/components/related-content/index.js +8 -15
  135. package/dist/components/shared/dev-section/dev-section-page.d.ts +9 -0
  136. package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -1
  137. package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
  138. package/dist/components/shared/dev-section/index.d.ts +1 -1
  139. package/dist/components/shared/dev-section/index.d.ts.map +1 -1
  140. package/dist/components/shared/doc-search/use-doc-search.d.ts.map +1 -1
  141. package/dist/components/shared/legal-document/legal-document-page.d.ts.map +1 -1
  142. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  143. package/dist/components/tickets/index.cjs +100 -112
  144. package/dist/components/tickets/index.cjs.map +1 -1
  145. package/dist/components/tickets/index.js +20 -32
  146. package/dist/components/tickets/index.js.map +1 -1
  147. package/dist/components/ui/file-manager/index.cjs +50 -52
  148. package/dist/components/ui/file-manager/index.cjs.map +1 -1
  149. package/dist/components/ui/file-manager/index.js +4 -6
  150. package/dist/components/ui/file-manager/index.js.map +1 -1
  151. package/dist/components/ui/index.cjs +13 -19
  152. package/dist/components/ui/index.cjs.map +1 -1
  153. package/dist/components/ui/index.d.ts +2 -0
  154. package/dist/components/ui/index.d.ts.map +1 -1
  155. package/dist/components/ui/index.js +133 -139
  156. package/dist/components/ui/release-changelog-section.d.ts +6 -2
  157. package/dist/components/ui/release-changelog-section.d.ts.map +1 -1
  158. package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
  159. package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
  160. package/dist/components/ui/simple-markdown-renderer.d.ts +2 -8
  161. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
  162. package/dist/contexts/chat-runtime-context.d.ts +14 -0
  163. package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
  164. package/dist/contexts/index.cjs +3 -3
  165. package/dist/contexts/index.js +5 -5
  166. package/dist/embed-shims/index.cjs +3 -3
  167. package/dist/embed-shims/index.cjs.map +1 -1
  168. package/dist/embed-shims/index.js +4 -4
  169. package/dist/hooks/index.cjs +4 -9
  170. package/dist/hooks/index.cjs.map +1 -1
  171. package/dist/hooks/index.js +6 -11
  172. package/dist/index.cjs +14 -20
  173. package/dist/index.cjs.map +1 -1
  174. package/dist/index.js +362 -368
  175. package/dist/types/doc-source.d.ts +31 -1
  176. package/dist/types/doc-source.d.ts.map +1 -1
  177. package/dist/utils/index.cjs +4 -0
  178. package/dist/utils/index.cjs.map +1 -1
  179. package/dist/utils/index.d.ts +1 -0
  180. package/dist/utils/index.d.ts.map +1 -1
  181. package/dist/utils/index.js +4 -1
  182. package/dist/utils/index.js.map +1 -1
  183. package/dist/utils/page-header-constants.d.ts +15 -0
  184. package/dist/utils/page-header-constants.d.ts.map +1 -0
  185. package/dist/utils/social-embed-cache.d.ts +29 -0
  186. package/dist/utils/social-embed-cache.d.ts.map +1 -0
  187. package/package.json +7 -1
  188. package/src/components/case-studies/index.ts +4 -0
  189. package/src/components/case-studies/share-experience-section.tsx +185 -0
  190. package/src/components/chat/embeddable-chat.tsx +1 -1
  191. package/src/components/docs/doc-viewer.tsx +111 -19
  192. package/src/components/docs/docs-hub-page.tsx +149 -0
  193. package/src/components/docs/index.ts +17 -0
  194. package/src/components/docs/skeletons.tsx +138 -0
  195. package/src/components/docs/use-docs-resolve-link.ts +52 -0
  196. package/src/components/docs/use-document-tree.ts +21 -0
  197. package/src/components/embeds/embed-container.tsx +80 -0
  198. package/src/components/embeds/embed-iframe.tsx +7 -9
  199. package/src/components/embeds/file-download-card.tsx +54 -0
  200. package/src/components/embeds/index.ts +30 -0
  201. package/src/components/embeds/linkedin-embed-client.tsx +100 -0
  202. package/src/components/embeds/markdown-image.tsx +88 -0
  203. package/src/components/embeds/og-link-preview.tsx +13 -13
  204. package/src/components/embeds/reddit-embed-client.tsx +550 -0
  205. package/src/components/embeds/rich-markdown-runtime.tsx +79 -0
  206. package/src/components/embeds/twitter-embed-client.tsx +308 -0
  207. package/src/components/layout/page-header.tsx +182 -0
  208. package/src/components/layout/page-layout.tsx +14 -1
  209. package/src/components/layout/page-with-header.tsx +110 -0
  210. package/src/components/layout/title-block.tsx +40 -62
  211. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +3 -3
  212. package/src/components/shared/dev-section/dev-section-page.tsx +9 -1
  213. package/src/components/shared/dev-section/dev-section-view.tsx +14 -9
  214. package/src/components/shared/dev-section/index.ts +1 -1
  215. package/src/components/shared/doc-search/use-doc-search.ts +7 -3
  216. package/src/components/shared/legal-document/legal-document-page.tsx +2 -2
  217. package/src/components/shared/product-release/release-detail-page.tsx +6 -4
  218. package/src/components/ui/index.ts +2 -0
  219. package/src/components/ui/release-changelog-section.tsx +7 -2
  220. package/src/components/ui/rich-markdown-renderer.tsx +1203 -0
  221. package/src/components/ui/simple-markdown-renderer.tsx +7 -11
  222. package/src/contexts/chat-runtime-context.tsx +14 -0
  223. package/src/types/doc-source.ts +33 -1
  224. package/src/utils/index.ts +1 -0
  225. package/src/utils/page-header-constants.ts +15 -0
  226. package/src/utils/social-embed-cache.ts +391 -0
  227. package/dist/chunk-26PKDALD.js +0 -2379
  228. package/dist/chunk-26PKDALD.js.map +0 -1
  229. package/dist/chunk-3MCHAFHB.js +0 -89
  230. package/dist/chunk-3MCHAFHB.js.map +0 -1
  231. package/dist/chunk-5E2HOSSH.cjs.map +0 -1
  232. package/dist/chunk-66AANIOC.cjs +0 -619
  233. package/dist/chunk-66AANIOC.cjs.map +0 -1
  234. package/dist/chunk-6JINAOI7.cjs +0 -311
  235. package/dist/chunk-6JINAOI7.cjs.map +0 -1
  236. package/dist/chunk-7RIYT7ZH.js.map +0 -1
  237. package/dist/chunk-AQOWFSMB.cjs.map +0 -1
  238. package/dist/chunk-BOCFIKYS.cjs +0 -3009
  239. package/dist/chunk-BOCFIKYS.cjs.map +0 -1
  240. package/dist/chunk-D652TJBQ.js +0 -3009
  241. package/dist/chunk-D652TJBQ.js.map +0 -1
  242. package/dist/chunk-E4XABBSU.js.map +0 -1
  243. package/dist/chunk-EL6QLAWX.js.map +0 -1
  244. package/dist/chunk-EYEW6PTA.cjs.map +0 -1
  245. package/dist/chunk-FQJK446R.js +0 -1606
  246. package/dist/chunk-FQJK446R.js.map +0 -1
  247. package/dist/chunk-GLLDTKZK.cjs.map +0 -1
  248. package/dist/chunk-IE6OU3WQ.cjs.map +0 -1
  249. package/dist/chunk-J54Z3OCR.cjs +0 -1606
  250. package/dist/chunk-J54Z3OCR.cjs.map +0 -1
  251. package/dist/chunk-K2PFPBMF.js.map +0 -1
  252. package/dist/chunk-KXCRGTRN.cjs +0 -2379
  253. package/dist/chunk-KXCRGTRN.cjs.map +0 -1
  254. package/dist/chunk-LCNMR277.js.map +0 -1
  255. package/dist/chunk-LFGGF7OT.cjs +0 -449
  256. package/dist/chunk-LFGGF7OT.cjs.map +0 -1
  257. package/dist/chunk-M2OCXTNT.js +0 -311
  258. package/dist/chunk-M2OCXTNT.js.map +0 -1
  259. package/dist/chunk-ME4EVDFP.js +0 -619
  260. package/dist/chunk-ME4EVDFP.js.map +0 -1
  261. package/dist/chunk-OQ6X7ZOC.js +0 -449
  262. package/dist/chunk-OQ6X7ZOC.js.map +0 -1
  263. package/dist/chunk-OY7OF7E7.js.map +0 -1
  264. package/dist/chunk-POKKCWKF.js +0 -354
  265. package/dist/chunk-POKKCWKF.js.map +0 -1
  266. package/dist/chunk-QHIXS3W2.cjs.map +0 -1
  267. package/dist/chunk-TFSYSWPS.cjs +0 -89
  268. package/dist/chunk-TFSYSWPS.cjs.map +0 -1
  269. package/dist/chunk-W6M2FLLT.cjs.map +0 -1
  270. package/dist/chunk-X647HY3F.cjs.map +0 -1
  271. package/dist/chunk-X6BV7MB7.cjs.map +0 -1
  272. package/dist/chunk-XREEV72C.cjs.map +0 -1
  273. package/dist/chunk-YETA25JW.cjs +0 -354
  274. package/dist/chunk-YETA25JW.cjs.map +0 -1
  275. package/dist/chunk-YIGPRLQY.cjs.map +0 -1
  276. /package/dist/{chunk-3ZXUQQL4.js.map → chunk-PI4WSYQV.js.map} +0 -0
@@ -1,34 +1,17 @@
1
1
  "use client";
2
- import {
3
- ELAPSED_MS_FIELD,
4
- HONEYPOT_FIELD,
5
- ToolTypeValues,
6
- consumeAccessCode,
7
- normalizeHashFragment,
8
- scrollElementIntoView,
9
- transformPlatformConfigsToOptions,
10
- validateAccessCode,
11
- validateAndConsumeAccessCode
12
- } from "./chunk-OQ6X7ZOC.js";
13
2
  import {
14
3
  useRequiredEndpointsRuntime
15
4
  } from "./chunk-MJNXIEV2.js";
16
- import {
17
- createNatsClient
18
- } from "./chunk-PHWQLKVE.js";
19
- import {
20
- contentFetch
21
- } from "./chunk-3MCHAFHB.js";
22
- import {
23
- useDebounce
24
- } from "./chunk-POKKCWKF.js";
25
5
  import {
26
6
  init_next_navigation,
27
7
  useRouter,
28
8
  useSearchParams
29
9
  } from "./chunk-PLJLE4A4.js";
30
10
  import {
31
- OpenFrameLogo
11
+ FlamingoLogo,
12
+ MiamiCyberGangLogoFaceOnly,
13
+ OpenFrameLogo,
14
+ OpenmspLogo
32
15
  } from "./chunk-V4IIBNTA.js";
33
16
  import {
34
17
  cn,
@@ -43,12 +26,108 @@ import {
43
26
  TacticalRmmLogoIcon,
44
27
  XmarkIcon
45
28
  } from "./chunk-J7AV6H63.js";
29
+ import {
30
+ createNatsClient
31
+ } from "./chunk-PHWQLKVE.js";
32
+
33
+ // src/hooks/ui/use-auto-limit-tags.ts
34
+ import { useCallback, useEffect, useRef, useState } from "react";
35
+ function useAutoLimitTags({
36
+ count,
37
+ limitTags = "auto",
38
+ placeholder = "",
39
+ reserveInputWidth = true
40
+ }) {
41
+ const middleRef = useRef(null);
42
+ const measureRef = useRef(null);
43
+ const textMeasureRef = useRef(null);
44
+ const badgeRef = useRef(null);
45
+ const inputRef = useRef(null);
46
+ const [visibleCount, setVisibleCount] = useState(count);
47
+ const recalculate = useCallback(() => {
48
+ if (limitTags !== "auto") {
49
+ setVisibleCount(Math.min(limitTags, count));
50
+ return;
51
+ }
52
+ const middle = middleRef.current;
53
+ const measure = measureRef.current;
54
+ if (!middle || !measure) {
55
+ setVisibleCount(count);
56
+ return;
57
+ }
58
+ if (count === 0) {
59
+ setVisibleCount(0);
60
+ return;
61
+ }
62
+ const cs = getComputedStyle(middle);
63
+ const padL = parseFloat(cs.paddingLeft) || 0;
64
+ const padR = parseFloat(cs.paddingRight) || 0;
65
+ const gap = parseFloat(cs.gap) || 0;
66
+ const middleW = middle.clientWidth;
67
+ let inputReservedW = 0;
68
+ let trailingGap = 0;
69
+ if (reserveInputWidth) {
70
+ const textW = textMeasureRef.current?.offsetWidth ?? 60;
71
+ const inputMinW = inputRef.current ? parseFloat(getComputedStyle(inputRef.current).minWidth) || 60 : 60;
72
+ inputReservedW = Math.max(textW + 8, inputMinW);
73
+ trailingGap = gap;
74
+ }
75
+ const available = middleW - padL - padR - inputReservedW - trailingGap;
76
+ const tagEls = Array.from(measure.children);
77
+ const widths = tagEls.map((el) => el.offsetWidth);
78
+ let total = 0;
79
+ for (let i = 0; i < widths.length; i++) {
80
+ total += widths[i] + (i > 0 ? gap : 0);
81
+ }
82
+ if (total <= available) {
83
+ setVisibleCount(count);
84
+ return;
85
+ }
86
+ const badgeW = badgeRef.current?.offsetWidth ?? 40;
87
+ const spaceWithBadge = available - badgeW - gap;
88
+ let used = 0;
89
+ let fitCount = 0;
90
+ for (let i = 0; i < widths.length; i++) {
91
+ const need = widths[i] + (i > 0 ? gap : 0);
92
+ if (used + need > spaceWithBadge) break;
93
+ used += need;
94
+ fitCount++;
95
+ }
96
+ setVisibleCount(Math.max(0, fitCount));
97
+ }, [count, limitTags, placeholder, reserveInputWidth]);
98
+ useEffect(() => {
99
+ recalculate();
100
+ }, [recalculate]);
101
+ useEffect(() => {
102
+ const el = middleRef.current;
103
+ if (!el) return;
104
+ const ro = new ResizeObserver(recalculate);
105
+ ro.observe(el);
106
+ return () => ro.disconnect();
107
+ }, [recalculate]);
108
+ return { visibleCount, middleRef, measureRef, textMeasureRef, badgeRef, inputRef };
109
+ }
110
+
111
+ // src/hooks/ui/use-debounce.ts
112
+ import { useState as useState2, useEffect as useEffect2 } from "react";
113
+ function useDebounce(value, delay = 500) {
114
+ const [debouncedValue, setDebouncedValue] = useState2(value);
115
+ useEffect2(() => {
116
+ const timer = setTimeout(() => {
117
+ setDebouncedValue(value);
118
+ }, delay);
119
+ return () => {
120
+ clearTimeout(timer);
121
+ };
122
+ }, [value, delay]);
123
+ return debouncedValue;
124
+ }
46
125
 
47
126
  // src/hooks/ui/use-header-height.ts
48
- import { useEffect, useState } from "react";
127
+ import { useEffect as useEffect3, useState as useState3 } from "react";
49
128
  function useHeaderHeight(defaultHeight = 64) {
50
- const [height, setHeight] = useState(defaultHeight);
51
- useEffect(() => {
129
+ const [height, setHeight] = useState3(defaultHeight);
130
+ useEffect3(() => {
52
131
  const measure = () => {
53
132
  let total = 0;
54
133
  const header2 = document.querySelector("header");
@@ -87,22 +166,22 @@ function useHeaderHeight(defaultHeight = 64) {
87
166
  }
88
167
 
89
168
  // src/hooks/ui/use-horizontal-scrollbar.ts
90
- import { useRef, useState as useState2, useCallback } from "react";
169
+ import { useRef as useRef2, useState as useState4, useCallback as useCallback2 } from "react";
91
170
  function useHorizontalScrollbar() {
92
- const scrollElRef = useRef(null);
93
- const trackRef = useRef(null);
94
- const thumbRef = useRef(null);
95
- const roRef = useRef(null);
96
- const moRef = useRef(null);
97
- const [thumbRatio, setThumbRatio] = useState2(0);
98
- const [canScrollLeft, setCanScrollLeft] = useState2(false);
99
- const [canScrollRight, setCanScrollRight] = useState2(false);
100
- const prevCanScrollLeftRef = useRef(false);
101
- const prevCanScrollRightRef = useRef(false);
102
- const isDraggingRef = useRef(false);
103
- const dragStartRef = useRef({ mouseX: 0, scrollLeft: 0 });
104
- const rafIdRef = useRef(0);
105
- const syncThumbToDOM = useCallback(() => {
171
+ const scrollElRef = useRef2(null);
172
+ const trackRef = useRef2(null);
173
+ const thumbRef = useRef2(null);
174
+ const roRef = useRef2(null);
175
+ const moRef = useRef2(null);
176
+ const [thumbRatio, setThumbRatio] = useState4(0);
177
+ const [canScrollLeft, setCanScrollLeft] = useState4(false);
178
+ const [canScrollRight, setCanScrollRight] = useState4(false);
179
+ const prevCanScrollLeftRef = useRef2(false);
180
+ const prevCanScrollRightRef = useRef2(false);
181
+ const isDraggingRef = useRef2(false);
182
+ const dragStartRef = useRef2({ mouseX: 0, scrollLeft: 0 });
183
+ const rafIdRef = useRef2(0);
184
+ const syncThumbToDOM = useCallback2(() => {
106
185
  const el = scrollElRef.current;
107
186
  const thumb = thumbRef.current;
108
187
  if (!el || !thumb) return;
@@ -115,7 +194,7 @@ function useHorizontalScrollbar() {
115
194
  const fraction = Math.min(Math.max(el.scrollLeft, 0), maxScroll) / maxScroll;
116
195
  thumb.style.left = `${fraction * (1 - ratio) * 100}%`;
117
196
  }, []);
118
- const syncEdgeFades = useCallback(() => {
197
+ const syncEdgeFades = useCallback2(() => {
119
198
  const el = scrollElRef.current;
120
199
  if (!el) return;
121
200
  const maxScroll = el.scrollWidth - el.clientWidth;
@@ -143,7 +222,7 @@ function useHorizontalScrollbar() {
143
222
  setCanScrollRight(right);
144
223
  }
145
224
  }, []);
146
- const measure = useCallback(() => {
225
+ const measure = useCallback2(() => {
147
226
  const el = scrollElRef.current;
148
227
  if (!el) return;
149
228
  const ratio = el.clientWidth / el.scrollWidth;
@@ -151,7 +230,7 @@ function useHorizontalScrollbar() {
151
230
  syncThumbToDOM();
152
231
  syncEdgeFades();
153
232
  }, [syncThumbToDOM, syncEdgeFades]);
154
- const scrollRef = useCallback((node) => {
233
+ const scrollRef = useCallback2((node) => {
155
234
  if (roRef.current) {
156
235
  roRef.current.disconnect();
157
236
  roRef.current = null;
@@ -193,7 +272,7 @@ function useHorizontalScrollbar() {
193
272
  setCanScrollRight(false);
194
273
  }
195
274
  }, [measure, syncThumbToDOM, syncEdgeFades]);
196
- const onScroll = useCallback(() => {
275
+ const onScroll = useCallback2(() => {
197
276
  if (isDraggingRef.current) return;
198
277
  if (rafIdRef.current) cancelAnimationFrame(rafIdRef.current);
199
278
  rafIdRef.current = requestAnimationFrame(() => {
@@ -201,7 +280,7 @@ function useHorizontalScrollbar() {
201
280
  syncEdgeFades();
202
281
  });
203
282
  }, [syncThumbToDOM, syncEdgeFades]);
204
- const onTrackClick = useCallback((e) => {
283
+ const onTrackClick = useCallback2((e) => {
205
284
  if (e.target.closest("[data-scrollbar-thumb]")) return;
206
285
  const track = trackRef.current;
207
286
  const el = scrollElRef.current;
@@ -219,13 +298,13 @@ function useHorizontalScrollbar() {
219
298
  const targetScrollLeft = targetFraction * (el.scrollWidth - el.clientWidth);
220
299
  el.scrollTo({ left: targetScrollLeft, behavior: "smooth" });
221
300
  }, []);
222
- const onTrackWheel = useCallback((e) => {
301
+ const onTrackWheel = useCallback2((e) => {
223
302
  const el = scrollElRef.current;
224
303
  if (!el) return;
225
304
  e.preventDefault();
226
305
  el.scrollLeft += e.deltaX || e.deltaY;
227
306
  }, []);
228
- const onThumbPointerDown = useCallback((e) => {
307
+ const onThumbPointerDown = useCallback2((e) => {
229
308
  e.preventDefault();
230
309
  e.stopPropagation();
231
310
  const el = scrollElRef.current;
@@ -236,7 +315,7 @@ function useHorizontalScrollbar() {
236
315
  target.setPointerCapture(e.pointerId);
237
316
  target.style.cursor = "grabbing";
238
317
  }, []);
239
- const onThumbPointerMove = useCallback((e) => {
318
+ const onThumbPointerMove = useCallback2((e) => {
240
319
  if (!isDraggingRef.current) return;
241
320
  const el = scrollElRef.current;
242
321
  const track = trackRef.current;
@@ -256,7 +335,7 @@ function useHorizontalScrollbar() {
256
335
  syncThumbToDOM();
257
336
  syncEdgeFades();
258
337
  }, [syncThumbToDOM, syncEdgeFades]);
259
- const onThumbPointerUp = useCallback((e) => {
338
+ const onThumbPointerUp = useCallback2((e) => {
260
339
  isDraggingRef.current = false;
261
340
  const target = e.currentTarget;
262
341
  target.releasePointerCapture(e.pointerId);
@@ -279,7 +358,7 @@ function useHorizontalScrollbar() {
279
358
  }
280
359
 
281
360
  // src/hooks/ui/use-image-edge-color.ts
282
- import { useState as useState3, useEffect as useEffect2 } from "react";
361
+ import { useState as useState5, useEffect as useEffect4 } from "react";
283
362
  function extractEdgeColor(img) {
284
363
  const canvas = document.createElement("canvas");
285
364
  const ctx = canvas.getContext("2d");
@@ -335,8 +414,8 @@ function extractEdgeColor(img) {
335
414
  return `rgb(${r}, ${g}, ${b})`;
336
415
  }
337
416
  function useImageEdgeColor(imageUrl, fallback = "#000000") {
338
- const [color, setColor] = useState3(fallback);
339
- useEffect2(() => {
417
+ const [color, setColor] = useState5(fallback);
418
+ useEffect4(() => {
340
419
  if (!imageUrl) {
341
420
  setColor(fallback);
342
421
  return;
@@ -365,9 +444,9 @@ function useImageEdgeColor(imageUrl, fallback = "#000000") {
365
444
  }
366
445
 
367
446
  // src/hooks/ui/use-local-storage.ts
368
- import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
447
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState6 } from "react";
369
448
  function useLocalStorage(key, initialValue) {
370
- const [storedValue, setStoredValue] = useState4(() => {
449
+ const [storedValue, setStoredValue] = useState6(() => {
371
450
  try {
372
451
  if (typeof window !== "undefined") {
373
452
  const item = window.localStorage.getItem(key);
@@ -378,9 +457,9 @@ function useLocalStorage(key, initialValue) {
378
457
  }
379
458
  return initialValue;
380
459
  });
381
- const isInitialized = useRef2(true);
382
- const isFromStorageEvent = useRef2(false);
383
- useEffect3(() => {
460
+ const isInitialized = useRef3(true);
461
+ const isFromStorageEvent = useRef3(false);
462
+ useEffect5(() => {
384
463
  if (!isInitialized.current) return;
385
464
  const handleStorageChange = (e) => {
386
465
  if (e.key === key && e.newValue !== null) {
@@ -419,7 +498,7 @@ function useLocalStorage(key, initialValue) {
419
498
  window.removeEventListener("localStorageUpdate", handleCustomStorageUpdate);
420
499
  };
421
500
  }, [key]);
422
- useEffect3(() => {
501
+ useEffect5(() => {
423
502
  if (!isInitialized.current) return;
424
503
  if (isFromStorageEvent.current) {
425
504
  isFromStorageEvent.current = false;
@@ -445,9 +524,9 @@ function useLocalStorage(key, initialValue) {
445
524
  }
446
525
 
447
526
  // src/hooks/ui/use-media-query.ts
448
- import { useLayoutEffect, useState as useState5 } from "react";
527
+ import { useLayoutEffect, useState as useState7 } from "react";
449
528
  function useMediaQuery(query) {
450
- const [matches, setMatches] = useState5(void 0);
529
+ const [matches, setMatches] = useState7(void 0);
451
530
  useLayoutEffect(() => {
452
531
  const matchMedia = window.matchMedia(query);
453
532
  const handleChange = () => {
@@ -480,24 +559,24 @@ function useLgUp() {
480
559
  }
481
560
 
482
561
  // src/hooks/ui/use-memoized-callback.ts
483
- import { useCallback as useCallback2, useRef as useRef3 } from "react";
562
+ import { useCallback as useCallback3, useRef as useRef4 } from "react";
484
563
  function useMemoizedCallback(callback, dependencies) {
485
- const callbackRef = useRef3(callback);
486
- const dependenciesRef = useRef3(dependencies);
564
+ const callbackRef = useRef4(callback);
565
+ const dependenciesRef = useRef4(dependencies);
487
566
  callbackRef.current = callback;
488
567
  const depsChanged = dependencies.some((dep, i) => !Object.is(dep, dependenciesRef.current[i]));
489
568
  if (depsChanged) {
490
569
  dependenciesRef.current = dependencies;
491
570
  }
492
- return useCallback2(((...args) => callbackRef.current(...args)), [depsChanged]);
571
+ return useCallback3(((...args) => callbackRef.current(...args)), [depsChanged]);
493
572
  }
494
573
 
495
574
  // src/hooks/ui/use-notification-permission.ts
496
- import { useCallback as useCallback3, useEffect as useEffect4, useState as useState6 } from "react";
575
+ import { useCallback as useCallback4, useEffect as useEffect6, useState as useState8 } from "react";
497
576
  function useNotificationPermission() {
498
- const [supported, setSupported] = useState6(false);
499
- const [permission, setPermission] = useState6("default");
500
- useEffect4(() => {
577
+ const [supported, setSupported] = useState8(false);
578
+ const [permission, setPermission] = useState8("default");
579
+ useEffect6(() => {
501
580
  if (typeof window === "undefined" || !("Notification" in window)) return;
502
581
  setSupported(true);
503
582
  const sync = () => setPermission(Notification.permission);
@@ -518,7 +597,7 @@ function useNotificationPermission() {
518
597
  window.removeEventListener("focus", sync);
519
598
  };
520
599
  }, []);
521
- const request = useCallback3(async () => {
600
+ const request = useCallback4(async () => {
522
601
  if (typeof window === "undefined" || !("Notification" in window)) {
523
602
  return "denied";
524
603
  }
@@ -533,7 +612,7 @@ function useNotificationPermission() {
533
612
  }
534
613
 
535
614
  // src/hooks/ui/use-onboarding-state.ts
536
- import { useState as useState7, useEffect as useEffect5, useCallback as useCallback4 } from "react";
615
+ import { useState as useState9, useEffect as useEffect7, useCallback as useCallback5 } from "react";
537
616
 
538
617
  // src/utils/onboarding-storage.ts
539
618
  var DEFAULT_STATE = {
@@ -608,9 +687,9 @@ function dismissOnboarding(key) {
608
687
 
609
688
  // src/hooks/ui/use-onboarding-state.ts
610
689
  function useOnboardingState(storageKey = "openframe-onboarding-state") {
611
- const [state, setState] = useState7(() => loadOnboardingState(storageKey));
612
- const [, forceUpdate] = useState7(0);
613
- useEffect5(() => {
690
+ const [state, setState] = useState9(() => loadOnboardingState(storageKey));
691
+ const [, forceUpdate] = useState9(0);
692
+ useEffect7(() => {
614
693
  const handleStorageUpdate = (e) => {
615
694
  if (e.detail.key === storageKey) {
616
695
  const newState = loadOnboardingState(storageKey);
@@ -624,38 +703,38 @@ function useOnboardingState(storageKey = "openframe-onboarding-state") {
624
703
  window.removeEventListener("localStorageUpdate", handleStorageUpdate);
625
704
  };
626
705
  }, [storageKey]);
627
- const markComplete = useCallback4((stepId) => {
706
+ const markComplete = useCallback5((stepId) => {
628
707
  console.log(`\u{1F3AF} markComplete called for: "${stepId}"`);
629
708
  const newState = markStepComplete(storageKey, stepId);
630
709
  setState(newState);
631
710
  forceUpdate((prev) => prev + 1);
632
711
  }, [storageKey]);
633
- const markSkipped = useCallback4((stepId) => {
712
+ const markSkipped = useCallback5((stepId) => {
634
713
  console.log(`\u23ED\uFE0F markSkipped called for: "${stepId}"`);
635
714
  const newState = markStepSkipped(storageKey, stepId);
636
715
  setState(newState);
637
716
  forceUpdate((prev) => prev + 1);
638
717
  }, [storageKey]);
639
- const dismissOnboarding2 = useCallback4(() => {
718
+ const dismissOnboarding2 = useCallback5(() => {
640
719
  console.log(`\u{1F6AB} dismissOnboarding called`);
641
720
  const newState = dismissOnboarding(storageKey);
642
721
  setState(newState);
643
722
  forceUpdate((prev) => prev + 1);
644
723
  }, [storageKey]);
645
- const markMultipleComplete2 = useCallback4((stepIds) => {
724
+ const markMultipleComplete2 = useCallback5((stepIds) => {
646
725
  console.log(`\u{1F3AF} markMultipleComplete called for:`, stepIds);
647
726
  const newState = markMultipleComplete(storageKey, stepIds);
648
727
  setState(newState);
649
728
  forceUpdate((prev) => prev + 1);
650
729
  console.log(`\u{1F4DD} State after batch:`, newState);
651
730
  }, [storageKey]);
652
- const isStepComplete = useCallback4((stepId) => {
731
+ const isStepComplete = useCallback5((stepId) => {
653
732
  return state.completedSteps.includes(stepId);
654
733
  }, [state.completedSteps]);
655
- const isStepSkipped = useCallback4((stepId) => {
734
+ const isStepSkipped = useCallback5((stepId) => {
656
735
  return state.skippedSteps.includes(stepId);
657
736
  }, [state.skippedSteps]);
658
- const allStepsComplete = useCallback4((steps) => {
737
+ const allStepsComplete = useCallback5((steps) => {
659
738
  return steps.every((step) => isStepComplete(step.id) || isStepSkipped(step.id));
660
739
  }, [isStepComplete, isStepSkipped]);
661
740
  return {
@@ -671,19 +750,19 @@ function useOnboardingState(storageKey = "openframe-onboarding-state") {
671
750
  }
672
751
 
673
752
  // src/hooks/ui/use-search.ts
674
- import { useState as useState8, useEffect as useEffect6, useCallback as useCallback5 } from "react";
753
+ import { useState as useState10, useEffect as useEffect8, useCallback as useCallback6 } from "react";
675
754
  function useSearch(config) {
676
755
  const { searchFn, mapResult, debounceMs = 300, minQueryLength = 2 } = config;
677
- const [query, setQuery] = useState8("");
678
- const [results, setResults] = useState8([]);
679
- const [isLoading, setIsLoading] = useState8(false);
680
- const [error, setError] = useState8(null);
756
+ const [query, setQuery] = useState10("");
757
+ const [results, setResults] = useState10([]);
758
+ const [isLoading, setIsLoading] = useState10(false);
759
+ const [error, setError] = useState10(null);
681
760
  const debouncedQuery = useDebounce(query, debounceMs);
682
- const clearResults = useCallback5(() => {
761
+ const clearResults = useCallback6(() => {
683
762
  setResults([]);
684
763
  setError(null);
685
764
  }, []);
686
- useEffect6(() => {
765
+ useEffect8(() => {
687
766
  if (!debouncedQuery || debouncedQuery.length < minQueryLength) {
688
767
  setResults([]);
689
768
  setIsLoading(false);
@@ -756,11 +835,11 @@ function useTablePagination(config) {
756
835
  }
757
836
 
758
837
  // src/hooks/ui/use-throttle.ts
759
- import { useState as useState9, useEffect as useEffect7, useRef as useRef4 } from "react";
838
+ import { useState as useState11, useEffect as useEffect9, useRef as useRef5 } from "react";
760
839
  function useThrottle(value, limit = 200) {
761
- const [throttledValue, setThrottledValue] = useState9(value);
762
- const lastUpdated = useRef4(Date.now());
763
- useEffect7(() => {
840
+ const [throttledValue, setThrottledValue] = useState11(value);
841
+ const lastUpdated = useRef5(Date.now());
842
+ useEffect9(() => {
764
843
  const now = Date.now();
765
844
  const elapsed = now - lastUpdated.current;
766
845
  if (elapsed >= limit) {
@@ -780,13 +859,13 @@ function useThrottle(value, limit = 200) {
780
859
  }
781
860
 
782
861
  // src/hooks/ui/use-window-size.ts
783
- import { useLayoutEffect as useLayoutEffect2, useState as useState10 } from "react";
862
+ import { useLayoutEffect as useLayoutEffect2, useState as useState12 } from "react";
784
863
  function useWindowSize() {
785
- const [windowSize, setWindowSize] = useState10({
864
+ const [windowSize, setWindowSize] = useState12({
786
865
  width: 0,
787
866
  height: 0
788
867
  });
789
- const [isClient, setIsClient] = useState10(false);
868
+ const [isClient, setIsClient] = useState12(false);
790
869
  useLayoutEffect2(() => {
791
870
  setIsClient(true);
792
871
  if (!isClient) return;
@@ -804,14 +883,188 @@ function useWindowSize() {
804
883
  }
805
884
 
806
885
  // src/hooks/platform/use-platform-config.ts
807
- import { useState as useState11, useEffect as useEffect8 } from "react";
886
+ import { useState as useState13, useEffect as useEffect10 } from "react";
887
+
888
+ // src/utils/platform-config.tsx
889
+ import { Globe } from "lucide-react";
890
+ import { jsx } from "react/jsx-runtime";
891
+ var platformIcons = {
892
+ openframe: /* @__PURE__ */ jsx(OpenFrameLogo, { className: "h-5 w-5", lowerPathColor: "#FFC008", upperPathColor: "#ffffff" }),
893
+ openmsp: /* @__PURE__ */ jsx(OpenmspLogo, { className: "h-5 w-5" }),
894
+ flamingo: /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#EC4899" }),
895
+ "flamingo-teaser": /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#EC4899" }),
896
+ "marketing-hub": /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#F357BB" }),
897
+ "product-hub": /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#5EA62E" }),
898
+ "revenue-hub": /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#FFC008" }),
899
+ "people-hub": /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#5EFAF0" }),
900
+ "company-hub": /* @__PURE__ */ jsx(FlamingoLogo, { className: "h-5 w-5", fill: "#f36666" }),
901
+ tmcg: /* @__PURE__ */ jsx(MiamiCyberGangLogoFaceOnly, { className: "h-5 w-5" }),
902
+ universal: /* @__PURE__ */ jsx(Globe, { className: "h-5 w-5 text-[#10B981]" })
903
+ };
904
+ var platformColors = {
905
+ openmsp: "bg-[#3B82F6]",
906
+ openframe: "bg-[#8B5CF6]",
907
+ flamingo: "bg-[#EC4899]",
908
+ "flamingo-teaser": "bg-[#F59E0B]",
909
+ "marketing-hub": "bg-[#F357BB]",
910
+ "product-hub": "bg-[#5EA62E]",
911
+ "revenue-hub": "bg-[#FFC008]",
912
+ "people-hub": "bg-[#5EFAF0]",
913
+ "company-hub": "bg-[#f36666]",
914
+ tmcg: "bg-[#FF6B6B]",
915
+ universal: "bg-[#10B981]"
916
+ };
917
+ var platformDisplayNames = {
918
+ openmsp: "OpenMSP",
919
+ openframe: "OpenFrame",
920
+ flamingo: "Flamingo",
921
+ "flamingo-teaser": "Flamingo Teaser",
922
+ "marketing-hub": "Flamingo Marketing Hub",
923
+ "product-hub": "Flamingo Product Hub",
924
+ "revenue-hub": "Flamingo Revenue Hub",
925
+ "people-hub": "Flamingo People Hub",
926
+ "company-hub": "Flamingo Company Hub",
927
+ tmcg: "TMCG",
928
+ universal: "Universal"
929
+ };
930
+ var platformDescriptions = {
931
+ openmsp: "Comprehensive directory and comparison platform for managed service providers (MSPs) and technology vendors. Reduce vendor costs and discover open-source alternatives.",
932
+ openframe: "AI-driven open-source security operations center (SOC) and endpoint detection platform for MSPs.",
933
+ flamingo: "AI-driven open-source OS for MSPs. Swap bloated vendor tools for open ones. Automate the boring crap. Take your margin back.",
934
+ "flamingo-teaser": "Preview of Flamingo - the AI-driven open-source OS for MSPs.",
935
+ tmcg: "The Miami Cyber Gang - A cybersecurity community focused on education and collaboration.",
936
+ universal: "Cross-platform universal content."
937
+ };
938
+ var platformSlogans = {
939
+ openmsp: "Find Your Perfect MSP Partner",
940
+ openframe: "Open-Source Security Operations",
941
+ flamingo: "Open-Source OS for MSPs",
942
+ "flamingo-teaser": "Coming Soon: Open-Source OS for MSPs",
943
+ tmcg: "Miami Cyber Community",
944
+ universal: "Universal Platform"
945
+ };
946
+ var platformHexColors = {
947
+ openmsp: "#FFC008",
948
+ openframe: "#FFC008",
949
+ flamingo: "#FF6B9D",
950
+ universal: "#FFC008",
951
+ "flamingo-teaser": "#F59E0B",
952
+ "marketing-hub": "#F357BB",
953
+ "product-hub": "#5EA62E",
954
+ "revenue-hub": "#FFC008",
955
+ "people-hub": "#5EFAF0",
956
+ "company-hub": "#f36666",
957
+ tmcg: "#FF6B6B"
958
+ };
959
+ var platformIconNames = {
960
+ openmsp: "openmsp-logo",
961
+ openframe: "openframe-logo",
962
+ flamingo: "flamingo-logo",
963
+ universal: "globe",
964
+ "flamingo-teaser": "flamingo-logo",
965
+ "marketing-hub": "flamingo-logo",
966
+ "product-hub": "flamingo-logo",
967
+ "revenue-hub": "flamingo-logo",
968
+ "people-hub": "flamingo-logo",
969
+ "company-hub": "flamingo-logo",
970
+ tmcg: "tmcg-logo"
971
+ };
972
+ function getDefaultColorForPlatform(platformName) {
973
+ return platformHexColors[platformName] || platformHexColors.universal;
974
+ }
975
+ function getDefaultIconForPlatform(platformName) {
976
+ return platformIconNames[platformName] || platformIconNames.universal;
977
+ }
978
+ function transformPlatformConfigsToOptions(platformConfigs) {
979
+ return platformConfigs.map((platform) => ({
980
+ id: platform.id,
981
+ // Database UUID for matching
982
+ name: platform.name,
983
+ // Platform name enum
984
+ displayName: platform.display_name,
985
+ // Human-readable name
986
+ description: platform.description,
987
+ icon: platformIcons[platform.name] || platformIcons.universal,
988
+ color: platformColors[platform.name] || platformColors.universal
989
+ }));
990
+ }
991
+ function getPlatformIcon(platformName) {
992
+ return platformIcons[platformName] || platformIcons.universal;
993
+ }
994
+ function getPlatformColor(platformName) {
995
+ return platformColors[platformName] || platformColors.universal;
996
+ }
997
+ function getPlatformDisplayName(platformName) {
998
+ return platformDisplayNames[platformName] || platformName;
999
+ }
1000
+ function getPlatformDescription(platformName) {
1001
+ return platformDescriptions[platformName] || platformName;
1002
+ }
1003
+ function getPlatformSlogan(platformName) {
1004
+ return platformSlogans[platformName] || platformName;
1005
+ }
1006
+ function getSmallPlatformIcon(platformName) {
1007
+ const className = "h-4 w-4 flex-shrink-0";
1008
+ switch (platformName) {
1009
+ case "openframe":
1010
+ return /* @__PURE__ */ jsx(OpenFrameLogo, { className, lowerPathColor: "#FFC008", upperPathColor: "#ffffff" });
1011
+ case "openmsp":
1012
+ return /* @__PURE__ */ jsx(OpenmspLogo, { className, frontBubbleColor: "#f1f1f1", innerFrontBubbleColor: "#000000", backBubbleColor: "#FFC008" });
1013
+ case "flamingo":
1014
+ case "flamingo-teaser":
1015
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className: `${className}`, fill: "#EC4899" });
1016
+ case "marketing-hub":
1017
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-flamingo-pink-base)" });
1018
+ case "product-hub":
1019
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-attention-green-success)" });
1020
+ case "revenue-hub":
1021
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-attention-yellow-warning)" });
1022
+ case "people-hub":
1023
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-flamingo-cyan-base)" });
1024
+ case "company-hub":
1025
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-attention-red-error)" });
1026
+ case "tmcg":
1027
+ return /* @__PURE__ */ jsx(MiamiCyberGangLogoFaceOnly, { className });
1028
+ case "universal":
1029
+ default:
1030
+ return /* @__PURE__ */ jsx(Globe, { className });
1031
+ }
1032
+ }
1033
+ function getPlatformIconComponent(platformName, className = "h-6 w-6") {
1034
+ switch (platformName) {
1035
+ case "openframe":
1036
+ return /* @__PURE__ */ jsx(OpenFrameLogo, { className });
1037
+ case "openmsp":
1038
+ return /* @__PURE__ */ jsx(OpenmspLogo, { className, color: "#f1f1f1" });
1039
+ case "flamingo":
1040
+ case "flamingo-teaser":
1041
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className: `${className} text-white` });
1042
+ case "marketing-hub":
1043
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-flamingo-pink-base)" });
1044
+ case "product-hub":
1045
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-attention-green-success)" });
1046
+ case "revenue-hub":
1047
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-attention-yellow-warning)" });
1048
+ case "people-hub":
1049
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-flamingo-cyan-base)" });
1050
+ case "company-hub":
1051
+ return /* @__PURE__ */ jsx(FlamingoLogo, { className, fill: "var(--ods-attention-red-error)" });
1052
+ case "tmcg":
1053
+ return /* @__PURE__ */ jsx(MiamiCyberGangLogoFaceOnly, { size: 24, className });
1054
+ case "universal":
1055
+ default:
1056
+ return /* @__PURE__ */ jsx(Globe, { className });
1057
+ }
1058
+ }
1059
+
1060
+ // src/hooks/platform/use-platform-config.ts
808
1061
  var platformCache = null;
809
1062
  var fetchPromise = null;
810
1063
  function usePlatformConfig() {
811
- const [platforms, setPlatforms] = useState11(platformCache || []);
812
- const [isLoading, setIsLoading] = useState11(!platformCache);
813
- const [error, setError] = useState11(null);
814
- useEffect8(() => {
1064
+ const [platforms, setPlatforms] = useState13(platformCache || []);
1065
+ const [isLoading, setIsLoading] = useState13(!platformCache);
1066
+ const [error, setError] = useState13(null);
1067
+ useEffect10(() => {
815
1068
  if (platformCache) {
816
1069
  setPlatforms(platformCache);
817
1070
  setIsLoading(false);
@@ -882,11 +1135,35 @@ import { toast as sonnerToast2 } from "sonner";
882
1135
  import * as React from "react";
883
1136
  import { Toaster as SonnerToaster, toast as sonnerToast } from "sonner";
884
1137
 
1138
+ // src/types/tool.types.ts
1139
+ var ToolTypeValues = {
1140
+ TACTICAL_RMM: "TACTICAL_RMM",
1141
+ FLEET_MDM: "FLEET_MDM",
1142
+ MESHCENTRAL: "MESHCENTRAL",
1143
+ AUTHENTIK: "AUTHENTIK",
1144
+ OPENFRAME: "OPENFRAME",
1145
+ OPENFRAME_CHAT: "OPENFRAME_CHAT",
1146
+ OPENFRAME_CLIENT: "OPENFRAME_CLIENT",
1147
+ OSQUERY: "OSQUERY",
1148
+ SYSTEM: "SYSTEM"
1149
+ };
1150
+ var toolLabels = {
1151
+ TACTICAL_RMM: "Tactical",
1152
+ FLEET_MDM: "Fleet",
1153
+ MESHCENTRAL: "MeshCentral",
1154
+ AUTHENTIK: "Authentik",
1155
+ OPENFRAME: "OpenFrame",
1156
+ OPENFRAME_CHAT: "OpenFrame Chat",
1157
+ OPENFRAME_CLIENT: "OpenFrame Client",
1158
+ OSQUERY: "Osquery",
1159
+ SYSTEM: "System"
1160
+ };
1161
+
885
1162
  // src/components/tool-icon.tsx
886
- import { Fragment, jsx } from "react/jsx-runtime";
1163
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
887
1164
  var renderOpenFrameLogo = (_size, className) => (
888
1165
  // eslint-disable-next-line deprecation/deprecation
889
- /* @__PURE__ */ jsx(
1166
+ /* @__PURE__ */ jsx2(
890
1167
  OpenFrameLogo,
891
1168
  {
892
1169
  className: className ?? "h-4 w-auto",
@@ -896,22 +1173,22 @@ var renderOpenFrameLogo = (_size, className) => (
896
1173
  )
897
1174
  );
898
1175
  var toolIconMap = {
899
- [ToolTypeValues.FLEET_MDM]: (size, className) => /* @__PURE__ */ jsx(FleetMdmLogoGreyIcon, { size, className }),
900
- [ToolTypeValues.MESHCENTRAL]: (size, className) => /* @__PURE__ */ jsx(MeshcentralLogoGreyIcon, { size, className }),
901
- [ToolTypeValues.TACTICAL_RMM]: (size, className) => /* @__PURE__ */ jsx(TacticalRmmLogoIcon, { size, className }),
1176
+ [ToolTypeValues.FLEET_MDM]: (size, className) => /* @__PURE__ */ jsx2(FleetMdmLogoGreyIcon, { size, className }),
1177
+ [ToolTypeValues.MESHCENTRAL]: (size, className) => /* @__PURE__ */ jsx2(MeshcentralLogoGreyIcon, { size, className }),
1178
+ [ToolTypeValues.TACTICAL_RMM]: (size, className) => /* @__PURE__ */ jsx2(TacticalRmmLogoIcon, { size, className }),
902
1179
  [ToolTypeValues.OPENFRAME]: renderOpenFrameLogo,
903
1180
  [ToolTypeValues.OPENFRAME_CHAT]: renderOpenFrameLogo,
904
1181
  [ToolTypeValues.OPENFRAME_CLIENT]: renderOpenFrameLogo,
905
- [ToolTypeValues.AUTHENTIK]: (size, className) => /* @__PURE__ */ jsx(AuthentikLogoGreyIcon, { size, className }),
906
- [ToolTypeValues.OSQUERY]: (size, className) => /* @__PURE__ */ jsx(OsqueryLogoGreyIcon, { size, className }),
1182
+ [ToolTypeValues.AUTHENTIK]: (size, className) => /* @__PURE__ */ jsx2(AuthentikLogoGreyIcon, { size, className }),
1183
+ [ToolTypeValues.OSQUERY]: (size, className) => /* @__PURE__ */ jsx2(OsqueryLogoGreyIcon, { size, className }),
907
1184
  [ToolTypeValues.SYSTEM]: () => null
908
1185
  };
909
- var ToolIcon = ({ toolType, size = 16, className }) => /* @__PURE__ */ jsx(Fragment, { children: toolIconMap[toolType]?.(size, className) ?? null });
1186
+ var ToolIcon = ({ toolType, size = 16, className }) => /* @__PURE__ */ jsx2(Fragment, { children: toolIconMap[toolType]?.(size, className) ?? null });
910
1187
  ToolIcon.displayName = "ToolIcon";
911
1188
 
912
1189
  // src/components/ui/toaster.tsx
913
1190
  init_cn();
914
- import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
1191
+ import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
915
1192
  var dotColorByVariant = {
916
1193
  default: "bg-ods-text-secondary",
917
1194
  success: "bg-ods-success",
@@ -944,22 +1221,22 @@ function ToastHeader({
944
1221
  className
945
1222
  ),
946
1223
  children: [
947
- /* @__PURE__ */ jsx2("div", { className: "flex size-6 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx2("span", { className: cn("size-[9px] rounded-full", dotColorByVariant[variant]) }) }),
1224
+ /* @__PURE__ */ jsx3("div", { className: "flex size-6 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx3("span", { className: cn("size-[9px] rounded-full", dotColorByVariant[variant]) }) }),
948
1225
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center font-['DM_Sans'] font-medium", children: [
949
- title ? /* @__PURE__ */ jsx2("p", { className: "truncate pr-5 text-[18px] leading-6 text-ods-text-primary", title: typeof title === "string" ? title : void 0, children: title }) : null,
950
- description ? /* @__PURE__ */ jsx2("p", { className: "text-[14px] leading-5 text-ods-text-secondary line-clamp-3", title: typeof description === "string" ? description : void 0, children: description }) : null
1226
+ title ? /* @__PURE__ */ jsx3("p", { className: "truncate pr-5 text-[18px] leading-6 text-ods-text-primary", title: typeof title === "string" ? title : void 0, children: title }) : null,
1227
+ description ? /* @__PURE__ */ jsx3("p", { className: "text-[14px] leading-5 text-ods-text-secondary line-clamp-3", title: typeof description === "string" ? description : void 0, children: description }) : null
951
1228
  ] }),
952
- dismissible ? /* @__PURE__ */ jsx2(
1229
+ dismissible ? /* @__PURE__ */ jsx3(
953
1230
  "button",
954
1231
  {
955
1232
  type: "button",
956
1233
  "aria-label": "Close",
957
1234
  onClick: () => sonnerToast.dismiss(id),
958
1235
  className: "absolute right-[7px] top-[7px] flex size-4 items-center justify-center text-ods-text-secondary transition-colors hover:text-ods-text-primary",
959
- children: /* @__PURE__ */ jsx2(XmarkIcon, { size: 16 })
1236
+ children: /* @__PURE__ */ jsx3(XmarkIcon, { size: 16 })
960
1237
  }
961
1238
  ) : null,
962
- showProgress && duration !== Infinity && duration > 0 ? /* @__PURE__ */ jsx2(
1239
+ showProgress && duration !== Infinity && duration > 0 ? /* @__PURE__ */ jsx3(
963
1240
  "div",
964
1241
  {
965
1242
  className: cn(
@@ -984,7 +1261,7 @@ function ToastCard({
984
1261
  dismissible = true,
985
1262
  className
986
1263
  }) {
987
- return /* @__PURE__ */ jsx2(
1264
+ return /* @__PURE__ */ jsx3(
988
1265
  "div",
989
1266
  {
990
1267
  role: "status",
@@ -992,7 +1269,7 @@ function ToastCard({
992
1269
  "w-[368px] max-w-[calc(100vw-32px)] overflow-hidden rounded-md border border-ods-border bg-ods-card shadow-lg",
993
1270
  className
994
1271
  ),
995
- children: /* @__PURE__ */ jsx2(
1272
+ children: /* @__PURE__ */ jsx3(
996
1273
  ToastHeader,
997
1274
  {
998
1275
  id,
@@ -1041,7 +1318,7 @@ function CommandApprovalToast({
1041
1318
  className
1042
1319
  ),
1043
1320
  children: [
1044
- /* @__PURE__ */ jsx2(
1321
+ /* @__PURE__ */ jsx3(
1045
1322
  ToastHeader,
1046
1323
  {
1047
1324
  id,
@@ -1054,7 +1331,7 @@ function CommandApprovalToast({
1054
1331
  className: "border-b border-ods-border"
1055
1332
  }
1056
1333
  ),
1057
- /* @__PURE__ */ jsx2(
1334
+ /* @__PURE__ */ jsx3(
1058
1335
  "div",
1059
1336
  {
1060
1337
  className: "grid transition-[grid-template-rows] duration-300 ease-out",
@@ -1062,13 +1339,13 @@ function CommandApprovalToast({
1062
1339
  "aria-hidden": !expanded,
1063
1340
  children: /* @__PURE__ */ jsxs("div", { className: "overflow-hidden", children: [
1064
1341
  /* @__PURE__ */ jsxs("div", { className: "flex h-11 w-full items-center gap-2 border-b border-ods-border bg-ods-card px-3 py-2", children: [
1065
- /* @__PURE__ */ jsx2("p", { className: "min-w-0 flex-1 truncate font-['DM_Sans'] text-[14px] font-medium leading-5 text-ods-text-primary", title: command, children: command }),
1066
- toolType ? /* @__PURE__ */ jsx2(ToolIcon, { toolType, size: 16 }) : null
1342
+ /* @__PURE__ */ jsx3("p", { className: "min-w-0 flex-1 truncate font-['DM_Sans'] text-[14px] font-medium leading-5 text-ods-text-primary", title: command, children: command }),
1343
+ toolType ? /* @__PURE__ */ jsx3(ToolIcon, { toolType, size: 16 }) : null
1067
1344
  ] }),
1068
1345
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 bg-ods-bg p-3", children: [
1069
- approvalDescription ? /* @__PURE__ */ jsx2("p", { className: "font-['DM_Sans'] text-[14px] font-medium leading-5 text-ods-text-secondary", children: approvalDescription }) : null,
1346
+ approvalDescription ? /* @__PURE__ */ jsx3("p", { className: "font-['DM_Sans'] text-[14px] font-medium leading-5 text-ods-text-secondary", children: approvalDescription }) : null,
1070
1347
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
1071
- /* @__PURE__ */ jsx2(
1348
+ /* @__PURE__ */ jsx3(
1072
1349
  "button",
1073
1350
  {
1074
1351
  type: "button",
@@ -1078,7 +1355,7 @@ function CommandApprovalToast({
1078
1355
  children: approveLabel
1079
1356
  }
1080
1357
  ),
1081
- /* @__PURE__ */ jsx2(
1358
+ /* @__PURE__ */ jsx3(
1082
1359
  "button",
1083
1360
  {
1084
1361
  type: "button",
@@ -1101,8 +1378,8 @@ function CommandApprovalToast({
1101
1378
  className: "flex w-full items-center gap-2 bg-ods-card px-3 py-2 text-left font-['DM_Sans'] text-[14px] font-medium leading-5 text-ods-text-primary transition-colors hover:bg-ods-bg-hover",
1102
1379
  "aria-expanded": false,
1103
1380
  children: [
1104
- /* @__PURE__ */ jsx2("span", { className: "flex-1", children: "Show Command" }),
1105
- /* @__PURE__ */ jsx2(Chevron02DownIcon, { size: 16 })
1381
+ /* @__PURE__ */ jsx3("span", { className: "flex-1", children: "Show Command" }),
1382
+ /* @__PURE__ */ jsx3(Chevron02DownIcon, { size: 16 })
1106
1383
  ]
1107
1384
  }
1108
1385
  )
@@ -1119,13 +1396,13 @@ function Toaster({
1119
1396
  } = {}) {
1120
1397
  const { classNames: userClassNames, ...restToastOptions } = toastOptions ?? {};
1121
1398
  return /* @__PURE__ */ jsxs(Fragment2, { children: [
1122
- /* @__PURE__ */ jsx2("style", { children: `
1399
+ /* @__PURE__ */ jsx3("style", { children: `
1123
1400
  @keyframes toast-progress {
1124
1401
  from { transform: scaleX(1); }
1125
1402
  to { transform: scaleX(0); }
1126
1403
  }
1127
1404
  ` }),
1128
- /* @__PURE__ */ jsx2(
1405
+ /* @__PURE__ */ jsx3(
1129
1406
  SonnerToaster,
1130
1407
  {
1131
1408
  position,
@@ -1155,7 +1432,7 @@ function showToast(options) {
1155
1432
  ...rest
1156
1433
  } = opts;
1157
1434
  return sonnerToast.custom(
1158
- (id) => /* @__PURE__ */ jsx2(
1435
+ (id) => /* @__PURE__ */ jsx3(
1159
1436
  ToastCard,
1160
1437
  {
1161
1438
  id,
@@ -1187,7 +1464,7 @@ function showCommandApprovalToast(options) {
1187
1464
  ...rest
1188
1465
  } = options;
1189
1466
  return sonnerToast.custom(
1190
- (id) => /* @__PURE__ */ jsx2(
1467
+ (id) => /* @__PURE__ */ jsx3(
1191
1468
  CommandApprovalToast,
1192
1469
  {
1193
1470
  id,
@@ -1236,16 +1513,270 @@ var useToast = () => ({
1236
1513
  });
1237
1514
 
1238
1515
  // src/hooks/use-contact-submission.ts
1239
- import { useState as useState13, useCallback as useCallback6, useEffect as useEffect9 } from "react";
1516
+ import { useState as useState15, useCallback as useCallback7, useEffect as useEffect11 } from "react";
1240
1517
  init_next_navigation();
1518
+
1519
+ // src/utils/local-storage-adapter.ts
1520
+ function getStorage(backend) {
1521
+ if (typeof window === "undefined") return null;
1522
+ try {
1523
+ return backend === "session" ? window.sessionStorage : window.localStorage;
1524
+ } catch {
1525
+ return null;
1526
+ }
1527
+ }
1528
+ function createLocalStorageAdapter(options) {
1529
+ const tag = options.logTag ?? "[local-storage]";
1530
+ const backend = options.backend ?? "local";
1531
+ const resolveKey = () => {
1532
+ const ns = options.namespace?.();
1533
+ return ns ? `${ns}.${options.key}` : options.key;
1534
+ };
1535
+ return {
1536
+ resolveKey,
1537
+ load() {
1538
+ const storage = getStorage(backend);
1539
+ if (!storage) return null;
1540
+ try {
1541
+ const raw = storage.getItem(resolveKey());
1542
+ if (!raw) return null;
1543
+ const parsed = JSON.parse(raw);
1544
+ if (options.validate && !options.validate(parsed)) return null;
1545
+ return parsed;
1546
+ } catch (err) {
1547
+ console.warn(`${tag} parse failed for key ${resolveKey()}:`, err);
1548
+ return null;
1549
+ }
1550
+ },
1551
+ save(value) {
1552
+ const storage = getStorage(backend);
1553
+ if (!storage) return;
1554
+ try {
1555
+ storage.setItem(resolveKey(), JSON.stringify(value));
1556
+ } catch (err) {
1557
+ console.warn(`${tag} write failed for key ${resolveKey()}:`, err);
1558
+ }
1559
+ },
1560
+ clear() {
1561
+ const storage = getStorage(backend);
1562
+ if (!storage) return;
1563
+ try {
1564
+ storage.removeItem(resolveKey());
1565
+ } catch (err) {
1566
+ console.warn(`${tag} clear failed for key ${resolveKey()}:`, err);
1567
+ }
1568
+ }
1569
+ };
1570
+ }
1571
+
1572
+ // src/utils/app-config.ts
1573
+ function getAppType() {
1574
+ return process.env.NEXT_PUBLIC_APP_TYPE || "openmsp";
1575
+ }
1576
+
1577
+ // src/utils/embed-proxy-auth-storage.ts
1578
+ function isValidPersistedAuth(value) {
1579
+ if (!value || typeof value !== "object") return false;
1580
+ const v = value;
1581
+ if (typeof v.secret !== "string" || v.secret.trim().length === 0 || typeof v.email !== "string" || v.email.trim().length === 0) return false;
1582
+ if (v.firstName != null && typeof v.firstName !== "string") return false;
1583
+ if (v.lastName != null && typeof v.lastName !== "string") return false;
1584
+ if (v.avatarUrl != null && typeof v.avatarUrl !== "string") return false;
1585
+ return true;
1586
+ }
1587
+ var adapter = createLocalStorageAdapter({
1588
+ // Storage key unchanged from the legacy chat-prefixed helper. Renaming
1589
+ // it would silently log every existing admin out — the key is a
1590
+ // storage contract, not a code identifier.
1591
+ key: "chat.proxy-auth.v1",
1592
+ namespace: () => getAppType(),
1593
+ validate: isValidPersistedAuth,
1594
+ logTag: "[embed-proxy-auth-storage]",
1595
+ // localStorage — survives tab close, new tabs, and browser restarts.
1596
+ // Admin re-pasting creds every tab cycle was the dev-experience
1597
+ // tradeoff prior `sessionStorage` setup demanded — rejected. See
1598
+ // file-level doc comment for the security tradeoff rationale.
1599
+ backend: "local"
1600
+ });
1601
+ function normalizeOptional(value) {
1602
+ if (!value) return void 0;
1603
+ const trimmed = value.trim();
1604
+ return trimmed.length > 0 ? trimmed : void 0;
1605
+ }
1606
+ function getEmbedProxyAuth() {
1607
+ const persisted = adapter.load();
1608
+ if (!persisted) return null;
1609
+ return {
1610
+ secret: persisted.secret,
1611
+ email: persisted.email.trim().toLowerCase(),
1612
+ firstName: normalizeOptional(persisted.firstName),
1613
+ lastName: normalizeOptional(persisted.lastName),
1614
+ avatarUrl: normalizeOptional(persisted.avatarUrl)
1615
+ };
1616
+ }
1617
+ function getPersistedProxyEmail() {
1618
+ const persisted = adapter.load();
1619
+ return persisted?.email.trim().toLowerCase() ?? null;
1620
+ }
1621
+ function setEmbedProxyAuth(value) {
1622
+ adapter.save({
1623
+ secret: value.secret,
1624
+ email: value.email.trim().toLowerCase(),
1625
+ firstName: normalizeOptional(value.firstName),
1626
+ lastName: normalizeOptional(value.lastName),
1627
+ avatarUrl: normalizeOptional(value.avatarUrl)
1628
+ });
1629
+ }
1630
+ function clearEmbedProxyAuth() {
1631
+ adapter.clear();
1632
+ }
1633
+ function applyProxyAuth(url, baseHeaders = { "Content-Type": "application/json" }) {
1634
+ const auth = getEmbedProxyAuth();
1635
+ const headers = { ...baseHeaders };
1636
+ if (auth?.secret) {
1637
+ headers.Authorization = `Bearer ${auth.secret}`;
1638
+ }
1639
+ if (auth?.email) {
1640
+ headers["X-Chat-Act-As"] = auth.email;
1641
+ }
1642
+ if (auth?.firstName) headers["X-Chat-First-Name"] = auth.firstName;
1643
+ if (auth?.lastName) headers["X-Chat-Last-Name"] = auth.lastName;
1644
+ if (auth?.avatarUrl) headers["X-Chat-Avatar-Url"] = auth.avatarUrl;
1645
+ return { url, headers };
1646
+ }
1647
+
1648
+ // src/utils/embed-authed-fetch.ts
1649
+ var ADAPTER_GLOBAL_KEY = "__embedAuthedFetchAdapter__";
1650
+ function getRegisteredAuthAdapter() {
1651
+ if (typeof globalThis === "undefined") return null;
1652
+ return globalThis[ADAPTER_GLOBAL_KEY] ?? null;
1653
+ }
1654
+ function storeRegisteredAuthAdapter(adapter2) {
1655
+ if (typeof globalThis === "undefined") return;
1656
+ globalThis[ADAPTER_GLOBAL_KEY] = adapter2;
1657
+ }
1658
+ function setEmbedAuthAdapter(adapter2) {
1659
+ if (adapter2 && getRegisteredAuthAdapter() && process.env.NODE_ENV !== "production") {
1660
+ console.warn(
1661
+ "[setEmbedAuthAdapter] overwriting a previously-registered auth adapter. Two chat-runtime providers should not coexist \u2014 verify mount order and pass `null` from the unmounting provider."
1662
+ );
1663
+ }
1664
+ storeRegisteredAuthAdapter(adapter2);
1665
+ }
1666
+ function hasEmbedAuthAdapter() {
1667
+ return getRegisteredAuthAdapter() !== null;
1668
+ }
1669
+ function embedAuthedFetch(url, init = {}) {
1670
+ assertSameOrigin(url);
1671
+ let baseHeaders;
1672
+ if (init.headers === void 0) {
1673
+ baseHeaders = { "Content-Type": "application/json" };
1674
+ } else {
1675
+ baseHeaders = {};
1676
+ if (init.headers instanceof Headers) {
1677
+ init.headers.forEach((v, k) => {
1678
+ baseHeaders[k] = v;
1679
+ });
1680
+ } else if (Array.isArray(init.headers)) {
1681
+ for (const [k, v] of init.headers) baseHeaders[k] = v;
1682
+ } else {
1683
+ Object.assign(baseHeaders, init.headers);
1684
+ }
1685
+ }
1686
+ return fetchWithRefresh(url, init, baseHeaders, false);
1687
+ }
1688
+ var IN_FLIGHT_REFRESH_GLOBAL_KEY = "__embedAuthedFetchInFlightRefresh__";
1689
+ function getInFlightRefresh() {
1690
+ if (typeof globalThis === "undefined") return null;
1691
+ return globalThis[IN_FLIGHT_REFRESH_GLOBAL_KEY] ?? null;
1692
+ }
1693
+ function setInFlightRefresh(refresh) {
1694
+ if (typeof globalThis === "undefined") return;
1695
+ globalThis[IN_FLIGHT_REFRESH_GLOBAL_KEY] = refresh;
1696
+ }
1697
+ function dedupedRefresh() {
1698
+ const adapter2 = getRegisteredAuthAdapter();
1699
+ if (!adapter2?.refresh) return Promise.resolve(false);
1700
+ let inFlightRefresh = getInFlightRefresh();
1701
+ if (!inFlightRefresh) {
1702
+ inFlightRefresh = Promise.resolve().then(() => adapter2.refresh()).catch(() => false).finally(() => {
1703
+ setInFlightRefresh(null);
1704
+ });
1705
+ setInFlightRefresh(inFlightRefresh);
1706
+ }
1707
+ return inFlightRefresh;
1708
+ }
1709
+ async function fetchWithRefresh(url, init, baseHeaders, isRetry) {
1710
+ const { url: authedUrl, headers } = applyProxyAuth(url, { ...baseHeaders });
1711
+ const adapter2 = getRegisteredAuthAdapter();
1712
+ if (adapter2?.getHeaders) {
1713
+ for (const [k, v] of Object.entries(adapter2.getHeaders())) {
1714
+ if (v !== void 0) headers[k] = v;
1715
+ }
1716
+ }
1717
+ const credentials = adapter2?.credentials ?? init.credentials ?? "same-origin";
1718
+ const response = await fetch(authedUrl, {
1719
+ ...init,
1720
+ headers,
1721
+ // Default `same-origin` carries Supabase cookies for the MPH proxy-
1722
+ // auth model. Hosts on different origins (openframe-frontend ↔
1723
+ // openframe gateway) register `credentials: 'include'` via the
1724
+ // adapter to make their own cookies travel cross-origin (CORS +
1725
+ // `SameSite=None` must be configured server-side for that to work).
1726
+ credentials
1727
+ });
1728
+ if (response.status === 401 && !isRetry && adapter2?.refresh) {
1729
+ const refreshed = await dedupedRefresh();
1730
+ if (refreshed) {
1731
+ return fetchWithRefresh(url, init, baseHeaders, true);
1732
+ }
1733
+ }
1734
+ return response;
1735
+ }
1736
+ function assertSameOrigin(url) {
1737
+ if (typeof window === "undefined") return;
1738
+ let target;
1739
+ let pageOrigin;
1740
+ try {
1741
+ target = new URL(url, window.location.href);
1742
+ pageOrigin = new URL(window.location.href).origin;
1743
+ } catch {
1744
+ throw new Error(`embedAuthedFetch: refusing to fetch malformed URL (${JSON.stringify(url)})`);
1745
+ }
1746
+ if (target.protocol !== "http:" && target.protocol !== "https:") {
1747
+ throw new Error(
1748
+ `embedAuthedFetch: refusing non-http(s) URL (${target.protocol}) \u2014 pass a relative /api/* path instead`
1749
+ );
1750
+ }
1751
+ if (target.origin !== pageOrigin) {
1752
+ if (process.env.NODE_ENV !== "production") {
1753
+ console.warn(
1754
+ `[embedAuthedFetch] cross-origin fetch to ${target.origin} allowed in dev (NODE_ENV !== 'production'). Production builds will reject this \u2014 wire a same-origin proxy before shipping.`
1755
+ );
1756
+ return;
1757
+ }
1758
+ throw new Error(
1759
+ `embedAuthedFetch: refusing cross-origin fetch to ${target.origin} \u2014 pass a relative /api/* path instead`
1760
+ );
1761
+ }
1762
+ }
1763
+
1764
+ // src/utils/embed-content-fetch.ts
1765
+ var contentFetch = (input, init) => {
1766
+ if (!hasEmbedAuthAdapter()) return fetch(input, init);
1767
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1768
+ return embedAuthedFetch(url, init);
1769
+ };
1770
+
1771
+ // src/hooks/use-contact-submission.ts
1241
1772
  function useContactSubmission(options = {}) {
1242
1773
  const { userId, successRedirectUrl, successToastMessage, onSuccess } = options;
1243
1774
  const { toast: toast2 } = useToast();
1244
1775
  const router = useRouter();
1245
1776
  const { contactUrl } = useRequiredEndpointsRuntime();
1246
- const [isSubmitting, setIsSubmitting] = useState13(false);
1247
- const [isSuccess, setIsSuccess] = useState13(false);
1248
- const submit = useCallback6(async (formData) => {
1777
+ const [isSubmitting, setIsSubmitting] = useState15(false);
1778
+ const [isSuccess, setIsSuccess] = useState15(false);
1779
+ const submit = useCallback7(async (formData) => {
1249
1780
  if (isSubmitting) return;
1250
1781
  setIsSubmitting(true);
1251
1782
  try {
@@ -1280,7 +1811,7 @@ function useContactSubmission(options = {}) {
1280
1811
  setIsSubmitting(false);
1281
1812
  }
1282
1813
  }, [isSubmitting, toast2, userId, successToastMessage, contactUrl]);
1283
- useEffect9(() => {
1814
+ useEffect11(() => {
1284
1815
  if (isSuccess && successRedirectUrl) {
1285
1816
  console.log("\u{1F680} Contact submission successful, redirecting to:", successRedirectUrl);
1286
1817
  const timer = setTimeout(() => {
@@ -1294,7 +1825,7 @@ function useContactSubmission(options = {}) {
1294
1825
  return () => clearTimeout(timer);
1295
1826
  }
1296
1827
  }, [isSuccess, successRedirectUrl, router]);
1297
- useEffect9(() => {
1828
+ useEffect11(() => {
1298
1829
  if (isSuccess && onSuccess) {
1299
1830
  onSuccess();
1300
1831
  }
@@ -1303,19 +1834,19 @@ function useContactSubmission(options = {}) {
1303
1834
  }
1304
1835
 
1305
1836
  // src/hooks/use-quick-action-hint.ts
1306
- import { useEffect as useEffect10, useState as useState14, useRef as useRef5, useCallback as useCallback7 } from "react";
1837
+ import { useEffect as useEffect12, useState as useState16, useRef as useRef6, useCallback as useCallback8 } from "react";
1307
1838
  function useQuickActionHint({
1308
1839
  actionCount,
1309
1840
  cycleDuration = 5e3,
1310
1841
  enabled = true
1311
1842
  }) {
1312
- const [activeHintIndex, setActiveHintIndex] = useState14(-1);
1313
- const containerRef = useRef5(null);
1314
- const timeoutRef = useRef5(null);
1315
- const cycleCountRef = useRef5(0);
1316
- const currentIndexRef = useRef5(0);
1317
- const sequenceRef = useRef5([]);
1318
- const shuffleIndices = useCallback7((length) => {
1843
+ const [activeHintIndex, setActiveHintIndex] = useState16(-1);
1844
+ const containerRef = useRef6(null);
1845
+ const timeoutRef = useRef6(null);
1846
+ const cycleCountRef = useRef6(0);
1847
+ const currentIndexRef = useRef6(0);
1848
+ const sequenceRef = useRef6([]);
1849
+ const shuffleIndices = useCallback8((length) => {
1319
1850
  const indices = Array.from({ length }, (_, i) => i);
1320
1851
  for (let i = indices.length - 1; i > 0; i--) {
1321
1852
  const j = Math.floor(Math.random() * (i + 1));
@@ -1323,7 +1854,7 @@ function useQuickActionHint({
1323
1854
  }
1324
1855
  return indices;
1325
1856
  }, []);
1326
- const stopHint = useCallback7(() => {
1857
+ const stopHint = useCallback8(() => {
1327
1858
  if (timeoutRef.current) {
1328
1859
  clearTimeout(timeoutRef.current);
1329
1860
  timeoutRef.current = null;
@@ -1333,7 +1864,7 @@ function useQuickActionHint({
1333
1864
  currentIndexRef.current = 0;
1334
1865
  sequenceRef.current = [];
1335
1866
  }, []);
1336
- const advanceHint = useCallback7(() => {
1867
+ const advanceHint = useCallback8(() => {
1337
1868
  if (currentIndexRef.current === 0) {
1338
1869
  sequenceRef.current = shuffleIndices(actionCount);
1339
1870
  }
@@ -1346,7 +1877,7 @@ function useQuickActionHint({
1346
1877
  }
1347
1878
  timeoutRef.current = setTimeout(advanceHint, cycleDuration);
1348
1879
  }, [actionCount, cycleDuration, shuffleIndices]);
1349
- useEffect10(() => {
1880
+ useEffect12(() => {
1350
1881
  if (!enabled || actionCount === 0) {
1351
1882
  return;
1352
1883
  }
@@ -1372,7 +1903,7 @@ function useQuickActionHint({
1372
1903
  }
1373
1904
 
1374
1905
  // src/hooks/use-copy-to-clipboard.ts
1375
- import { useCallback as useCallback8, useState as useState15 } from "react";
1906
+ import { useCallback as useCallback9, useState as useState17 } from "react";
1376
1907
  function useCopyToClipboard({
1377
1908
  successTitle = "Copied",
1378
1909
  successDescription = "Copied to clipboard",
@@ -1381,8 +1912,8 @@ function useCopyToClipboard({
1381
1912
  resetDelay = 2e3
1382
1913
  } = {}) {
1383
1914
  const { toast: toast2 } = useToast();
1384
- const [copied, setCopied] = useState15(false);
1385
- const copy = useCallback8(
1915
+ const [copied, setCopied] = useState17(false);
1916
+ const copy = useCallback9(
1386
1917
  async (text) => {
1387
1918
  try {
1388
1919
  await navigator.clipboard.writeText(text);
@@ -1399,7 +1930,7 @@ function useCopyToClipboard({
1399
1930
  }
1400
1931
 
1401
1932
  // src/hooks/use-batch-images.ts
1402
- import { useEffect as useEffect11, useMemo as useMemo2, useState as useState16, useRef as useRef6 } from "react";
1933
+ import { useEffect as useEffect13, useMemo as useMemo2, useState as useState18, useRef as useRef7 } from "react";
1403
1934
  var globalBatchImageConfig = {};
1404
1935
  function configureBatchImageFetch(config) {
1405
1936
  globalBatchImageConfig = { ...globalBatchImageConfig, ...config };
@@ -1472,14 +2003,14 @@ async function batchFetchAuthenticatedImages(imageUrls, config) {
1472
2003
  return results;
1473
2004
  }
1474
2005
  function useBatchImages(imageUrls, config) {
1475
- const [fetchedImages, setFetchedImages] = useState16({});
1476
- const [loading, setLoading] = useState16(false);
2006
+ const [fetchedImages, setFetchedImages] = useState18({});
2007
+ const [loading, setLoading] = useState18(false);
1477
2008
  const uniqueUrls = useMemo2(
1478
2009
  () => Array.from(new Set(imageUrls.filter((url) => Boolean(url)))),
1479
2010
  [imageUrls]
1480
2011
  );
1481
- const requestedUrls = useRef6(/* @__PURE__ */ new Set());
1482
- useEffect11(() => {
2012
+ const requestedUrls = useRef7(/* @__PURE__ */ new Set());
2013
+ useEffect13(() => {
1483
2014
  if (uniqueUrls.length === 0) {
1484
2015
  setFetchedImages({});
1485
2016
  return;
@@ -1509,7 +2040,7 @@ function useBatchImages(imageUrls, config) {
1509
2040
  }
1510
2041
 
1511
2042
  // src/hooks/use-authenticated-image.ts
1512
- import { useEffect as useEffect12, useState as useState17, useRef as useRef7 } from "react";
2043
+ import { useEffect as useEffect14, useState as useState19, useRef as useRef8 } from "react";
1513
2044
  var globalImageConfig = {};
1514
2045
  var imageCache = /* @__PURE__ */ new Map();
1515
2046
  var pendingRequests = /* @__PURE__ */ new Map();
@@ -1538,11 +2069,11 @@ function getImageConfig() {
1538
2069
  };
1539
2070
  }
1540
2071
  function useAuthenticatedImage(imageUrl, refreshKey, config) {
1541
- const [fetchedImageUrl, setFetchedImageUrl] = useState17();
1542
- const [isLoading, setIsLoading] = useState17(false);
1543
- const [error, setError] = useState17(null);
1544
- const currentCacheKeyRef = useRef7(null);
1545
- useEffect12(() => {
2072
+ const [fetchedImageUrl, setFetchedImageUrl] = useState19();
2073
+ const [isLoading, setIsLoading] = useState19(false);
2074
+ const [error, setError] = useState19(null);
2075
+ const currentCacheKeyRef = useRef8(null);
2076
+ useEffect14(() => {
1546
2077
  if (!imageUrl) {
1547
2078
  setFetchedImageUrl(void 0);
1548
2079
  setIsLoading(false);
@@ -1653,7 +2184,7 @@ function useAuthenticatedImage(imageUrl, refreshKey, config) {
1653
2184
  });
1654
2185
  pendingRequests.set(cacheKey, fetchPromise2);
1655
2186
  }, [imageUrl, refreshKey, config]);
1656
- useEffect12(() => {
2187
+ useEffect14(() => {
1657
2188
  return () => {
1658
2189
  if (currentCacheKeyRef.current) {
1659
2190
  const entry = imageCache.get(currentCacheKeyRef.current);
@@ -1668,7 +2199,7 @@ function useAuthenticatedImage(imageUrl, refreshKey, config) {
1668
2199
 
1669
2200
  // src/hooks/state/use-query-params.ts
1670
2201
  init_next_navigation();
1671
- import { useEffect as useEffect13, useState as useState18, useMemo as useMemo3, useCallback as useCallback9 } from "react";
2202
+ import { useEffect as useEffect15, useState as useState20, useMemo as useMemo3, useCallback as useCallback10 } from "react";
1672
2203
 
1673
2204
  // src/hooks/state/graphql-parser.ts
1674
2205
  import {
@@ -2130,14 +2661,14 @@ var introspector = new GraphQLIntrospector();
2130
2661
  function useQueryParams(query, options = {}) {
2131
2662
  const router = useRouter();
2132
2663
  const searchParams = useSearchParams();
2133
- const [isLoading, setIsLoading] = useState18(true);
2134
- const [isReady, setIsReady] = useState18(false);
2135
- const [error, setError] = useState18(null);
2136
- const [schema, setSchema] = useState18({});
2664
+ const [isLoading, setIsLoading] = useState20(true);
2665
+ const [isReady, setIsReady] = useState20(false);
2666
+ const [error, setError] = useState20(null);
2667
+ const [schema, setSchema] = useState20({});
2137
2668
  const defaultValues = options.defaultValues || {};
2138
2669
  const skipIntrospection = options.skipIntrospection || false;
2139
2670
  const debug = options.debug || false;
2140
- useEffect13(() => {
2671
+ useEffect15(() => {
2141
2672
  async function initialize() {
2142
2673
  try {
2143
2674
  if (debug) console.log("[useQueryParams] Initializing...");
@@ -2207,14 +2738,14 @@ function useQueryParams(query, options = {}) {
2207
2738
  });
2208
2739
  return result;
2209
2740
  }, [searchParams]);
2210
- const updateUrl = useCallback9((newParams) => {
2741
+ const updateUrl = useCallback10((newParams) => {
2211
2742
  const url = newParams.toString() ? `?${newParams.toString()}` : window.location.pathname;
2212
2743
  if (debug) {
2213
2744
  console.log("[useQueryParams] Updating URL:", url);
2214
2745
  }
2215
2746
  router.replace(url, { scroll: false });
2216
2747
  }, [router, debug]);
2217
- const setParam = useCallback9((key, value) => {
2748
+ const setParam = useCallback10((key, value) => {
2218
2749
  if (!isReady) {
2219
2750
  console.warn("[useQueryParams] Schema not ready, cannot set param");
2220
2751
  return;
@@ -2228,7 +2759,7 @@ function useQueryParams(query, options = {}) {
2228
2759
  console.error("[useQueryParams] Failed to set param:", err);
2229
2760
  }
2230
2761
  }, [variables, schema, isReady, updateUrl]);
2231
- const setParams = useCallback9((updates) => {
2762
+ const setParams = useCallback10((updates) => {
2232
2763
  if (!isReady) {
2233
2764
  console.warn("[useQueryParams] Schema not ready, cannot set params");
2234
2765
  return;
@@ -2242,7 +2773,7 @@ function useQueryParams(query, options = {}) {
2242
2773
  console.error("[useQueryParams] Failed to set params:", err);
2243
2774
  }
2244
2775
  }, [variables, schema, isReady, updateUrl]);
2245
- const clearParamsHandler = useCallback9((keys) => {
2776
+ const clearParamsHandler = useCallback10((keys) => {
2246
2777
  if (!isReady) {
2247
2778
  console.warn("[useQueryParams] Schema not ready, cannot clear params");
2248
2779
  return;
@@ -2256,7 +2787,7 @@ function useQueryParams(query, options = {}) {
2256
2787
  console.error("[useQueryParams] Failed to clear params:", err);
2257
2788
  }
2258
2789
  }, [variables, schema, isReady, updateUrl]);
2259
- const resetParams = useCallback9(() => {
2790
+ const resetParams = useCallback10(() => {
2260
2791
  if (debug) {
2261
2792
  console.log("[useQueryParams] Resetting params");
2262
2793
  }
@@ -2289,9 +2820,9 @@ function applyParamMapping(schema, mapping) {
2289
2820
 
2290
2821
  // src/hooks/state/use-api-params.ts
2291
2822
  init_next_navigation();
2292
- import { useCallback as useCallback10, useMemo as useMemo4, useRef as useRef8 } from "react";
2823
+ import { useCallback as useCallback11, useMemo as useMemo4, useRef as useRef9 } from "react";
2293
2824
  function useContentStable(value, key) {
2294
- const ref = useRef8(void 0);
2825
+ const ref = useRef9(void 0);
2295
2826
  if (ref.current && ref.current.key === key) return ref.current.value;
2296
2827
  ref.current = { value, key };
2297
2828
  return value;
@@ -2324,7 +2855,7 @@ function useApiParams(schema, options = {}) {
2324
2855
  }
2325
2856
  return flattened;
2326
2857
  }, [schemaKey]);
2327
- const prevParamsRef = useRef8(void 0);
2858
+ const prevParamsRef = useRef9(void 0);
2328
2859
  const params = useMemo4(() => {
2329
2860
  const sp = new URLSearchParams(searchString);
2330
2861
  const result = {};
@@ -2348,7 +2879,7 @@ function useApiParams(schema, options = {}) {
2348
2879
  prevParamsRef.current = result;
2349
2880
  return result;
2350
2881
  }, [searchString, schemaKey, debug]);
2351
- const addParamToSearchParams = useCallback10((searchParams, key, value) => {
2882
+ const addParamToSearchParams = useCallback11((searchParams, key, value) => {
2352
2883
  if (value === void 0 || value === "" || value === null) {
2353
2884
  return;
2354
2885
  }
@@ -2376,7 +2907,7 @@ function useApiParams(schema, options = {}) {
2376
2907
  }
2377
2908
  return newParams;
2378
2909
  }, [params, schemaKey, flattenedSchema, addParamToSearchParams]);
2379
- const updateUrl = useCallback10((newParams, keysToRemove = []) => {
2910
+ const updateUrl = useCallback11((newParams, keysToRemove = []) => {
2380
2911
  const finalParams = new URLSearchParams(searchString);
2381
2912
  keysToRemove.forEach((key) => {
2382
2913
  if (key in stableSchema) {
@@ -2412,7 +2943,7 @@ function useApiParams(schema, options = {}) {
2412
2943
  }
2413
2944
  return false;
2414
2945
  };
2415
- const setParam = useCallback10((key, value) => {
2946
+ const setParam = useCallback11((key, value) => {
2416
2947
  const config = stableSchema[key];
2417
2948
  if (!config) {
2418
2949
  console.warn(`[useApiParams] Unknown parameter: ${key}`);
@@ -2426,7 +2957,7 @@ function useApiParams(schema, options = {}) {
2426
2957
  updateUrl(newParams);
2427
2958
  }
2428
2959
  }, [schemaKey, updateUrl, addParamToSearchParams]);
2429
- const setParams = useCallback10((updates) => {
2960
+ const setParams = useCallback11((updates) => {
2430
2961
  const newParams = new URLSearchParams();
2431
2962
  const keysToRemove = [];
2432
2963
  for (const [key, value] of Object.entries(updates)) {
@@ -2443,11 +2974,11 @@ function useApiParams(schema, options = {}) {
2443
2974
  }
2444
2975
  updateUrl(newParams, keysToRemove);
2445
2976
  }, [schemaKey, updateUrl, addParamToSearchParams]);
2446
- const clearParams2 = useCallback10((keys) => {
2977
+ const clearParams2 = useCallback11((keys) => {
2447
2978
  const newParams = new URLSearchParams();
2448
2979
  updateUrl(newParams, keys);
2449
2980
  }, [updateUrl]);
2450
- const resetParams = useCallback10(() => {
2981
+ const resetParams = useCallback11(() => {
2451
2982
  if (debug) {
2452
2983
  console.log("[useApiParams] Resetting params");
2453
2984
  }
@@ -2484,7 +3015,7 @@ function createSearchParams(params) {
2484
3015
  }
2485
3016
 
2486
3017
  // src/hooks/state/use-cursor-pagination-state.ts
2487
- import { useCallback as useCallback11, useEffect as useEffect14, useRef as useRef9, useState as useState19 } from "react";
3018
+ import { useCallback as useCallback12, useEffect as useEffect16, useRef as useRef10, useState as useState21 } from "react";
2488
3019
  var urlSchema = {
2489
3020
  search: { type: "string", default: "" },
2490
3021
  cursor: { type: "string", default: "" }
@@ -2495,13 +3026,13 @@ function useCursorPaginationState(options) {
2495
3026
  onSearchChange
2496
3027
  } = options;
2497
3028
  const { params, setParam, setParams } = useApiParams(urlSchema);
2498
- const [searchInput, setSearchInput] = useState19(params.search || "");
2499
- const [hasLoadedBeyondFirst, setHasLoadedBeyondFirst] = useState19(false);
2500
- const [initialLoadCount, setInitialLoadCount] = useState19(0);
2501
- const lastSearchRef = useRef9(null);
2502
- const isSyncingFromUrl = useRef9(false);
2503
- const isInitialLoadInProgress = useRef9(true);
2504
- useEffect14(() => {
3029
+ const [searchInput, setSearchInput] = useState21(params.search || "");
3030
+ const [hasLoadedBeyondFirst, setHasLoadedBeyondFirst] = useState21(false);
3031
+ const [initialLoadCount, setInitialLoadCount] = useState21(0);
3032
+ const lastSearchRef = useRef10(null);
3033
+ const isSyncingFromUrl = useRef10(false);
3034
+ const isInitialLoadInProgress = useRef10(true);
3035
+ useEffect16(() => {
2505
3036
  if (isInitialLoadInProgress.current) return;
2506
3037
  const urlSearch = params.search || "";
2507
3038
  if (urlSearch !== searchInput) {
@@ -2512,7 +3043,7 @@ function useCursorPaginationState(options) {
2512
3043
  }, 0);
2513
3044
  }
2514
3045
  }, [params.search, initialLoadCount]);
2515
- useEffect14(() => {
3046
+ useEffect16(() => {
2516
3047
  if (isInitialLoadInProgress.current) return;
2517
3048
  if (isSyncingFromUrl.current) return;
2518
3049
  if (searchInput !== params.search) {
@@ -2523,7 +3054,7 @@ function useCursorPaginationState(options) {
2523
3054
  });
2524
3055
  }
2525
3056
  }, [searchInput, params.search, setParams, initialLoadCount]);
2526
- useEffect14(() => {
3057
+ useEffect16(() => {
2527
3058
  if (initialLoadCount === 0) {
2528
3059
  const cursor = params.cursor || null;
2529
3060
  const search = params.search || "";
@@ -2537,7 +3068,7 @@ function useCursorPaginationState(options) {
2537
3068
  });
2538
3069
  }
2539
3070
  }, []);
2540
- useEffect14(() => {
3071
+ useEffect16(() => {
2541
3072
  if (isInitialLoadInProgress.current) return;
2542
3073
  if (initialLoadCount === 0) return;
2543
3074
  const currentSearch = params.search || "";
@@ -2547,7 +3078,7 @@ function useCursorPaginationState(options) {
2547
3078
  onSearchChange(currentSearch);
2548
3079
  }
2549
3080
  }, [params.search, onSearchChange, initialLoadCount]);
2550
- const handleNextPage = useCallback11(
3081
+ const handleNextPage = useCallback12(
2551
3082
  async (endCursor, fetchFn) => {
2552
3083
  setParam("cursor", endCursor);
2553
3084
  await fetchFn();
@@ -2555,7 +3086,7 @@ function useCursorPaginationState(options) {
2555
3086
  },
2556
3087
  [setParam]
2557
3088
  );
2558
- const handleResetToFirstPage = useCallback11(
3089
+ const handleResetToFirstPage = useCallback12(
2559
3090
  async (fetchFn) => {
2560
3091
  setParam("cursor", "");
2561
3092
  await fetchFn();
@@ -2577,16 +3108,16 @@ function useCursorPaginationState(options) {
2577
3108
  }
2578
3109
 
2579
3110
  // src/hooks/nats/use-nats-client.ts
2580
- import { useEffect as useEffect15, useMemo as useMemo5, useState as useState20 } from "react";
3111
+ import { useEffect as useEffect17, useMemo as useMemo5, useState as useState22 } from "react";
2581
3112
  function useNatsClient(clientOptions, options = {}) {
2582
3113
  const { autoConnect = true } = options;
2583
3114
  const client = useMemo5(() => {
2584
3115
  if (!clientOptions) return null;
2585
3116
  return createNatsClient(clientOptions);
2586
3117
  }, [clientOptions]);
2587
- const [status, setStatus] = useState20("disconnected");
2588
- const [lastError, setLastError] = useState20(null);
2589
- useEffect15(() => {
3118
+ const [status, setStatus] = useState22("disconnected");
3119
+ const [lastError, setLastError] = useState22(null);
3120
+ useEffect17(() => {
2590
3121
  if (!client) {
2591
3122
  setStatus("disconnected");
2592
3123
  setLastError(null);
@@ -2616,8 +3147,125 @@ function useNatsClient(clientOptions, options = {}) {
2616
3147
  };
2617
3148
  }
2618
3149
 
3150
+ // src/hooks/use-near-viewport.ts
3151
+ import { useEffect as useEffect18, useRef as useRef11, useState as useState23, useCallback as useCallback13 } from "react";
3152
+ var observers = /* @__PURE__ */ new Map();
3153
+ var subscribers = /* @__PURE__ */ new WeakMap();
3154
+ function getObserverFor(rootMargin) {
3155
+ const existing = observers.get(rootMargin);
3156
+ if (existing) return existing;
3157
+ const io = new IntersectionObserver(
3158
+ (entries) => {
3159
+ entries.forEach((entry) => {
3160
+ if (!entry.isIntersecting) return;
3161
+ const cb = subscribers.get(entry.target);
3162
+ if (cb) {
3163
+ cb();
3164
+ io.unobserve(entry.target);
3165
+ subscribers.delete(entry.target);
3166
+ }
3167
+ });
3168
+ },
3169
+ { rootMargin }
3170
+ );
3171
+ observers.set(rootMargin, io);
3172
+ return io;
3173
+ }
3174
+ function useNearViewport(rootMargin = "500px") {
3175
+ const [isNear, setIsNear] = useState23(false);
3176
+ const elRef = useRef11(null);
3177
+ const ref = useCallback13(
3178
+ (node) => {
3179
+ const prev = elRef.current;
3180
+ if (prev) {
3181
+ const stillOurs = subscribers.get(prev);
3182
+ if (stillOurs) {
3183
+ subscribers.delete(prev);
3184
+ observers.get(rootMargin)?.unobserve(prev);
3185
+ }
3186
+ }
3187
+ elRef.current = node;
3188
+ if (!node) return;
3189
+ const cb = () => setIsNear(true);
3190
+ subscribers.set(node, cb);
3191
+ getObserverFor(rootMargin).observe(node);
3192
+ },
3193
+ [rootMargin]
3194
+ );
3195
+ useEffect18(() => {
3196
+ return () => {
3197
+ const el = elRef.current;
3198
+ if (!el) return;
3199
+ if (subscribers.get(el)) {
3200
+ subscribers.delete(el);
3201
+ observers.get(rootMargin)?.unobserve(el);
3202
+ }
3203
+ };
3204
+ }, [rootMargin]);
3205
+ return { ref, isNear };
3206
+ }
3207
+
2619
3208
  // src/hooks/use-access-code-integration.ts
2620
3209
  import React2 from "react";
3210
+
3211
+ // src/utils/access-code-client.ts
3212
+ async function validateAccessCode(email, code, endpoints) {
3213
+ try {
3214
+ const response = await fetch(endpoints.validateUrl, {
3215
+ method: "POST",
3216
+ headers: {
3217
+ "Content-Type": "application/json"
3218
+ },
3219
+ body: JSON.stringify({ email, code })
3220
+ });
3221
+ if (!response.ok) {
3222
+ const error = await response.json().catch(() => ({}));
3223
+ throw new Error(error.error || "Validation request failed");
3224
+ }
3225
+ return await response.json();
3226
+ } catch (error) {
3227
+ return {
3228
+ valid: false,
3229
+ message: error instanceof Error ? error.message : "Validation failed"
3230
+ };
3231
+ }
3232
+ }
3233
+ async function consumeAccessCode(email, code, endpoints) {
3234
+ try {
3235
+ const response = await fetch(endpoints.consumeUrl, {
3236
+ method: "POST",
3237
+ headers: {
3238
+ "Content-Type": "application/json"
3239
+ },
3240
+ body: JSON.stringify({ email, code })
3241
+ });
3242
+ if (!response.ok) {
3243
+ const error = await response.json().catch(() => ({}));
3244
+ throw new Error(error.error || "Consumption request failed");
3245
+ }
3246
+ return await response.json();
3247
+ } catch (error) {
3248
+ return {
3249
+ success: false,
3250
+ consumed: false,
3251
+ message: error instanceof Error ? error.message : "Consumption failed"
3252
+ };
3253
+ }
3254
+ }
3255
+ async function validateAndConsumeAccessCode(email, code, endpoints) {
3256
+ const validation = await validateAccessCode(email, code, endpoints);
3257
+ if (!validation.valid) {
3258
+ return validation;
3259
+ }
3260
+ const consumption = await consumeAccessCode(email, code, endpoints);
3261
+ return {
3262
+ ...validation,
3263
+ consumed: consumption.consumed,
3264
+ message: consumption.consumed ? `Access granted for ${validation.cohort_name}` : consumption.message || validation.message
3265
+ };
3266
+ }
3267
+
3268
+ // src/hooks/use-access-code-integration.ts
2621
3269
  function useAccessCodeIntegration() {
2622
3270
  const runtime = useRequiredEndpointsRuntime();
2623
3271
  const endpoints = runtime.accessCode;
@@ -2659,12 +3307,158 @@ function useAccessCodeIntegration() {
2659
3307
  };
2660
3308
  }
2661
3309
 
3310
+ // src/hooks/use-og-placeholder.ts
3311
+ import { useMemo as useMemo6 } from "react";
3312
+ function useOgPlaceholder(buildUrl, title, siteName = "", enabled = true, aspect = "wide") {
3313
+ return useMemo6(() => {
3314
+ if (!enabled || !title) return null;
3315
+ const options = {};
3316
+ if (siteName) options.site = siteName;
3317
+ if (aspect === "square") options.aspect = "square";
3318
+ return buildUrl(title, options);
3319
+ }, [buildUrl, title, siteName, enabled, aspect]);
3320
+ }
3321
+
3322
+ // src/hooks/use-scroll-to-hash.ts
3323
+ import { useEffect as useEffect19 } from "react";
3324
+
3325
+ // src/utils/scroll-into-view.ts
3326
+ var activeRaf = 0;
3327
+ var teardownActive = null;
3328
+ function cancelActiveScroll() {
3329
+ if (activeRaf) {
3330
+ cancelAnimationFrame(activeRaf);
3331
+ activeRaf = 0;
3332
+ }
3333
+ if (teardownActive) {
3334
+ teardownActive();
3335
+ teardownActive = null;
3336
+ }
3337
+ }
3338
+ var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
3339
+ function getScrollableAncestor(el) {
3340
+ for (let node = el.parentElement; node; node = node.parentElement) {
3341
+ const overflowY = getComputedStyle(node).overflowY;
3342
+ if ((overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && node.scrollHeight > node.clientHeight) {
3343
+ return node;
3344
+ }
3345
+ }
3346
+ return null;
3347
+ }
3348
+ function scrollElementIntoView(target, options = {}) {
3349
+ if (typeof window === "undefined" || !target) return;
3350
+ const { headerOffset = 0, behavior = "smooth", adjustTargetY, durationMs = 320 } = options;
3351
+ const container = getScrollableAncestor(target);
3352
+ const readCurrent = () => container ? container.scrollTop : window.scrollY;
3353
+ const writeTo = (y) => {
3354
+ if (container) container.scrollTop = y;
3355
+ else window.scrollTo(0, y);
3356
+ };
3357
+ const computeTarget = () => {
3358
+ const raw = container ? container.scrollTop + (target.getBoundingClientRect().top - container.getBoundingClientRect().top) - headerOffset : target.getBoundingClientRect().top + window.scrollY - headerOffset;
3359
+ const adjusted = adjustTargetY ? adjustTargetY(raw) : raw;
3360
+ const maxScroll = container ? Math.max(0, container.scrollHeight - container.clientHeight) : Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
3361
+ return Math.min(Math.max(0, adjusted), maxScroll);
3362
+ };
3363
+ cancelActiveScroll();
3364
+ const prefersReduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3365
+ if (behavior === "instant" || behavior === "auto" || prefersReduced) {
3366
+ writeTo(computeTarget());
3367
+ return;
3368
+ }
3369
+ let startY = null;
3370
+ let startTime = 0;
3371
+ const onUserGesture = () => cancelActiveScroll();
3372
+ window.addEventListener("wheel", onUserGesture, { passive: true });
3373
+ window.addEventListener("touchmove", onUserGesture, { passive: true });
3374
+ teardownActive = () => {
3375
+ window.removeEventListener("wheel", onUserGesture);
3376
+ window.removeEventListener("touchmove", onUserGesture);
3377
+ };
3378
+ const step = (now) => {
3379
+ if (startY === null) {
3380
+ startY = readCurrent();
3381
+ startTime = now;
3382
+ }
3383
+ const targetY = computeTarget();
3384
+ const t = Math.min(1, (now - startTime) / durationMs);
3385
+ const y = startY + (targetY - startY) * easeOutCubic(t);
3386
+ writeTo(y);
3387
+ if (t < 1) {
3388
+ activeRaf = requestAnimationFrame(step);
3389
+ } else {
3390
+ writeTo(computeTarget());
3391
+ activeRaf = 0;
3392
+ if (teardownActive) {
3393
+ teardownActive();
3394
+ teardownActive = null;
3395
+ }
3396
+ }
3397
+ };
3398
+ activeRaf = requestAnimationFrame(step);
3399
+ }
3400
+
3401
+ // src/utils/same-page-hash-nav.ts
3402
+ var STICKY_HEADER_OFFSET_PX = 96;
3403
+ var HUB_HEADER_OFFSET_PX = 80;
3404
+ function normalizeHashFragment(hash) {
3405
+ if (!hash) return "";
3406
+ const second = hash.indexOf("#", 1);
3407
+ return second < 0 ? hash : hash.slice(0, second);
3408
+ }
3409
+ function navigateSamePageHash(target, options = {}) {
3410
+ if (typeof window === "undefined") return false;
3411
+ const { headerOffset = 0, history: historyMode = "push" } = options;
3412
+ const normalizedTarget = target.startsWith("#") ? window.location.pathname + window.location.search + target : target;
3413
+ let url;
3414
+ try {
3415
+ url = new URL(normalizedTarget, window.location.href);
3416
+ } catch {
3417
+ return false;
3418
+ }
3419
+ if (url.origin !== window.location.origin || url.pathname !== window.location.pathname || url.search !== window.location.search) {
3420
+ return false;
3421
+ }
3422
+ const current = window.location.pathname + window.location.search + window.location.hash;
3423
+ const normalizedHash = normalizeHashFragment(url.hash);
3424
+ if (process.env.NODE_ENV === "development" && normalizedHash !== url.hash) {
3425
+ console.warn(
3426
+ `[navigateSamePageHash] malformed fragment "${url.hash}" \u2192 normalizing to "${normalizedHash}". Fix the upstream composer.`
3427
+ );
3428
+ }
3429
+ const next = url.pathname + url.search + normalizedHash;
3430
+ const id = normalizedHash && normalizedHash !== "#" ? normalizedHash.slice(1) : "";
3431
+ if (!id && next !== current) return false;
3432
+ if (next !== current) {
3433
+ const oldURL = window.location.href;
3434
+ if (historyMode === "replace") {
3435
+ window.history.replaceState(null, "", next);
3436
+ } else {
3437
+ window.history.pushState(null, "", next);
3438
+ }
3439
+ window.dispatchEvent(new HashChangeEvent("hashchange", {
3440
+ oldURL,
3441
+ newURL: window.location.href
3442
+ }));
3443
+ }
3444
+ const el = id ? document.getElementById(id) : null;
3445
+ if (id && !el && process.env.NODE_ENV === "development") {
3446
+ console.warn(
3447
+ `[navigateSamePageHash] anchor "#${id}" not found \u2014 scrolling to top.`
3448
+ );
3449
+ }
3450
+ scrollElementIntoView(el ?? document.documentElement, {
3451
+ behavior: "smooth",
3452
+ headerOffset
3453
+ });
3454
+ return true;
3455
+ }
3456
+
2662
3457
  // src/hooks/use-scroll-to-hash.ts
2663
- import { useEffect as useEffect16 } from "react";
2664
3458
  var MAX_POLL_FRAMES = 60;
2665
3459
  function useScrollToHash(readyDep = true, options) {
2666
3460
  const headerOffset = options?.headerOffset ?? 0;
2667
- useEffect16(() => {
3461
+ useEffect19(() => {
2668
3462
  if (typeof window === "undefined") return;
2669
3463
  if (readyDep === null || readyDep === false) return;
2670
3464
  let rafId = null;
@@ -2704,18 +3498,40 @@ function useScrollToHash(readyDep = true, options) {
2704
3498
  }
2705
3499
 
2706
3500
  // src/hooks/use-humanity-signals.ts
2707
- import { useCallback as useCallback12, useRef as useRef10 } from "react";
3501
+ import { useCallback as useCallback14, useRef as useRef12 } from "react";
3502
+
3503
+ // src/utils/humanity-signals.ts
3504
+ var HONEYPOT_FIELD = "contact_url_confirm";
3505
+ var ELAPSED_MS_FIELD = "form_elapsed_ms";
3506
+ var DEFAULT_MIN_FILL_MS = 700;
3507
+ function extractHumanitySignals(body) {
3508
+ const b = body ?? {};
3509
+ const rawHp = b[HONEYPOT_FIELD];
3510
+ const honeypot = rawHp == null ? "" : String(rawHp);
3511
+ const rawMs = b[ELAPSED_MS_FIELD];
3512
+ const elapsedMs = typeof rawMs === "number" && Number.isFinite(rawMs) ? rawMs : null;
3513
+ return { honeypot, elapsedMs };
3514
+ }
3515
+ function evaluateHumanitySignals(body, opts) {
3516
+ const { honeypot, elapsedMs } = extractHumanitySignals(body);
3517
+ if (honeypot.trim() !== "") return { ok: false, reason: "honeypot" };
3518
+ if (elapsedMs !== null && elapsedMs < opts.minFillMs) return { ok: false, reason: "too_fast" };
3519
+ return { ok: true };
3520
+ }
3521
+ var splitCsvEnv = (s) => s?.split(",").map((t) => t.trim()).filter(Boolean) ?? [];
3522
+
3523
+ // src/hooks/use-humanity-signals.ts
2708
3524
  function useHumanitySignals() {
2709
- const ref = useRef10(null);
2710
- const mountedAt = useRef10(typeof performance !== "undefined" ? performance.now() : 0);
2711
- const getSignals = useCallback12(
3525
+ const ref = useRef12(null);
3526
+ const mountedAt = useRef12(typeof performance !== "undefined" ? performance.now() : 0);
3527
+ const getSignals = useCallback14(
2712
3528
  () => ({
2713
3529
  [HONEYPOT_FIELD]: ref.current?.value ?? "",
2714
3530
  [ELAPSED_MS_FIELD]: typeof performance !== "undefined" ? Math.round(performance.now() - mountedAt.current) : 0
2715
3531
  }),
2716
3532
  []
2717
3533
  );
2718
- const resetSignals = useCallback12(() => {
3534
+ const resetSignals = useCallback14(() => {
2719
3535
  if (ref.current) ref.current.value = "";
2720
3536
  if (typeof performance !== "undefined") mountedAt.current = performance.now();
2721
3537
  }, []);
@@ -2723,6 +3539,8 @@ function useHumanitySignals() {
2723
3539
  }
2724
3540
 
2725
3541
  export {
3542
+ useAutoLimitTags,
3543
+ useDebounce,
2726
3544
  useHeaderHeight,
2727
3545
  useHorizontalScrollbar,
2728
3546
  useImageEdgeColor,
@@ -2739,9 +3557,28 @@ export {
2739
3557
  useTablePagination,
2740
3558
  useThrottle,
2741
3559
  useWindowSize,
3560
+ platformIcons,
3561
+ platformColors,
3562
+ platformDisplayNames,
3563
+ platformDescriptions,
3564
+ platformSlogans,
3565
+ platformHexColors,
3566
+ platformIconNames,
3567
+ getDefaultColorForPlatform,
3568
+ getDefaultIconForPlatform,
3569
+ transformPlatformConfigsToOptions,
3570
+ getPlatformIcon,
3571
+ getPlatformColor,
3572
+ getPlatformDisplayName,
3573
+ getPlatformDescription,
3574
+ getPlatformSlogan,
3575
+ getSmallPlatformIcon,
3576
+ getPlatformIconComponent,
2742
3577
  usePlatformConfig,
2743
3578
  usePlatformByValue,
2744
3579
  useValidatePlatform,
3580
+ ToolTypeValues,
3581
+ toolLabels,
2745
3582
  ToolIcon,
2746
3583
  dotColorByVariant,
2747
3584
  progressColorByVariant,
@@ -2752,6 +3589,15 @@ export {
2752
3589
  showCommandApprovalToast,
2753
3590
  toast,
2754
3591
  useToast,
3592
+ getAppType,
3593
+ getEmbedProxyAuth,
3594
+ getPersistedProxyEmail,
3595
+ setEmbedProxyAuth,
3596
+ clearEmbedProxyAuth,
3597
+ applyProxyAuth,
3598
+ setEmbedAuthAdapter,
3599
+ embedAuthedFetch,
3600
+ contentFetch,
2755
3601
  useContactSubmission,
2756
3602
  useQuickActionHint,
2757
3603
  useCopyToClipboard,
@@ -2784,8 +3630,24 @@ export {
2784
3630
  createSearchParams,
2785
3631
  useCursorPaginationState,
2786
3632
  useNatsClient,
3633
+ useNearViewport,
3634
+ validateAccessCode,
3635
+ consumeAccessCode,
3636
+ validateAndConsumeAccessCode,
2787
3637
  useAccessCodeIntegration,
3638
+ useOgPlaceholder,
3639
+ scrollElementIntoView,
3640
+ STICKY_HEADER_OFFSET_PX,
3641
+ HUB_HEADER_OFFSET_PX,
3642
+ normalizeHashFragment,
3643
+ navigateSamePageHash,
2788
3644
  useScrollToHash,
3645
+ HONEYPOT_FIELD,
3646
+ ELAPSED_MS_FIELD,
3647
+ DEFAULT_MIN_FILL_MS,
3648
+ extractHumanitySignals,
3649
+ evaluateHumanitySignals,
3650
+ splitCsvEnv,
2789
3651
  useHumanitySignals
2790
3652
  };
2791
- //# sourceMappingURL=chunk-7RIYT7ZH.js.map
3653
+ //# sourceMappingURL=chunk-2QG57XOJ.js.map