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

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 (186) hide show
  1. package/dist/{chunk-22RSJ57Z.js → chunk-2YVTGPM7.js} +2 -2
  2. package/dist/{chunk-QNWQQ4RT.cjs → chunk-53O6PQ3F.cjs} +7 -7
  3. package/dist/{chunk-QNWQQ4RT.cjs.map → chunk-53O6PQ3F.cjs.map} +1 -1
  4. package/dist/{chunk-54X75QTC.cjs → chunk-5XPK2QGU.cjs} +14 -14
  5. package/dist/{chunk-54X75QTC.cjs.map → chunk-5XPK2QGU.cjs.map} +1 -1
  6. package/dist/{chunk-4HBYEZ3I.cjs → chunk-7BNWMOPM.cjs} +82 -82
  7. package/dist/{chunk-4HBYEZ3I.cjs.map → chunk-7BNWMOPM.cjs.map} +1 -1
  8. package/dist/{chunk-HLEXYTOE.js → chunk-7EPGJOKN.js} +4 -4
  9. package/dist/{chunk-XRSJER2B.js → chunk-A5NHAHV6.js} +4 -4
  10. package/dist/{chunk-P27OGSQL.cjs → chunk-ATREVNIN.cjs} +37 -37
  11. package/dist/{chunk-P27OGSQL.cjs.map → chunk-ATREVNIN.cjs.map} +1 -1
  12. package/dist/{chunk-S5M5SEW2.js → chunk-AVAZ6BD6.js} +5 -5
  13. package/dist/{chunk-DLRE5Y3G.js → chunk-C2SVJVEB.js} +2 -2
  14. package/dist/{chunk-KZXKT5AX.cjs → chunk-CG7OWW7X.cjs} +14 -14
  15. package/dist/{chunk-KZXKT5AX.cjs.map → chunk-CG7OWW7X.cjs.map} +1 -1
  16. package/dist/{chunk-AIXPQT3G.js → chunk-CKO6NKNE.js} +801 -222
  17. package/dist/chunk-CKO6NKNE.js.map +1 -0
  18. package/dist/{chunk-UFXV64WQ.cjs → chunk-CKUPANVU.cjs} +9 -9
  19. package/dist/{chunk-UFXV64WQ.cjs.map → chunk-CKUPANVU.cjs.map} +1 -1
  20. package/dist/{chunk-3ZAIHRQH.cjs → chunk-DKJDO7YM.cjs} +35 -35
  21. package/dist/{chunk-3ZAIHRQH.cjs.map → chunk-DKJDO7YM.cjs.map} +1 -1
  22. package/dist/{chunk-D7BFS7N6.cjs → chunk-DUURFYGS.cjs} +3 -3
  23. package/dist/{chunk-D7BFS7N6.cjs.map → chunk-DUURFYGS.cjs.map} +1 -1
  24. package/dist/{chunk-NMXL34Z2.cjs → chunk-DVSPRY2V.cjs} +3 -3
  25. package/dist/{chunk-NMXL34Z2.cjs.map → chunk-DVSPRY2V.cjs.map} +1 -1
  26. package/dist/{chunk-VLWUKFQE.cjs → chunk-EXWS2QSD.cjs} +1301 -722
  27. package/dist/chunk-EXWS2QSD.cjs.map +1 -0
  28. package/dist/{chunk-5CBQBTGN.cjs → chunk-F6LXGP6S.cjs} +7 -7
  29. package/dist/{chunk-5CBQBTGN.cjs.map → chunk-F6LXGP6S.cjs.map} +1 -1
  30. package/dist/{chunk-MDYTAKJ5.cjs → chunk-FTK3S57R.cjs} +22 -22
  31. package/dist/{chunk-MDYTAKJ5.cjs.map → chunk-FTK3S57R.cjs.map} +1 -1
  32. package/dist/{chunk-UAKWSR3U.js → chunk-GDZ7IC5B.js} +2 -2
  33. package/dist/{chunk-OD2WMUAO.cjs → chunk-GZRU2A4L.cjs} +197 -197
  34. package/dist/{chunk-OD2WMUAO.cjs.map → chunk-GZRU2A4L.cjs.map} +1 -1
  35. package/dist/{chunk-Q76YVNCQ.js → chunk-HM7ZGABI.js} +2 -2
  36. package/dist/{chunk-YCKGUQA2.cjs → chunk-I27PWVWE.cjs} +17 -12
  37. package/dist/chunk-I27PWVWE.cjs.map +1 -0
  38. package/dist/{chunk-TSENCZUL.js → chunk-IQTQIBZ3.js} +11 -11
  39. package/dist/{chunk-QUGV2GT3.cjs → chunk-KDCIYRZV.cjs} +34 -34
  40. package/dist/{chunk-QUGV2GT3.cjs.map → chunk-KDCIYRZV.cjs.map} +1 -1
  41. package/dist/{chunk-2MGTNYHW.cjs → chunk-KQZMM3QJ.cjs} +100 -100
  42. package/dist/{chunk-2MGTNYHW.cjs.map → chunk-KQZMM3QJ.cjs.map} +1 -1
  43. package/dist/{chunk-BJHM2OEY.cjs → chunk-LNKLB7GM.cjs} +17 -17
  44. package/dist/{chunk-BJHM2OEY.cjs.map → chunk-LNKLB7GM.cjs.map} +1 -1
  45. package/dist/{chunk-Z622ACFD.js → chunk-LYJJH4D6.js} +5 -5
  46. package/dist/{chunk-42RQ4NIL.js → chunk-LYQULSK4.js} +6 -6
  47. package/dist/{chunk-TBJWPNHU.js → chunk-MCNQKS6I.js} +4 -4
  48. package/dist/chunk-MCNQKS6I.js.map +1 -0
  49. package/dist/{chunk-JGVJRUGB.cjs → chunk-MIZU2OTO.cjs} +4 -4
  50. package/dist/{chunk-JGVJRUGB.cjs.map → chunk-MIZU2OTO.cjs.map} +1 -1
  51. package/dist/{chunk-BOM6MMWB.cjs → chunk-N3526CTH.cjs} +3 -3
  52. package/dist/{chunk-BOM6MMWB.cjs.map → chunk-N3526CTH.cjs.map} +1 -1
  53. package/dist/{chunk-2PQKZWWP.js → chunk-PKZGXRU2.js} +6 -6
  54. package/dist/{chunk-FCQR6VY4.cjs → chunk-Q25VYAEL.cjs} +26 -26
  55. package/dist/{chunk-FCQR6VY4.cjs.map → chunk-Q25VYAEL.cjs.map} +1 -1
  56. package/dist/{chunk-25X43LSD.js → chunk-RK47E3PP.js} +4 -4
  57. package/dist/{chunk-V5NOURSR.js → chunk-RLNNV46K.js} +5 -5
  58. package/dist/{chunk-ZBQZJAC7.cjs → chunk-RTKF5AYA.cjs} +7 -7
  59. package/dist/chunk-RTKF5AYA.cjs.map +1 -0
  60. package/dist/{chunk-RF2EYX3B.js → chunk-SC44YJCF.js} +2 -2
  61. package/dist/{chunk-2OD2PPNO.cjs → chunk-SDORFSFK.cjs} +37 -37
  62. package/dist/{chunk-2OD2PPNO.cjs.map → chunk-SDORFSFK.cjs.map} +1 -1
  63. package/dist/{chunk-5A3WSWZD.js → chunk-TEIHEFXP.js} +8 -8
  64. package/dist/{chunk-U4OJCJTH.js → chunk-TXFGG47A.js} +4 -4
  65. package/dist/{chunk-DMXI5D2Q.cjs → chunk-USKQURNF.cjs} +3 -3
  66. package/dist/{chunk-DMXI5D2Q.cjs.map → chunk-USKQURNF.cjs.map} +1 -1
  67. package/dist/{chunk-2KILTCND.js → chunk-UVUNBXS6.js} +6 -1
  68. package/dist/chunk-UVUNBXS6.js.map +1 -0
  69. package/dist/{chunk-X4WBGDNW.cjs → chunk-WTYQCT4A.cjs} +18 -18
  70. package/dist/{chunk-X4WBGDNW.cjs.map → chunk-WTYQCT4A.cjs.map} +1 -1
  71. package/dist/{chunk-OC3QH5WG.js → chunk-X5XFKI6N.js} +2 -2
  72. package/dist/{chunk-2V4KPBNZ.js → chunk-XAOAYDGR.js} +3 -3
  73. package/dist/{chunk-4BUPMSLY.js → chunk-Y7ZEVTLC.js} +3 -3
  74. package/dist/{chunk-DOUG4UEZ.js → chunk-Z7QCNCBN.js} +11 -11
  75. package/dist/{chunk-OQVHGPHA.js → chunk-ZAV2F4VX.js} +3 -3
  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 +39 -7
  89. package/dist/components/features/index.cjs.map +1 -1
  90. package/dist/components/features/index.d.ts +1 -0
  91. package/dist/components/features/index.d.ts.map +1 -1
  92. package/dist/components/features/index.js +38 -6
  93. package/dist/components/features/remote-session/index.d.ts +13 -0
  94. package/dist/components/features/remote-session/index.d.ts.map +1 -0
  95. package/dist/components/features/remote-session/remote-access-request-card.d.ts +19 -0
  96. package/dist/components/features/remote-session/remote-access-request-card.d.ts.map +1 -0
  97. package/dist/components/features/remote-session/remote-session-block.d.ts +41 -0
  98. package/dist/components/features/remote-session/remote-session-block.d.ts.map +1 -0
  99. package/dist/components/features/remote-session/remote-session-chat-panel.d.ts +23 -0
  100. package/dist/components/features/remote-session/remote-session-chat-panel.d.ts.map +1 -0
  101. package/dist/components/features/remote-session/remote-session-dialog.d.ts +15 -0
  102. package/dist/components/features/remote-session/remote-session-dialog.d.ts.map +1 -0
  103. package/dist/components/features/remote-session/remote-session-overlay.d.ts +12 -0
  104. package/dist/components/features/remote-session/remote-session-overlay.d.ts.map +1 -0
  105. package/dist/components/features/remote-session/remote-session-parts.d.ts +34 -0
  106. package/dist/components/features/remote-session/remote-session-parts.d.ts.map +1 -0
  107. package/dist/components/features/remote-session/types.d.ts +33 -0
  108. package/dist/components/features/remote-session/types.d.ts.map +1 -0
  109. package/dist/components/help-center-pages/index.cjs +101 -101
  110. package/dist/components/help-center-pages/index.js +21 -21
  111. package/dist/components/index.cjs +97 -65
  112. package/dist/components/index.cjs.map +1 -1
  113. package/dist/components/index.js +53 -21
  114. package/dist/components/index.js.map +1 -1
  115. package/dist/components/meeting-scheduler/index.cjs +58 -58
  116. package/dist/components/meeting-scheduler/index.js +8 -8
  117. package/dist/components/navigation/index.cjs +10 -10
  118. package/dist/components/navigation/index.js +9 -9
  119. package/dist/components/onboarding-guides/index.cjs +11 -11
  120. package/dist/components/onboarding-guides/index.js +10 -10
  121. package/dist/components/related-content/index.cjs +6 -6
  122. package/dist/components/related-content/index.js +5 -5
  123. package/dist/components/tickets/index.cjs +12 -12
  124. package/dist/components/tickets/index.js +11 -11
  125. package/dist/components/tickets/ticket-live-provider.d.ts.map +1 -1
  126. package/dist/components/ui/index.cjs +7 -7
  127. package/dist/components/ui/index.js +6 -6
  128. package/dist/hooks/index.cjs +4 -4
  129. package/dist/hooks/index.js +3 -3
  130. package/dist/index.cjs +44 -12
  131. package/dist/index.cjs.map +1 -1
  132. package/dist/index.js +42 -10
  133. package/dist/index.js.map +1 -1
  134. package/dist/utils/embed-authed-fetch.d.ts +18 -0
  135. package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
  136. package/dist/utils/index.cjs +50 -6
  137. package/dist/utils/index.cjs.map +1 -1
  138. package/dist/utils/index.js +50 -6
  139. package/dist/utils/index.js.map +1 -1
  140. package/dist/utils/sse-subscription.d.ts +29 -11
  141. package/dist/utils/sse-subscription.d.ts.map +1 -1
  142. package/package.json +1 -1
  143. package/src/components/features/__tests__/remote-session.test.tsx +97 -0
  144. package/src/components/features/index.ts +1 -0
  145. package/src/components/features/remote-session/index.ts +36 -0
  146. package/src/components/features/remote-session/remote-access-request-card.tsx +102 -0
  147. package/src/components/features/remote-session/remote-session-block.tsx +182 -0
  148. package/src/components/features/remote-session/remote-session-chat-panel.tsx +178 -0
  149. package/src/components/features/remote-session/remote-session-dialog.tsx +77 -0
  150. package/src/components/features/remote-session/remote-session-overlay.tsx +33 -0
  151. package/src/components/features/remote-session/remote-session-parts.tsx +138 -0
  152. package/src/components/features/remote-session/types.ts +41 -0
  153. package/src/components/tickets/ticket-live-provider.tsx +14 -12
  154. package/src/stories/RemoteSessionAccessRequest.stories.tsx +71 -0
  155. package/src/stories/RemoteSessionBlock.stories.tsx +164 -0
  156. package/src/stories/RemoteSessionDialog.stories.tsx +49 -0
  157. package/src/stories/RemoteSessionIndicator.stories.tsx +44 -0
  158. package/src/utils/embed-authed-fetch.ts +21 -0
  159. package/src/utils/sse-subscription.ts +107 -20
  160. package/dist/chunk-2KILTCND.js.map +0 -1
  161. package/dist/chunk-AIXPQT3G.js.map +0 -1
  162. package/dist/chunk-TBJWPNHU.js.map +0 -1
  163. package/dist/chunk-VLWUKFQE.cjs.map +0 -1
  164. package/dist/chunk-YCKGUQA2.cjs.map +0 -1
  165. package/dist/chunk-ZBQZJAC7.cjs.map +0 -1
  166. /package/dist/{chunk-22RSJ57Z.js.map → chunk-2YVTGPM7.js.map} +0 -0
  167. /package/dist/{chunk-HLEXYTOE.js.map → chunk-7EPGJOKN.js.map} +0 -0
  168. /package/dist/{chunk-XRSJER2B.js.map → chunk-A5NHAHV6.js.map} +0 -0
  169. /package/dist/{chunk-S5M5SEW2.js.map → chunk-AVAZ6BD6.js.map} +0 -0
  170. /package/dist/{chunk-DLRE5Y3G.js.map → chunk-C2SVJVEB.js.map} +0 -0
  171. /package/dist/{chunk-UAKWSR3U.js.map → chunk-GDZ7IC5B.js.map} +0 -0
  172. /package/dist/{chunk-Q76YVNCQ.js.map → chunk-HM7ZGABI.js.map} +0 -0
  173. /package/dist/{chunk-TSENCZUL.js.map → chunk-IQTQIBZ3.js.map} +0 -0
  174. /package/dist/{chunk-Z622ACFD.js.map → chunk-LYJJH4D6.js.map} +0 -0
  175. /package/dist/{chunk-42RQ4NIL.js.map → chunk-LYQULSK4.js.map} +0 -0
  176. /package/dist/{chunk-2PQKZWWP.js.map → chunk-PKZGXRU2.js.map} +0 -0
  177. /package/dist/{chunk-25X43LSD.js.map → chunk-RK47E3PP.js.map} +0 -0
  178. /package/dist/{chunk-V5NOURSR.js.map → chunk-RLNNV46K.js.map} +0 -0
  179. /package/dist/{chunk-RF2EYX3B.js.map → chunk-SC44YJCF.js.map} +0 -0
  180. /package/dist/{chunk-5A3WSWZD.js.map → chunk-TEIHEFXP.js.map} +0 -0
  181. /package/dist/{chunk-U4OJCJTH.js.map → chunk-TXFGG47A.js.map} +0 -0
  182. /package/dist/{chunk-OC3QH5WG.js.map → chunk-X5XFKI6N.js.map} +0 -0
  183. /package/dist/{chunk-2V4KPBNZ.js.map → chunk-XAOAYDGR.js.map} +0 -0
  184. /package/dist/{chunk-4BUPMSLY.js.map → chunk-Y7ZEVTLC.js.map} +0 -0
  185. /package/dist/{chunk-DOUG4UEZ.js.map → chunk-Z7QCNCBN.js.map} +0 -0
  186. /package/dist/{chunk-OQVHGPHA.js.map → chunk-ZAV2F4VX.js.map} +0 -0
