@flamingo-stack/openframe-frontend-core 0.0.690-snapshot.20261001124055 → 0.0.690

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 (149) hide show
  1. package/dist/{chunk-2YVTGPM7.js → chunk-22RSJ57Z.js} +2 -2
  2. package/dist/{chunk-JSG7WZKV.js → chunk-25X43LSD.js} +4 -4
  3. package/dist/{chunk-UVUNBXS6.js → chunk-2KILTCND.js} +1 -6
  4. package/dist/chunk-2KILTCND.js.map +1 -0
  5. package/dist/{chunk-ESFPY5FF.cjs → chunk-2MGTNYHW.cjs} +100 -100
  6. package/dist/{chunk-ESFPY5FF.cjs.map → chunk-2MGTNYHW.cjs.map} +1 -1
  7. package/dist/{chunk-SDORFSFK.cjs → chunk-2OD2PPNO.cjs} +37 -37
  8. package/dist/{chunk-SDORFSFK.cjs.map → chunk-2OD2PPNO.cjs.map} +1 -1
  9. package/dist/{chunk-DC3G4XGP.js → chunk-2PQKZWWP.js} +6 -6
  10. package/dist/{chunk-XAOAYDGR.js → chunk-2V4KPBNZ.js} +3 -3
  11. package/dist/{chunk-H54RDLVC.cjs → chunk-3ZAIHRQH.cjs} +35 -35
  12. package/dist/{chunk-H54RDLVC.cjs.map → chunk-3ZAIHRQH.cjs.map} +1 -1
  13. package/dist/{chunk-TSNGVWQO.js → chunk-42RQ4NIL.js} +6 -6
  14. package/dist/{chunk-Y7ZEVTLC.js → chunk-4BUPMSLY.js} +3 -3
  15. package/dist/{chunk-SA4KQCNB.cjs → chunk-4HBYEZ3I.cjs} +82 -82
  16. package/dist/{chunk-SA4KQCNB.cjs.map → chunk-4HBYEZ3I.cjs.map} +1 -1
  17. package/dist/{chunk-LCENRN2N.cjs → chunk-54X75QTC.cjs} +14 -14
  18. package/dist/{chunk-LCENRN2N.cjs.map → chunk-54X75QTC.cjs.map} +1 -1
  19. package/dist/{chunk-Z5FHXCXF.js → chunk-5A3WSWZD.js} +8 -8
  20. package/dist/{chunk-WZYIAKIH.cjs → chunk-5CBQBTGN.cjs} +7 -7
  21. package/dist/{chunk-WZYIAKIH.cjs.map → chunk-5CBQBTGN.cjs.map} +1 -1
  22. package/dist/{chunk-KMW2PXBU.js → chunk-AIXPQT3G.js} +11 -52
  23. package/dist/chunk-AIXPQT3G.js.map +1 -0
  24. package/dist/{chunk-LNKLB7GM.cjs → chunk-BJHM2OEY.cjs} +17 -17
  25. package/dist/{chunk-LNKLB7GM.cjs.map → chunk-BJHM2OEY.cjs.map} +1 -1
  26. package/dist/{chunk-N3526CTH.cjs → chunk-BOM6MMWB.cjs} +3 -3
  27. package/dist/{chunk-N3526CTH.cjs.map → chunk-BOM6MMWB.cjs.map} +1 -1
  28. package/dist/{chunk-N4S7RBUM.cjs → chunk-D7BFS7N6.cjs} +3 -3
  29. package/dist/{chunk-N4S7RBUM.cjs.map → chunk-D7BFS7N6.cjs.map} +1 -1
  30. package/dist/{chunk-C2SVJVEB.js → chunk-DLRE5Y3G.js} +2 -2
  31. package/dist/{chunk-USKQURNF.cjs → chunk-DMXI5D2Q.cjs} +3 -3
  32. package/dist/{chunk-USKQURNF.cjs.map → chunk-DMXI5D2Q.cjs.map} +1 -1
  33. package/dist/{chunk-7WYWPELF.js → chunk-DOUG4UEZ.js} +11 -11
  34. package/dist/{chunk-Y3BC7TOJ.cjs → chunk-FCQR6VY4.cjs} +26 -26
  35. package/dist/{chunk-Y3BC7TOJ.cjs.map → chunk-FCQR6VY4.cjs.map} +1 -1
  36. package/dist/{chunk-7EPGJOKN.js → chunk-HLEXYTOE.js} +4 -4
  37. package/dist/{chunk-MIZU2OTO.cjs → chunk-JGVJRUGB.cjs} +4 -4
  38. package/dist/{chunk-MIZU2OTO.cjs.map → chunk-JGVJRUGB.cjs.map} +1 -1
  39. package/dist/{chunk-DDWLF2JV.cjs → chunk-KZXKT5AX.cjs} +14 -14
  40. package/dist/{chunk-DDWLF2JV.cjs.map → chunk-KZXKT5AX.cjs.map} +1 -1
  41. package/dist/{chunk-4N2JOPAQ.cjs → chunk-MDYTAKJ5.cjs} +22 -22
  42. package/dist/{chunk-4N2JOPAQ.cjs.map → chunk-MDYTAKJ5.cjs.map} +1 -1
  43. package/dist/{chunk-DVSPRY2V.cjs → chunk-NMXL34Z2.cjs} +3 -3
  44. package/dist/{chunk-DVSPRY2V.cjs.map → chunk-NMXL34Z2.cjs.map} +1 -1
  45. package/dist/{chunk-CMYDGVTZ.js → chunk-OC3QH5WG.js} +2 -2
  46. package/dist/{chunk-GZRU2A4L.cjs → chunk-OD2WMUAO.cjs} +197 -197
  47. package/dist/{chunk-GZRU2A4L.cjs.map → chunk-OD2WMUAO.cjs.map} +1 -1
  48. package/dist/{chunk-DZMDG7PC.js → chunk-OQVHGPHA.js} +3 -3
  49. package/dist/{chunk-Y3Q76RAA.cjs → chunk-P27OGSQL.cjs} +37 -37
  50. package/dist/{chunk-Y3Q76RAA.cjs.map → chunk-P27OGSQL.cjs.map} +1 -1
  51. package/dist/{chunk-HM7ZGABI.js → chunk-Q76YVNCQ.js} +2 -2
  52. package/dist/{chunk-53O6PQ3F.cjs → chunk-QNWQQ4RT.cjs} +7 -7
  53. package/dist/{chunk-53O6PQ3F.cjs.map → chunk-QNWQQ4RT.cjs.map} +1 -1
  54. package/dist/{chunk-BXE2RZEV.cjs → chunk-QUGV2GT3.cjs} +34 -34
  55. package/dist/{chunk-BXE2RZEV.cjs.map → chunk-QUGV2GT3.cjs.map} +1 -1
  56. package/dist/{chunk-SC44YJCF.js → chunk-RF2EYX3B.js} +2 -2
  57. package/dist/{chunk-FNMQ2J44.js → chunk-S5M5SEW2.js} +5 -5
  58. package/dist/{chunk-KVDLL4LU.js → chunk-TBJWPNHU.js} +4 -4
  59. package/dist/chunk-TBJWPNHU.js.map +1 -0
  60. package/dist/{chunk-MJI5GVT7.js → chunk-TSENCZUL.js} +11 -11
  61. package/dist/{chunk-XBYF4SGD.js → chunk-U4OJCJTH.js} +4 -4
  62. package/dist/{chunk-GDZ7IC5B.js → chunk-UAKWSR3U.js} +2 -2
  63. package/dist/{chunk-ZLYLHP64.cjs → chunk-UFXV64WQ.cjs} +9 -9
  64. package/dist/{chunk-ZLYLHP64.cjs.map → chunk-UFXV64WQ.cjs.map} +1 -1
  65. package/dist/{chunk-4HZXOQRM.js → chunk-V5NOURSR.js} +5 -5
  66. package/dist/{chunk-CWXRZVDI.cjs → chunk-VLWUKFQE.cjs} +710 -751
  67. package/dist/chunk-VLWUKFQE.cjs.map +1 -0
  68. package/dist/{chunk-5N2ZSB42.cjs → chunk-X4WBGDNW.cjs} +18 -18
  69. package/dist/{chunk-5N2ZSB42.cjs.map → chunk-X4WBGDNW.cjs.map} +1 -1
  70. package/dist/{chunk-6Z26ZMR4.js → chunk-XRSJER2B.js} +4 -4
  71. package/dist/{chunk-I27PWVWE.cjs → chunk-YCKGUQA2.cjs} +12 -17
  72. package/dist/chunk-YCKGUQA2.cjs.map +1 -0
  73. package/dist/{chunk-Y7ZQZOE6.js → chunk-Z622ACFD.js} +5 -5
  74. package/dist/{chunk-BXAGR3PO.cjs → chunk-ZBQZJAC7.cjs} +7 -7
  75. package/dist/chunk-ZBQZJAC7.cjs.map +1 -0
  76. package/dist/components/case-studies/index.cjs +20 -20
  77. package/dist/components/case-studies/index.js +7 -7
  78. package/dist/components/chat/index.cjs +4 -4
  79. package/dist/components/chat/index.js +3 -3
  80. package/dist/components/contact/index.cjs +8 -8
  81. package/dist/components/contact/index.js +7 -7
  82. package/dist/components/docs/index.cjs +11 -11
  83. package/dist/components/docs/index.js +10 -10
  84. package/dist/components/embeds/index.cjs +8 -8
  85. package/dist/components/embeds/index.js +7 -7
  86. package/dist/components/faq/index.cjs +9 -9
  87. package/dist/components/faq/index.js +8 -8
  88. package/dist/components/features/index.cjs +7 -7
  89. package/dist/components/features/index.js +6 -6
  90. package/dist/components/help-center-pages/index.cjs +101 -101
  91. package/dist/components/help-center-pages/index.js +21 -21
  92. package/dist/components/index.cjs +65 -65
  93. package/dist/components/index.js +21 -21
  94. package/dist/components/meeting-scheduler/index.cjs +58 -58
  95. package/dist/components/meeting-scheduler/index.js +8 -8
  96. package/dist/components/navigation/index.cjs +10 -10
  97. package/dist/components/navigation/index.js +9 -9
  98. package/dist/components/onboarding-guides/index.cjs +11 -11
  99. package/dist/components/onboarding-guides/index.js +10 -10
  100. package/dist/components/related-content/index.cjs +6 -6
  101. package/dist/components/related-content/index.js +5 -5
  102. package/dist/components/tickets/index.cjs +12 -12
  103. package/dist/components/tickets/index.js +11 -11
  104. package/dist/components/tickets/ticket-live-provider.d.ts.map +1 -1
  105. package/dist/components/ui/index.cjs +7 -7
  106. package/dist/components/ui/index.js +6 -6
  107. package/dist/hooks/index.cjs +4 -4
  108. package/dist/hooks/index.js +3 -3
  109. package/dist/index.cjs +12 -12
  110. package/dist/index.js +10 -10
  111. package/dist/utils/embed-authed-fetch.d.ts +0 -18
  112. package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
  113. package/dist/utils/index.cjs +6 -50
  114. package/dist/utils/index.cjs.map +1 -1
  115. package/dist/utils/index.js +6 -50
  116. package/dist/utils/index.js.map +1 -1
  117. package/dist/utils/sse-subscription.d.ts +11 -29
  118. package/dist/utils/sse-subscription.d.ts.map +1 -1
  119. package/package.json +1 -1
  120. package/src/components/tickets/ticket-live-provider.tsx +12 -14
  121. package/src/utils/embed-authed-fetch.ts +0 -21
  122. package/src/utils/sse-subscription.ts +20 -107
  123. package/dist/chunk-BXAGR3PO.cjs.map +0 -1
  124. package/dist/chunk-CWXRZVDI.cjs.map +0 -1
  125. package/dist/chunk-I27PWVWE.cjs.map +0 -1
  126. package/dist/chunk-KMW2PXBU.js.map +0 -1
  127. package/dist/chunk-KVDLL4LU.js.map +0 -1
  128. package/dist/chunk-UVUNBXS6.js.map +0 -1
  129. /package/dist/{chunk-2YVTGPM7.js.map → chunk-22RSJ57Z.js.map} +0 -0
  130. /package/dist/{chunk-JSG7WZKV.js.map → chunk-25X43LSD.js.map} +0 -0
  131. /package/dist/{chunk-DC3G4XGP.js.map → chunk-2PQKZWWP.js.map} +0 -0
  132. /package/dist/{chunk-XAOAYDGR.js.map → chunk-2V4KPBNZ.js.map} +0 -0
  133. /package/dist/{chunk-TSNGVWQO.js.map → chunk-42RQ4NIL.js.map} +0 -0
  134. /package/dist/{chunk-Y7ZEVTLC.js.map → chunk-4BUPMSLY.js.map} +0 -0
  135. /package/dist/{chunk-Z5FHXCXF.js.map → chunk-5A3WSWZD.js.map} +0 -0
  136. /package/dist/{chunk-C2SVJVEB.js.map → chunk-DLRE5Y3G.js.map} +0 -0
  137. /package/dist/{chunk-7WYWPELF.js.map → chunk-DOUG4UEZ.js.map} +0 -0
  138. /package/dist/{chunk-7EPGJOKN.js.map → chunk-HLEXYTOE.js.map} +0 -0
  139. /package/dist/{chunk-CMYDGVTZ.js.map → chunk-OC3QH5WG.js.map} +0 -0
  140. /package/dist/{chunk-DZMDG7PC.js.map → chunk-OQVHGPHA.js.map} +0 -0
  141. /package/dist/{chunk-HM7ZGABI.js.map → chunk-Q76YVNCQ.js.map} +0 -0
  142. /package/dist/{chunk-SC44YJCF.js.map → chunk-RF2EYX3B.js.map} +0 -0
  143. /package/dist/{chunk-FNMQ2J44.js.map → chunk-S5M5SEW2.js.map} +0 -0
  144. /package/dist/{chunk-MJI5GVT7.js.map → chunk-TSENCZUL.js.map} +0 -0
  145. /package/dist/{chunk-XBYF4SGD.js.map → chunk-U4OJCJTH.js.map} +0 -0
  146. /package/dist/{chunk-GDZ7IC5B.js.map → chunk-UAKWSR3U.js.map} +0 -0
  147. /package/dist/{chunk-4HZXOQRM.js.map → chunk-V5NOURSR.js.map} +0 -0
  148. /package/dist/{chunk-6Z26ZMR4.js.map → chunk-XRSJER2B.js.map} +0 -0
  149. /package/dist/{chunk-Y7ZQZOE6.js.map → chunk-Z622ACFD.js.map} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  useAuthedImageSrc,