@@ -4,13 +4,13 @@ import {
4
4
  getOSLabel,
5
5
  getShellLabel,
6
6
  getToolLabel
7
- } from "./chunk-AIXPQT3G.js";
7
+ } from "./chunk-CKO6NKNE.js";
8
8
  import {
9
9
  useOnboardingState
10
- } from "./chunk-2V4KPBNZ.js";
10
+ } from "./chunk-XAOAYDGR.js";
11
11
  import {
12
12
  SECTION_HEADING_CLASS
13
- } from "./chunk-4BUPMSLY.js";
13
+ } from "./chunk-Y7ZEVTLC.js";
14
14
  import {
15
15
  ToolIcon,
16
16
  cn as cn2
@@ -37,7 +37,7 @@ import {
37
37
  InteractiveCard,
38
38
  StatusBadge,
39
39
  nameInitials
40
- } from "./chunk-22RSJ57Z.js";
40
+ } from "./chunk-2YVTGPM7.js";
41
41
  import {
42
42
  next_image_default
43
43
  } from "./chunk-MDKCTSDS.js";
@@ -562,4 +562,4 @@ export {
562
562
  OnboardingStepCard,
563
563
  OnboardingWalkthrough
564
564
  };
565
- //# sourceMappingURL=chunk-Z622ACFD.js.map
565
+ //# sourceMappingURL=chunk-LYJJH4D6.js.map
@@ -3,16 +3,16 @@ import {
3
3
  FileDownloadCard,
4
4
  GoogleSheetsViewer,
5
5
  PdfViewer
6
- } from "./chunk-XRSJER2B.js";
6
+ } from "./chunk-A5NHAHV6.js";
7
7
  import {
8
8
  MobileNavigationDropdown,
9
9
  MultiLevelNavigation,
10
10
  StickySectionNav
11
- } from "./chunk-OC3QH5WG.js";
11
+ } from "./chunk-X5XFKI6N.js";
12
12
  import {
13
13
  DocSearchBar,
14
14
  useDocSearch
15
- } from "./chunk-S5M5SEW2.js";
15
+ } from "./chunk-AVAZ6BD6.js";
16
16
  import {
17
17
  CategorySidebarSkeleton,
18
18
  DEFAULT_FOLDER_INDEX_FILE,
@@ -22,10 +22,10 @@ import {
22
22
  findDocNodeByPath,
23
23
  getDocAncestorNodeIds,
24
24
  stripFolderIndexFromPath
25
- } from "./chunk-AIXPQT3G.js";
25
+ } from "./chunk-CKO6NKNE.js";
26
26
  import {
27
27
  contentFetch
28
- } from "./chunk-UAKWSR3U.js";
28
+ } from "./chunk-GDZ7IC5B.js";
29
29
  import {
30
30
  HUB_HEADER_OFFSET_PX,
31
31
  apiErrorMessage,
@@ -1098,4 +1098,4 @@ export {
1098
1098
  EmbedSkeleton,
1099
1099
  DocsHubPage
1100
1100
  };
1101
- //# sourceMappingURL=chunk-42RQ4NIL.js.map
1101
+ //# sourceMappingURL=chunk-LYQULSK4.js.map
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
  import {
3
3
  createSseSubscription
4
- } from "./chunk-AIXPQT3G.js";
4
+ } from "./chunk-CKO6NKNE.js";
5
5
  import {
6
6
  useChatIdentity
7
- } from "./chunk-4BUPMSLY.js";
7
+ } from "./chunk-Y7ZEVTLC.js";
8
8
  import {
9
9
  embedAuthedFetch
10
- } from "./chunk-2KILTCND.js";
10
+ } from "./chunk-UVUNBXS6.js";
11
11
  import {
12
12
  useChatRuntime
13
13
  } from "./chunk-3SMLYVM2.js";
@@ -275,4 +275,4 @@ export {
275
275
  TICKET_MARK_READ_DEBOUNCE_MS,
276
276
  TOAST_COPY
277
277
  };
278
- //# sourceMappingURL=chunk-TBJWPNHU.js.map
278
+ //# sourceMappingURL=chunk-MCNQKS6I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/tickets/ticket-live-provider.tsx","../src/components/tickets/types.ts"],"sourcesContent":["'use client';\n\n/**\n * TicketLiveProvider — the ONE client home for support-ticket realtime.\n *\n * DOMAIN logic only — connection lifecycle is owned entirely by the\n * common SSE client (`createSseSubscription`; its module header lists\n * the policies); this provider consumes its consolidated\n * `onConnectedChange` signal.\n *\n * Owns:\n * - the unread summary — delivered EXCLUSIVELY by the stream\n * (`ticket-summary` frames on connect + debounced after events) and\n * by `ticket-read` responses. The client holds NO summary fetch\n * path: resync-on-(re)connect is served by the SERVER (every\n * connect pushes a fresh summary);\n * - query invalidation on events (list + open drawer refresh live);\n * - `markRead` with local write-through zeroing for 0-latency,\n * reconciled by the response's server-computed summary;\n * - the ticket-contract `no-stream` (204) retry policy.\n *\n * There is NO polling anywhere and NO summary endpoint. Hosts whose\n * stream endpoint 404s or is absent get no unread indication — the\n * indication IS a realtime feature (a 404ing endpoint is re-probed on\n * presence, rate-limited by the client).\n *\n * Invalidation semantics on events — deliberately `invalidateQueries`,\n * NOT `setQueryData` patches: the ticket hooks run `gcTime: 0`, so\n * there is usually no cache entry to patch, and `TicketsCacheSlot`\n * shape rules apply when there is.\n */\n\nimport { useQueryClient } from '@tanstack/react-query';\nimport React from 'react';\nimport { useChatRuntime } from '../../contexts/chat-runtime-context';\nimport { embedAuthedFetch } from '../../utils/embed-authed-fetch';\nimport { createSseSubscription, type SseSubscription, type SseTransportStatus } from '../../utils/sse-subscription';\nimport { useChatIdentity } from '../chat/hooks/use-chat-identity';\nimport type { TicketMessageStreamData, TicketStreamEvent, TicketUnreadSummary } from './types';\n\n// Ticket realtime is its OWN api surface — deliberately NOT under\n// `/api/chat/agent/*` (that prefix is the chat agent's tool surface;\n// ticket streaming/read-receipts are unrelated to chat).\nconst TICKET_STREAM_ENDPOINT = '/api/tickets/stream';\nconst TICKET_READ_ENDPOINT = '/api/tickets/read';\n\nconst EMPTY_SUMMARY: TicketUnreadSummary = { totalUnread: 0, tickets: {}, latestUnreadAt: {} };\n\n/** ISO stamps can carry mixed timezone offsets (server `+00:00` vs an\n * optimistic local `Z`) — lexicographic string comparison mis-orders\n * those, so every stamp comparison goes through parsed epoch ms.\n * Missing/unparseable stamps sort oldest. */\nconst stampMs = (stamp: string | null | undefined): number => {\n if (!stamp) return 0;\n const ms = Date.parse(stamp);\n return Number.isNaN(ms) ? 0 : ms;\n};\n\n/** Trailing debounce for event-burst invalidations (an agent pasting 5\n * messages must not fire 5 list refetches). */\nconst INVALIDATE_DEBOUNCE_MS = 1_500;\n\nexport interface TicketLiveContextValue {\n /** Viewer has a resolved, non-anon chat identity. Surfaces (e.g. the\n * header cell) hide themselves when false — computed here so they\n * don't each spin up their own identity resolution. */\n authed: boolean;\n /** Realtime-subscribed (server-confirmed), not merely HTTP-open. */\n connected: boolean;\n /** Unread total across tickets — drives the header count badge. */\n unreadTotal: number;\n /** Per-ticket unread counts (missing key = 0) — drives row dots.\n * The currently-open ticket is masked to 0. */\n unreadByTicket: Record<string, number>;\n /** The ticket with the NEWEST unread reply (open ticket excluded) —\n * the header cell routes to it via the SSOT deep link\n * (`buildTicketOpenHref`). Null when nothing is unread. */\n nextUnreadTicketId: string | null;\n /** Mark a ticket read: POSTs the receipt, zeroes the ticket locally\n * (0-latency), then applies the response's server-computed summary. */\n markRead: (ticketExternalId: string) => Promise<void>;\n /** Drawer open/close registration — suppresses unread bumps for the\n * open ticket and masks it in the derived map. */\n setOpenTicketId: (ticketExternalId: string | null) => void;\n /** Hint that the viewer just created a ticket — reconnects the stream\n * (fresh ownership set + fresh server-pushed summary). */\n notifyTicketCreated: () => void;\n}\n\nconst TicketLiveContext = React.createContext<TicketLiveContextValue | null>(null);\n\nexport function useOptionalTicketLive(): TicketLiveContextValue | null {\n return React.useContext(TicketLiveContext);\n}\n\nexport function useTicketLive(): TicketLiveContextValue {\n const ctx = React.useContext(TicketLiveContext);\n if (!ctx) {\n throw new Error('useTicketLive must be used within <TicketLiveProvider>');\n }\n return ctx;\n}\n\nfunction isTicketMessageData(value: unknown): value is TicketMessageStreamData {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as TicketMessageStreamData).ticket_external_id === 'string'\n );\n}\n\nfunction isSummaryData(value: unknown): value is TicketUnreadSummary {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as TicketUnreadSummary).totalUnread === 'number' &&\n typeof (value as TicketUnreadSummary).tickets === 'object'\n );\n}\n\nexport interface TicketLiveProviderProps {\n children: React.ReactNode;\n /**\n * Whether ticket realtime is available to this host at all (feature flag,\n * session, entitlement). `false` provides NO context — `useOptionalTicketLive()`\n * reads `null`, `useTicketLive()` throws, and no identity or stream request is\n * made: byte-for-byte what a host got by not mounting this at all.\n *\n * It exists so a host never has to mount this CONDITIONALLY. A host that writes\n * `enabled ? <TicketLiveProvider>{children}</TicketLiveProvider> : <>{children}</>`\n * changes the element TYPE at that position the moment its flag answers, and React\n * tears down and remounts everything below. In openframe-frontend that wrapper sat\n * around the whole `AppLayout`, so every cold load remounted the app shell and the\n * page under it — visibly, as a chat drawer opened by a deep link replaying its\n * open animation. Mount it always and pass the answer here instead.\n *\n * One consequence of mounting it always: a `ChatRuntime` must be above it even\n * while disabled. The identity resolver reads one unconditionally (Rules of\n * Hooks) and throws without it; `enabled` gates the request, not the read.\n */\n enabled?: boolean;\n}\n\nexport function TicketLiveProvider({ children, enabled = true }: TicketLiveProviderProps) {\n const runtime = useChatRuntime();\n const identity = useChatIdentity(enabled);\n const queryClient = useQueryClient();\n\n const streamUrl = runtime?.endpoints.ticketStreamUrl ?? TICKET_STREAM_ENDPOINT;\n const readUrl = runtime?.endpoints.ticketReadUrl ?? TICKET_READ_ENDPOINT;\n\n // Gates the stream below (via `streamEnabled`) as well as every surface that\n // reads `authed` — one predicate, so a disabled provider is also a silent one.\n const authed = enabled && identity.authTier !== 'anon' && !!identity.user?.email;\n\n const [connected, setConnected] = React.useState(false);\n // The SINGLE unread source — written ONLY from server-computed\n // summaries (stream frames + markRead responses) plus the two\n // 0-latency optimistic writes (event bump, markRead zero), both\n // reconciled by the next server summary.\n const [summary, setSummary] = React.useState<TicketUnreadSummary>(EMPTY_SUMMARY);\n // State + ref for the open drawer: state drives the derived map,\n // the ref lets stream callbacks read it without re-subscribing.\n const [openTicketIdState, setOpenTicketIdState] = React.useState<string | null>(null);\n const openTicketIdRef = React.useRef<string | null>(null);\n\n // ---- Debounced (trailing) invalidation of ticket queries on events ----\n const pendingTicketIdsRef = React.useRef<Set<string>>(new Set());\n const invalidateTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const flushInvalidations = React.useCallback(() => {\n invalidateTimerRef.current = null;\n const ids = Array.from(pendingTicketIdsRef.current);\n pendingTicketIdsRef.current.clear();\n void queryClient.invalidateQueries({ queryKey: ['tickets'] });\n for (const id of ids) {\n void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', id] });\n }\n }, [queryClient]);\n const scheduleInvalidation = React.useCallback(\n (ticketId?: string) => {\n if (ticketId) pendingTicketIdsRef.current.add(ticketId);\n if (invalidateTimerRef.current) return;\n invalidateTimerRef.current = setTimeout(flushInvalidations, INVALIDATE_DEBOUNCE_MS);\n },\n [flushInvalidations],\n );\n\n // ---- Optimistic unread writes (reconciled by server summaries) ----\n const bumpUnread = React.useCallback((ticketId: string, createdAt?: string | null) => {\n setSummary(prev => {\n const stamp = createdAt ?? new Date().toISOString();\n const existing = prev.latestUnreadAt[ticketId];\n return {\n totalUnread: prev.totalUnread + 1,\n tickets: { ...prev.tickets, [ticketId]: (prev.tickets[ticketId] ?? 0) + 1 },\n latestUnreadAt: {\n ...prev.latestUnreadAt,\n [ticketId]: existing && stampMs(existing) > stampMs(stamp) ? existing : stamp,\n },\n };\n });\n }, []);\n\n const zeroUnread = React.useCallback((ticketId: string) => {\n setSummary(prev => {\n const current = prev.tickets[ticketId] ?? 0;\n if (current === 0) return prev;\n const tickets = { ...prev.tickets };\n delete tickets[ticketId];\n const latestUnreadAt = { ...prev.latestUnreadAt };\n delete latestUnreadAt[ticketId];\n return { totalUnread: Math.max(0, prev.totalUnread - current), tickets, latestUnreadAt };\n });\n }, []);\n\n // ---- Stream lifecycle (owned by the SSE CLIENT — the provider only\n // consumes its consolidated signals) ----\n const subscriptionRef = React.useRef<SseSubscription | null>(null);\n const noStreamRef = React.useRef(false);\n\n // Stable handler refs so the subscription effect doesn't churn.\n const handlersRef = React.useRef<{\n onEvent: (eventName: string, data: unknown) => void;\n onStatus: (status: SseTransportStatus) => void;\n onConnected: (connected: boolean) => void;\n }>({ onEvent: () => {}, onStatus: () => {}, onConnected: () => {} });\n\n const handleConnected = (isConnected: boolean) => {\n setConnected(isConnected);\n if (isConnected) {\n // Event-gap healing on every (re)connect: the SERVER pushes a\n // fresh summary; the open drawer refetches through the ACL'd path.\n const openId = openTicketIdRef.current;\n if (openId) {\n void queryClient.invalidateQueries({ queryKey: ['ticket-engagements', openId] });\n }\n }\n };\n\n const handleEvent = (eventName: string, data: unknown) => {\n if (eventName === 'ticket-summary') {\n const frame = data as TicketStreamEvent | null;\n if (isSummaryData(frame?.data)) {\n setSummary({\n totalUnread: frame.data.totalUnread,\n tickets: frame.data.tickets ?? {},\n latestUnreadAt: frame.data.latestUnreadAt ?? {},\n });\n }\n return;\n }\n\n if (eventName === 'ticket-resync') {\n // Ownership gained / degraded payload — a fresh ticket-summary\n // frame follows from the server; refresh the query surfaces.\n scheduleInvalidation();\n return;\n }\n\n if (eventName === 'ticket-status') {\n // Meaningful ticket-row change (close/reopen/pipeline move). The\n // server pushes an updated summary right after (closures count as\n // unread server-side); here we just refresh the query surfaces so\n // the status badge flips live.\n const frame = data as TicketStreamEvent | null;\n const ticketId = (frame?.data as { ticket_external_id?: string } | undefined)?.ticket_external_id;\n scheduleInvalidation(ticketId);\n return;\n }\n\n if (eventName === 'ticket-message') {\n const frame = data as TicketStreamEvent | null;\n const payload = frame?.data;\n if (!isTicketMessageData(payload)) {\n scheduleInvalidation();\n return;\n }\n // Always invalidate (own replies from another tab render live);\n // bump unread ONLY on the server-stamped predicate, and never for\n // the ticket whose drawer is open. The server's debounced summary\n // push reconciles moments later.\n scheduleInvalidation(payload.ticket_external_id);\n if (payload.countsAsUnread && payload.ticket_external_id !== openTicketIdRef.current) {\n bumpUnread(payload.ticket_external_id, payload.hubspot_created_at);\n }\n }\n };\n\n const handleStatus = (status: SseTransportStatus) => {\n // The client owns connection lifecycle. The only transport status\n // with DOMAIN meaning is the ticket contract's `no-stream` (204 =\n // zero owned tickets): remember it so a domain signal (own\n // create_ticket, window focus) can retry.\n if (status === 'open') noStreamRef.current = false;\n if (status === 'no-stream') noStreamRef.current = true;\n };\n\n // Publish the three handlers into the stable ref. In an unconditional effect\n // rather than in the render body: every caller is an SSE transport callback,\n // which can only fire after a commit, and a render attempt React discards\n // must not be able to leave a handler behind that closes over state the user\n // never saw. Declared BEFORE the subscription effect so it is filled first\n // in the same flush.\n React.useEffect(() => {\n handlersRef.current.onConnected = handleConnected;\n handlersRef.current.onEvent = handleEvent;\n handlersRef.current.onStatus = handleStatus;\n });\n\n // Mount/unmount of the subscription — gated on auth + a usable URL.\n const streamEnabled = authed && !!streamUrl;\n React.useEffect(() => {\n if (!streamEnabled) return undefined;\n if (typeof window === 'undefined') return undefined;\n\n subscriptionRef.current = createSseSubscription({\n url: streamUrl,\n onEvent: (name, data) => handlersRef.current.onEvent(name, data),\n onStatusChange: status => handlersRef.current.onStatus(status),\n onConnectedChange: isConnected => handlersRef.current.onConnected(isConnected),\n // Long-lived per-user stream: suspend while hidden (scale relief),\n // resume + reconnect on visible/online — all inside the client.\n pauseWhenHidden: true,\n });\n\n const retryIfIdle = () => {\n // After a 204 (no-stream), a focus signal is the retry trigger. Only\n // 204 — the client's contract leaves that retry to the caller; it\n // retries terminal / 401 states on its own.\n if (noStreamRef.current) {\n noStreamRef.current = false;\n subscriptionRef.current?.reconnectNow();\n }\n };\n window.addEventListener('focus', retryIfIdle);\n\n return () => {\n window.removeEventListener('focus', retryIfIdle);\n if (invalidateTimerRef.current) {\n clearTimeout(invalidateTimerRef.current);\n invalidateTimerRef.current = null;\n }\n subscriptionRef.current?.close();\n subscriptionRef.current = null;\n setConnected(false);\n setSummary(EMPTY_SUMMARY);\n };\n // handlersRef is stable; streamUrl/auth changes remount the stream.\n }, [streamEnabled, streamUrl]);\n\n // ---- Public API ----\n const markRead = React.useCallback(\n async (ticketExternalId: string) => {\n try {\n const response = await embedAuthedFetch(readUrl, {\n method: 'POST',\n body: JSON.stringify({ ticket_id: ticketExternalId }),\n });\n if (!response.ok) return;\n // 0-latency local zero, then reconcile with the response's\n // server-computed summary (THE truth — includes closure\n // accounting and other tickets).\n zeroUnread(ticketExternalId);\n const body = (await response.json().catch(() => null)) as { summary?: TicketUnreadSummary } | null;\n if (isSummaryData(body?.summary)) {\n setSummary({\n totalUnread: body.summary.totalUnread,\n tickets: body.summary.tickets ?? {},\n latestUnreadAt: body.summary.latestUnreadAt ?? {},\n });\n }\n } catch {\n // Non-fatal — the receipt lands on the next markRead; unread\n // state reconciles via the next server summary push.\n }\n },\n [readUrl, zeroUnread],\n );\n\n const setOpenTicketId = React.useCallback((ticketExternalId: string | null) => {\n openTicketIdRef.current = ticketExternalId;\n setOpenTicketIdState(ticketExternalId);\n }, []);\n\n const notifyTicketCreated = React.useCallback(() => {\n // Fresh connect → fresh ownership set + fresh server summary\n // (covers both the no-stream 204 wait state and an already-live\n // stream whose set predates the new ticket).\n noStreamRef.current = false;\n subscriptionRef.current?.reconnectNow();\n }, []);\n\n // Derived unread map — open ticket masked to 0 so a server summary\n // landing inside the markRead debounce window can't transiently\n // re-badge it.\n const value = React.useMemo<TicketLiveContextValue>(() => {\n let unreadByTicket = summary.tickets;\n let unreadTotal = summary.totalUnread;\n if (openTicketIdState && unreadByTicket[openTicketIdState]) {\n const masked = unreadByTicket[openTicketIdState];\n unreadByTicket = { ...unreadByTicket };\n delete unreadByTicket[openTicketIdState];\n unreadTotal = Math.max(0, unreadTotal - masked);\n }\n // Ticket with the NEWEST unread reply (compared as parsed epoch ms\n // via `stampMs` — mixed timezone offsets break string ordering) —\n // header cell routes there. Falls back to any unread key if a\n // timestamp is missing.\n let nextUnreadTicketId: string | null = null;\n let nextStampMs = 0;\n for (const id of Object.keys(unreadByTicket)) {\n const ms = stampMs(summary.latestUnreadAt[id]);\n if (nextUnreadTicketId === null || ms > nextStampMs) {\n nextUnreadTicketId = id;\n nextStampMs = ms;\n }\n }\n return {\n authed,\n connected,\n unreadTotal,\n unreadByTicket,\n nextUnreadTicketId,\n markRead,\n setOpenTicketId,\n notifyTicketCreated,\n };\n }, [summary, authed, connected, openTicketIdState, markRead, setOpenTicketId, notifyTicketCreated]);\n\n return <TicketLiveContext.Provider value={enabled ? value : null}>{children}</TicketLiveContext.Provider>;\n}\n","/**\n * Wire shape of a row returned by `POST /api/chat/agent/find-ticket`.\n * Mirrors the executor's projection at `lib/data/hubspot-tools.ts`\n * (`FIND_TICKET_SELECT` / `FindTicketResult`).\n *\n * Cross-repo duplication is INTENTIONAL: this lib ships independently\n * of the hub, so we can't import `FindTicketResult` from\n * `hubspot-tools.ts` directly. If the server adds a column to\n * `FIND_TICKET_SELECT`, also add it here. The smoke test in §F of the\n * plan covers the happy path; a wire-contract test belongs in the hub.\n *\n * `find-ticket` returns `customer_emails: string[]` (jsonb array), NOT\n * a single `customer_email`. The list is server-self-scoped to the\n * caller's session email; the array is exposed for admin/staff\n * surfaces, which the ticket center doesn't render.\n */\nexport interface TicketData {\n id: string;\n /** HubSpot ticket id (display number, e.g. \"1234\"). */\n external_id: string;\n subject: string | null;\n /** Short (≤400 char) HTML-stripped preview of the ticket body —\n * used for the list-card subtitle when needed. */\n preview: string | null;\n /** Longer (≤4k char) sanitized body. INCLUDES every appended\n * `content_addendum` comment because the `update_ticket` executor\n * reads + re-writes the `content` property server-side with a\n * `---` separator. Render this in the drawer's description block\n * and the user sees both the original message + every comment they\n * (or staff) added. */\n body: string | null;\n /** Canonical OPEN | CLOSED (HubSpot pipeline derived). */\n status: string | null;\n /** Human label like \"New\" / \"Working on it\" / \"Waiting on contact\" /\n * \"Closed\". Drives the badge text; canonical status drives color. */\n pipeline_stage_label: string | null;\n clickup_task_id: string | null;\n /** Snapshot of the linked ClickUp delivery task — populated server-side\n * via the `clickup_tasks` mirror when `clickup_task_id` is set. Drives\n * the \"Linked delivery\" card surface on the ticket drawer (status\n * badge + ClickUp deep link). `null` when no link OR the ClickUp row\n * was deleted / not yet synced. */\n clickup: TicketClickupSummary | null;\n priority: string | null;\n customer_emails: string[];\n customer_company: string | null;\n /** HubSpot contact's display name. Drives the customer attribution\n * on the drawer when the viewer is NOT the customer themselves\n * (admin browsing / multi-contact second viewer). Conversations\n * API messages don't carry per-message sender info on Custom\n * Channels, so this is the only reliable source for \"what's the\n * customer's name.\" */\n customer_name: string | null;\n /** HubSpot owner id of the agent assigned to this ticket. Carried as\n * raw id for debugging; rendering goes through `assignedOwner`. Null\n * when unassigned. */\n assigned_to: string | null;\n /** Resolved assigned-owner profile — name + email + avatar. Populated\n * server-side via `attachOwnerProfiles` which joins through the\n * `hubspot_owners` mirror to `profiles` by email. Drives the\n * \"Assigned to\" attribution in the drawer header. Null when\n * unassigned OR the owner couldn't be resolved (rare — only when\n * the agent was deleted from HubSpot between the ticket update and\n * the next owners reconcile). */\n assignedOwner: TicketAssignedOwner | null;\n hubspot_updated_at: string;\n}\n\n/** Resolved profile of a ticket's assigned agent — surfaced in the\n * drawer header. Subset of the server's `MirroredOwnerProfile`\n * trimmed to just the rendering fields. */\nexport interface TicketAssignedOwner {\n name: string | null;\n email: string | null;\n avatarUrl: string | null;\n}\n\n/** Compact projection of a linked ClickUp task — matches the server's\n * `ClickupSummary` and aligns with `DeliveryItem` so the linked-card\n * on a ticket can render through the same `DeliveryRow` primitive used\n * on `/bug-fixes-and-enhancements`. */\nexport interface TicketClickupSummary {\n /** ClickUp task external_id (e.g. \"86ad4e022\"). Used as the\n * `?focus=<id>` URL param to scroll the public delivery page to\n * this row. */\n external_id: string;\n title: string | null;\n description: string | null;\n /** ClickUp status name — e.g. \"complete\" / \"working\" / \"design approved\"\n * / \"waiting for release\". Used as the badge label. */\n status: string | null;\n /** ClickUp's per-status hex color (e.g. \"#008844\"). Forwarded to the\n * badge so colors match the ClickUp board exactly. */\n status_color: string | null;\n /** Bucket — `'backlog' | 'working' | 'complete' | 'unknown'`. Used as\n * a fallback when status_color is missing. */\n status_category: string | null;\n /** ClickUp custom item label (`'Bug'` / `'Request'`) — drives the\n * type badge (\"BUG-FIX\" / \"ENHANCEMENT\"). */\n task_type: string | null;\n custom_item_id: number | null;\n /** Every ClickUp list the task is associated with. UI joins with \", \". */\n list_names: string[];\n /** Unix-ms timestamps so the row's \"ACTIVE X ago\" subtitle uses the\n * shared `getRelativeTime()` helper. */\n date_opened: number | null;\n date_updated: number | null;\n date_closed: number | null;\n /** Direct https://app.clickup.com/t/<id> deep link. Kept on the wire\n * for admin surfaces; the customer-facing linked card navigates\n * internally instead. */\n clickup_url: string | null;\n /** Composed server-side via the SAME `buildDevSectionUrl` helper the\n * chat-inline delivery card uses. Carries `?search=<id>` so the\n * delivery list filters to that single task on landing. */\n delivery_href: string;\n /** Target platform name for the host's `useNavLink` to decide\n * same-tab vs new-tab on cross-platform links. */\n delivery_target_platform: string;\n /** Release version label set by the delivery team, e.g. \"0.9\" / \"1.0\".\n * Shown beside the status when present. */\n target_version: string | null;\n}\n\n/**\n * Optimistic placeholder a `submitTicket` call prepends to the list\n * BEFORE the server roundtrip resolves. Drawer is hidden until the\n * real id arrives. The wrapper destructures `_optimistic` before\n * forwarding to `<ChatTicketItem>` so the DOM doesn't see an unknown\n * prop.\n */\nexport interface OptimisticTicket extends TicketData {\n _optimistic: true;\n}\n\nexport type AnyTicket = TicketData | OptimisticTicket;\n\nexport function isOptimistic(t: AnyTicket): t is OptimisticTicket {\n return (t as OptimisticTicket)._optimistic === true;\n}\n\n/**\n * Shape of a single `['tickets', …]` TanStack-Query cache slot.\n * Mirrors `FindTicketResponse` in `hooks/use-tickets-list.ts` — kept\n * here because the cache-mutation call sites in `useTicketActions` and\n * `<HelpCenterList>` would otherwise have to redeclare the shape inline.\n *\n * A 2026-05-29 prod regression (`t.map is not a function` on\n * close/reopen) was caused by assuming the cache held a bare\n * `TicketData[]` instead of this wrapper — every helper that calls\n * `queryClient.setQueriesData` / `getQueriesData` on `['tickets']`\n * MUST type the value through this shape and project / reassemble\n * `tickets` explicitly.\n */\nexport interface TicketsCacheSlot {\n tickets?: TicketData[];\n count?: number;\n totalCount?: number;\n page?: number;\n pageSize?: number;\n totalPages?: number;\n scope?: 'self' | 'all';\n}\n\n/**\n * Stable server-side error codes the ticket-action helpers route\n * through `mapTicketActionError`. Anything else is treated as a generic\n * server error.\n *\n * Reply-specific codes (`HUBSPOT_5XX` / `HUBSPOT_400_VALIDATION` /\n * `HUBSPOT_404_THREAD` / `HUBSPOT_REPLY_UNKNOWN`) drive the drawer\n * banner that appears above the composer when a customer reply fails.\n * Distinct from `HUBSPOT_DISCONNECTED` (whole-system) and\n * `TICKET_NOT_FOUND` (terminal-row) — the reply codes are per-attempt\n * + retryable except for `HUBSPOT_404_THREAD`.\n */\nexport type TicketActionErrorCode =\n | 'PROPOSAL_NOT_CLAIMABLE'\n | 'TICKET_NOT_FOUND'\n | 'TICKET_OWNERSHIP_DENIED'\n | 'HUBSPOT_DISCONNECTED'\n | 'RATE_LIMITED'\n | 'INVALID_TOOL_ARGS'\n | 'HUBSPOT_5XX'\n | 'HUBSPOT_400_VALIDATION'\n | 'HUBSPOT_404_THREAD'\n | 'HUBSPOT_REPLY_UNKNOWN'\n | 'UNKNOWN';\n\nexport interface MappedTicketActionError {\n code: TicketActionErrorCode;\n /** Human-readable copy safe to show in a toast. */\n message: string;\n /** When true, the form should disable submit + show the\n * support-down banner. Set only for HUBSPOT_DISCONNECTED. */\n supportSystemDown: boolean;\n /** When true, the helper should remove the affected row optimistically\n * (TICKET_NOT_FOUND). */\n removeRowFromCache: boolean;\n /** Retry hint surfaced from a 429 response. Caller decides whether\n * to mention it in the toast. */\n retryAfterSeconds?: number;\n}\n\n/**\n * Defensive client-side cap on ticket text (initial content + comment\n * addendums). HubSpot Note engagements accept more, but a 100KB paste\n * should fail fast at the UI rather than burning a server round-trip.\n * Both the open-ticket form and the per-row comment textarea import\n * this so a future server-side hardening only touches one place.\n */\nexport const TICKET_TEXT_MAX_CHARS = 5000;\n\n/**\n * Wire contract of the ticket live stream (`GET /api/chat/agent/\n * ticket-stream`, SSE). Producer (the hub route's `mapEvent`) imports\n * THIS type — same producer/consumer SSOT pattern as\n * `ConversationEngagementWire = TicketEngagement`. A schema drift here\n * would be a silent serialization bug; keep one named contract.\n *\n * Frames carry METADATA ONLY — never message bodies. Content is always\n * refetched through the ACL'd `list-engagements` path (one read path).\n *\n * Event names (`eventType`):\n * - `ticket-message` — a message row landed in the mirror for one of\n * the viewer's tickets. `data` carries the metadata.\n * - `ticket-status` — a MEANINGFUL ticket-row change (status /\n * pipeline stage / closure) landed in the mirror. Server-side\n * old-vs-new diffing drops no-op writes (sync stamps). Clients\n * invalidate + refetch the summary (closures count as unread\n * server-side via `closed_at` vs the read receipt).\n * - `ticket-summary` — the server-computed `TicketUnreadSummary`.\n * THE stream is the summary webservice: pushed on connect (server-\n * side resync-on-reconnect) and re-pushed (debounced) after every\n * delivered event. Clients hold NO summary fetch path.\n * - `ticket-resync` — degraded frame: something happened that the\n * server couldn't fully describe (truncated Realtime payload, or\n * the connection's ownership set just gained tickets). A fresh\n * `ticket-summary` follows it; clients invalidate queries.\n *\n * The server also emits `status` frames (`subscribed` / `retrying` /\n * `reconnect_failed`) from the shared SSE utility — clients derive\n * `connected` from those, NOT from the HTTP stream being open.\n */\nexport type TicketStreamEventType = 'ticket-message' | 'ticket-status' | 'ticket-summary' | 'ticket-resync';\n\n/** Metadata payload of a `ticket-message` frame. */\nexport type TicketMessageStreamData = {\n ticket_external_id: string;\n /** Mirror row's HubSpot message id. */\n message_id: string;\n /** Server-stamped via the same direction predicate the wire mapper\n * uses. Clients never re-derive author classification. */\n authorRole: 'customer' | 'support';\n /** Server-stamped via the visibility+direction unread predicate\n * (`countsAsUnread` in the hub's conversations DAL). ONLY frames\n * with `true` may bump client-side unread state. */\n countsAsUnread: boolean;\n hubspot_created_at: string | null;\n};\n\n/** Metadata payload of a `ticket-status` frame. */\nexport type TicketStatusStreamData = {\n ticket_external_id: string;\n /** Canonical OPEN | CLOSED after the change. */\n status: string | null;\n pipeline_stage_label: string | null;\n closed_at: string | null;\n};\n\nexport interface TicketStreamEvent {\n eventType: TicketStreamEventType;\n /** Human-readable label (SSE envelope compat) — clients ignore it. */\n message: string;\n /** `ticket-message` → TicketMessageStreamData; `ticket-status` →\n * TicketStatusStreamData; `ticket-summary` → TicketUnreadSummary;\n * absent on `ticket-resync`. */\n data?: TicketMessageStreamData | TicketStatusStreamData | TicketUnreadSummary;\n}\n\n/**\n * The SINGLE unread source, delivered EXCLUSIVELY over the stream\n * (`ticket-summary` frames on connect + after events) and in\n * `ticket-read` responses. There is no summary fetch endpoint. Header\n * badge total AND per-row dots both read this via the\n * `TicketLiveProvider` map; missing keys mean 0.\n */\nexport type TicketUnreadSummary = {\n totalUnread: number;\n tickets: Record<string, number>;\n /** ISO timestamp of the NEWEST unread message per ticket (same keys as\n * `tickets`). Drives \"route to the most recent update\" on the header\n * cell: the ticket with the max value here is `nextUnreadTicketId`. */\n latestUnreadAt: Record<string, string>;\n};\n\n/**\n * Trailing debounce for `markRead` while the drawer is open and new\n * messages stream in — flushed on drawer close so a fast open/close\n * still persists the receipt. Named beside its sibling cadences so\n * every timing knob lives in one file.\n */\nexport const TICKET_MARK_READ_DEBOUNCE_MS = 2000;\n\n/**\n * Ticket deep-link SSOT — DEFINED in the server-safe param-keys module\n * (`utils/dev-sections/dev-section-param-keys.ts`, beside its sibling\n * `devSectionAnchorId`) because producers span both worlds: the hub's\n * SERVER-side chat-ref builder and the client header cell. Re-exported\n * here so ticket-surface consumers keep one import home.\n */\nexport { TICKET_OPEN_PARAM, buildTicketOpenHref } from '../../utils/dev-sections/dev-section-param-keys';\n\n/**\n * Centralized toast copy. Keep all wording here so QA / localization\n * can find every user-visible string in one file.\n */\nexport const TOAST_COPY = {\n open_success: { title: 'Ticket opened', description: 'We received your message and will follow up shortly.' },\n open_mirror_pending: { title: 'Ticket opened', description: 'Syncing — your ticket will appear momentarily.' },\n close_success: { title: 'Ticket closed' },\n reopen_success: { title: 'Ticket reopened' },\n comment_success: { title: 'Comment added' },\n attach_success: { title: 'Files attached' },\n // Failure variants are constructed dynamically from MappedTicketActionError.\n} as const;\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkYCKGUQA2cjs = require('./chunk-YCKGUQA2.cjs');
5
+ var _chunkI27PWVWEcjs = require('./chunk-I27PWVWE.cjs');
6
6
 