8
8
  useImageEdgeColor,
9
9
  useOgPlaceholderUrl
10
- } from "./chunk-UVUNBXS6.js";
10
+ } from "./chunk-2KILTCND.js";
11
11
  import {
12
12
  FigmaIcon,
13
13
  ImageIcon,
@@ -4369,4 +4369,4 @@ export {
4369
4369
  canonicalContentRefType,
4370
4370
  buildListUrl
4371
4371
  };
4372
- //# sourceMappingURL=chunk-2YVTGPM7.js.map
4372
+ //# sourceMappingURL=chunk-22RSJ57Z.js.map
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  EmptyState
4
- } from "./chunk-HM7ZGABI.js";
4
+ } from "./chunk-Q76YVNCQ.js";
5
5
  import {
6
6
  OPENFRAME_DEV_SECTIONS,
7
7
  PageLayout,
@@ -10,7 +10,7 @@ import {
10
10
  StatusFilterComponent,
11
11
  TASK_TYPE_LABELS,
12
12
  TASK_TYPE_TEXT_COLORS
13
- } from "./chunk-KMW2PXBU.js";
13
+ } from "./chunk-AIXPQT3G.js";
14
14
  import {
15
15
  usePathname,
16
16
  useRouter,
@@ -20,7 +20,7 @@ import {
20
20
  AvatarStack,
21
21
  StatusBadge,
22
22
  getStatusColorScheme
23
- } from "./chunk-2YVTGPM7.js";
23
+ } from "./chunk-22RSJ57Z.js";
24
24
  import {
25
25
  next_link_default
26
26
  } from "./chunk-BTROSBHF.js";
@@ -302,4 +302,4 @@ export {
302
302
  DevCardRowSkeletonList,
303
303
  DeliveryRow
304
304
  };