7
7
  // src/utils/search-params.ts
8
8
  function positiveInt(raw, fallback, { min = 1, max } = {}) {
@@ -122,9 +122,9 @@ function withQuery(path, queryString) {
122
122
 
123
123
  // src/utils/embed-content-fetch.ts
124
124
  var contentFetch = (input, init) => {
125
- if (!_chunkYCKGUQA2cjs.hasEmbedAuthAdapter.call(void 0, )) return fetch(input, init);
125
+ if (!_chunkI27PWVWEcjs.hasEmbedAuthAdapter.call(void 0, )) return fetch(input, init);
126
126
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
127
- return _chunkYCKGUQA2cjs.embedAuthedFetch.call(void 0, url, init);
127
+ return _chunkI27PWVWEcjs.embedAuthedFetch.call(void 0, url, init);
128
128
  };
129
129
 
130
130
 
@@ -142,4 +142,4 @@ var contentFetch = (input, init) => {
142
142
 
143
143
 
144
144
  exports.contentFetch = contentFetch; exports.positiveInt = positiveInt; exports.PAGE_PARAM_LIMITS = PAGE_PARAM_LIMITS; exports.pageCount = pageCount; exports.clampPageToTotal = clampPageToTotal; exports.defineParamSchema = defineParamSchema; exports.fromKeys = fromKeys; exports.firstParamValue = firstParamValue; exports.parseSchemaParams = parseSchemaParams; exports.shouldIncludeInUrl = shouldIncludeInUrl; exports.createSearchParams = createSearchParams; exports.serializeSchemaParams = serializeSchemaParams; exports.withQuery = withQuery;
145
- //# sourceMappingURL=chunk-JGVJRUGB.cjs.map
145
+ //# sourceMappingURL=chunk-MIZU2OTO.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-JGVJRUGB.cjs","../src/utils/search-params.ts","../src/utils/embed-content-fetch.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-JGVJRUGB.cjs","sourcesContent":[null,"/**\n * URL parameter schema engine — the ONE grammar for every URL/API integer,\n * the ONE omission rule, the ONE serializer.\n *\n * Hoisted out of `hooks/state/` so SERVER code (route handlers, DALs) and the\n * CLIENT hook parse the same URL with the same function: a param contract that\n * disagrees across the wire is the bug class this removes. JSX-free leaf with\n * its own `exports` subpath; `hooks/state/*` re-exports every name it used to own.\n */\n\n/** JavaScript types a URL parameter can carry. `int` is a whole number with a floor. */\nexport type JSType = 'string' | 'number' | 'int' | 'boolean' | 'array' | 'object';\n\n// ── The integer grammar ─────────────────────────────────────────────────────\n\nexport interface PositiveIntOptions {\n /** Smallest accepted value (default 1). Anything below falls back. */\n min?: number;\n /** Largest accepted value. Applied to the PARSED value AND to the fallback. */\n max?: number;\n}\n\n/**\n * THE integer grammar for URL params, env vars, and page sizes.\n *\n * `null`/`undefined`/`''` → fallback. Otherwise `Math.trunc(Number(raw))`, which\n * accepts `'12'`, `12`, `'12.9'` (→ 12) and `'0x10'` (→ 16), and rejects `'12abc'`\n * and `'abc'` (`NaN`) — the historical `parseInt` accepted `'12abc'` as 12 and\n * produced `NaN` for `'abc'`. A value below `min` falls back. The RESULT (parsed\n * value or fallback alike) is clamped to `max`, so \"never exceeds max\" has one owner.\n */\nexport function positiveInt<TFallback = number>(\n raw: string | number | null | undefined,\n fallback: TFallback,\n { min = 1, max }: PositiveIntOptions = {},\n): number | TFallback {\n // The fallback is returned AS GIVEN — capping only ever applies to a value\n // parsed out of the input, so a caller with no default (`null`) gets its\n // sentinel back untouched instead of `Math.min(null, max)`.\n const capped = (v: number) => (max == null ? v : Math.min(v, max));\n if (raw === null || raw === undefined || raw === '') {\n return typeof fallback === 'number' ? capped(fallback) : fallback;\n }\n const n = Math.trunc(Number(raw));\n if (!Number.isFinite(n) || n < min) {\n return typeof fallback === 'number' ? capped(fallback) : fallback;\n }\n return capped(n);\n}\n\n/** Ceilings every paged surface shares. */\nexport const PAGE_PARAM_LIMITS = { maxPage: 10_000, maxPageSize: 100 } as const;\n\n/** Total pages for a row count. Floors at 1, so an empty result is still \"page 1 of 1\". */\nexport function pageCount(total: number, pageSize: number): number {\n if (!Number.isFinite(total) || !Number.isFinite(pageSize) || pageSize <= 0) return 1;\n return Math.max(1, Math.ceil(total / pageSize));\n}\n\n/** Clamp a requested page into `1..totalPages`. */\nexport function clampPageToTotal(page: number, totalPages: number): number {\n return Math.min(page, Math.max(1, totalPages));\n}\n\n// ── Schema ──────────────────────────────────────────────────────────────────\n\ntype OutputTypeMap = {\n string: string;\n number: number;\n int: number;\n boolean: boolean;\n array: string[];\n object: Record<string, unknown>;\n};\n\nexport type OutputTypeForJSType<T extends JSType> = OutputTypeMap[T];\n\nexport interface BaseParamConfig<T extends JSType = JSType> {\n type: T;\n default?: OutputTypeMap[T];\n required?: boolean;\n}\n\n/** An integer parameter with an optional floor and ceiling. */\nexport interface IntParamConfig extends BaseParamConfig<'int'> {\n min?: number;\n max?: number;\n}\n\nexport type ParamConfig<T extends JSType = JSType> = T extends 'int' ? IntParamConfig : BaseParamConfig<T>;\n\nexport type ParamSchema = Record<string, ParamConfig>;\n\n/** Identity helper that preserves literal types through a schema object. */\nexport function defineParamSchema<T extends ParamSchema>(schema: T): T {\n return schema;\n}\n\n/**\n * Build a `Record<K, V>` from a key tuple. Keeps `keyof` EXACT when spreading a\n * shared config across derived keys (an inline `Object.fromEntries` widens to\n * `Record<string, V>` and loses the key union).\n */\nexport function fromKeys<K extends string, V>(keys: readonly K[], value: V): Record<K, V> {\n return Object.fromEntries(keys.map(k => [k, value])) as Record<K, V>;\n}\n\n// ── Reading ─────────────────────────────────────────────────────────────────\n\n/** Anything a URL can be read from: `URLSearchParams`, Next's `searchParams`, or a plain record. */\nexport type ParamInput = URLSearchParams | Record<string, string | string[] | undefined> | null | undefined;\n\n/** THE first-value rule for a duplicated key (`?a=1&a=2` reads as `1`). */\nexport function firstParamValue(input: ParamInput, key: string): string | undefined {\n if (!input) return undefined;\n if (input instanceof URLSearchParams) return input.get(key) ?? undefined;\n const value = input[key];\n return Array.isArray(value) ? value[0] : value;\n}\n\n/** Every value for a key (arrays read repeated params, then a `,`-joined single value). */\nfunction allParamValues(input: ParamInput, key: string): string[] {\n if (!input) return [];\n const raw = input instanceof URLSearchParams ? input.getAll(key) : input[key];\n const list = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];\n if (list.length === 1 && list[0]?.includes(',')) return list[0].split(',').filter(Boolean);\n return list.filter(v => v !== undefined && v !== '');\n}\n\nexport interface ParseSchemaOptions {\n /**\n * What an ABSENT scalar with no declared `default` resolves to.\n * `'undefined'` (default) keeps today's behaviour; `'null'` is the explicit\n * \"this filter is unset\" spelling that survives JSON and `URLSearchParams`.\n */\n absent?: 'undefined' | 'null';\n}\n\nexport type AbsentValue<Opts extends ParseSchemaOptions | undefined> = Opts extends { absent: 'null' }\n ? null\n : undefined;\n\n/**\n * The parsed shape of a schema: keys with a `default` and every array key are\n * non-nullable; an undefaulted scalar carries the caller's absent value.\n */\nexport type InferParsedParams<TSchema extends ParamSchema, Absent = undefined> = {\n [K in keyof TSchema]: TSchema[K] extends { type: 'array' }\n ? string[]\n : TSchema[K] extends { default: infer D }\n ? D\n : TSchema[K]['type'] extends infer T\n ? T extends JSType\n ? OutputTypeForJSType<T> | Absent\n : never\n : never;\n};\n\nfunction coerceScalar(raw: string | undefined, config: ParamConfig): unknown {\n if (raw === undefined || raw === '') return undefined;\n switch (config.type) {\n case 'int': {\n const int = config;\n // ONE integer grammar, default or not. Without a declared default the\n // fallback is `undefined` (an unparseable value is absent), which\n // `positiveInt` returns untouched — the branch used to open-code the\n // truncation, the min floor and the max clamp a second time.\n return positiveInt<undefined>(raw, int.default as undefined, { min: int.min, max: int.max });\n }\n case 'number': {\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n }\n case 'boolean':\n return raw === 'true' || raw === '1';\n case 'object':\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n default:\n return raw;\n }\n}\n\n/**\n * Parse a URL into a schema's shape. THE reader — the server route and the\n * client hook both call this, so a URL means exactly one thing.\n *\n * PRECEDENCE: an absent or empty SCALAR takes the declared `default` when there\n * is one, else the `absent` value. ARRAY keys never take `absent`: they are `[]`.\n */\nexport function parseSchemaParams<TSchema extends ParamSchema, Opts extends ParseSchemaOptions = Record<string, never>>(\n schema: TSchema,\n input: ParamInput,\n options?: Opts,\n): InferParsedParams<TSchema, AbsentValue<Opts>> {\n const absent = options?.absent === 'null' ? null : undefined;\n const out: Record<string, unknown> = {};\n for (const [key, config] of Object.entries(schema)) {\n if (config.type === 'array') {\n const values = allParamValues(input, key);\n out[key] = values.length > 0 ? values : (config.default ?? []);\n continue;\n }\n const coerced = coerceScalar(firstParamValue(input, key), config);\n out[key] = coerced !== undefined ? coerced : (config.default ?? absent);\n }\n return out as InferParsedParams<TSchema, AbsentValue<Opts>>;\n}\n\n// ── Writing ─────────────────────────────────────────────────────────────────\n\n/**\n * A schema entry OR a legacy flattened param (which spells its default\n * `defaultValue`).\n *\n * A UNION, not a bag of two optional fields: with only `default?` and\n * `defaultValue?` this is a WEAK TYPE, and TypeScript rejects any argument\n * sharing neither — including `{ type: 'string' }`, the single most common\n * schema entry there is. Naming the real config shapes fixes that without an\n * index signature, which `FlattenedParam` (an interface) could never satisfy.\n */\ntype OmissionConfig = ParamConfig | { default?: unknown; defaultValue?: unknown };\n\n/**\n * THE omission rule: `null`, `undefined`, `''`, `[]`, and a value equal to the\n * declared default are left out, so a URL only ever carries what differs.\n */\nexport function shouldIncludeInUrl(value: unknown, config: OmissionConfig | undefined): boolean {\n if (value === null || value === undefined) return false;\n if (Array.isArray(value) && value.length === 0) return false;\n if (value === '') return false;\n // `in` narrowing rather than a cast: the union's members genuinely differ —\n // a schema entry spells its default `default`, a flattened param spells it\n // `defaultValue`, and neither is obliged to carry the other's field.\n const fromDefault = config && 'default' in config ? config.default : undefined;\n const fromLegacy = config && 'defaultValue' in config ? config.defaultValue : undefined;\n const declared = fromDefault !== undefined ? fromDefault : fromLegacy;\n if (declared !== undefined && value === declared) return false;\n return true;\n}\n\nexport interface CreateSearchParamsOptions {\n /** Join arrays with this separator instead of repeating the key (`;` for the hub's list APIs). */\n arrayJoin?: string;\n}\n\n/** Build `URLSearchParams` from a value record (arrays repeat the key by default). */\nexport function createSearchParams(\n params: Record<string, unknown>,\n { arrayJoin }: CreateSearchParamsOptions = {},\n): URLSearchParams {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n // The emptiness half of the omission rule has ONE owner. (No config here:\n // callers that must also drop schema DEFAULTS filter through\n // `shouldIncludeInUrl` with the config first — see `serializeSchemaParams`.)\n if (!shouldIncludeInUrl(value, undefined)) continue;\n if (Array.isArray(value)) {\n // Empty ELEMENTS are dropped, and an array that filters to nothing is\n // absent (a cleared multi-select must not leave `?tags=` behind).\n const items = value.filter(v => v !== null && v !== undefined && v !== '');\n if (items.length === 0) continue;\n if (arrayJoin) search.set(key, items.join(arrayJoin));\n else for (const item of items) search.append(key, String(item));\n continue;\n }\n if (typeof value === 'object') {\n search.set(key, JSON.stringify(value));\n continue;\n }\n search.set(key, String(value));\n }\n return search;\n}\n\n/** Serialize params to a query string, omitting everything `shouldIncludeInUrl` rejects. */\nexport function serializeSchemaParams<TSchema extends ParamSchema>(\n schema: TSchema,\n params: Record<string, unknown>,\n options?: CreateSearchParamsOptions,\n): string {\n const kept: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(params)) {\n if (shouldIncludeInUrl(value, schema[key])) kept[key] = value;\n }\n return createSearchParams(kept, options).toString();\n}\n\n/** Append a query string to a path, or return the path unchanged when it is empty. */\nexport function withQuery(path: string, queryString: string | null | undefined): string {\n return queryString ? `${path}?${queryString}` : path;\n}\n","'use client';\n\n/**\n * `contentFetch` — the fetch the lib's SELF-FETCHING content surfaces use:\n * roadmap (list + vote + per-task refresh), delivery, product releases,\n * onboarding guides (catalog + detail), legal docs, and the release-detail\n * injected roadmap/delivery sections.\n *\n * It deliberately reuses the SINGLE embed-auth knob — the registered\n * `EmbedAuthAdapter` — instead of introducing a second registration:\n * - adapter registered (host opted into embedded auth, e.g. openframe-frontend\n * for its embedded chat) → route through `embedAuthedFetch`, so content GETs/\n * POSTs carry the SAME bearer/cookie + 401-refresh as the chat.\n * - no adapter (the public hub, or the embedding example whose proxy injects\n * auth server-side) → a plain, byte-for-byte unchanged `fetch`.\n *\n * So a host wires auth ONCE (the chat adapter) and content inherits it; there is\n * no content-specific fetcher to configure.\n */\n\nimport { embedAuthedFetch, hasEmbedAuthAdapter } from './embed-authed-fetch';\n\nexport const contentFetch: typeof fetch = (input, init) => {\n if (!hasEmbedAuthAdapter()) return fetch(input, init);\n // embedAuthedFetch takes a string url; coerce the broader `fetch` input shape\n // (content surfaces always pass strings, but handle URL/Request defensively).\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n return embedAuthedFetch(url, init);\n};\n"]}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-MIZU2OTO.cjs","../src/utils/search-params.ts","../src/utils/embed-content-fetch.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-MIZU2OTO.cjs","sourcesContent":[null,"/**\n * URL parameter schema engine — the ONE grammar for every URL/API integer,\n * the ONE omission rule, the ONE serializer.\n *\n * Hoisted out of `hooks/state/` so SERVER code (route handlers, DALs) and the\n * CLIENT hook parse the same URL with the same function: a param contract that\n * disagrees across the wire is the bug class this removes. JSX-free leaf with\n * its own `exports` subpath; `hooks/state/*` re-exports every name it used to own.\n */\n\n/** JavaScript types a URL parameter can carry. `int` is a whole number with a floor. */\nexport type JSType = 'string' | 'number' | 'int' | 'boolean' | 'array' | 'object';\n\n// ── The integer grammar ─────────────────────────────────────────────────────\n\nexport interface PositiveIntOptions {\n /** Smallest accepted value (default 1). Anything below falls back. */\n min?: number;\n /** Largest accepted value. Applied to the PARSED value AND to the fallback. */\n max?: number;\n}\n\n/**\n * THE integer grammar for URL params, env vars, and page sizes.\n *\n * `null`/`undefined`/`''` → fallback. Otherwise `Math.trunc(Number(raw))`, which\n * accepts `'12'`, `12`, `'12.9'` (→ 12) and `'0x10'` (→ 16), and rejects `'12abc'`\n * and `'abc'` (`NaN`) — the historical `parseInt` accepted `'12abc'` as 12 and\n * produced `NaN` for `'abc'`. A value below `min` falls back. The RESULT (parsed\n * value or fallback alike) is clamped to `max`, so \"never exceeds max\" has one owner.\n */\nexport function positiveInt<TFallback = number>(\n raw: string | number | null | undefined,\n fallback: TFallback,\n { min = 1, max }: PositiveIntOptions = {},\n): number | TFallback {\n // The fallback is returned AS GIVEN — capping only ever applies to a value\n // parsed out of the input, so a caller with no default (`null`) gets its\n // sentinel back untouched instead of `Math.min(null, max)`.\n const capped = (v: number) => (max == null ? v : Math.min(v, max));\n if (raw === null || raw === undefined || raw === '') {\n return typeof fallback === 'number' ? capped(fallback) : fallback;\n }\n const n = Math.trunc(Number(raw));\n if (!Number.isFinite(n) || n < min) {\n return typeof fallback === 'number' ? capped(fallback) : fallback;\n }\n return capped(n);\n}\n\n/** Ceilings every paged surface shares. */\nexport const PAGE_PARAM_LIMITS = { maxPage: 10_000, maxPageSize: 100 } as const;\n\n/** Total pages for a row count. Floors at 1, so an empty result is still \"page 1 of 1\". */\nexport function pageCount(total: number, pageSize: number): number {\n if (!Number.isFinite(total) || !Number.isFinite(pageSize) || pageSize <= 0) return 1;\n return Math.max(1, Math.ceil(total / pageSize));\n}\n\n/** Clamp a requested page into `1..totalPages`. */\nexport function clampPageToTotal(page: number, totalPages: number): number {\n return Math.min(page, Math.max(1, totalPages));\n}\n\n// ── Schema ──────────────────────────────────────────────────────────────────\n\ntype OutputTypeMap = {\n string: string;\n number: number;\n int: number;\n boolean: boolean;\n array: string[];\n object: Record<string, unknown>;\n};\n\nexport type OutputTypeForJSType<T extends JSType> = OutputTypeMap[T];\n\nexport interface BaseParamConfig<T extends JSType = JSType> {\n type: T;\n default?: OutputTypeMap[T];\n required?: boolean;\n}\n\n/** An integer parameter with an optional floor and ceiling. */\nexport interface IntParamConfig extends BaseParamConfig<'int'> {\n min?: number;\n max?: number;\n}\n\nexport type ParamConfig<T extends JSType = JSType> = T extends 'int' ? IntParamConfig : BaseParamConfig<T>;\n\nexport type ParamSchema = Record<string, ParamConfig>;\n\n/** Identity helper that preserves literal types through a schema object. */\nexport function defineParamSchema<T extends ParamSchema>(schema: T): T {\n return schema;\n}\n\n/**\n * Build a `Record<K, V>` from a key tuple. Keeps `keyof` EXACT when spreading a\n * shared config across derived keys (an inline `Object.fromEntries` widens to\n * `Record<string, V>` and loses the key union).\n */\nexport function fromKeys<K extends string, V>(keys: readonly K[], value: V): Record<K, V> {\n return Object.fromEntries(keys.map(k => [k, value])) as Record<K, V>;\n}\n\n// ── Reading ─────────────────────────────────────────────────────────────────\n\n/** Anything a URL can be read from: `URLSearchParams`, Next's `searchParams`, or a plain record. */\nexport type ParamInput = URLSearchParams | Record<string, string | string[] | undefined> | null | undefined;\n\n/** THE first-value rule for a duplicated key (`?a=1&a=2` reads as `1`). */\nexport function firstParamValue(input: ParamInput, key: string): string | undefined {\n if (!input) return undefined;\n if (input instanceof URLSearchParams) return input.get(key) ?? undefined;\n const value = input[key];\n return Array.isArray(value) ? value[0] : value;\n}\n\n/** Every value for a key (arrays read repeated params, then a `,`-joined single value). */\nfunction allParamValues(input: ParamInput, key: string): string[] {\n if (!input) return [];\n const raw = input instanceof URLSearchParams ? input.getAll(key) : input[key];\n const list = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];\n if (list.length === 1 && list[0]?.includes(',')) return list[0].split(',').filter(Boolean);\n return list.filter(v => v !== undefined && v !== '');\n}\n\nexport interface ParseSchemaOptions {\n /**\n * What an ABSENT scalar with no declared `default` resolves to.\n * `'undefined'` (default) keeps today's behaviour; `'null'` is the explicit\n * \"this filter is unset\" spelling that survives JSON and `URLSearchParams`.\n */\n absent?: 'undefined' | 'null';\n}\n\nexport type AbsentValue<Opts extends ParseSchemaOptions | undefined> = Opts extends { absent: 'null' }\n ? null\n : undefined;\n\n/**\n * The parsed shape of a schema: keys with a `default` and every array key are\n * non-nullable; an undefaulted scalar carries the caller's absent value.\n */\nexport type InferParsedParams<TSchema extends ParamSchema, Absent = undefined> = {\n [K in keyof TSchema]: TSchema[K] extends { type: 'array' }\n ? string[]\n : TSchema[K] extends { default: infer D }\n ? D\n : TSchema[K]['type'] extends infer T\n ? T extends JSType\n ? OutputTypeForJSType<T> | Absent\n : never\n : never;\n};\n\nfunction coerceScalar(raw: string | undefined, config: ParamConfig): unknown {\n if (raw === undefined || raw === '') return undefined;\n switch (config.type) {\n case 'int': {\n const int = config;\n // ONE integer grammar, default or not. Without a declared default the\n // fallback is `undefined` (an unparseable value is absent), which\n // `positiveInt` returns untouched — the branch used to open-code the\n // truncation, the min floor and the max clamp a second time.\n return positiveInt<undefined>(raw, int.default as undefined, { min: int.min, max: int.max });\n }\n case 'number': {\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n }\n case 'boolean':\n return raw === 'true' || raw === '1';\n case 'object':\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n default:\n return raw;\n }\n}\n\n/**\n * Parse a URL into a schema's shape. THE reader — the server route and the\n * client hook both call this, so a URL means exactly one thing.\n *\n * PRECEDENCE: an absent or empty SCALAR takes the declared `default` when there\n * is one, else the `absent` value. ARRAY keys never take `absent`: they are `[]`.\n */\nexport function parseSchemaParams<TSchema extends ParamSchema, Opts extends ParseSchemaOptions = Record<string, never>>(\n schema: TSchema,\n input: ParamInput,\n options?: Opts,\n): InferParsedParams<TSchema, AbsentValue<Opts>> {\n const absent = options?.absent === 'null' ? null : undefined;\n const out: Record<string, unknown> = {};\n for (const [key, config] of Object.entries(schema)) {\n if (config.type === 'array') {\n const values = allParamValues(input, key);\n out[key] = values.length > 0 ? values : (config.default ?? []);\n continue;\n }\n const coerced = coerceScalar(firstParamValue(input, key), config);\n out[key] = coerced !== undefined ? coerced : (config.default ?? absent);\n }\n return out as InferParsedParams<TSchema, AbsentValue<Opts>>;\n}\n\n// ── Writing ─────────────────────────────────────────────────────────────────\n\n/**\n * A schema entry OR a legacy flattened param (which spells its default\n * `defaultValue`).\n *\n * A UNION, not a bag of two optional fields: with only `default?` and\n * `defaultValue?` this is a WEAK TYPE, and TypeScript rejects any argument\n * sharing neither — including `{ type: 'string' }`, the single most common\n * schema entry there is. Naming the real config shapes fixes that without an\n * index signature, which `FlattenedParam` (an interface) could never satisfy.\n */\ntype OmissionConfig = ParamConfig | { default?: unknown; defaultValue?: unknown };\n\n/**\n * THE omission rule: `null`, `undefined`, `''`, `[]`, and a value equal to the\n * declared default are left out, so a URL only ever carries what differs.\n */\nexport function shouldIncludeInUrl(value: unknown, config: OmissionConfig | undefined): boolean {\n if (value === null || value === undefined) return false;\n if (Array.isArray(value) && value.length === 0) return false;\n if (value === '') return false;\n // `in` narrowing rather than a cast: the union's members genuinely differ —\n // a schema entry spells its default `default`, a flattened param spells it\n // `defaultValue`, and neither is obliged to carry the other's field.\n const fromDefault = config && 'default' in config ? config.default : undefined;\n const fromLegacy = config && 'defaultValue' in config ? config.defaultValue : undefined;\n const declared = fromDefault !== undefined ? fromDefault : fromLegacy;\n if (declared !== undefined && value === declared) return false;\n return true;\n}\n\nexport interface CreateSearchParamsOptions {\n /** Join arrays with this separator instead of repeating the key (`;` for the hub's list APIs). */\n arrayJoin?: string;\n}\n\n/** Build `URLSearchParams` from a value record (arrays repeat the key by default). */\nexport function createSearchParams(\n params: Record<string, unknown>,\n { arrayJoin }: CreateSearchParamsOptions = {},\n): URLSearchParams {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n // The emptiness half of the omission rule has ONE owner. (No config here:\n // callers that must also drop schema DEFAULTS filter through\n // `shouldIncludeInUrl` with the config first — see `serializeSchemaParams`.)\n if (!shouldIncludeInUrl(value, undefined)) continue;\n if (Array.isArray(value)) {\n // Empty ELEMENTS are dropped, and an array that filters to nothing is\n // absent (a cleared multi-select must not leave `?tags=` behind).\n const items = value.filter(v => v !== null && v !== undefined && v !== '');\n if (items.length === 0) continue;\n if (arrayJoin) search.set(key, items.join(arrayJoin));\n else for (const item of items) search.append(key, String(item));\n continue;\n }\n if (typeof value === 'object') {\n search.set(key, JSON.stringify(value));\n continue;\n }\n search.set(key, String(value));\n }\n return search;\n}\n\n/** Serialize params to a query string, omitting everything `shouldIncludeInUrl` rejects. */\nexport function serializeSchemaParams<TSchema extends ParamSchema>(\n schema: TSchema,\n params: Record<string, unknown>,\n options?: CreateSearchParamsOptions,\n): string {\n const kept: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(params)) {\n if (shouldIncludeInUrl(value, schema[key])) kept[key] = value;\n }\n return createSearchParams(kept, options).toString();\n}\n\n/** Append a query string to a path, or return the path unchanged when it is empty. */\nexport function withQuery(path: string, queryString: string | null | undefined): string {\n return queryString ? `${path}?${queryString}` : path;\n}\n","'use client';\n\n/**\n * `contentFetch` — the fetch the lib's SELF-FETCHING content surfaces use:\n * roadmap (list + vote + per-task refresh), delivery, product releases,\n * onboarding guides (catalog + detail), legal docs, and the release-detail\n * injected roadmap/delivery sections.\n *\n * It deliberately reuses the SINGLE embed-auth knob — the registered\n * `EmbedAuthAdapter` — instead of introducing a second registration:\n * - adapter registered (host opted into embedded auth, e.g. openframe-frontend\n * for its embedded chat) → route through `embedAuthedFetch`, so content GETs/\n * POSTs carry the SAME bearer/cookie + 401-refresh as the chat.\n * - no adapter (the public hub, or the embedding example whose proxy injects\n * auth server-side) → a plain, byte-for-byte unchanged `fetch`.\n *\n * So a host wires auth ONCE (the chat adapter) and content inherits it; there is\n * no content-specific fetcher to configure.\n */\n\nimport { embedAuthedFetch, hasEmbedAuthAdapter } from './embed-authed-fetch';\n\nexport const contentFetch: typeof fetch = (input, init) => {\n if (!hasEmbedAuthAdapter()) return fetch(input, init);\n // embedAuthedFetch takes a string url; coerce the broader `fetch` input shape\n // (content surfaces always pass strings, but handle URL/Request defensively).\n const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;\n return embedAuthedFetch(url, init);\n};\n"]}
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
2
 
3
3
 
4
- var _chunkNMXL34Z2cjs = require('./chunk-NMXL34Z2.cjs');
4
+ var _chunkDVSPRY2Vcjs = require('./chunk-DVSPRY2V.cjs');
5
5
 
6
6
 
7
7
  var _chunkX7CEWD6Vcjs = require('./chunk-X7CEWD6V.cjs');
@@ -116,7 +116,7 @@ function PersistentPaginationWrapper({
116
116
  },
117
117
  "aria-hidden": isPaginationDisabled,
118
118
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
119
- _chunkNMXL34Z2cjs.UnifiedPagination,
119
+ _chunkDVSPRY2Vcjs.UnifiedPagination,
120
120
  {
121
121
  currentPage: displayCurrentPage,
122
122
  totalPages: displayTotalPages,
@@ -136,4 +136,4 @@ function PersistentPaginationWrapper({
136
136
 
137
137
 
138
138
  exports.PersistentPagination = PersistentPagination; exports.usePaginationLoading = usePaginationLoading; exports.PersistentPaginationWrapper = PersistentPaginationWrapper;
139
- //# sourceMappingURL=chunk-BOM6MMWB.cjs.map
139
+ //# sourceMappingURL=chunk-N3526CTH.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-BOM6MMWB.cjs","../src/components/persistent-pagination.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACwFQ,+CAAA;AA/BD,SAAS,oBAAA,CAAqB;AAAA,EACnC,SAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,gBAAA,EAAkB,GAAA;AAAA,EAClB,mBAAA,EAAqB;AACvB,CAAA,EAA8B;AAI5B,EAAA,uBACE,8BAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,kCAAA;AAAA,QACT,qCAAA;AAAA,QACA,kCAAA;AAAA,QACA,UAAA,GAAa,qBAAA;AAAA,QACb;AAAA,MACF,CAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,UAAA,EAAY,gBAAA,EAAkB,CAAA;AAAA,QACvC,kBAAA,EAAoB,CAAA,EAAA;AACtB,MAAA;AACK,MAAA;AACM,MAAA;AACA,MAAA;AACG,MAAA;AAGb,MAAA;AACC,QAAA;AAA0D,UAAA;AACJ,UAAA;AAAY,UAAA;AAAK,UAAA;AACvE,QAAA;AAIF,wBAAA;AAAC,UAAA;AAAA,UAAA;AACe,YAAA;AACP,YAAA;AACL,cAAA;AACF,YAAA;AACa,YAAA;AAEZ,YAAA;AAAA,UAAA;AACH,QAAA;AAAA,MAAA;AAAA,IAAA;AAGF,EAAA;AAEJ;AAKgB;AACR,EAAA;AAEkB,EAAA;AACT,IAAA;AACG,IAAA;AACK,IAAA;AACD,IAAA;AACtB,EAAA;AAE0B,EAAA;AACT,IAAA;AACU,MAAA;AACzB,IAAA;AACe,IAAA;AACjB,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAcgB;AACd,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACmC;AAGhB,EAAA;AACO,EAAA;AACC,EAAA;AAErB,EAAA;AACgB,EAAA;AAGpB,EAAA;AAAC,IAAA;AAAA,IAAA;AACY,MAAA;AACT,QAAA;AACC,QAAA;AACD,QAAA;AACF,MAAA;AACO,MAAA;AACI,QAAA;AACW,QAAA;AACtB,MAAA;AACK,MAAA;AACM,MAAA;AACA,MAAA;AACG,MAAA;AACI,MAAA;AAGhB,MAAA;AAAc,QAAA;AAShB,wBAAA;AAAC,UAAA;AAAA,UAAA;AACe,YAAA;AACP,YAAA;AACL,cAAA;AACF,YAAA;AACa,YAAA;AAKb,YAAA;AAAC,cAAA;AAAA,cAAA;AACc,gBAAA;AACD,gBAAA;AACZ,gBAAA;AAAiD,gBAAA;AAAA,cAAA;AACnD,YAAA;AAAA,UAAA;AACF,QAAA;AAAA,MAAA;AAAA,IAAA;AAGF,EAAA;AAEJ;ADvF6B;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-BOM6MMWB.cjs","sourcesContent":[null,"'use client';\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn';\nimport { UnifiedPagination } from './unified-pagination';\n\ninterface PersistentPaginationProps {\n /**\n * Whether pagination is currently in a loading state\n */\n isLoading: boolean;\n /**\n * The pagination component\n */\n children: ReactNode;\n /**\n * Current page number\n */\n currentPage: number;\n /**\n * Total number of pages\n */\n totalPages: number;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * Disabled opacity (0-1)\n */\n disabledOpacity?: number;\n /**\n * Transition duration in milliseconds\n */\n transitionDuration?: number;\n /**\n * Whether to show loading state in pagination\n */\n showLoadingState?: boolean;\n}\n\n/**\n * PersistentPagination\n *\n * A wrapper component that keeps pagination visible during loading states\n * but provides visual feedback that it's temporarily disabled.\n *\n * Features:\n * - Maintains pagination visibility during content loading\n * - Disables interaction but preserves layout\n * - Shows current page context even when loading\n * - Provides accessibility support for loading states\n * - Smooth transitions between enabled/disabled states\n *\n * Usage:\n * ```tsx\n * <PersistentPagination\n * isLoading={isLoadingVendors}\n * currentPage={currentPage}\n * totalPages={totalPages}\n * >\n * <Pagination currentPage={currentPage} totalPages={totalPages} />\n * </PersistentPagination>\n * ```\n */\nexport function PersistentPagination({\n isLoading,\n children,\n currentPage,\n totalPages,\n className,\n disabledOpacity = 0.5,\n transitionDuration = 300,\n}: PersistentPaginationProps) {\n // ALWAYS show pagination for predictable layout - just gray it out when not needed\n // Removed condition that hides pagination completely\n\n return (\n <div\n className={cn(\n 'relative transition-all ease-in-out',\n 'flex items-center justify-center',\n isLoading && 'pointer-events-none',\n className,\n )}\n style={{\n opacity: isLoading ? disabledOpacity : 1,\n transitionDuration: `${transitionDuration}ms`,\n }}\n role=\"navigation\"\n aria-label=\"Pagination\"\n aria-busy={isLoading}\n data-loading={isLoading}\n >\n {/* Loading state announcement for screen readers */}\n {isLoading && (\n <div className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n Pagination temporarily disabled while loading page {currentPage} of {totalPages}\n </div>\n )}\n\n {/* Pagination controls with disabled state */}\n <div\n className={cn('relative transition-all ease-in-out', isLoading && 'cursor-not-allowed')}\n style={{\n transitionDuration: `${transitionDuration}ms`,\n }}\n aria-hidden={isLoading}\n >\n {children}\n </div>\n\n {/* REMOVED: Loading overlay - only card skeletons should show during loading */}\n </div>\n );\n}\n\n/**\n * Hook for managing pagination loading states\n */\nexport function usePaginationLoading(isLoading: boolean, currentPage: number, totalPages: number) {\n const shouldShowPagination = totalPages > 1 || isLoading;\n\n const paginationProps = {\n 'aria-busy': isLoading,\n 'data-loading': isLoading,\n 'data-current-page': currentPage,\n 'data-total-pages': totalPages,\n };\n\n const getLoadingMessage = () => {\n if (isLoading) {\n return `Loading page ${currentPage} of ${totalPages}`;\n }\n return `Page ${currentPage} of ${totalPages}`;\n };\n\n return {\n shouldShowPagination,\n paginationProps,\n getLoadingMessage,\n };\n}\n\n/**\n * Enhanced pagination component that includes loading states\n */\ninterface PersistentPaginationWrapperProps {\n isLoading: boolean;\n currentPage: number;\n totalPages: number;\n onPageChange?: (page: number) => void;\n className?: string;\n variant?: 'vendor' | 'blog';\n}\n\nexport function PersistentPaginationWrapper({\n isLoading,\n currentPage,\n totalPages,\n onPageChange,\n className,\n}: PersistentPaginationWrapperProps) {\n // ALWAYS show pagination for predictable layout\n // For no results (totalPages = 0), show grayed out pagination\n const hasResults = totalPages > 0;\n const displayTotalPages = hasResults ? totalPages : 1;\n const displayCurrentPage = hasResults ? currentPage : 1;\n // Only disable during loading, but still show when no results (just grayed out)\n const isPaginationDisabled = isLoading;\n const hasNoResults = !hasResults && !isLoading;\n\n return (\n <div\n className={cn(\n 'relative flex items-center justify-center transition-all ease-in-out',\n (isPaginationDisabled || hasNoResults) && 'pointer-events-none',\n className,\n )}\n style={{\n opacity: isPaginationDisabled ? 0.3 : hasNoResults ? 0.5 : 1,\n transitionDuration: '300ms',\n }}\n role=\"navigation\"\n aria-label=\"Pagination\"\n aria-busy={isLoading}\n data-loading={isLoading}\n data-has-results={hasResults}\n >\n {/* Loading/no results state announcement for screen readers */}\n {(isLoading || !hasResults) && (\n <div className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {isLoading\n ? `Pagination temporarily disabled while loading page ${displayCurrentPage} of ${displayTotalPages}`\n : `No results available - pagination disabled`}\n </div>\n )}\n\n {/* Pagination controls with disabled state */}\n <div\n className={cn('relative transition-all ease-in-out', isPaginationDisabled && 'cursor-not-allowed')}\n style={{\n transitionDuration: '300ms',\n }}\n aria-hidden={isPaginationDisabled}\n >\n {/* UnifiedPagination directly — Pagination and BlogPagination in\n multi-platform-hub are both thin wrappers around it, so the wrapper\n never has to be handed down from the consuming app. */}\n <UnifiedPagination\n currentPage={displayCurrentPage}\n totalPages={displayTotalPages}\n onPageChange={hasResults ? onPageChange : () => {}} // Provide empty function instead of undefined\n />\n </div>\n\n {/* REMOVED: Loading overlays - only card skeletons should show during loading */}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-N3526CTH.cjs","../src/components/persistent-pagination.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACwFQ,+CAAA;AA/BD,SAAS,oBAAA,CAAqB;AAAA,EACnC,SAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,gBAAA,EAAkB,GAAA;AAAA,EAClB,mBAAA,EAAqB;AACvB,CAAA,EAA8B;AAI5B,EAAA,uBACE,8BAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,kCAAA;AAAA,QACT,qCAAA;AAAA,QACA,kCAAA;AAAA,QACA,UAAA,GAAa,qBAAA;AAAA,QACb;AAAA,MACF,CAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,OAAA,EAAS,UAAA,EAAY,gBAAA,EAAkB,CAAA;AAAA,QACvC,kBAAA,EAAoB,CAAA,EAAA;AACtB,MAAA;AACK,MAAA;AACM,MAAA;AACA,MAAA;AACG,MAAA;AAGb,MAAA;AACC,QAAA;AAA0D,UAAA;AACJ,UAAA;AAAY,UAAA;AAAK,UAAA;AACvE,QAAA;AAIF,wBAAA;AAAC,UAAA;AAAA,UAAA;AACe,YAAA;AACP,YAAA;AACL,cAAA;AACF,YAAA;AACa,YAAA;AAEZ,YAAA;AAAA,UAAA;AACH,QAAA;AAAA,MAAA;AAAA,IAAA;AAGF,EAAA;AAEJ;AAKgB;AACR,EAAA;AAEkB,EAAA;AACT,IAAA;AACG,IAAA;AACK,IAAA;AACD,IAAA;AACtB,EAAA;AAE0B,EAAA;AACT,IAAA;AACU,MAAA;AACzB,IAAA;AACe,IAAA;AACjB,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAcgB;AACd,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACmC;AAGhB,EAAA;AACO,EAAA;AACC,EAAA;AAErB,EAAA;AACgB,EAAA;AAGpB,EAAA;AAAC,IAAA;AAAA,IAAA;AACY,MAAA;AACT,QAAA;AACC,QAAA;AACD,QAAA;AACF,MAAA;AACO,MAAA;AACI,QAAA;AACW,QAAA;AACtB,MAAA;AACK,MAAA;AACM,MAAA;AACA,MAAA;AACG,MAAA;AACI,MAAA;AAGhB,MAAA;AAAc,QAAA;AAShB,wBAAA;AAAC,UAAA;AAAA,UAAA;AACe,YAAA;AACP,YAAA;AACL,cAAA;AACF,YAAA;AACa,YAAA;AAKb,YAAA;AAAC,cAAA;AAAA,cAAA;AACc,gBAAA;AACD,gBAAA;AACZ,gBAAA;AAAiD,gBAAA;AAAA,cAAA;AACnD,YAAA;AAAA,UAAA;AACF,QAAA;AAAA,MAAA;AAAA,IAAA;AAGF,EAAA;AAEJ;ADvF6B;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-N3526CTH.cjs","sourcesContent":[null,"'use client';\n\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn';\nimport { UnifiedPagination } from './unified-pagination';\n\ninterface PersistentPaginationProps {\n /**\n * Whether pagination is currently in a loading state\n */\n isLoading: boolean;\n /**\n * The pagination component\n */\n children: ReactNode;\n /**\n * Current page number\n */\n currentPage: number;\n /**\n * Total number of pages\n */\n totalPages: number;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * Disabled opacity (0-1)\n */\n disabledOpacity?: number;\n /**\n * Transition duration in milliseconds\n */\n transitionDuration?: number;\n /**\n * Whether to show loading state in pagination\n */\n showLoadingState?: boolean;\n}\n\n/**\n * PersistentPagination\n *\n * A wrapper component that keeps pagination visible during loading states\n * but provides visual feedback that it's temporarily disabled.\n *\n * Features:\n * - Maintains pagination visibility during content loading\n * - Disables interaction but preserves layout\n * - Shows current page context even when loading\n * - Provides accessibility support for loading states\n * - Smooth transitions between enabled/disabled states\n *\n * Usage:\n * ```tsx\n * <PersistentPagination\n * isLoading={isLoadingVendors}\n * currentPage={currentPage}\n * totalPages={totalPages}\n * >\n * <Pagination currentPage={currentPage} totalPages={totalPages} />\n * </PersistentPagination>\n * ```\n */\nexport function PersistentPagination({\n isLoading,\n children,\n currentPage,\n totalPages,\n className,\n disabledOpacity = 0.5,\n transitionDuration = 300,\n}: PersistentPaginationProps) {\n // ALWAYS show pagination for predictable layout - just gray it out when not needed\n // Removed condition that hides pagination completely\n\n return (\n <div\n className={cn(\n 'relative transition-all ease-in-out',\n 'flex items-center justify-center',\n isLoading && 'pointer-events-none',\n className,\n )}\n style={{\n opacity: isLoading ? disabledOpacity : 1,\n transitionDuration: `${transitionDuration}ms`,\n }}\n role=\"navigation\"\n aria-label=\"Pagination\"\n aria-busy={isLoading}\n data-loading={isLoading}\n >\n {/* Loading state announcement for screen readers */}\n {isLoading && (\n <div className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n Pagination temporarily disabled while loading page {currentPage} of {totalPages}\n </div>\n )}\n\n {/* Pagination controls with disabled state */}\n <div\n className={cn('relative transition-all ease-in-out', isLoading && 'cursor-not-allowed')}\n style={{\n transitionDuration: `${transitionDuration}ms`,\n }}\n aria-hidden={isLoading}\n >\n {children}\n </div>\n\n {/* REMOVED: Loading overlay - only card skeletons should show during loading */}\n </div>\n );\n}\n\n/**\n * Hook for managing pagination loading states\n */\nexport function usePaginationLoading(isLoading: boolean, currentPage: number, totalPages: number) {\n const shouldShowPagination = totalPages > 1 || isLoading;\n\n const paginationProps = {\n 'aria-busy': isLoading,\n 'data-loading': isLoading,\n 'data-current-page': currentPage,\n 'data-total-pages': totalPages,\n };\n\n const getLoadingMessage = () => {\n if (isLoading) {\n return `Loading page ${currentPage} of ${totalPages}`;\n }\n return `Page ${currentPage} of ${totalPages}`;\n };\n\n return {\n shouldShowPagination,\n paginationProps,\n getLoadingMessage,\n };\n}\n\n/**\n * Enhanced pagination component that includes loading states\n */\ninterface PersistentPaginationWrapperProps {\n isLoading: boolean;\n currentPage: number;\n totalPages: number;\n onPageChange?: (page: number) => void;\n className?: string;\n variant?: 'vendor' | 'blog';\n}\n\nexport function PersistentPaginationWrapper({\n isLoading,\n currentPage,\n totalPages,\n onPageChange,\n className,\n}: PersistentPaginationWrapperProps) {\n // ALWAYS show pagination for predictable layout\n // For no results (totalPages = 0), show grayed out pagination\n const hasResults = totalPages > 0;\n const displayTotalPages = hasResults ? totalPages : 1;\n const displayCurrentPage = hasResults ? currentPage : 1;\n // Only disable during loading, but still show when no results (just grayed out)\n const isPaginationDisabled = isLoading;\n const hasNoResults = !hasResults && !isLoading;\n\n return (\n <div\n className={cn(\n 'relative flex items-center justify-center transition-all ease-in-out',\n (isPaginationDisabled || hasNoResults) && 'pointer-events-none',\n className,\n )}\n style={{\n opacity: isPaginationDisabled ? 0.3 : hasNoResults ? 0.5 : 1,\n transitionDuration: '300ms',\n }}\n role=\"navigation\"\n aria-label=\"Pagination\"\n aria-busy={isLoading}\n data-loading={isLoading}\n data-has-results={hasResults}\n >\n {/* Loading/no results state announcement for screen readers */}\n {(isLoading || !hasResults) && (\n <div className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {isLoading\n ? `Pagination temporarily disabled while loading page ${displayCurrentPage} of ${displayTotalPages}`\n : `No results available - pagination disabled`}\n </div>\n )}\n\n {/* Pagination controls with disabled state */}\n <div\n className={cn('relative transition-all ease-in-out', isPaginationDisabled && 'cursor-not-allowed')}\n style={{\n transitionDuration: '300ms',\n }}\n aria-hidden={isPaginationDisabled}\n >\n {/* UnifiedPagination directly — Pagination and BlogPagination in\n multi-platform-hub are both thin wrappers around it, so the wrapper\n never has to be handed down from the consuming app. */}\n <UnifiedPagination\n currentPage={displayCurrentPage}\n totalPages={displayTotalPages}\n onPageChange={hasResults ? onPageChange : () => {}} // Provide empty function instead of undefined\n />\n </div>\n\n {/* REMOVED: Loading overlays - only card skeletons should show during loading */}\n </div>\n );\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  useOptionalTicketLive
4
- } from "./chunk-TBJWPNHU.js";
4
+ } from "./chunk-MCNQKS6I.js";
5
5
  import {
6
6
  AppLayoutDrawerContainerContext,
7
7
  AppLayoutDrawerCoordinationContext,
@@ -13,16 +13,16 @@ import {
13
13
  UnreadDot,
14
14
  buildTicketOpenHref,
15
15
  useOptionalNotifications
16
- } from "./chunk-AIXPQT3G.js";
16
+ } from "./chunk-CKO6NKNE.js";
17
17
  import {
18
18
  useFocusTrap,
19
19
  useLocalStorage
20
- } from "./chunk-2V4KPBNZ.js";
20
+ } from "./chunk-XAOAYDGR.js";
21
21
  import {
22
22
  OVERLAY_BACKDROP_CLASS,
23
23
  Tag,
24
24
  useIsHydrated
25
- } from "./chunk-4BUPMSLY.js";
25
+ } from "./chunk-Y7ZEVTLC.js";
26
26
  import {
27
27
  useHeaderHeight,
28
28
  useIsomorphicLayoutEffect,
@@ -48,7 +48,7 @@ import {
48
48
  } from "./chunk-PBDSPANS.js";
49
49
  import {
50
50
  SquareAvatar
51
- } from "./chunk-22RSJ57Z.js";
51
+ } from "./chunk-2YVTGPM7.js";
52
52
  import {
53
53
  OpenFrameLogo
54
54
  } from "./chunk-OTX6XNSA.js";
@@ -1918,4 +1918,4 @@ export {
1918
1918
  MobileBurgerMenu,
1919
1919
  AppLayout
1920
1920
  };
1921
- //# sourceMappingURL=chunk-2PQKZWWP.js.map
1921
+ //# sourceMappingURL=chunk-PKZGXRU2.js.map
@@ -8,18 +8,18 @@
8
8
 
9
9
 
10
10
 
11
- var _chunkVLWUKFQEcjs = require('./chunk-VLWUKFQE.cjs');
11
+ var _chunkEXWS2QSDcjs = require('./chunk-EXWS2QSD.cjs');
12
12
 
13
13
 
14
14
 
15
15
 
16
- var _chunkBJHM2OEYcjs = require('./chunk-BJHM2OEY.cjs');
16
+ var _chunkLNKLB7GMcjs = require('./chunk-LNKLB7GM.cjs');
17
17
 
18
18
 
19
19
 
20
20
 
21
21
 
22
- var _chunkOD2WMUAOcjs = require('./chunk-OD2WMUAO.cjs');
22
+ var _chunkGZRU2A4Lcjs = require('./chunk-GZRU2A4L.cjs');
23
23
 
24
24
 
25
25
 
@@ -110,14 +110,14 @@ function ContactForm({
110
110
  successToastMessage = "Redirecting you to join our community...",
111
111
  rescue: rescueForm = null
112
112
  } = {}) {
113
- const attachments = _chunkOD2WMUAOcjs.useChatAttachments.call(void 0, );
114
- const builtInSubmission = _chunkBJHM2OEYcjs.useContactSubmission.call(void 0, {
113
+ const attachments = _chunkGZRU2A4Lcjs.useChatAttachments.call(void 0, );
114
+ const builtInSubmission = _chunkLNKLB7GMcjs.useContactSubmission.call(void 0, {
115
115
  userId,
116
116
  successRedirectUrl,
117
117
  successToastMessage
118
118
  });
119
119
  const [customSubmitting, setCustomSubmitting] = _react.useState.call(void 0, false);
120
- const { honeypotInputProps, getSignals, resetSignals } = _chunkBJHM2OEYcjs.useHumanitySignals.call(void 0, );
120
+ const { honeypotInputProps, getSignals, resetSignals } = _chunkLNKLB7GMcjs.useHumanitySignals.call(void 0, );
121
121
  const isSubmitting = onCustomSubmit ? customSubmitting : builtInSubmission.isSubmitting;
122
122
  const isSuccess = onCustomSubmit ? false : builtInSubmission.isSuccess;
123
123
  const form = _reacthookform.useForm.call(void 0, {
@@ -143,7 +143,7 @@ function ContactForm({
143
143
  ),
144
144
  [hideFields]
145
145
  );
146
- const rescue = _chunkBJHM2OEYcjs.useRescuedForm.call(void 0, form, { rescue: rescueForm, fields: rescueFieldNames, getSignals });
146
+ const rescue = _chunkLNKLB7GMcjs.useRescuedForm.call(void 0, form, { rescue: rescueForm, fields: rescueFieldNames, getSignals });
147
147
  const handleFormSubmit = async (data) => {
148
148
  if (isSubmitting) return;
149
149
  if (attachmentsEnabled && attachments.hasInflightUploads) return;
@@ -181,7 +181,7 @@ function ContactForm({
181
181
  className: `flex h-full flex-col ${!noBorder ? "rounded-2xl border border-ods-border md:rounded-3xl" : ""} ${!noPadding ? "p-6 md:p-8 lg:p-10" : ""}`,
182
182
  children: [
183
183
  (title || subtitle) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "mb-6 md:mb-8", children: [
184
- title && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: `${_chunkOD2WMUAOcjs.SECTION_HEADING_CLASS} mb-3 md:mb-4`, children: title }),
184
+ title && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: `${_chunkGZRU2A4Lcjs.SECTION_HEADING_CLASS} mb-3 md:mb-4`, children: title }),
185
185
  subtitle && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-primary text-h4", children: subtitle })
186
186
  ] }),
187
187
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -194,13 +194,13 @@ function ContactForm({
194
194
  );
195
195
  }),
196
196
  className: "flex flex-grow flex-col space-y-4 md:space-y-6",
197
- ..._chunkVLWUKFQEcjs.HUBSPOT_DO_NOT_COLLECT_FORM_PROPS,
197
+ ..._chunkEXWS2QSDcjs.HUBSPOT_DO_NOT_COLLECT_FORM_PROPS,
198
198
  children: [
199
199
  !showName && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { type: "hidden", ...register("name") }),
200
200
  !showEmail && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { type: "hidden", ...register("email") }),
201
201
  !showHelpCategory && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { type: "hidden", ...register("helpCategory") }),
202
202
  !showMessage && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { type: "hidden", ...register("message") }),
203
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.HoneypotField, { ...honeypotInputProps }),
203
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.HoneypotField, { ...honeypotInputProps }),
204
204
  extraTopField,
205
205
  showNameEmailRow && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6", children: [
206
206
  showName && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col", children: [
@@ -252,17 +252,17 @@ function ContactForm({
252
252
  {
253
253
  control,
254
254
  name: "companySize",
255
- render: ({ field }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkVLWUKFQEcjs.Select, { onValueChange: field.onChange, value: _nullishCoalesce(field.value, () => ( "")), children: [
255
+ render: ({ field }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.Select, { onValueChange: field.onChange, value: _nullishCoalesce(field.value, () => ( "")), children: [
256
256
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
257
- _chunkVLWUKFQEcjs.SelectTrigger,
257
+ _chunkEXWS2QSDcjs.SelectTrigger,
258
258
  {
259
259
  id: "companySize",
260
260
  "aria-label": "Company Size",
261
261
  className: "h-12 border-ods-border bg-ods-card px-3 text-ods-text-primary",
262
- children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectValue, { placeholder: "Select company size" })
262
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectValue, { placeholder: "Select company size" })
263
263
  }
264
264
  ),
265
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectContent, { children: companySizeOptions.map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectItem, { value: opt, children: opt }, opt)) })
265
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectContent, { children: companySizeOptions.map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectItem, { value: opt, children: opt }, opt)) })
266
266
  ] })