305
- //# sourceMappingURL=chunk-JSG7WZKV.js.map
305
+ //# sourceMappingURL=chunk-25X43LSD.js.map
@@ -453,10 +453,6 @@ function setEmbedAuthAdapter(adapter2) {
453
453
  function hasEmbedAuthAdapter() {
454
454
  return getRegisteredAuthAdapter() !== null;
455
455
  }
456
- function subscribeEmbedCredentialChange(listener) {
457
- return getRegisteredAuthAdapter()?.subscribe?.(listener) ?? (() => {
458
- });
459
- }
460
456
  function needsBearerAssetFetch(url) {
461
457
  if (typeof window === "undefined") return false;
462
458
  const adapter2 = getRegisteredAuthAdapter();
@@ -708,7 +704,6 @@ export {
708
704
  applyProxyAuth,
709
705
  setEmbedAuthAdapter,
710
706
  hasEmbedAuthAdapter,
711
- subscribeEmbedCredentialChange,
712
707
  embedAuthedFetch,
713
708
  clearAuthedAssetCache,
714
709
  useAuthedAssetSrc,
@@ -717,4 +712,4 @@ export {
717
712
  buildOgPlaceholderUrl,
718
713
  useOgPlaceholderUrl
719
714
  };
720
- //# sourceMappingURL=chunk-UVUNBXS6.js.map
715
+ //# sourceMappingURL=chunk-2KILTCND.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/ui/use-image-edge-color.ts","../src/utils/platform-identity.ts","../src/utils/app-config.ts","../src/utils/local-storage-adapter.ts","../src/utils/embed-proxy-auth-storage.ts","../src/utils/embed-authed-fetch.ts","../src/hooks/use-authed-asset-src.ts","../src/hooks/use-authed-image-src.ts","../src/utils/og-placeholder.ts","../src/hooks/use-og-placeholder-url.ts"],"sourcesContent":["'use client';\n\nimport { useState, useEffect } from 'react';\n\n/**\n * Extract the dominant edge color from an image using color bucketing.\n * Samples pixels along left and right edges (the visible letterbox areas),\n * groups them into color buckets, and returns the most common bucket.\n * This avoids muddy averages when edges have mixed colors (e.g., sky + sand).\n */\nfunction extractEdgeColor(img: HTMLImageElement): string {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n if (!ctx) return '#000000';\n\n const maxSize = 100;\n const scale = Math.min(maxSize / img.naturalWidth, maxSize / img.naturalHeight);\n const w = Math.round(img.naturalWidth * scale);\n const h = Math.round(img.naturalHeight * scale);\n canvas.width = w;\n canvas.height = h;\n\n ctx.drawImage(img, 0, 0, w, h);\n\n const data = ctx.getImageData(0, 0, w, h).data;\n\n // Sample left edge, right edge, top edge, bottom edge (15% band)\n const edgeW = Math.max(2, Math.round(w * 0.15));\n const edgeH = Math.max(2, Math.round(h * 0.15));\n\n // Color bucketing: quantize to 32-step buckets for grouping similar colors\n const bucketSize = 32;\n const buckets = new Map<string, { r: number; g: number; b: number; count: number }>();\n\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const isEdge = x < edgeW || x >= w - edgeW || y < edgeH || y >= h - edgeH;\n\n if (!isEdge) continue;\n\n const i = (y * w + x) * 4;\n const a = data[i + 3];\n if (a < 128) continue;\n\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n\n // Quantize to bucket\n const br = Math.floor(r / bucketSize) * bucketSize;\n const bg = Math.floor(g / bucketSize) * bucketSize;\n const bb = Math.floor(b / bucketSize) * bucketSize;\n const key = `${br},${bg},${bb}`;\n\n const existing = buckets.get(key);\n if (existing) {\n existing.r += r;\n existing.g += g;\n existing.b += b;\n existing.count++;\n } else {\n buckets.set(key, { r, g, b, count: 1 });\n }\n }\n }\n\n if (buckets.size === 0) return '#000000';\n\n // Find the most common color bucket\n let bestBucket: { r: number; g: number; b: number; count: number } | null = null;\n for (const bucket of buckets.values()) {\n if (!bestBucket || bucket.count > bestBucket.count) {\n bestBucket = bucket;\n }\n }\n\n if (!bestBucket || bestBucket.count === 0) return '#000000';\n\n // Return average color within the winning bucket\n const r = Math.round(bestBucket.r / bestBucket.count);\n const g = Math.round(bestBucket.g / bestBucket.count);\n const b = Math.round(bestBucket.b / bestBucket.count);\n\n return `rgb(${r}, ${g}, ${b})`;\n}\n\n/**\n * Hook that extracts the dominant edge color from an image URL.\n * Returns a CSS color string for use as a background behind object-contain images.\n *\n * Always sets crossOrigin='anonymous' — required for canvas pixel access.\n * Most CDNs (Supabase, Cloudflare, our image proxy) return CORS headers.\n * If the server doesn't support CORS, onerror fires and we use the fallback.\n *\n * @param imageUrl - URL of the image to analyze\n * @param fallback - Fallback color if extraction fails (default: '#000000')\n * @returns CSS color string (e.g., 'rgb(34, 28, 22)')\n */\nexport function useImageEdgeColor(imageUrl: string | undefined | null, fallback = '#000000'): string {\n const [color, setColor] = useState(fallback);\n\n // Losing the url (or being given a different fallback) is a prop-driven\n // reset, so it happens while rendering — React's documented alternative to a\n // sync-from-props effect. From the effect it would commit one frame with the\n // departed image's colour still painted behind an image that is already gone.\n const [resetFor, setResetFor] = useState({ imageUrl, fallback });\n if (resetFor.imageUrl !== imageUrl || resetFor.fallback !== fallback) {\n setResetFor({ imageUrl, fallback });\n if (!imageUrl) setColor(fallback);\n }\n\n useEffect(() => {\n if (!imageUrl) return undefined;\n\n let cancelled = false;\n const img = new Image();\n img.crossOrigin = 'anonymous';\n\n img.onload = () => {\n if (cancelled) return;\n try {\n setColor(extractEdgeColor(img));\n } catch {\n setColor(fallback);\n }\n };\n\n img.onerror = () => {\n if (cancelled) return;\n setColor(fallback);\n };\n\n img.src = imageUrl;\n\n return () => {\n cancelled = true;\n };\n }, [imageUrl, fallback]);\n\n return color;\n}\n","/**\n * Platform identity + brand SSOT (single source of truth).\n *\n * ONE module owns every per-platform STRING (display/short/long name, description,\n * slogan, icon name) and every per-platform COLOUR DECISION (the accent/secondary\n * \"stem\", the theme, the surface). Everything downstream DERIVES from here:\n * `platform-config.tsx`'s icon + colour maps, `PlatformBadge`, `getPlatformConfig`,\n * the hub's `AppConfig.brandColors`, the hub's manifest generator and `theme-color`.\n *\n * JSX-FREE + zero-dependency ON PURPOSE: it carries its own tsup entry and\n * `exports` subpath (`@flamingo-stack/openframe-frontend-core/utils/platform-identity`)\n * so hub SCRIPTS and `server-only` modules can import it without dragging React,\n * the icon components, or the utils barrel into their graph.\n *\n * TWO accepted by-construction copies of the colour decision remain, both LOCKED\n * by `src/__tests__/mlg-platform-leaves.test.ts` in this package:\n * 1. `platformHexColors` below (hand-typed hex mirror of each accent token).\n * 2. The `[data-app-type='…']` blocks in `src/styles/ods-colors.css`.\n */\n\nimport type { PlatformName } from '../types/platform';\n\n// ── Strings ─────────────────────────────────────────────────────────────────\n\n/** Human-readable platform names. THE naming source for lib-side presentation. */\nexport const platformDisplayNames = {\n openmsp: 'OpenMSP',\n openframe: 'OpenFrame',\n flamingo: 'Flamingo',\n 'flamingo-teaser': 'Flamingo Teaser',\n 'marketing-hub': 'Flamingo Marketing Hub',\n 'product-hub': 'Flamingo Product Hub',\n 'revenue-hub': 'Flamingo Revenue Hub',\n 'people-hub': 'Flamingo People Hub',\n 'company-hub': 'Flamingo Company Hub',\n tmcg: 'TMCG',\n mlg: 'Major League GitHub',\n universal: 'Universal',\n};\n\n/**\n * Short-name OVERRIDES only. A platform without a row falls back to its display\n * name, so this map never lists a name that equals `platformDisplayNames`.\n */\nexport const platformShortNames: Partial<Record<PlatformName, string>> = {\n mlg: 'MLG',\n};\n\nexport const platformDescriptions = {\n openmsp:\n 'Comprehensive directory and comparison platform for managed service providers (MSPs) and technology vendors. Reduce vendor costs and discover open-source alternatives.',\n openframe: 'AI-driven open-source security operations center (SOC) and endpoint detection platform for MSPs.',\n flamingo:\n 'AI-driven open-source OS for MSPs. Swap bloated vendor tools for open ones. Automate the boring crap. Take your margin back.',\n 'flamingo-teaser': 'Preview of Flamingo - the AI-driven open-source OS for MSPs.',\n tmcg: 'The Miami Cyber Gang - A cybersecurity community focused on education and collaboration.',\n mlg: 'Scouting report for US open-source talent. Rank GitHub contributors by programming language, city, state, region, and nearest Major League Soccer club.',\n universal: 'Cross-platform universal content.',\n};\n\nexport const platformSlogans = {\n openmsp: 'Find Your Perfect MSP Partner',\n openframe: 'Open-Source Security Operations',\n flamingo: 'Open-Source OS for MSPs',\n 'flamingo-teaser': 'Coming Soon: Open-Source OS for MSPs',\n tmcg: 'Miami Cyber Community',\n mlg: 'GitHub Scouting Report: Major League Edition',\n universal: 'Universal Platform',\n};\n\nexport const platformIconNames = {\n openmsp: 'openmsp-logo',\n openframe: 'openframe-logo',\n flamingo: 'flamingo-logo',\n universal: 'globe',\n 'flamingo-teaser': 'flamingo-logo',\n 'marketing-hub': 'flamingo-logo',\n 'product-hub': 'flamingo-logo',\n 'revenue-hub': 'flamingo-logo',\n 'people-hub': 'flamingo-logo',\n 'company-hub': 'flamingo-logo',\n tmcg: 'tmcg-logo',\n mlg: 'mlg-logo',\n};\n\nexport function getPlatformDisplayName(platformName: string): string {\n return platformDisplayNames[platformName as keyof typeof platformDisplayNames] || platformName;\n}\n\n/** Compact label (nav chips, manifest `short_name`, JSON-LD `alternateName`). */\nexport function getPlatformShortName(platformName: string): string {\n return platformShortNames[platformName as PlatformName] ?? getPlatformDisplayName(platformName);\n}\n\nexport function getPlatformDescription(platformName: string): string {\n return platformDescriptions[platformName as keyof typeof platformDescriptions] || platformName;\n}\n\nexport function getPlatformSlogan(platformName: string): string {\n return platformSlogans[platformName as keyof typeof platformSlogans] || platformName;\n}\n\nexport function getDefaultIconForPlatform(platformName: string): string {\n return platformIconNames[platformName as keyof typeof platformIconNames] || platformIconNames.universal;\n}\n\n// ── Colour stems ────────────────────────────────────────────────────────────\n\n/**\n * The six brand/attention colours (plus the neutral) a platform may adopt, named\n * by STEM rather than by token so the token spelling has exactly one home below.\n */\nexport type OdsColorStem =\n 'flamingo-pink' | 'flamingo-cyan' | 'open-yellow' | 'success' | 'warning' | 'error' | 'text-secondary';\n\n/** Stem → ODS token name (without the `--ods-` prefix). THE token spelling. */\nexport const ODS_STEM_TOKENS: Record<OdsColorStem, string> = {\n 'flamingo-pink': 'flamingo-pink-base',\n 'flamingo-cyan': 'flamingo-cyan-base',\n 'open-yellow': 'open-yellow-base',\n success: 'attention-green-success',\n warning: 'attention-yellow-warning',\n error: 'attention-red-error',\n 'text-secondary': 'system-greys-grey',\n};\n\n/**\n * Stem → Tailwind class strings. STATIC LITERALS on purpose: the Tailwind\n * scanner reads source text, so a template-built class would be purged.\n * This is the ONLY place a stem-to-class literal exists.\n */\nexport const ODS_STEM_CLASSES: Record<OdsColorStem, { text: string; bg: string; bgSoft: string; borderSoft: string }> =\n {\n 'flamingo-pink': {\n text: 'text-ods-flamingo-pink',\n bg: 'bg-ods-flamingo-pink',\n bgSoft: 'bg-ods-flamingo-pink/10',\n borderSoft: 'border-ods-flamingo-pink/30',\n },\n 'flamingo-cyan': {\n text: 'text-ods-flamingo-cyan',\n bg: 'bg-ods-flamingo-cyan',\n bgSoft: 'bg-ods-flamingo-cyan/10',\n borderSoft: 'border-ods-flamingo-cyan/30',\n },\n 'open-yellow': {\n text: 'text-ods-open-yellow',\n bg: 'bg-ods-open-yellow',\n bgSoft: 'bg-ods-open-yellow/10',\n borderSoft: 'border-ods-open-yellow/30',\n },\n success: {\n text: 'text-ods-success',\n bg: 'bg-ods-success',\n bgSoft: 'bg-ods-success/10',\n borderSoft: 'border-ods-success/30',\n },\n warning: {\n text: 'text-ods-warning',\n bg: 'bg-ods-warning',\n bgSoft: 'bg-ods-warning/10',\n borderSoft: 'border-ods-warning/30',\n },\n error: {\n text: 'text-ods-error',\n bg: 'bg-ods-error',\n bgSoft: 'bg-ods-error/10',\n borderSoft: 'border-ods-error/30',\n },\n 'text-secondary': {\n text: 'text-ods-text-secondary',\n bg: 'bg-ods-text-secondary',\n bgSoft: 'bg-ods-text-secondary/10',\n borderSoft: 'border-ods-text-secondary/30',\n },\n };\n\n// ── Theme + surface ─────────────────────────────────────────────────────────\n\n/**\n * Every platform ships the dark theme today (the hub's flamingo config says so\n * explicitly, and every `[data-app-type]` block inherits the dark `:root`).\n * ONE constant instead of a per-platform field: reintroduce the field only when\n * a platform actually differs.\n */\nexport const PLATFORM_THEME = 'dark' as const;\n\n/** The dark surface's tokens, consumed by config `brandColors` and the manifest. */\nexport const PLATFORM_SURFACE = {\n background: 'system-greys-background',\n text: 'system-greys-white',\n} as const;\n\nexport const getPlatformBackgroundVarName = (): string => `--ods-${PLATFORM_SURFACE.background}`;\nexport const getPlatformTextVarName = (): string => `--ods-${PLATFORM_SURFACE.text}`;\n\n// ── Brand record ────────────────────────────────────────────────────────────\n\nexport interface PlatformBrand {\n /** Primary brand colour (`--color-accent-primary` in the platform's CSS block). */\n accentStem: OdsColorStem;\n /** Link colour (`--color-link`). Equals `accentStem` for most platforms. */\n secondaryStem: OdsColorStem;\n}\n\n/**\n * THE accent/link table. Every row is verified against that platform's\n * `[data-app-type='…']` block in `ods-colors.css` by the hub's parity test.\n */\nexport const PLATFORM_BRAND: Record<PlatformName, PlatformBrand> = {\n openframe: { accentStem: 'open-yellow', secondaryStem: 'flamingo-cyan' },\n openmsp: { accentStem: 'open-yellow', secondaryStem: 'open-yellow' },\n flamingo: { accentStem: 'flamingo-pink', secondaryStem: 'flamingo-pink' },\n 'flamingo-teaser': { accentStem: 'flamingo-pink', secondaryStem: 'flamingo-pink' },\n tmcg: { accentStem: 'flamingo-pink', secondaryStem: 'flamingo-pink' },\n 'marketing-hub': { accentStem: 'flamingo-pink', secondaryStem: 'flamingo-pink' },\n 'product-hub': { accentStem: 'success', secondaryStem: 'success' },\n 'revenue-hub': { accentStem: 'warning', secondaryStem: 'warning' },\n 'people-hub': { accentStem: 'flamingo-cyan', secondaryStem: 'flamingo-cyan' },\n 'company-hub': { accentStem: 'error', secondaryStem: 'error' },\n mlg: { accentStem: 'flamingo-pink', secondaryStem: 'flamingo-cyan' },\n universal: { accentStem: 'text-secondary', secondaryStem: 'text-secondary' },\n};\n\n/** The brand row for a platform, falling back to `universal` for anything unknown. */\nexport function getPlatformBrand(platformName: string): PlatformBrand {\n return PLATFORM_BRAND[platformName as PlatformName] ?? PLATFORM_BRAND.universal;\n}\n\nexport function platformAccentToken(platformName: string): string {\n return ODS_STEM_TOKENS[getPlatformBrand(platformName).accentStem];\n}\n\nexport function platformSecondaryToken(platformName: string): string {\n return ODS_STEM_TOKENS[getPlatformBrand(platformName).secondaryStem];\n}\n\n/** `--ods-…` CSS variable NAME for a platform's accent (input to `resolveOdsColor`). */\nexport function platformAccentVarName(platformName: string): string {\n return `--ods-${platformAccentToken(platformName)}`;\n}\n\nexport function platformSecondaryVarName(platformName: string): string {\n return `--ods-${platformSecondaryToken(platformName)}`;\n}\n\n/** `var(--ods-…)` VALUE for a platform's accent (config `brandColors` data). */\nexport function getPlatformAccentColor(platformName: string): string {\n return `var(${platformAccentVarName(platformName)})`;\n}\n\nexport function getPlatformSecondaryColor(platformName: string): string {\n return `var(${platformSecondaryVarName(platformName)})`;\n}\n\n/** Tailwind classes for a platform, DERIVED from its stems. */\nexport function getPlatformBrandClasses(platformName: string): {\n accentText: string;\n accentBg: string;\n accentBgSoft: string;\n accentBorderSoft: string;\n secondaryText: string;\n} {\n const brand = getPlatformBrand(platformName);\n const accent = ODS_STEM_CLASSES[brand.accentStem];\n const secondary = ODS_STEM_CLASSES[brand.secondaryStem];\n return {\n accentText: accent.text,\n accentBg: accent.bg,\n accentBgSoft: accent.bgSoft,\n accentBorderSoft: accent.borderSoft,\n secondaryText: secondary.text,\n };\n}\n\n/** Platform background classes, DERIVED (was a hand-typed hex map). */\nexport const platformColors: Record<PlatformName, string> = Object.fromEntries(\n (Object.keys(PLATFORM_BRAND) as PlatformName[]).map(p => [p, getPlatformBrandClasses(p).accentBg]),\n) as Record<PlatformName, string>;\n\nexport function getPlatformColor(platformName: string) {\n return platformColors[platformName as PlatformName] || platformColors.universal;\n}\n\n/**\n * Hand-typed hex mirror of each platform's accent token (the DB `default_color`\n * and other JS-value consumers). Accepted copy #1 — locked by\n * `src/__tests__/mlg-platform-leaves.test.ts` in this package, which asserts\n * every row against the `[data-app-type]` blocks in `ods-colors.css`.\n * (The hub has its OWN parity test, but it holds different copies: the asset\n * generator's map and the generated manifests.)\n */\nexport const platformHexColors: Record<PlatformName, string> = {\n openmsp: '#ffc008',\n openframe: '#ffc008',\n flamingo: '#f357bb',\n universal: '#888888',\n 'flamingo-teaser': '#f357bb',\n 'marketing-hub': '#f357bb',\n 'product-hub': '#5ea62e',\n 'revenue-hub': '#e1b32f',\n 'people-hub': '#5efaf0',\n 'company-hub': '#f36666',\n tmcg: '#f357bb',\n mlg: '#f357bb',\n};\n\nexport function getDefaultColorForPlatform(platformName: string): string {\n return platformHexColors[platformName as PlatformName] || platformHexColors.universal;\n}\n","// Stub app config\nexport const APP_CONFIG = {\n app: {\n type: 'openmsp',\n name: 'OpenMSP',\n domain: 'openmsp.ai',\n },\n features: {\n announcements: true,\n notifications: true,\n },\n} as const;\n\nexport function getAppConfig() {\n return APP_CONFIG;\n}\n\nexport function getAppType() {\n // The exact `process.env.NEXT_PUBLIC_APP_TYPE` member expression is kept so\n // Next/webpack can statically inline it. The try/catch makes the call safe\n // in non-Next React hosts (Vite/CRA embeds) where `process` does not exist\n // at runtime — those hosts get the 'openmsp' fallback, which is fine for\n // embeds: dismissal cookies are domain-scoped and an embed domain serves\n // one platform's announcements.\n try {\n return process.env.NEXT_PUBLIC_APP_TYPE || 'openmsp';\n } catch {\n return 'openmsp';\n }\n}\n","/**\n * Thin JSON-typed Web-Storage adapter (localStorage or sessionStorage).\n *\n * Centralizes the SSR-guard + try/catch + silent quota-failure pattern\n * that every per-feature storage util would otherwise re-implement.\n *\n * Optional `namespace` prefix supports platform / user partitioning —\n * the resolver runs lazily at call time so the namespace can vary across\n * the lifetime of the page (e.g. proxy-auth switches user, which switches\n * the key suffix).\n *\n * Backend selection (`backend: 'local' | 'session'`):\n * - `'local'` (default): persists across browser sessions. Use for\n * UI state, chat history metadata, feature-flag opt-ins.\n * - `'session'`: cleared when the tab closes. Use for ANY auth-adjacent\n * value (bearer tokens, act-as identity, proxy credentials). Reduces\n * the XSS-exfiltration attack window from \"indefinite\" to \"until tab\n * close\" without losing per-session ergonomics.\n */\n\nexport type WebStorageBackend = 'local' | 'session';\n\nexport interface LocalStorageAdapter<T> {\n load(): T | null;\n save(value: T): void;\n clear(): void;\n /** Resolved storage key for the current call. Useful for tests. */\n resolveKey(): string;\n}\n\nexport interface LocalStorageAdapterOptions<T> {\n /** Base storage key. Combined with `namespace()` when provided. */\n key: string;\n /** Optional dynamic namespace prefix appended via `.` separator.\n * Called on EVERY read/write so the key can vary across the page\n * lifetime (e.g. when the platform or user identity changes). */\n namespace?: () => string | null | undefined;\n /** Runtime shape check. Falsey return → `load()` yields null. */\n validate?: (parsed: unknown) => parsed is T;\n /** Diagnostic prefix written to `console.warn` on parse / write\n * failures. Defaults to `'[local-storage]'`. */\n logTag?: string;\n /** Which Web-Storage backend to use. Defaults to `'local'`. Pass\n * `'session'` for anything auth-adjacent so the value evaporates\n * when the tab closes. */\n backend?: WebStorageBackend;\n}\n\nfunction getStorage(backend: WebStorageBackend): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return backend === 'session' ? window.sessionStorage : window.localStorage;\n } catch {\n // Some sandboxed contexts (Safari private mode older versions,\n // strict CSP) throw on storage access — treat as unavailable.\n return null;\n }\n}\n\nexport function createLocalStorageAdapter<T>(options: LocalStorageAdapterOptions<T>): LocalStorageAdapter<T> {\n const tag = options.logTag ?? '[local-storage]';\n const backend: WebStorageBackend = options.backend ?? 'local';\n const resolveKey = (): string => {\n const ns = options.namespace?.();\n return ns ? `${ns}.${options.key}` : options.key;\n };\n\n return {\n resolveKey,\n load() {\n const storage = getStorage(backend);\n if (!storage) return null;\n try {\n const raw = storage.getItem(resolveKey());\n if (!raw) return null;\n const parsed = JSON.parse(raw) as unknown;\n if (options.validate && !options.validate(parsed)) return null;\n return parsed as T;\n } catch (err) {\n console.warn(`${tag} parse failed for key ${resolveKey()}:`, err);\n return null;\n }\n },\n save(value: T) {\n const storage = getStorage(backend);\n if (!storage) return;\n try {\n storage.setItem(resolveKey(), JSON.stringify(value));\n } catch (err) {\n console.warn(`${tag} write failed for key ${resolveKey()}:`, err);\n }\n },\n clear() {\n const storage = getStorage(backend);\n if (!storage) return;\n try {\n storage.removeItem(resolveKey());\n } catch (err) {\n console.warn(`${tag} clear failed for key ${resolveKey()}:`, err);\n }\n },\n };\n}\n","'use client';\n\n/**\n * Client-side persistence for embed-surface proxy credentials (a\n * PLATFORM API KEY + impersonation email). Used by every embedded\n * surface — the chat widget AND the ticket center AND any future\n * embedded React component that needs to identify itself as the\n * impersonated customer.\n *\n * When set, the surface attaches the creds as\n * `Authorization: Bearer <platform API key>` + `X-Chat-Act-As: <email>`\n * on every call to `/api/docs/chat`, `/api/chat/*`, and any other route\n * gated by `requireChatAuth` — proving to the server that this session\n * is acting on behalf of <email>. The key is minted in the hub's\n * `/admin/api-keys` and is platform-bound (`fpk_<platform>_…`): a\n * SERVICE key may act as any email; a PERSONAL key only as its owner.\n *\n * **Naming history:** the stored field is still named `secret` and the\n * wire-side header names are `X-Chat-*`. Those are client/server\n * contracts predating the per-platform key migration (the old shared\n * `CHAT_PROXY_SECRET` env is removed hub-side); renaming them would\n * break persisted state + the wire shape for zero behavior change. The\n * CLIENT-side helpers were renamed `Embed*` so non-chat surfaces (e.g.\n * ticket center) don't have to import a chat-prefixed symbol just to\n * send the same headers.\n *\n * Persists to **`localStorage`** so the bearer token + act-as identity\n * survive tab close, new-tab opens, and browser restarts — the\n * `/debug` paste-creds UI is an admin tool and re-pasting every tab\n * cycle was rejected as a dev-experience tradeoff that wasn't worth\n * the security gain. An XSS sink on this origin can read the value\n * indefinitely (vs only-this-tab with sessionStorage), but `/debug`\n * is admin-gated behind the platform's `askAI.enabled` flag, the key\n * is server-verified (hash-at-rest, platform-bound, revocable in\n * `/admin/api-keys`), and the act-as reach is bounded by the key's\n * tier. Explicit \"Clear\" button on the creds bar is the supported\n * logout path; closing the tab is no longer.\n *\n * Namespaced under `<platform>.chat.proxy-auth.v1` (the storage key is\n * unchanged from the old chat-prefixed helper — that's a storage\n * contract; renaming it would log everyone out).\n */\n\nimport { getAppType } from './app-config';\nimport { createLocalStorageAdapter } from './local-storage-adapter';\n\n/**\n * Every OPTIONAL header of the chat-proxy contract, with the field it is stored\n * in: the ONE list storage, validation and `applyProxyAuth` all loop over, so a\n * new header is one row. `identity` rows are the acting user's display identity\n * (the hub threads them through `resolveChatProxyIdentity`); `visitor` rows are\n * the visitor context a gateway forwards (honoured hub-side only from a SERVICE\n * key). Empty or omitted = not sent.\n */\nexport const EMBED_PROXY_OPTIONAL_HEADERS = [\n { field: 'firstName', group: 'identity', header: 'X-Chat-First-Name' },\n { field: 'lastName', group: 'identity', header: 'X-Chat-Last-Name' },\n { field: 'avatarUrl', group: 'identity', header: 'X-Chat-Avatar-Url' },\n { field: 'originUrl', group: 'visitor', header: 'X-Chat-Origin-Url' },\n { field: 'ip', group: 'visitor', header: 'X-Chat-Ip' },\n { field: 'country', group: 'visitor', header: 'X-Chat-Country' },\n { field: 'visitorId', group: 'visitor', header: 'X-Chat-User-Id' },\n] as const;\n\nexport type EmbedProxyOptionalField = (typeof EMBED_PROXY_OPTIONAL_HEADERS)[number]['field'];\n\nexport type EmbedProxyAuth = {\n secret: string;\n email: string;\n} & Partial<Record<EmbedProxyOptionalField, string>>;\n\nfunction isValidPersistedAuth(value: unknown): value is EmbedProxyAuth {\n if (!value || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n if (\n typeof v.secret !== 'string' ||\n v.secret.trim().length === 0 ||\n typeof v.email !== 'string' ||\n v.email.trim().length === 0\n )\n return false;\n // Optional fields: when present must be strings. Empty string is treated\n // as absent later (in `getEmbedProxyAuth`).\n return EMBED_PROXY_OPTIONAL_HEADERS.every(({ field }) => v[field] == null || typeof v[field] === 'string');\n}\n\nconst adapter = createLocalStorageAdapter<EmbedProxyAuth>({\n // Storage key unchanged from the legacy chat-prefixed helper. Renaming\n // it would silently log every existing admin out — the key is a\n // storage contract, not a code identifier.\n key: 'chat.proxy-auth.v1',\n namespace: () => getAppType(),\n validate: isValidPersistedAuth,\n logTag: '[embed-proxy-auth-storage]',\n // localStorage — survives tab close, new tabs, and browser restarts.\n // Admin re-pasting creds every tab cycle was the dev-experience\n // tradeoff prior `sessionStorage` setup demanded — rejected. See\n // file-level doc comment for the security tradeoff rationale.\n backend: 'local',\n});\n\n/** Trim + null-coerce an optional identity field so consumers can do\n * `auth.firstName ?? ''` without worrying about whitespace-only strings. */\nfunction normalizeOptional(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\n/** Every optional field of `value`, trimmed, blank ones dropped. */\nfunction normalizedOptionalFields(value: Partial<Record<EmbedProxyOptionalField, string>>) {\n const out: Partial<Record<EmbedProxyOptionalField, string>> = {};\n for (const { field } of EMBED_PROXY_OPTIONAL_HEADERS) {\n const normalized = normalizeOptional(value[field]);\n if (normalized) out[field] = normalized;\n }\n return out;\n}\n\n/**\n * Returns full credentials (secret + email + optional identity passthrough)\n * when secret + email are available. Returns `null` when nothing is saved —\n * callers treat that as \"fall back to cookie auth\".\n */\nexport function getEmbedProxyAuth(): EmbedProxyAuth | null {\n const persisted = adapter.load();\n if (!persisted) return null;\n return {\n secret: persisted.secret,\n email: persisted.email.trim().toLowerCase(),\n ...normalizedOptionalFields(persisted),\n };\n}\n\n/**\n * Returns the LAST email the admin saved. The proxy creds bar reads\n * this to pre-fill the email field on mount.\n */\nexport function getPersistedProxyEmail(): string | null {\n const persisted = adapter.load();\n return persisted?.email.trim().toLowerCase() ?? null;\n}\n\n/** Save the proxy creds. Secret + email are required; identity-passthrough\n * fields are persisted only when non-empty. */\nexport function setEmbedProxyAuth(value: EmbedProxyAuth): void {\n adapter.save({\n secret: value.secret,\n email: value.email.trim().toLowerCase(),\n ...normalizedOptionalFields(value),\n });\n}\n\n/** Drop the persisted creds. */\nexport function clearEmbedProxyAuth(): void {\n adapter.clear();\n}\n\n/**\n * Apply the embed-proxy auth (Bearer + X-Chat-Act-As) to a fetch call's\n * URL + headers. Used by every embedded-surface route that needs to\n * identify itself as the proxied customer (chat stream, agent-* routes,\n * ticket-center actions). When proxy auth is absent (regular\n * cookie-session users), returns the inputs unchanged so the cookie-auth\n * path still works.\n *\n * `X-Chat-Act-As` header (vs a URL query param) keeps PII out of access\n * logs, Sentry breadcrumbs, browser history, and CDN analytics.\n */\nexport function applyProxyAuth(\n url: string,\n baseHeaders: Record<string, string> = { 'Content-Type': 'application/json' },\n): { url: string; headers: Record<string, string> } {\n const auth = getEmbedProxyAuth();\n const headers = { ...baseHeaders };\n if (auth?.secret) {\n headers.Authorization = `Bearer ${auth.secret}`;\n }\n if (auth?.email) {\n headers['X-Chat-Act-As'] = auth.email;\n }\n // Optional headers — only attached when present so the server's\n // \"required vs optional\" header shape stays exact.\n for (const { field, header } of EMBED_PROXY_OPTIONAL_HEADERS) {\n const value = auth?.[field];\n if (value) headers[header] = value;\n }\n return { url, headers };\n}\n","'use client';\n\n/**\n * Shared `fetch` wrapper for any embedded surface (chat, ticket center,\n * future widgets) that needs to carry the bearer-act-as identity\n * (proxy `Authorization` + `X-Chat-Act-As` headers from\n * `embed-proxy-auth-storage.ts`).\n *\n * Wire header names are `X-Chat-*` for historical reasons — that's a\n * server contract, not a UI namespace. The wrapper itself is generic.\n *\n * Drop-in replacement for `fetch()` — `Authorization` / `X-Chat-Act-As`\n * are merged into `init.headers` when proxy creds are stashed in\n * sessionStorage, otherwise the call falls through to the cookie-auth\n * path unchanged.\n *\n * Use this for any client-side fetch hitting `/api/chat/*`, `/api/docs/chat/*`,\n * or `/api/storage/generate-upload-url` (chat-attachment surface — shared\n * with the ticket center). Routes that do NOT need bearer-act-as\n * (e.g. `/api/profile/me`) keep using vanilla `fetch`.\n */\n\nimport { applyProxyAuth } from './embed-proxy-auth-storage';\n\n// =============================================================================\n// Host-supplied auth adapter (opt-in)\n// =============================================================================\n\n/**\n * Hosts that have their own auth model (cookie sessions, app-specific\n * JWT in localStorage, OAuth access tokens, …) can register an adapter\n * to override the lib's default `embedProxyAuth` flow. When set, the\n * adapter's `getHeaders()` result is merged onto every `embedAuthedFetch`\n * call AFTER the default proxy-auth header step (so adapter headers\n * win over both caller and proxy values), and `credentials` overrides\n * the default `'same-origin'` behaviour.\n *\n * Default (no adapter): MPH-style proxy-impersonation — bearer + act-as\n * read from localStorage, `credentials: 'same-origin'`. No consumer\n * needs to touch this unless they want a different auth model.\n *\n * Use cases:\n * - openframe-frontend has its own JWT in `localStorage.of_access_token`\n * and cookie-based session; register an adapter to attach the JWT\n * and request `credentials: 'include'` so cookies travel cross-origin\n * to the openframe gateway.\n * - Future embed hosts with OAuth access tokens, signed URLs, etc.\n *\n * Lifetime: setter is module-level (intentionally — `embedAuthedFetch`\n * is a plain utility, not a hook, so it can't read React context). Host\n * runtime providers should call `setEmbedAuthAdapter(...)` on mount and\n * `setEmbedAuthAdapter(null)` on unmount. Multiple hosts registering at\n * once is a programming error (one chat panel per app).\n */\nexport interface EmbedAuthAdapter {\n /** Headers merged onto every embedded-fetch call. Return `{}` to add\n * nothing. Called per-request so reactive token refresh sees the latest\n * value from your auth store / storage. Values typed as\n * `string | undefined` so the common narrowed shape\n * `{ Authorization: token ? 'Bearer …' : undefined }` (or a conditional\n * `token ? { Authorization: … } : {}`) assigns cleanly — `undefined`\n * values are filtered before being merged into the request headers. */\n getHeaders?: () => Record<string, string | undefined>;\n /** `RequestInit.credentials` mode. Default when no adapter: callers'\n * `init.credentials` or `'same-origin'`. Use `'include'` for cookie\n * auth against a different origin (CORS + `SameSite=None` required). */\n credentials?: RequestCredentials;\n /**\n * Optional 401 self-heal. When a request comes back `401`,\n * `embedAuthedFetch` calls this once, and — if it resolves `true` —\n * retries the SAME request exactly once with freshly-recomputed\n * headers (so a rotated bearer from `getHeaders()` is picked up).\n * Resolve `false` to surface the 401 to the caller unchanged.\n *\n * This is the capability the openframe `apiClient` has had all along\n * (refresh-the-access-token-then-retry); registering it here gives the\n * embedded chat/ticket surfaces the same self-healing auth instead of\n * dying on an expired token. Concurrent 401s are de-duplicated by the\n * wrapper, so this fires at most once per refresh cycle even when a\n * stampede of chat requests all expire together — your implementation\n * does NOT need its own in-flight guard (though a token-refresh manager\n * that already dedups is harmless).\n *\n * Keep it idempotent and side-effect-light: on failure the wrapper just\n * returns the original 401 — logout/redirect decisions belong to the\n * host's own auth layer, not to this fetch wrapper.\n */\n refresh?: () => Promise<boolean>;\n /**\n * Exact extra origins (`scheme://host[:port]`, as produced by `URL.origin`)\n * the same-origin guard additionally accepts. The guard normally rejects\n * every cross-origin URL in production — but native-shell hosts serve the\n * page from a local pseudo-origin (`capacitor://localhost`,\n * `tauri://localhost`) with NO server behind it, so the gateway their\n * bearer already belongs to is unavoidably cross-origin. Listing it here\n * is the host's explicit sanction to send the adapter's credentials there.\n * Compared AFTER the http(s)-protocol check, so non-http(s) schemes can\n * never be allowlisted. Omit (or leave empty) everywhere else — same-origin\n * remains the rule, this is the narrow exception.\n */\n allowedOrigins?: string[];\n}\n\n/**\n * The registered adapter is parked on `globalThis`, NOT in a module-private\n * `let`. Reason: this lib ships multiple entry points (`/utils`,\n * `/components/chat`, …) and a consumer's bundler can inline this file into\n * more than one chunk — giving each chunk its OWN module scope. If the host\n * calls `setEmbedAuthAdapter` from the `/utils` copy while the chat's\n * `embedAuthedFetch` runs from the `/components/chat` copy, a module-local\n * `let` would be set on one copy and read as `null` on the other (the exact\n * \"credentials: same-origin, no Bearer, no refresh\" symptom). A single\n * `globalThis` slot is shared across every copy, so registration always\n * reaches the fetch path.\n */\nconst ADAPTER_GLOBAL_KEY = '__embedAuthedFetchAdapter__';\n\nfunction getRegisteredAuthAdapter(): EmbedAuthAdapter | null {\n if (typeof globalThis === 'undefined') return null;\n return ((globalThis as Record<string, unknown>)[ADAPTER_GLOBAL_KEY] as EmbedAuthAdapter | null) ?? null;\n}\n\nfunction storeRegisteredAuthAdapter(adapter: EmbedAuthAdapter | null): void {\n if (typeof globalThis === 'undefined') return;\n (globalThis as Record<string, unknown>)[ADAPTER_GLOBAL_KEY] = adapter;\n}\n\n/**\n * Register a host-owned auth adapter for `embedAuthedFetch`. Pass `null`\n * to clear (typically on provider unmount).\n *\n * Module-level state — there is one chat panel per app, so a single\n * registration is sufficient. Calling this twice with different non-null\n * adapters replaces the previous one (the most recent registration wins);\n * a `console.warn` flags the overwrite so duplicate-provider mounts get\n * caught in dev.\n */\nexport function setEmbedAuthAdapter(adapter: EmbedAuthAdapter | null): void {\n if (adapter && getRegisteredAuthAdapter() && process.env.NODE_ENV !== 'production') {\n console.warn(\n '[setEmbedAuthAdapter] overwriting a previously-registered auth ' +\n 'adapter. Two chat-runtime providers should not coexist — verify ' +\n 'mount order and pass `null` from the unmounting provider.',\n );\n }\n storeRegisteredAuthAdapter(adapter);\n}\n\n/**\n * Whether a host auth adapter is currently registered. Lets sibling helpers\n * (e.g. `contentFetch`) route through `embedAuthedFetch` ONLY when a host has\n * opted into embedded auth, and stay a plain `fetch` otherwise — so there's a\n * single auth knob (the adapter), not a second content-fetch registration.\n */\nexport function hasEmbedAuthAdapter(): boolean {\n return getRegisteredAuthAdapter() !== null;\n}\n\n/**\n * Whether `url` is an asset the browser CANNOT load natively because its\n * auth rides in request headers.\n *\n * Native asset loads (`<img src>`, `<track src>`, CSS `background-image`)\n * can't carry custom headers, so a URL this returns `true` for must go\n * through `embedAuthedFetch` → blob object-URL instead (see\n * `useAuthedAssetSrc` / `useAuthedImageSrc`).\n *\n * The single signal is the registered adapter: when it supplies an\n * `Authorization` header, the host authenticates by HEADER, so nothing the\n * browser fetches on its own is authenticated. That holds for BOTH shapes a\n * header-auth host takes:\n *\n * - a cross-origin gateway URL — sanctioned via `allowedOrigins` (native\n * shells, whose page origin is `capacitor://` / `tauri://`);\n * - a same-origin / relative reverse-proxy path (`/content/api/...`) —\n * dev-ticket web, where the token lives in localStorage and there is NO\n * session cookie to fall back on. This case used to be excluded on the\n * assumption that same-origin implies \"cookies work\"; true for a cookie\n * session, false for every header-auth host, which is exactly when this\n * predicate is consulted at all.\n *\n * Everything else loads natively, unchanged: cookie-auth web (the adapter\n * supplies no `Authorization`), no adapter at all (the hub), and third-party\n * origins the bearer does NOT belong to (public images) — those stay out of\n * reach of the token by the same origin rules `embedAuthedFetch` enforces.\n */\nexport function needsBearerAssetFetch(url: string): boolean {\n if (typeof window === 'undefined') return false;\n const adapter = getRegisteredAuthAdapter();\n if (!adapter || adapter.getHeaders?.().Authorization === undefined) return false;\n let target: URL;\n let pageOrigin: string;\n try {\n target = new URL(url, window.location.href);\n // Same reason `assertSameOrigin` derives it this way: test environments\n // mock `window.location` as a plain object with no `origin` field.\n pageOrigin = new URL(window.location.href).origin;\n } catch {\n return false;\n }\n if (target.protocol !== 'http:' && target.protocol !== 'https:') return false;\n if (target.origin === pageOrigin) return true;\n return adapter.allowedOrigins?.includes(target.origin) ?? false;\n}\n\n/**\n * `fetch` wrapper that attaches embed-proxy bearer headers (when\n * present in sessionStorage) and forces `credentials: 'same-origin'`\n * so Supabase auth cookies travel too.\n *\n * **Header merge direction (proxy WINS over caller):** the implementation\n * spreads `baseHeaders` first inside `applyProxyAuth`, then sets the\n * `Authorization` / `X-Chat-*` keys — so the proxy values take precedence\n * over anything the caller passed. The motivation is that the bearer +\n * act-as identity is the source of truth for embedded auth; a caller\n * accidentally passing a stale `Authorization` header should NOT override\n * the live proxy creds.\n *\n * **Cross-origin defense:** the wrapper assumes a same-origin `/api/…`\n * relative URL. Absolute URLs are accepted only when their origin matches\n * the current window's origin — or appears in the registered adapter's\n * `allowedOrigins` (the native-shell hatch; see that field's doc) — and\n * cross-origin URLs otherwise throw before the bearer leaves the page.\n * This is a defense-in-depth guard for future call sites — outside the\n * allowlisted-shell case there is no legitimate cross-origin use of this\n * fetch wrapper.\n *\n * **401 self-heal:** when a registered adapter supplies `refresh`, a `401`\n * response triggers a single token refresh + retry of the same request\n * (see `EmbedAuthAdapter.refresh`). This is the openframe `apiClient`'s\n * refresh-then-retry behaviour, lifted into the lib so embedded surfaces\n * no longer need a host-side `window.fetch` monkey-patch to survive an\n * expired access token mid-chat. With no adapter (or no `refresh`), the\n * 401 passes straight through unchanged.\n */\nexport function embedAuthedFetch(url: string, init: RequestInit = {}): Promise<Response> {\n // Same-origin guard runs SYNCHRONOUSLY (not awaited inside the async\n // helper below) so a bearer-leaking cross-origin URL throws before any\n // promise is created — callers and tests rely on the synchronous throw.\n assertSameOrigin(url);\n\n // `applyProxyAuth` accepts `Record<string, string>`; normalize the\n // caller's headers to that shape ONCE, up front. RequestInit accepts\n // `HeadersInit` which is broader (Headers instance OR array of tuples).\n // We re-derive the per-request headers from this base on every attempt\n // (initial + post-refresh retry) so a rotated bearer is picked up.\n //\n // When the caller passes no headers, fall back to the same default\n // `applyProxyAuth` uses internally — `Content-Type: application/json` —\n // so JSON POSTs keep their content-type when only `embedAuthedFetch(url)`\n // is used at the call site. GET callers that explicitly want no body\n // headers can pass `init.headers = {}` to opt out.\n let baseHeaders: Record<string, string>;\n if (init.headers === undefined) {\n baseHeaders = { 'Content-Type': 'application/json' };\n } else {\n baseHeaders = {};\n if (init.headers instanceof Headers) {\n init.headers.forEach((v, k) => {\n baseHeaders[k] = v;\n });\n } else if (Array.isArray(init.headers)) {\n for (const [k, v] of init.headers) baseHeaders[k] = v;\n } else {\n Object.assign(baseHeaders, init.headers);\n }\n }\n\n return fetchWithRefresh(url, init, baseHeaders, false);\n}\n\n/**\n * Single in-flight refresh shared across all concurrent `embedAuthedFetch`\n * callers. A stampede of chat requests that all 401 at the same moment must\n * trigger the adapter's `refresh()` ONCE, not N times — otherwise an\n * expiring session fires a thundering herd of refresh calls at the auth\n * server. Resets to `null` once settled so the next genuine expiry can\n * refresh again.\n */\n// Stored on `globalThis` rather than a module-local so the \"single refresh\"\n// guarantee survives module duplication. Bundlers can ship more than one copy\n// of this module (e.g. across chunks or a host + embedded build); a per-module\n// variable would let each copy run its own refresh cycle, re-creating the\n// thundering-herd this dedupe exists to prevent.\nconst IN_FLIGHT_REFRESH_GLOBAL_KEY = '__embedAuthedFetchInFlightRefresh__';\n\nfunction getInFlightRefresh(): Promise<boolean> | null {\n if (typeof globalThis === 'undefined') return null;\n return (\n ((globalThis as Record<string, unknown>)[IN_FLIGHT_REFRESH_GLOBAL_KEY] as Promise<boolean> | null | undefined) ??\n null\n );\n}\n\nfunction setInFlightRefresh(refresh: Promise<boolean> | null): void {\n if (typeof globalThis === 'undefined') return;\n (globalThis as Record<string, unknown>)[IN_FLIGHT_REFRESH_GLOBAL_KEY] = refresh;\n}\n\nfunction dedupedRefresh(): Promise<boolean> {\n const adapter = getRegisteredAuthAdapter();\n // Bound and captured BEFORE the closure below. `refresh` is an optional\n // property, so TypeScript drops the `if (!adapter?.refresh)` narrowing inside\n // the `.then()` callback — the property could in principle be reassigned\n // between the guard and the call. `bind` keeps the adapter as the receiver.\n const refresh = adapter?.refresh?.bind(adapter);\n if (!refresh) return Promise.resolve(false);\n let inFlightRefresh = getInFlightRefresh();\n if (!inFlightRefresh) {\n // Wrap in `Promise.resolve` so an adapter that throws synchronously\n // (rather than rejecting) still funnels through the shared slot and\n // clears it. A rejected refresh is treated as \"could not refresh\".\n inFlightRefresh = Promise.resolve()\n .then(() => refresh())\n .catch(() => false)\n .finally(() => {\n setInFlightRefresh(null);\n });\n setInFlightRefresh(inFlightRefresh);\n }\n return inFlightRefresh;\n}\n\n/**\n * Core fetch path: merge proxy-auth + adapter headers, issue the request,\n * and — on a `401` with a refresh-capable adapter — refresh once and retry\n * the identical request a single time. Mirrors the openframe `apiClient`'s\n * refresh-then-retry contract (`isRetry` guards against infinite loops).\n */\nasync function fetchWithRefresh(\n url: string,\n init: RequestInit,\n baseHeaders: Record<string, string>,\n isRetry: boolean,\n): Promise<Response> {\n // Re-run the merge each attempt: `applyProxyAuth` reads the latest stored\n // proxy creds and `getHeaders()` reads the latest bearer, so a retry after\n // refresh carries the rotated token rather than the stale one. `{...baseHeaders}`\n // keeps the caller's normalized headers immutable across attempts.\n const { url: authedUrl, headers } = applyProxyAuth(url, { ...baseHeaders });\n\n // Host-supplied auth adapter layer. Runs AFTER the proxy-auth merge so\n // adapter headers override both caller and proxy values — the adapter\n // is the host's explicit \"this is my auth model\" override, intentionally\n // last-writer-wins. When no adapter is registered, this is a zero-cost\n // no-op (object spread of `{}`).\n const adapter = getRegisteredAuthAdapter();\n if (adapter?.getHeaders) {\n // Filter `undefined` values — the adapter type allows them so consumers\n // don't have to narrow `{ Authorization: token ? '…' : undefined }`-shaped\n // returns, but `fetch` headers must be strings.\n for (const [k, v] of Object.entries(adapter.getHeaders())) {\n if (v !== undefined) headers[k] = v;\n }\n }\n const credentials = adapter?.credentials ?? init.credentials ?? 'same-origin';\n\n const response = await fetch(authedUrl, {\n ...init,\n headers,\n // Default `same-origin` carries Supabase cookies for the MPH proxy-\n // auth model. Hosts on different origins (openframe-frontend ↔\n // openframe gateway) register `credentials: 'include'` via the\n // adapter to make their own cookies travel cross-origin (CORS +\n // `SameSite=None` must be configured server-side for that to work).\n credentials,\n });\n\n // 401 self-heal: refresh the token once and retry. Only when an adapter\n // opted into `refresh`, and only on the first attempt — a 401 on the\n // retry means the fresh token is also unauthorized, so surface it.\n if (response.status === 401 && !isRetry && adapter?.refresh) {\n const refreshed = await dedupedRefresh();\n if (refreshed) {\n return fetchWithRefresh(url, init, baseHeaders, true);\n }\n }\n\n return response;\n}\n\n/**\n * Reject any URL that resolves to a cross-origin destination or to a\n * non-http(s) scheme. Every input is resolved against\n * `window.location.href` so the same rule covers path-only\n * (`/api/...`), absolute (`https://...`), protocol-relative\n * (`//host/...`), AND whitespace-prefixed forms (`\\t//evil.com/...`) —\n * the WHATWG fetch spec strips leading ASCII whitespace before\n * parsing, so any regex-based \"skip relative\" shortcut is bypassable\n * with a leading `\\t`/`\\n`/`\\r`/space. We resolve unconditionally\n * instead and compare origins.\n *\n * Also blocks `javascript:` / `data:` / `blob:` etc. — only `http(s):`\n * is allowed. This is explicit allowlisting rather than relying on\n * `origin === 'null'` to fall out wrong.\n *\n * Server-side rendering: when `typeof window === 'undefined'` we skip\n * the check — the bearer comes from sessionStorage which doesn't exist\n * on the server, so there's nothing to leak anyway.\n */\nfunction assertSameOrigin(url: string): void {\n if (typeof window === 'undefined') return;\n let target: URL;\n let pageOrigin: string;\n try {\n target = new URL(url, window.location.href);\n // Derive the page origin from `href` rather than reading\n // `window.location.origin` directly so the check works in test\n // environments that mock `window.location` to a plain object\n // without an `origin` field (jsdom setups do this).\n pageOrigin = new URL(window.location.href).origin;\n } catch {\n throw new Error(`embedAuthedFetch: refusing to fetch malformed URL (${JSON.stringify(url)})`);\n }\n if (target.protocol !== 'http:' && target.protocol !== 'https:') {\n throw new Error(\n `embedAuthedFetch: refusing non-http(s) URL (${target.protocol}) — pass a relative /api/* path instead`,\n );\n }\n if (target.origin !== pageOrigin) {\n // Host-sanctioned cross-origin target (`EmbedAuthAdapter.allowedOrigins`).\n // Native shells serve the page from a local pseudo-origin with no server\n // behind it, so their gateway — the same backend the host already trusts\n // with this bearer — is unavoidably cross-origin. Exact `URL.origin`\n // match, and only reachable for http(s) targets (checked above).\n if (getRegisteredAuthAdapter()?.allowedOrigins?.includes(target.origin)) {\n return;\n }\n // Dev-mode escape hatch — embedded apps (e.g. openframe-frontend)\n // run on a different origin from their gateway during local dev,\n // and forcing a Next.js `rewrites()` workaround is more error-prone\n // than relaxing the guard for the dev build. In production\n // (`NODE_ENV === 'production'`) the guard stays absolute — same\n // defense-in-depth bearer-leak protection as before. The check is\n // baked at build time by Next/webpack/Turbopack so prod bundles\n // contain only the throwing branch (no dev string in the artifact).\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[embedAuthedFetch] cross-origin fetch to ${target.origin} ` +\n `allowed in dev (NODE_ENV !== 'production'). Production builds ` +\n `will reject this — wire a same-origin proxy before shipping.`,\n );\n return;\n }\n throw new Error(\n `embedAuthedFetch: refusing cross-origin fetch to ${target.origin} — pass a relative /api/* path instead`,\n );\n }\n}\n","'use client';\n\nimport { useEffect, useReducer } from 'react';\nimport { embedAuthedFetch, needsBearerAssetFetch } from '../utils/embed-authed-fetch';\n\n/**\n * Resolve ANY asset URL the browser would load natively — `<img src>`,\n * `<track src>`, a font, a poster — for hosts whose auth rides in request\n * HEADERS instead of cookies: native shells (`capacitor://`, `tauri://`) and\n * dev-ticket web, both in bearer mode. A native subresource load can't carry\n * an `Authorization` header, so a gateway URL that works on the cookie-auth\n * web 401s there.\n *\n * When `needsBearerAssetFetch` says the URL is bearer-authed, this hook\n * fetches it through `embedAuthedFetch` (bearer + deduped 401-refresh-retry +\n * the adapter's cross-origin guard — the SAME single auth knob every embedded\n * `fetch` in the lib rides) and returns a blob object-URL. Every other URL is\n * returned unchanged, so on the cookie-auth web the hook is a pass-through.\n *\n * Returns `undefined` while the fetch is in flight OR after it fails, so\n * callers show their existing placeholder / \"no captions\" branch instead of a\n * broken asset; a failed fetch retries on the next mount.\n *\n * Cache: module-level, session-lifetime, keyed by full URL — so the two\n * `<track>` URLs of one player, or the same avatar in twenty rows, cost one\n * request. Gateway URLs carry a `?v=<content-hash>` cache-buster, so changed\n * content is a new key; entries are never revoked (a VTT / avatar is a few KB\n * — refcount churn isn't worth it) and are dropped wholesale at session end\n * via `clearAuthedAssetCache`.\n */\n\nconst resolvedCache = new Map<string, string>();\nconst inFlight = new Map<string, Promise<void>>();\nlet cacheGeneration = 0;\n\n/**\n * Drop every cached blob and revoke its object URL. Hosts call this at\n * session end (logout / forced re-login) so a follow-on login as a\n * different identity cannot be served blobs fetched under the previous\n * one's bearer. Fetches still in flight when the clear happens are fenced\n * by a generation counter — their results are revoked instead of cached.\n */\nexport function clearAuthedAssetCache(): void {\n cacheGeneration++;\n for (const url of resolvedCache.values()) URL.revokeObjectURL(url);\n resolvedCache.clear();\n inFlight.clear();\n}\n\nfunction fetchAsBlobUrl(src: string, accept: string): Promise<void> {\n let pending = inFlight.get(src);\n if (!pending) {\n const startedGeneration = cacheGeneration;\n pending = embedAuthedFetch(src, { headers: { Accept: accept } })\n .then(async response => {\n if (!response.ok) throw new Error(`asset fetch failed: ${response.status}`);\n const blobUrl = URL.createObjectURL(await response.blob());\n if (startedGeneration === cacheGeneration) {\n resolvedCache.set(src, blobUrl);\n } else {\n URL.revokeObjectURL(blobUrl);\n }\n })\n .finally(() => {\n if (inFlight.get(src) === pending) inFlight.delete(src);\n });\n inFlight.set(src, pending);\n }\n return pending;\n}\n\n/**\n * @param src the asset URL (relative or absolute), or null/undefined\n * @param accept `Accept` header for the authed fetch — narrow it to what the\n * consumer renders (`image/*`, `text/vtt`); it also replaces\n * `embedAuthedFetch`'s default JSON `Content-Type` on the GET.\n */\nexport function useAuthedAssetSrc(src?: string | null, accept = '*/*'): string | undefined {\n const bearerSrc = src && needsBearerAssetFetch(src) ? src : null;\n const [, rerender] = useReducer((c: number) => c + 1, 0);\n\n useEffect(() => {\n if (!bearerSrc || resolvedCache.has(bearerSrc)) return undefined;\n let cancelled = false;\n fetchAsBlobUrl(bearerSrc, accept)\n .catch(() => {})\n .finally(() => {\n if (!cancelled) rerender();\n });\n return () => {\n cancelled = true;\n };\n }, [bearerSrc, accept]);\n\n if (!src) return undefined;\n if (!bearerSrc) return src;\n return resolvedCache.get(bearerSrc);\n}\n","'use client';\n\nimport { clearAuthedAssetCache, useAuthedAssetSrc } from './use-authed-asset-src';\n\n/**\n * Image flavor of `useAuthedAssetSrc` — see that hook for the full contract.\n * Kept as its own export because it is the lib's most-used consumer (avatars,\n * entity images, markdown `<img>`) and because hosts already import this name.\n *\n * Distinct from the legacy `useAuthenticatedImage`, which ALWAYS blob-fetches\n * with `credentials: 'include'` and its own global config. This hook is\n * pass-through-first and keys off the single auth knob the lib already has:\n * the registered `EmbedAuthAdapter`.\n */\nexport function useAuthedImageSrc(src?: string | null): string | undefined {\n return useAuthedAssetSrc(src, 'image/*');\n}\n\n/**\n * Session-end cache drop. Now an alias of `clearAuthedAssetCache` — one cache\n * backs every authed asset (images AND caption tracks), so hosts keep calling\n * this single function on logout / forced re-login.\n */\nexport const clearAuthedImageCache = clearAuthedAssetCache;\n","/**\n * Branded OG-placeholder URL construction — the DEFAULT cover-image fallback\n * for entity cards (onboarding guides, blog/case-study/release/etc.) that have\n * no image.\n *\n * ALL of the logic lives here, in the lib. A consumer hands over its runtime\n * `endpoints` object and NOTHING else — base resolution AND the `?title=…`\n * concatenation happen inside `buildOgPlaceholderUrl`. For the entity-card +\n * onboarding-detail surfaces this is the single entry point — no card builds an\n * og-placeholder URL itself (that was the bug this replaced: each embedder\n * wired a per-surface callback that concatenated the URL, and a host that\n * forgot it rendered a blank slot with no request). (Other server-side OG paths\n * such as the hub's blog og:image generator construct their own URLs and are\n * out of this module's scope.)\n *\n * The base API URL is taken from the endpoints the host already configures:\n * 1. explicit `endpoints.ogPlaceholderUrl`\n * 2. derived from the sibling `endpoints.imageProxyUrlPrefix` (same API base)\n * 3. same-origin relative `/api/og-placeholder`\n * The base may already carry pre-existing query params; they're preserved and\n * `title` (+ dimensions) are layered on top. Per-platform brand colors are NOT\n * baked into this URL — the `/api/og-placeholder` route resolves them\n * server-side from the platform. Most hosts leave `ogPlaceholderUrl` unset and\n * let the base derive from `imageProxyUrlPrefix`.\n */\n\n/** The slice of `ChatRuntime.endpoints` this module needs. */\nexport interface OgPlaceholderEndpoints {\n /** Explicit base URL for the og-placeholder route. May already carry query\n * params — they're preserved. Per-platform colors are NOT baked here; the\n * route resolves them server-side from the platform. */\n ogPlaceholderUrl?: string;\n /** Sibling image route under the SAME API base. When `ogPlaceholderUrl` is\n * unset, the base is derived from this by swapping the trailing\n * `/image-proxy` segment for `/og-placeholder` — so a host that already\n * proxies images gets the placeholder for free, with zero extra wiring. */\n imageProxyUrlPrefix?: string;\n}\n\nexport interface BuildOgPlaceholderOptions {\n /** Site name shown under the title. Skipped when empty. */\n site?: string;\n /** `'wide'` (1200×630, the route default — no `w`/`h` emitted) or\n * `'square'` (1024×1024, for compact 56×56 chat-inline slots). */\n aspect?: 'wide' | 'square';\n /** Explicit pixel overrides (win over `aspect`). */\n width?: number;\n height?: number;\n}\n\n/** Same-origin default — for hosts that serve the route themselves (the hub).\n * Cross-origin embedders override via `ogPlaceholderUrl` or inherit it from\n * `imageProxyUrlPrefix`. */\nconst DEFAULT_OG_PLACEHOLDER_PATH = '/api/og-placeholder';\n\n/** Resolve the og-placeholder route base from the host's endpoints.\n * Internal — callers go through `buildOgPlaceholderUrl(endpoints, …)`. */\nfunction resolveOgPlaceholderBase(endpoints?: OgPlaceholderEndpoints | null): string {\n if (endpoints?.ogPlaceholderUrl) return endpoints.ogPlaceholderUrl;\n const imageProxy = endpoints?.imageProxyUrlPrefix;\n if (imageProxy) {\n // `/image-proxy` and `/og-placeholder` are sibling API routes under one\n // base. Anchor to a path-segment boundary so we only rewrite the route\n // name, never an incidental substring.\n const derived = imageProxy.replace(/\\/image-proxy(?=$|[?/])/, '/og-placeholder');\n if (derived !== imageProxy) return derived;\n }\n return DEFAULT_OG_PLACEHOLDER_PATH;\n}\n\n/**\n * Build the branded og-placeholder image URL from the host's `endpoints` + a\n * title. This is the single entry point for entity-card + onboarding-detail\n * cover fallbacks: it resolves the route base from `endpoints` AND concatenates\n * `title` (+ dimensions), so those consumers never construct a URL themselves.\n *\n * Pure string construction — SSR- and browser-safe. Always returns a usable\n * URL (relative default at worst), so a missing/unknown image degrades\n * gracefully via the `<img onError>` recovery in the card components.\n */\nexport function buildOgPlaceholderUrl(\n endpoints: OgPlaceholderEndpoints | null | undefined,\n title: string,\n options: BuildOgPlaceholderOptions = {},\n): string {\n const base = resolveOgPlaceholderBase(endpoints);\n const qIndex = base.indexOf('?');\n const path = qIndex === -1 ? base : base.slice(0, qIndex);\n const params = new URLSearchParams(qIndex === -1 ? '' : base.slice(qIndex + 1));\n\n params.set('title', title);\n if (options.site) params.set('site', options.site);\n\n // Square aspect → request a 1024×1024 image so `object-cover` doesn't crop\n // the title off in compact slots. Wide leaves dimensions to the route\n // default (1200×630). Explicit width/height always win.\n const width = options.width ?? (options.aspect === 'square' ? 1024 : undefined);\n const height = options.height ?? (options.aspect === 'square' ? 1024 : undefined);\n // `Number.isFinite` does not coerce, so it already rejects `undefined` — no\n // separate `typeof === 'number'` guard needed.\n if (Number.isFinite(width)) params.set('w', String(width));\n if (Number.isFinite(height)) params.set('h', String(height));\n\n return `${path}?${params.toString()}`;\n}\n","'use client';\n\nimport { useMemo } from 'react';\n\nimport { useChatRuntime } from '../contexts/chat-runtime-context';\nimport { buildOgPlaceholderUrl } from '../utils/og-placeholder';\n\n/**\n * Resolve a branded og-placeholder image URL for a title, driven entirely by\n * the runtime `endpoints` (no injected builder).\n *\n * THE one og-placeholder hook. It reads the host's `endpoints` from\n * `ChatRuntime` and hands them to `buildOgPlaceholderUrl`, which resolves the\n * route base (explicit `ogPlaceholderUrl` → derived from `imageProxyUrlPrefix`\n * → same-origin `/api/og-placeholder`) and appends `?title=…`. Per-platform\n * brand colors are resolved SERVER-SIDE by the route — nothing is baked here.\n *\n * Replaces the old builder-injection `useOgPlaceholder(buildUrl, …)`: callers\n * no longer pass a URL builder. `useEntityCardPlaceholder` delegates here too,\n * so every surface shares one memo + one code path.\n */\nexport interface UseOgPlaceholderUrlArgs {\n /** Text to display on the placeholder. */\n title: string | undefined | null;\n /** Site name shown below the title (optional). */\n siteName?: string;\n /** `'wide'` (1200×630 social-card; default) or `'square'` (1024×1024 — for\n * compact chat-inline slots so `object-cover` doesn't crop the title off). */\n aspect?: 'wide' | 'square';\n /** When `false`, returns `null` instead of a URL. */\n enabled?: boolean;\n}\n\nexport function useOgPlaceholderUrl({\n title,\n siteName = '',\n aspect = 'wide',\n enabled = true,\n}: UseOgPlaceholderUrlArgs): string | null {\n const endpoints = useChatRuntime()?.endpoints;\n\n return useMemo(() => {\n if (!enabled || !title) return null;\n return buildOgPlaceholderUrl(endpoints, title, { site: siteName || undefined, aspect });\n }, [endpoints, title, siteName, aspect, enabled]);\n}\n"],"mappings":"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","names":["r","g","b","adapter","useEffect","useEffect"]}