267
267
  }
268
268
  ),
@@ -275,17 +275,17 @@ function ContactForm({
275
275
  {
276
276
  control,
277
277
  name: "referralSource",
278
- render: ({ field }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkVLWUKFQEcjs.Select, { onValueChange: field.onChange, value: _nullishCoalesce(field.value, () => ( "")), children: [
278
+ render: ({ field }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.Select, { onValueChange: field.onChange, value: _nullishCoalesce(field.value, () => ( "")), children: [
279
279
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
280
- _chunkVLWUKFQEcjs.SelectTrigger,
280
+ _chunkEXWS2QSDcjs.SelectTrigger,
281
281
  {
282
282
  id: "referralSource",
283
283
  "aria-label": "Referral Source",
284
284
  className: "h-12 border-ods-border bg-ods-card px-3 text-ods-text-primary",
285
- children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectValue, { placeholder: "Select an option" })
285
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectValue, { placeholder: "Select an option" })
286
286
  }
287
287
  ),
288
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectContent, { children: referralSourceOptions.map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectItem, { value: opt, children: opt }, opt)) })
288
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectContent, { children: referralSourceOptions.map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectItem, { value: opt, children: opt }, opt)) })
289
289
  ] })
290
290
  }
291
291
  ),
@@ -302,18 +302,18 @@ function ContactForm({
302
302
  {
303
303
  control,
304
304
  name: "helpCategory",
305
- render: ({ field }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkVLWUKFQEcjs.Select, { onValueChange: field.onChange, value: _nullishCoalesce(field.value, () => ( "")), children: [
305
+ render: ({ field }) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkEXWS2QSDcjs.Select, { onValueChange: field.onChange, value: _nullishCoalesce(field.value, () => ( "")), children: [
306
306
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
307
- _chunkVLWUKFQEcjs.SelectTrigger,
307
+ _chunkEXWS2QSDcjs.SelectTrigger,
308
308
  {
309
309
  id: "helpCategory",
310
310
  "aria-label": "Help Category",
311
311
  "aria-required": true,
312
312
  className: "h-12 border-ods-border bg-ods-card px-3 text-ods-text-primary",
313
- children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectValue, { placeholder: "Choose your main interest" })
313
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectValue, { placeholder: "Choose your main interest" })
314
314
  }
315
315
  ),
316
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectContent, { children: helpCategoryOptions.map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVLWUKFQEcjs.SelectItem, { value: opt, children: opt }, opt)) })
316
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectContent, { children: helpCategoryOptions.map((opt) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkEXWS2QSDcjs.SelectItem, { value: opt, children: opt }, opt)) })
317
317
  ] })
318
318
  }
319
319
  ),
@@ -325,7 +325,7 @@ function ContactForm({
325
325
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkKWX65373cjs.RequiredMark, {})
326
326
  ] }),
327
327
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
328
- _chunkVLWUKFQEcjs.Textarea,
328
+ _chunkEXWS2QSDcjs.Textarea,
329
329
  {
330
330
  id: "message",
331
331
  ...register("message"),
@@ -340,7 +340,7 @@ function ContactForm({
340
340
  ] }),
341
341
  attachmentsEnabled && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-2", children: [
342
342
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
343
- _chunkOD2WMUAOcjs.ChatAttachmentChipStrip,
343
+ _chunkGZRU2A4Lcjs.ChatAttachmentChipStrip,
344
344
  {
345
345
  attachments: attachments.attachments,
346
346
  onRemove: attachments.removeAttachment,
@@ -349,7 +349,7 @@ function ContactForm({
349
349
  ),
350
350
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
351
351
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
352
- _chunkOD2WMUAOcjs.ChatAttachmentAddButton,
352
+ _chunkGZRU2A4Lcjs.ChatAttachmentAddButton,
353
353
  {
354
354
  attachmentsEnabled: true,
355
355
  attachmentsCount: attachments.attachments.length,
@@ -385,4 +385,4 @@ function ContactForm({
385
385
 
386
386
 
387
387
  exports.ContactForm = ContactForm;
388
- //# sourceMappingURL=chunk-FCQR6VY4.cjs.map
388
+ //# sourceMappingURL=chunk-Q25VYAEL.cjs.map