@flamingo-stack/openframe-frontend-core 0.0.295 → 0.0.296-snapshot.20260621021605

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 (291) hide show
  1. package/README.md +9 -0
  2. package/dist/{chunk-7RIYT7ZH.js → chunk-2QG57XOJ.js} +1067 -205
  3. package/dist/chunk-2QG57XOJ.js.map +1 -0
  4. package/dist/{chunk-7KXD7CWD.js → chunk-3JIQVE7T.js} +9 -15
  5. package/dist/{chunk-7KXD7CWD.js.map → chunk-3JIQVE7T.js.map} +1 -1
  6. package/dist/{chunk-FT4FCV7L.cjs → chunk-4PSQS3SW.cjs} +7 -9
  7. package/dist/chunk-4PSQS3SW.cjs.map +1 -0
  8. package/dist/{chunk-OOKKGOPQ.js → chunk-4TLE6VLU.js} +30 -24
  9. package/dist/chunk-4TLE6VLU.js.map +1 -0
  10. package/dist/{chunk-6IBA2MQV.cjs → chunk-53FUMSZ5.cjs} +40 -46
  11. package/dist/chunk-53FUMSZ5.cjs.map +1 -0
  12. package/dist/{chunk-D3LEFMOA.cjs → chunk-54KNMC2R.cjs} +3 -3
  13. package/dist/{chunk-D3LEFMOA.cjs.map → chunk-54KNMC2R.cjs.map} +1 -1
  14. package/dist/{chunk-EYEW6PTA.cjs → chunk-6C526VNN.cjs} +358 -118
  15. package/dist/chunk-6C526VNN.cjs.map +1 -0
  16. package/dist/{chunk-5O6N3BKR.cjs → chunk-7OVGB2DQ.cjs} +19 -25
  17. package/dist/chunk-7OVGB2DQ.cjs.map +1 -0
  18. package/dist/{chunk-6GCI7JOE.js → chunk-AD6C23QY.js} +8 -7
  19. package/dist/{chunk-6GCI7JOE.js.map → chunk-AD6C23QY.js.map} +1 -1
  20. package/dist/chunk-F5OB2YAL.cjs +144 -0
  21. package/dist/chunk-F5OB2YAL.cjs.map +1 -0
  22. package/dist/chunk-FBWXMMRB.cjs +2 -0
  23. package/dist/chunk-FBWXMMRB.cjs.map +1 -0
  24. package/dist/{chunk-YIGPRLQY.cjs → chunk-FCDQNTDG.cjs} +21 -20
  25. package/dist/chunk-FCDQNTDG.cjs.map +1 -0
  26. package/dist/{chunk-XXI7BNB6.cjs → chunk-FQOTC3UU.cjs} +321 -18
  27. package/dist/chunk-FQOTC3UU.cjs.map +1 -0
  28. package/dist/{chunk-INDQMNP6.cjs → chunk-GUTS7HGA.cjs} +11658 -2146
  29. package/dist/chunk-GUTS7HGA.cjs.map +1 -0
  30. package/dist/chunk-GZ4C3XW6.js +2 -0
  31. package/dist/chunk-GZ4C3XW6.js.map +1 -0
  32. package/dist/{chunk-HOVJGXF7.js → chunk-IL47XWV5.js} +8 -14
  33. package/dist/{chunk-HOVJGXF7.js.map → chunk-IL47XWV5.js.map} +1 -1
  34. package/dist/{chunk-LCNMR277.js → chunk-IZ7JSBFP.js} +1 -1
  35. package/dist/chunk-IZ7JSBFP.js.map +1 -0
  36. package/dist/{chunk-5IJ46KAV.js → chunk-JALO4TAZ.js} +360 -57
  37. package/dist/chunk-JALO4TAZ.js.map +1 -0
  38. package/dist/{chunk-AQOWFSMB.cjs → chunk-L6PSSIUQ.cjs} +1 -1
  39. package/dist/chunk-L6PSSIUQ.cjs.map +1 -0
  40. package/dist/{chunk-J3RDKZ32.js → chunk-L7ULJKG7.js} +6 -10
  41. package/dist/{chunk-J3RDKZ32.js.map → chunk-L7ULJKG7.js.map} +1 -1
  42. package/dist/{chunk-6BZEAPNT.js → chunk-PC746XCO.js} +15120 -5608
  43. package/dist/chunk-PC746XCO.js.map +1 -0
  44. package/dist/{chunk-3ZXUQQL4.js → chunk-PI4WSYQV.js} +2 -2
  45. package/dist/{chunk-E4XABBSU.js → chunk-PWQUAVA3.js} +338 -98
  46. package/dist/chunk-PWQUAVA3.js.map +1 -0
  47. package/dist/chunk-SA2WPJVO.js +144 -0
  48. package/dist/chunk-SA2WPJVO.js.map +1 -0
  49. package/dist/{chunk-ETACGX2A.cjs → chunk-UNVE2SDJ.cjs} +37 -31
  50. package/dist/chunk-UNVE2SDJ.cjs.map +1 -0
  51. package/dist/{chunk-5E2HOSSH.cjs → chunk-WMSTJAZT.cjs} +913 -51
  52. package/dist/chunk-WMSTJAZT.cjs.map +1 -0
  53. package/dist/{chunk-EJXHZX2E.js → chunk-X4DOXQRT.js} +4 -6
  54. package/dist/{chunk-EJXHZX2E.js.map → chunk-X4DOXQRT.js.map} +1 -1
  55. package/dist/{chunk-A2YL7QRX.cjs → chunk-YBYI62OE.cjs} +33 -37
  56. package/dist/chunk-YBYI62OE.cjs.map +1 -0
  57. package/dist/components/case-studies/index.cjs +126 -0
  58. package/dist/components/case-studies/index.cjs.map +1 -0
  59. package/dist/components/case-studies/index.d.ts +2 -0
  60. package/dist/components/case-studies/index.d.ts.map +1 -0
  61. package/dist/components/case-studies/index.js +126 -0
  62. package/dist/components/case-studies/index.js.map +1 -0
  63. package/dist/components/case-studies/share-experience-section.d.ts +48 -0
  64. package/dist/components/case-studies/share-experience-section.d.ts.map +1 -0
  65. package/dist/components/chat/chat-container.d.ts.map +1 -1
  66. package/dist/components/chat/error-message-display.d.ts.map +1 -1
  67. package/dist/components/chat/index.cjs +8 -18
  68. package/dist/components/chat/index.cjs.map +1 -1
  69. package/dist/components/chat/index.js +75 -85
  70. package/dist/components/chat/types/component.types.d.ts +2 -0
  71. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  72. package/dist/components/contact/index.cjs +8 -15
  73. package/dist/components/contact/index.cjs.map +1 -1
  74. package/dist/components/contact/index.js +7 -14
  75. package/dist/components/docs/doc-viewer.d.ts +39 -2
  76. package/dist/components/docs/doc-viewer.d.ts.map +1 -1
  77. package/dist/components/docs/docs-hub-page.d.ts +46 -0
  78. package/dist/components/docs/docs-hub-page.d.ts.map +1 -0
  79. package/dist/components/docs/index.cjs +17 -9
  80. package/dist/components/docs/index.cjs.map +1 -1
  81. package/dist/components/docs/index.d.ts +4 -0
  82. package/dist/components/docs/index.d.ts.map +1 -1
  83. package/dist/components/docs/index.js +16 -8
  84. package/dist/components/docs/skeletons.d.ts +32 -0
  85. package/dist/components/docs/skeletons.d.ts.map +1 -0
  86. package/dist/components/docs/use-docs-resolve-link.d.ts +20 -0
  87. package/dist/components/docs/use-docs-resolve-link.d.ts.map +1 -0
  88. package/dist/components/docs/use-document-tree.d.ts.map +1 -1
  89. package/dist/components/embeds/embed-container.d.ts +37 -0
  90. package/dist/components/embeds/embed-container.d.ts.map +1 -0
  91. package/dist/components/embeds/embed-iframe.d.ts.map +1 -1
  92. package/dist/components/embeds/file-download-card.d.ts +18 -0
  93. package/dist/components/embeds/file-download-card.d.ts.map +1 -0
  94. package/dist/components/embeds/index.cjs +38 -15
  95. package/dist/components/embeds/index.cjs.map +1 -1
  96. package/dist/components/embeds/index.d.ts +8 -0
  97. package/dist/components/embeds/index.d.ts.map +1 -1
  98. package/dist/components/embeds/index.js +40 -17
  99. package/dist/components/embeds/linkedin-embed-client.d.ts +8 -0
  100. package/dist/components/embeds/linkedin-embed-client.d.ts.map +1 -0
  101. package/dist/components/embeds/markdown-image.d.ts +5 -0
  102. package/dist/components/embeds/markdown-image.d.ts.map +1 -0
  103. package/dist/components/embeds/reddit-embed-client.d.ts +7 -0
  104. package/dist/components/embeds/reddit-embed-client.d.ts.map +1 -0
  105. package/dist/components/embeds/rich-markdown-runtime.d.ts +46 -0
  106. package/dist/components/embeds/rich-markdown-runtime.d.ts.map +1 -0
  107. package/dist/components/embeds/twitter-embed-client.d.ts +8 -0
  108. package/dist/components/embeds/twitter-embed-client.d.ts.map +1 -0
  109. package/dist/components/faq/index.cjs +9 -16
  110. package/dist/components/faq/index.cjs.map +1 -1
  111. package/dist/components/faq/index.js +8 -15
  112. package/dist/components/features/index.cjs +8 -16
  113. package/dist/components/features/index.cjs.map +1 -1
  114. package/dist/components/features/index.js +24 -32
  115. package/dist/components/features/notifications/notification-drawer.d.ts.map +1 -1
  116. package/dist/components/features/notifications/notifications-context.d.ts +5 -1
  117. package/dist/components/features/notifications/notifications-context.d.ts.map +1 -1
  118. package/dist/components/index.cjs +257 -452
  119. package/dist/components/index.cjs.map +1 -1
  120. package/dist/components/index.js +781 -976
  121. package/dist/components/index.js.map +1 -1
  122. package/dist/components/layout/page-header.d.ts +78 -0
  123. package/dist/components/layout/page-header.d.ts.map +1 -0
  124. package/dist/components/layout/page-layout.d.ts +10 -1
  125. package/dist/components/layout/page-layout.d.ts.map +1 -1
  126. package/dist/components/layout/page-with-header.d.ts +67 -0
  127. package/dist/components/layout/page-with-header.d.ts.map +1 -0
  128. package/dist/components/layout/title-block.d.ts +17 -1
  129. package/dist/components/layout/title-block.d.ts.map +1 -1
  130. package/dist/components/navigation/index.cjs +7 -15
  131. package/dist/components/navigation/index.cjs.map +1 -1
  132. package/dist/components/navigation/index.js +9 -17
  133. package/dist/components/onboarding-guides/index.cjs +35 -36
  134. package/dist/components/onboarding-guides/index.cjs.map +1 -1
  135. package/dist/components/onboarding-guides/index.js +13 -14
  136. package/dist/components/onboarding-guides/index.js.map +1 -1
  137. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts +1 -1
  138. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  139. package/dist/components/related-content/index.cjs +9 -16
  140. package/dist/components/related-content/index.cjs.map +1 -1
  141. package/dist/components/related-content/index.js +8 -15
  142. package/dist/components/shared/dev-section/dev-section-page.d.ts +9 -0
  143. package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -1
  144. package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
  145. package/dist/components/shared/dev-section/index.d.ts +1 -1
  146. package/dist/components/shared/dev-section/index.d.ts.map +1 -1
  147. package/dist/components/shared/doc-search/use-doc-search.d.ts.map +1 -1
  148. package/dist/components/shared/legal-document/legal-document-page.d.ts.map +1 -1
  149. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  150. package/dist/components/tickets/index.cjs +100 -112
  151. package/dist/components/tickets/index.cjs.map +1 -1
  152. package/dist/components/tickets/index.js +20 -32
  153. package/dist/components/tickets/index.js.map +1 -1
  154. package/dist/components/ui/button/split-button.d.ts.map +1 -1
  155. package/dist/components/ui/file-manager/index.cjs +50 -52
  156. package/dist/components/ui/file-manager/index.cjs.map +1 -1
  157. package/dist/components/ui/file-manager/index.js +4 -6
  158. package/dist/components/ui/file-manager/index.js.map +1 -1
  159. package/dist/components/ui/index.cjs +13 -19
  160. package/dist/components/ui/index.cjs.map +1 -1
  161. package/dist/components/ui/index.d.ts +2 -0
  162. package/dist/components/ui/index.d.ts.map +1 -1
  163. package/dist/components/ui/index.js +133 -139
  164. package/dist/components/ui/release-changelog-section.d.ts +6 -2
  165. package/dist/components/ui/release-changelog-section.d.ts.map +1 -1
  166. package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
  167. package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
  168. package/dist/components/ui/simple-markdown-renderer.d.ts +2 -8
  169. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
  170. package/dist/contexts/chat-runtime-context.d.ts +14 -0
  171. package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
  172. package/dist/contexts/index.cjs +3 -3
  173. package/dist/contexts/index.js +5 -5
  174. package/dist/embed-shims/index.cjs +3 -3
  175. package/dist/embed-shims/index.cjs.map +1 -1
  176. package/dist/embed-shims/index.js +4 -4
  177. package/dist/hooks/index.cjs +4 -9
  178. package/dist/hooks/index.cjs.map +1 -1
  179. package/dist/hooks/index.js +6 -11
  180. package/dist/index.cjs +14 -20
  181. package/dist/index.cjs.map +1 -1
  182. package/dist/index.js +362 -368
  183. package/dist/types/doc-source.d.ts +31 -1
  184. package/dist/types/doc-source.d.ts.map +1 -1
  185. package/dist/utils/index.cjs +4 -0
  186. package/dist/utils/index.cjs.map +1 -1
  187. package/dist/utils/index.d.ts +1 -0
  188. package/dist/utils/index.d.ts.map +1 -1
  189. package/dist/utils/index.js +4 -1
  190. package/dist/utils/index.js.map +1 -1
  191. package/dist/utils/page-header-constants.d.ts +15 -0
  192. package/dist/utils/page-header-constants.d.ts.map +1 -0
  193. package/dist/utils/social-embed-cache.d.ts +29 -0
  194. package/dist/utils/social-embed-cache.d.ts.map +1 -0
  195. package/package.json +7 -1
  196. package/src/components/case-studies/index.ts +4 -0
  197. package/src/components/case-studies/share-experience-section.tsx +185 -0
  198. package/src/components/chat/chat-container.tsx +5 -7
  199. package/src/components/chat/embeddable-chat.tsx +1 -1
  200. package/src/components/chat/error-message-display.tsx +49 -31
  201. package/src/components/chat/types/component.types.ts +2 -0
  202. package/src/components/docs/doc-viewer.tsx +111 -19
  203. package/src/components/docs/docs-hub-page.tsx +149 -0
  204. package/src/components/docs/index.ts +17 -0
  205. package/src/components/docs/skeletons.tsx +138 -0
  206. package/src/components/docs/use-docs-resolve-link.ts +52 -0
  207. package/src/components/docs/use-document-tree.ts +21 -0
  208. package/src/components/embeds/embed-container.tsx +80 -0
  209. package/src/components/embeds/embed-iframe.tsx +7 -9
  210. package/src/components/embeds/file-download-card.tsx +54 -0
  211. package/src/components/embeds/index.ts +30 -0
  212. package/src/components/embeds/linkedin-embed-client.tsx +100 -0
  213. package/src/components/embeds/markdown-image.tsx +88 -0
  214. package/src/components/embeds/og-link-preview.tsx +13 -13
  215. package/src/components/embeds/reddit-embed-client.tsx +550 -0
  216. package/src/components/embeds/rich-markdown-runtime.tsx +79 -0
  217. package/src/components/embeds/twitter-embed-client.tsx +308 -0
  218. package/src/components/features/notifications/notification-drawer.tsx +18 -7
  219. package/src/components/features/notifications/notifications-context.tsx +7 -0
  220. package/src/components/layout/page-header.tsx +182 -0
  221. package/src/components/layout/page-layout.tsx +14 -1
  222. package/src/components/layout/page-with-header.tsx +110 -0
  223. package/src/components/layout/title-block.tsx +40 -62
  224. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +3 -3
  225. package/src/components/shared/dev-section/dev-section-page.tsx +9 -1
  226. package/src/components/shared/dev-section/dev-section-view.tsx +14 -9
  227. package/src/components/shared/dev-section/index.ts +1 -1
  228. package/src/components/shared/doc-search/use-doc-search.ts +7 -3
  229. package/src/components/shared/legal-document/legal-document-page.tsx +2 -2
  230. package/src/components/shared/product-release/release-detail-page.tsx +6 -4
  231. package/src/components/ui/button/split-button.tsx +5 -2
  232. package/src/components/ui/index.ts +2 -0
  233. package/src/components/ui/release-changelog-section.tsx +7 -2
  234. package/src/components/ui/rich-markdown-renderer.tsx +1203 -0
  235. package/src/components/ui/simple-markdown-renderer.tsx +7 -11
  236. package/src/contexts/chat-runtime-context.tsx +14 -0
  237. package/src/stories/NotificationDrawer.stories.tsx +2 -0
  238. package/src/types/doc-source.ts +33 -1
  239. package/src/utils/index.ts +1 -0
  240. package/src/utils/page-header-constants.ts +15 -0
  241. package/src/utils/social-embed-cache.ts +391 -0
  242. package/dist/chunk-26PKDALD.js +0 -2379
  243. package/dist/chunk-26PKDALD.js.map +0 -1
  244. package/dist/chunk-3MCHAFHB.js +0 -89
  245. package/dist/chunk-3MCHAFHB.js.map +0 -1
  246. package/dist/chunk-3XIB4VKS.cjs +0 -619
  247. package/dist/chunk-3XIB4VKS.cjs.map +0 -1
  248. package/dist/chunk-4W7NYJ3B.cjs +0 -3009
  249. package/dist/chunk-4W7NYJ3B.cjs.map +0 -1
  250. package/dist/chunk-5E2HOSSH.cjs.map +0 -1
  251. package/dist/chunk-5IJ46KAV.js.map +0 -1
  252. package/dist/chunk-5O6N3BKR.cjs.map +0 -1
  253. package/dist/chunk-6BZEAPNT.js.map +0 -1
  254. package/dist/chunk-6IBA2MQV.cjs.map +0 -1
  255. package/dist/chunk-6JINAOI7.cjs +0 -311
  256. package/dist/chunk-6JINAOI7.cjs.map +0 -1
  257. package/dist/chunk-7RIYT7ZH.js.map +0 -1
  258. package/dist/chunk-A2YL7QRX.cjs.map +0 -1
  259. package/dist/chunk-AQOWFSMB.cjs.map +0 -1
  260. package/dist/chunk-E4XABBSU.js.map +0 -1
  261. package/dist/chunk-ETACGX2A.cjs.map +0 -1
  262. package/dist/chunk-EYEW6PTA.cjs.map +0 -1
  263. package/dist/chunk-FQJK446R.js +0 -1606
  264. package/dist/chunk-FQJK446R.js.map +0 -1
  265. package/dist/chunk-FT4FCV7L.cjs.map +0 -1
  266. package/dist/chunk-INDQMNP6.cjs.map +0 -1
  267. package/dist/chunk-J54Z3OCR.cjs +0 -1606
  268. package/dist/chunk-J54Z3OCR.cjs.map +0 -1
  269. package/dist/chunk-KXCRGTRN.cjs +0 -2379
  270. package/dist/chunk-KXCRGTRN.cjs.map +0 -1
  271. package/dist/chunk-LCNMR277.js.map +0 -1
  272. package/dist/chunk-LFGGF7OT.cjs +0 -449
  273. package/dist/chunk-LFGGF7OT.cjs.map +0 -1
  274. package/dist/chunk-M2OCXTNT.js +0 -311
  275. package/dist/chunk-M2OCXTNT.js.map +0 -1
  276. package/dist/chunk-NSPOYUBH.js +0 -3009
  277. package/dist/chunk-NSPOYUBH.js.map +0 -1
  278. package/dist/chunk-OOKKGOPQ.js.map +0 -1
  279. package/dist/chunk-OQ6X7ZOC.js +0 -449
  280. package/dist/chunk-OQ6X7ZOC.js.map +0 -1
  281. package/dist/chunk-POKKCWKF.js +0 -354
  282. package/dist/chunk-POKKCWKF.js.map +0 -1
  283. package/dist/chunk-TFSYSWPS.cjs +0 -89
  284. package/dist/chunk-TFSYSWPS.cjs.map +0 -1
  285. package/dist/chunk-XXI7BNB6.cjs.map +0 -1
  286. package/dist/chunk-YD43AKI5.js +0 -619
  287. package/dist/chunk-YD43AKI5.js.map +0 -1
  288. package/dist/chunk-YETA25JW.cjs +0 -354
  289. package/dist/chunk-YETA25JW.cjs.map +0 -1
  290. package/dist/chunk-YIGPRLQY.cjs.map +0 -1
  291. /package/dist/{chunk-3ZXUQQL4.js.map → chunk-PI4WSYQV.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-A2YL7QRX.cjs","../src/components/contact/contact-form.tsx","../src/schemas/contact-schema.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-A2YL7QRX.cjs","sourcesContent":[null,"'use client'\n\n/**\n * `<ContactForm />` — the canonical contact form used by every public\n * surface (TMCG join, case-study pitch, generic /contact, Help Center\n * ticket creation, etc.).\n *\n * Self-contained inside the lib — host-specific values (user id for\n * tracking, platform-specific contact reasons, reddit-click attribution\n * id) flow IN via props. The hub passes them via a thin\n * `<ContactForm>` wrapper that resolves them from `useAuth` /\n * `getAppConfig` / `getStoredRedditClickId`. Other embedders pass\n * whatever they have (or omit).\n *\n * Field-hide + custom-submit + extra-top-field knobs let one form\n * serve both contact and ticket-creation flows without forking:\n * - Contact page: rendered with all fields visible, built-in submit\n * flow to `/api/contact` via `useContactSubmission`.\n * - Ticket page: hides name/email/companySize/referralSource/\n * helpCategory; supplies `extraTopField` (a Subject input) +\n * `onCustomSubmit` wired to `useTicketActions.submitTicket`.\n */\n\nimport { useState, type ReactNode } from 'react'\nimport { useForm, Controller } from 'react-hook-form'\nimport { zodResolver } from '@hookform/resolvers/zod'\nimport {\n ContactSchema,\n type ContactFormData,\n companySizeOptions,\n referralSourceOptions,\n defaultHelpCategoryOptions,\n} from '../../schemas/contact-schema'\nimport { SECTION_HEADING_CLASS } from '../layout/page-heading'\nimport {\n Button,\n type ButtonProps,\n Input,\n Textarea,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n Label,\n} from '../ui'\nimport { useContactSubmission } from '../../hooks/use-contact-submission'\nimport { useHumanitySignals } from '../../hooks/use-humanity-signals'\nimport { HoneypotField } from '../ui/honeypot-field'\nimport {\n ChatAttachmentAddButton,\n ChatAttachmentChipStrip,\n} from '../chat/chat-attachment-bar'\nimport { useChatAttachments } from '../chat/hooks/use-chat-attachments'\nimport type { ChatAttachment } from '../chat/utils/chat-attachment-markdown'\n\n/**\n * Fields the caller can suppress. Six values — every primary form\n * field plus `name` and `email` (newly hideable so ticket-creation\n * surfaces can hide them; they still need to validate, so the caller\n * MUST supply pre-filled values via `defaultValues` when hiding them).\n */\nexport type ContactFormHideableField =\n | 'name'\n | 'email'\n | 'companySize'\n | 'referralSource'\n | 'helpCategory'\n | 'message'\n\nexport interface ContactFormProps {\n /** Host-side user id passed to `useContactSubmission` for attribution.\n * Hub wrapper passes `useAuth().user?.id`; lib's Help Center surface\n * passes `useChatIdentity().user?.id`. Omit for anon flows. */\n userId?: string\n /** Platform-specific help-category dropdown options. Hub wrapper\n * passes `getAppConfig().contact.contactReasons`. Defaults to the\n * lib's `defaultHelpCategoryOptions`. */\n helpCategoryOptions?: readonly string[]\n /** Reddit click attribution id. Caller resolves from wherever they\n * stash it (hub: sessionStorage via `getStoredRedditClickId`). When\n * set, it's spread into the submission payload. */\n rdtCid?: string\n /** Called after a successful submit so the caller can clear their\n * attribution storage (hub wrapper calls `clearStoredRedditClickId`).\n * Fires for BOTH the built-in and custom submit paths. */\n onSubmitSuccess?: () => void\n\n prefilledReason?: string\n prefilledMessage?: string\n hideFields?: ContactFormHideableField[]\n /** Authoritative pre-fill for any field the caller hides. Merged\n * into react-hook-form's `defaultValues` AFTER the legacy\n * `prefilledReason` / `prefilledMessage` props (caller-supplied\n * wins). REQUIRED when hiding `name` / `email` / `helpCategory` —\n * those fields are still validated by Zod even when not rendered. */\n defaultValues?: Partial<ContactFormData>\n /** Optional custom submit handler. When provided, the form bypasses\n * the built-in `useContactSubmission` flow (no /api/contact call,\n * no success-redirect, no built-in toast) — the caller owns the\n * entire side-effect chain. Reset + `onSubmitSuccess` still fire\n * on a successful await.\n *\n * Receives the schema-validated form payload PLUS the ready\n * attachments array (empty when `attachmentsEnabled === false` or\n * the user hasn't picked any). Caller forwards `attachments` to\n * whichever sink owns the upload (e.g. `actions.submitTicket`'s\n * `attachments` field for HubSpot Note engagements). */\n onCustomSubmit?: (data: ContactFormData, attachments: ChatAttachment[]) => Promise<void>\n /** Turn on the attachments bar (file `+` button + chip strip) using\n * the same lib primitives the chat composer uses\n * (`<ChatAttachmentAddButton>` + `<ChatAttachmentChipStrip>` +\n * `useChatAttachments`). When `false` (the default), the form\n * doesn't render the bar AND the attachments array passed to\n * `onCustomSubmit` is always empty. */\n attachmentsEnabled?: boolean\n /** Render slot for an EXTRA field at the very top of the form,\n * ABOVE the name/email row. Use this for ticket surfaces that need\n * a Subject input — the field is NOT part of `ContactSchema`, so\n * the caller manages its own state + validation and reads the\n * value back inside `onCustomSubmit`. */\n extraTopField?: ReactNode\n\n title?: string\n subtitle?: string\n footerText?: string\n noBorder?: boolean\n noPadding?: boolean\n buttonVariant?: ButtonProps['variant']\n buttonClassName?: string\n /** Submit-button label. Defaults to \"Send Message\". Override for\n * ticket surfaces (e.g. \"Open ticket\"). */\n submitLabel?: string\n /** Success-state submit-button label (shown briefly after submit on\n * the built-in flow). Defaults to \"Message Sent!\". Has no effect\n * when `onCustomSubmit` is provided — the caller owns success UX. */\n submitSuccessLabel?: string\n successRedirectUrl?: string\n successToastMessage?: string\n}\n\nexport function ContactForm({\n userId,\n helpCategoryOptions = defaultHelpCategoryOptions,\n rdtCid,\n onSubmitSuccess,\n prefilledReason,\n prefilledMessage,\n hideFields = [],\n defaultValues: defaultValuesProp,\n onCustomSubmit,\n extraTopField,\n attachmentsEnabled = false,\n title = 'Hit Us Up',\n subtitle,\n footerText = 'We typically respond within 24 hours. We respect your privacy – no spam, ever.',\n noBorder = false,\n noPadding = false,\n buttonVariant = 'accent',\n buttonClassName = '',\n submitLabel = 'Send Message',\n submitSuccessLabel = 'Message Sent!',\n successRedirectUrl = '/blog#community',\n successToastMessage = 'Redirecting you to join our community...',\n}: ContactFormProps = {}) {\n // Attachments staging — same hook the chat composer + ticket\n // detail-drawer composer use. Files upload to Supabase as soon as\n // the user picks them; `readyAttachments` is the wire-shape array\n // ready for the next submit. `hasInflightUploads` disables Send\n // until every upload settles.\n const attachments = useChatAttachments()\n // Built-in contact-API flow. Hook is called unconditionally (rules\n // of hooks); we just don't dispatch its `submit` when the caller\n // passes `onCustomSubmit`. The hook owns its own toast + redirect\n // chain so bypassing it cleanly hands all side-effects to the caller.\n const builtInSubmission = useContactSubmission({\n userId,\n successRedirectUrl,\n successToastMessage,\n })\n // Independent in-flight tracker for the custom path — we can't reuse\n // `builtInSubmission.isSubmitting` because that hook never sees a\n // request when `onCustomSubmit` is active.\n const [customSubmitting, setCustomSubmitting] = useState(false)\n\n // Invisible bot-protection signals (honeypot + timing). Spread into the\n // submit payload for BOTH the built-in and custom paths; reset on success.\n const { honeypotInputProps, getSignals, resetSignals } = useHumanitySignals()\n\n const isSubmitting = onCustomSubmit ? customSubmitting : builtInSubmission.isSubmitting\n // `isSuccess` only ever fires on the built-in path; custom callers\n // own their own UX (no \"Message Sent!\" button-label flicker).\n const isSuccess = onCustomSubmit ? false : builtInSubmission.isSuccess\n\n const {\n register,\n handleSubmit,\n control,\n formState: { errors },\n reset,\n } = useForm<ContactFormData>({\n resolver: zodResolver(ContactSchema),\n defaultValues: {\n ...(prefilledReason && { helpCategory: prefilledReason }),\n ...(prefilledMessage && { message: prefilledMessage }),\n // Caller-supplied defaults win over the legacy `prefilled*` props\n // (they're the authoritative seed for hidden fields).\n ...defaultValuesProp,\n },\n })\n\n const handleFormSubmit = async (data: ContactFormData) => {\n if (isSubmitting) return\n if (attachmentsEnabled && attachments.hasInflightUploads) return\n try {\n const payload = { ...data, ...(rdtCid && { rdt_cid: rdtCid }), ...getSignals() }\n const readyAttachments = attachmentsEnabled ? attachments.readyAttachments : []\n if (onCustomSubmit) {\n setCustomSubmitting(true)\n try {\n await onCustomSubmit(payload, readyAttachments)\n } finally {\n setCustomSubmitting(false)\n }\n } else {\n await builtInSubmission.submit(payload)\n }\n onSubmitSuccess?.()\n reset()\n resetSignals()\n if (attachmentsEnabled) attachments.clear()\n } catch {\n // Error toast is owned by the active flow:\n // - built-in: `useContactSubmission` toasts inside `submit()`.\n // - custom: the caller toasts inside `onCustomSubmit`.\n // Either way we swallow here so a thrown error doesn't crash the\n // form tree (react-hook-form's onSubmit handler rejects upward).\n }\n }\n\n const showName = !hideFields.includes('name')\n const showEmail = !hideFields.includes('email')\n const showNameEmailRow = showName || showEmail\n const showCompanySize = !hideFields.includes('companySize')\n const showReferralSource = !hideFields.includes('referralSource')\n const showHelpCategory = !hideFields.includes('helpCategory')\n const showMessage = !hideFields.includes('message')\n\n return (\n <div\n className={`h-full flex flex-col ${!noBorder ? 'border border-ods-border rounded-2xl md:rounded-3xl' : ''} ${!noPadding ? 'p-6 md:p-8 lg:p-10' : ''}`}\n >\n {(title || subtitle) && (\n <div className=\"mb-6 md:mb-8\">\n {title && (\n <h2 className={`${SECTION_HEADING_CLASS} mb-3 md:mb-4`}>\n {title}\n </h2>\n )}\n {subtitle && (\n <p className=\"font-['DM_Sans'] font-medium text-[16px] md:text-[18px] leading-[24px] text-ods-text-primary\">\n {subtitle}\n </p>\n )}\n </div>\n )}\n\n <form\n onSubmit={handleSubmit(handleFormSubmit, (validationErrors) => {\n // When validation fails on a HIDDEN field (e.g. ticket form\n // hides name/email/helpCategory and seeds them via\n // `defaultValues`), there's no visible error UI for the user\n // — the submit button just appears dead. Log so the broken\n // defaultValues wiring is at least discoverable in DevTools.\n // eslint-disable-next-line no-console\n console.warn(\n '[ContactForm] submit blocked by validation:',\n Object.fromEntries(\n Object.entries(validationErrors).map(([k, v]) => [k, v?.message ?? v]),\n ),\n )\n })}\n className=\"flex flex-col flex-grow space-y-4 md:space-y-6\"\n >\n {/* Hidden inputs for fields that are required by `ContactSchema`\n but suppressed from the visible UI via `hideFields`. Without\n these, `register('name')` never runs, react-hook-form skips\n the field at submit time, and Zod's required-string check\n fails silently — the user clicks Submit and NOTHING visible\n happens (no error, no network call). The caller-supplied\n `defaultValues` seed the values; the hidden inputs just tell\n RHF to include them in the submit payload. */}\n {!showName && <input type=\"hidden\" {...register('name')} />}\n {!showEmail && <input type=\"hidden\" {...register('email')} />}\n {!showHelpCategory && <input type=\"hidden\" {...register('helpCategory')} />}\n {!showMessage && <input type=\"hidden\" {...register('message')} />}\n\n {/* Invisible honeypot — real users never fill it; bots that fill every field trip it. */}\n <HoneypotField {...honeypotInputProps} />\n\n {/* Extra top field (e.g. Subject for ticket forms). Rendered\n outside the schema-driven layout so the caller fully owns\n label / placeholder / state. */}\n {extraTopField}\n\n {showNameEmailRow && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6\">\n {showName && (\n <div className=\"flex flex-col\">\n <Label htmlFor=\"name\">\n Your Name<span className=\"text-ods-accent\">*</span>\n </Label>\n <Input\n id=\"name\"\n type=\"text\"\n {...register('name')}\n placeholder=\"Jane Doe\"\n aria-invalid={!!errors.name}\n aria-describedby=\"name-error\"\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3 h-12\"\n />\n {errors.name && (\n <span id=\"name-error\" className=\"text-ods-error text-xs font-['DM_Sans'] mt-1\">\n {errors.name.message}\n </span>\n )}\n </div>\n )}\n {showEmail && (\n <div className=\"flex flex-col\">\n <Label htmlFor=\"email\">\n Email<span className=\"text-ods-accent\">*</span>\n </Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register('email')}\n placeholder=\"jane@company.com\"\n aria-invalid={!!errors.email}\n aria-describedby=\"email-error\"\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary px-3 h-12\"\n />\n {errors.email && (\n <span id=\"email-error\" className=\"text-ods-error text-xs font-['DM_Sans'] mt-1\">\n {errors.email.message}\n </span>\n )}\n </div>\n )}\n </div>\n )}\n\n {(showCompanySize || showReferralSource) && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6\">\n {showCompanySize && (\n <div className=\"flex flex-col\">\n <Label htmlFor=\"companySize\">Company Size</Label>\n <Controller\n control={control}\n name=\"companySize\"\n render={({ field }) => (\n <Select onValueChange={field.onChange} defaultValue={field.value}>\n <SelectTrigger\n id=\"companySize\"\n aria-label=\"Company Size\"\n className=\"bg-ods-card border-ods-border text-ods-text-primary h-12 px-3\"\n >\n <SelectValue placeholder=\"Select company size\" />\n </SelectTrigger>\n <SelectContent>\n {companySizeOptions.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {opt}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n />\n {errors.companySize && (\n <span id=\"companySize-error\" className=\"text-ods-error text-xs font-['DM_Sans'] mt-1\">\n {errors.companySize.message}\n </span>\n )}\n </div>\n )}\n {showReferralSource && (\n <div className=\"flex flex-col\">\n <Label htmlFor=\"referralSource\">How did you hear about us?</Label>\n <Controller\n control={control}\n name=\"referralSource\"\n render={({ field }) => (\n <Select onValueChange={field.onChange} defaultValue={field.value}>\n <SelectTrigger\n id=\"referralSource\"\n aria-label=\"Referral Source\"\n className=\"bg-ods-card border-ods-border text-ods-text-primary h-12 px-3\"\n >\n <SelectValue placeholder=\"Select an option\" />\n </SelectTrigger>\n <SelectContent>\n {referralSourceOptions.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {opt}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n />\n {errors.referralSource && (\n <span id=\"referralSource-error\" className=\"text-ods-error text-xs font-['DM_Sans'] mt-1\">\n {errors.referralSource.message}\n </span>\n )}\n </div>\n )}\n </div>\n )}\n\n {showHelpCategory && (\n <div className=\"flex flex-col\">\n <Label htmlFor=\"helpCategory\">\n Choose your main interest<span className=\"text-ods-accent\">*</span>\n </Label>\n <Controller\n control={control}\n name=\"helpCategory\"\n render={({ field }) => (\n <Select onValueChange={field.onChange} defaultValue={field.value}>\n <SelectTrigger\n id=\"helpCategory\"\n aria-label=\"Help Category\"\n className=\"bg-ods-card border-ods-border text-ods-text-primary h-12 px-3\"\n >\n <SelectValue placeholder=\"Choose your main interest\" />\n </SelectTrigger>\n <SelectContent>\n {helpCategoryOptions.map((opt) => (\n <SelectItem key={opt} value={opt}>\n {opt}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n />\n {errors.helpCategory && (\n <span id=\"helpCategory-error\" className=\"text-ods-error text-xs font-['DM_Sans'] mt-1\">\n {errors.helpCategory.message}\n </span>\n )}\n </div>\n )}\n\n {showMessage && (\n <div className=\"flex flex-col flex-grow\">\n <Label htmlFor=\"message\">\n Your Message<span className=\"text-ods-accent\">*</span>\n </Label>\n <Textarea\n id=\"message\"\n {...register('message')}\n placeholder=\"Share your current challenges or questions about open-source alternatives...\"\n aria-invalid={!!errors.message}\n aria-describedby=\"message-error\"\n className=\"bg-ods-card border-ods-border text-ods-text-primary placeholder-ods-text-secondary h-full flex-grow\"\n />\n {errors.message && (\n <span id=\"message-error\" className=\"text-ods-error text-xs font-['DM_Sans'] mt-1\">\n {errors.message.message}\n </span>\n )}\n </div>\n )}\n\n {/* Attachments — only renders when `attachmentsEnabled` is on.\n Uses the SAME chip strip + add button + staging hook the\n chat composer and ticket-drawer composer use, so the visual\n chip styling + upload-progress UX are identical everywhere\n attachments appear. */}\n {attachmentsEnabled && (\n <div className=\"flex flex-col gap-2\">\n <ChatAttachmentChipStrip\n attachments={attachments.attachments}\n onRemove={attachments.removeAttachment}\n disabled={isSubmitting}\n />\n <div className=\"flex items-center gap-2\">\n <ChatAttachmentAddButton\n attachmentsEnabled\n attachmentsCount={attachments.attachments.length}\n onAddFiles={attachments.addFiles}\n disabled={isSubmitting}\n />\n <span className=\"text-xs text-ods-text-secondary\">\n Attach files (optional)\n </span>\n </div>\n </div>\n )}\n\n <div className=\"flex flex-col md:flex-row gap-4 md:gap-6 items-center justify-end w-full pt-2 mt-auto\">\n {footerText && (\n <p className=\"font-['DM_Sans'] text-ods-text-secondary text-xs md:text-sm leading-relaxed text-center md:text-left\">\n {footerText}\n </p>\n )}\n <Button\n type=\"submit\"\n loading={isSubmitting}\n disabled={\n isSubmitting ||\n isSuccess ||\n (attachmentsEnabled && attachments.hasInflightUploads)\n }\n variant={buttonVariant}\n className={`w-full md:w-auto ${buttonClassName}`}\n >\n {isSuccess ? submitSuccessLabel : submitLabel}\n </Button>\n </div>\n </form>\n </div>\n )\n}\n","import { z } from 'zod';\n\n// Dropdown option constants — re-exported by `<ContactForm>` consumers\n// that want to surface their own custom Select widgets keyed on the\n// same allowed-value set.\nexport const companySizeOptions = [\n '1-10',\n '11-50',\n '51-200',\n '201-500',\n '501-1000',\n '1001+',\n] as const;\n\nexport const referralSourceOptions = [\n 'Google',\n 'LinkedIn',\n 'Twitter/X',\n 'Reddit',\n 'Friend / Colleague',\n 'Other',\n] as const;\n\n// Default fallback options — used when the embedder doesn't supply\n// platform-specific help-category options via the `helpCategoryOptions`\n// prop on `<ContactForm>`.\nexport const defaultHelpCategoryOptions = [\n 'Open-Source Alternatives',\n 'Vendor Cost Reduction',\n 'MSP Best Practices',\n 'Partnerships',\n 'Press',\n 'Other',\n] as const;\n\n// Reusable LinkedIn URL validator — the single source of truth. Every\n// public form schema, every admin update schema, every HubSpot push\n// validator MUST reference this so validation rules cannot drift\n// across boundaries.\n//\n// Host validation parses the URL and checks the hostname suffix so an\n// adversarial input like `https://evil.com/linkedin.com/x` is rejected\n// (substring match would have accepted it — CodeQL alert\n// \"Incomplete URL substring sanitization\").\nexport const LinkedInUrlSchema = z\n .string()\n .url({ message: 'Please enter a valid LinkedIn URL' })\n .refine(\n (url) => {\n try {\n const host = new URL(url).hostname.toLowerCase()\n return host === 'linkedin.com' || host.endsWith('.linkedin.com')\n } catch {\n return false\n }\n },\n {\n message: 'Please enter a valid LinkedIn profile URL',\n },\n )\n .optional()\n .or(z.literal(''));\n\n/**\n * Base schema — fields shared by every contact-style form (main contact\n * form, TMCG join, data-room request, case-study pitch, etc.). Any\n * field that exists on a form but NOT on this schema is silently\n * stripped by `safeParse` — that's exactly the bug the LinkedIn field\n * hit historically.\n */\nexport const ContactBaseSchema = z.object({\n name: z\n .string()\n .min(2, { message: 'Name must be at least 2 characters' })\n .max(255, { message: 'Name is too long' }),\n email: z\n .string()\n .email({ message: 'Please enter a valid email address' })\n .max(255),\n linkedin_url: LinkedInUrlSchema,\n helpCategory: z\n .string()\n .min(1, { message: 'Please select what we can help you with' })\n .max(255, { message: 'Help category is too long' }),\n message: z\n .string()\n .min(10, { message: 'Message must be at least 10 characters' })\n .max(5000, { message: 'Message is too long (5,000 character limit)' }),\n rdt_cid: z.string().optional(),\n});\n\n// Public POST /api/contact validator — base + dropdown fields used by\n// the generic contact form. Other form-specific schemas extend\n// `ContactBaseSchema`.\nexport const ContactSchema = ContactBaseSchema.extend({\n companySize: z\n .string()\n .optional()\n .refine((val) => !val || companySizeOptions.includes(val as (typeof companySizeOptions)[number]), {\n message: 'Please select a valid company size',\n }),\n referralSource: z\n .string()\n .optional()\n .refine((val) => !val || referralSourceOptions.includes(val as (typeof referralSourceOptions)[number]), {\n message: 'Please select a valid referral source',\n }),\n});\n\nexport type ContactFormData = z.infer<typeof ContactSchema>;\n\nexport interface ContactApiResponse {\n success: boolean;\n error?: string;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-AQOWFSMB.cjs","../src/contexts/chat-runtime-context.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACA;ACiCA,8BAA0D;AA+KnD,IAAM,mBAAA,EAAqB,kCAAA,IAAsC,CAAA;AAQjE,SAAS,cAAA,CAAA,EAAqC;AACnD,EAAA,OAAO,+BAAA,kBAA6B,CAAA;AACtC;AAUO,SAAS,sBAAA,CAAA,EAAsC;AACpD,EAAA,MAAM,EAAA,EAAI,+BAAA,kBAA6B,CAAA;AACvC,EAAA,GAAA,CAAI,CAAC,CAAA,EAAG;AACN,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,IAMF,CAAA;AAAA,EACF;AACA,EAAA,OAAO,CAAA;AACT;ADlOA;AACA;AACE;AACA;AACA;AACF,kJAAC","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-AQOWFSMB.cjs","sourcesContent":[null,"'use client'\n\n/**\n * Chat runtime context — single seam for embedding the chat panel in a\n * different host (e.g. user1.openframe.ai reverse-proxying API calls\n * under /api/mingo-guide/* to hub.openframe.ai/api/*).\n *\n * Three concerns, one context:\n * 1. API endpoints: chatStreamUrl / approvalToolUrl / commandsUrl /\n * buildListUrl + attachment endpoints + chat-identity. The chat\n * reads them from runtime; hub vs embedded app supply different\n * strings via different providers.\n * 2. Navigation mode + callbacks: 'host' or 'embed' mode. Host wires\n * its own router/docNav via the optional `navigate` callback\n * (plain function, NOT a hook); embed forces new-tab via\n * `defaultContentOrigin` + lib's `resolveExternalNavigation`.\n * 3. Identity context: only `source` (required for localStorage\n * namespacing). The display identity (greeting first-name etc.)\n * comes from the server via `useChatIdentity()` — never injected\n * client-side, so it always matches the server-resolved auth.\n *\n * Sibling of EndpointsRuntimeContext (announcement bar, contact form,\n * access codes). Each runtime stays an independent React context so\n * embedders can opt into either feature without forcing the other.\n *\n * IMPORTANT for embedders: memoize the value passed to\n * `<ChatRuntimeContext.Provider value={...}>` (e.g. via React.useMemo).\n * Every change to its reference identity invalidates downstream\n * `useMemo` consumers (the chat input's slash-commands binding,\n * useNavLink's embed-resolution memo, useDocChat's streamFn factory).\n * The hub's `<HubRuntimeProvider>` already memoizes correctly with\n * stable deps. Embedded apps that build the value inline on each render\n * will pay an avoidable re-render cost across the entire chat tree.\n */\n\nimport { createContext, useContext, type ReactNode } from 'react'\n\nimport type { ComposeContentUrl } from '../utils/content-href'\n\n/**\n * Runtime config consumed by the chat panel.\n */\nexport interface ChatRuntime {\n endpoints: {\n /** POST streaming chat. Hub: '/api/docs/chat'. */\n chatStreamUrl: string\n /** POST agent approve/reject. Hub: '/api/chat/agent/confirm-tool'. */\n approvalToolUrl: string\n /** Customer-ticket agent endpoints (Help Center). OPTIONAL — when unset,\n * the ticket hooks fall back to the bare hub paths\n * (`/api/chat/agent/{find-ticket,ticket-action,list-engagements}`).\n * Embedders behind a reverse proxy set these to their proxied paths\n * (e.g. `/content/api/chat/agent/...`) so tickets route through the SAME\n * endpoint config + proxy as every other endpoint. */\n findTicketUrl?: string\n ticketActionUrl?: string\n listEngagementsUrl?: string\n /** GET slash-command catalog. Hub: '/api/docs/commands'. */\n commandsUrl: string\n /** GET per-platform empty-state config (admin-edited in\n * `/admin/chat-config`): `{ greeting, enabledRagTableIds, suggestedQueries }`.\n * Hub: '/api/docs/empty-state'. OPTIONAL — the in-app (host-mode) chat\n * injects these values as SSR props instead, so it leaves this unset.\n * Cross-origin EMBEDDERS (no server hop) set it to their proxied path\n * (e.g. '/content/api/docs/empty-state') so `<EmbeddableChat>` can fetch\n * the greeting / quick-action chips / RAG-source filter at runtime. When\n * unset, the chat falls back to the explicit `emptyStateGreeting` /\n * `suggestedQueries` / `enabledRagTableIds` props (or in-code defaults). */\n emptyStateUrl?: string\n /** Build entity-card list URL for a content type + ids. Hub delegates\n * to the rag-table-config registry; embedded app provides its own\n * per-type URL builder against the reverse proxy. Returns null when\n * the type has no list endpoint (caller skips rendering). */\n buildListUrl: (type: string, ids: string[]) => string | null\n /** Chat-attachment endpoints — added for the v2 attachment feature.\n *\n * Three concerns:\n * - `attachmentUploadUrl` — POSTed by the chat-attachment hook\n * to mint a Supabase signed-upload-URL + HMAC view token.\n * - `attachmentViewUrlPrefix` — embedded in markdown URLs the\n * chat hosts in user message bubbles (`![]()` / `[Attached]`).\n * Stored in chat history; chosen at SEND time. In host mode the\n * relative `/api/storage/view/chat-attachments/` is sufficient\n * (same-origin); embedders supply an absolute hub URL so the\n * browser can fetch cross-origin.\n * - `identityUrl` — GET endpoint the `useChatIdentity` hook\n * hits to learn the `{authTier, source, attachmentsEnabled}`\n * capability bag for the current session. Used beyond chat\n * (tickets / contact form / any embedded surface that needs\n * to identify the proxied customer), so the name has no\n * \"chat\" prefix even though the consuming hook still does. */\n attachmentUploadUrl: string\n attachmentViewUrlPrefix: string\n identityUrl: string\n /** Optional URL prefix for the image proxy (`<prefix>?url=<external>`).\n * When unset, lib's `getProxiedImageUrl` returns the original URL\n * unchanged. Hub default: '/api/image-proxy'. Embedders that don't\n * host an image-proxy route leave this undefined → images load\n * directly cross-origin (CORS-permitting). */\n imageProxyUrlPrefix?: string\n /** Optional list of hostnames that should bypass the image proxy\n * (rendered direct). Hub uses ['openmsp.ai']; embedders typically\n * leave it unset. Matches the `skipDomains` parameter of\n * `getProxiedImageUrl`. */\n imageProxySkipDomains?: string[]\n /** Supabase storage origin (e.g. `https://xyz.supabase.co`) — used\n * by `useVideoWarmup` to scope the `<link rel=\"preload\" as=\"video\">`\n * hint to MP4s the deployment actually hosts. Hub wires it via\n * `getSupabaseStorageOrigin()`; embedders without a Supabase\n * storage origin leave it unset (preload is then skipped; Mux/\n * YouTube preconnect still fires). */\n supabaseStorageOrigin?: string\n }\n navigation: {\n /** ONE knob, two behaviors:\n * - 'host' = use the host page's existing click-routing untouched.\n * The chat panel calls `navigate?.()` for in-app routing.\n * - 'embed' = guest inside another app: short-circuit at the top\n * of click handlers to force new-tab + absolutize via\n * resolveExternalNavigation. */\n mode: 'host' | 'embed'\n /** Embed-only fallback origin for relative URLs whose target platform\n * can't be inferred. Used by resolveExternalNavigation when\n * `targetPlatform` is null — without this, a relative `/foo` href would\n * window.open against the embedder's origin, which is WRONG.\n * Set to your content host (e.g. 'https://hub.openframe.ai').\n * Required by the embedded app whenever mode='embed'. */\n defaultContentOrigin?: string\n /** Override for opening external URLs. MUST BE SYNCHRONOUS —\n * Safari/Firefox block popups opened outside a direct user gesture.\n * Default: window.open(href, '_blank', 'noopener,noreferrer'). */\n openExternal?: (href: string) => void\n /** Optional in-app navigation callback (host-mode only).\n * Returns `true` if the host handled the click in-app\n * (router.push + docNav.navigate); returns `false`, `undefined`,\n * or `void` → lib falls back to window.location.assign(href).\n * Hub wires this via HubRuntimeProvider's HubNavigationWiring;\n * embedders not in Next.js leave it undefined. */\n navigate?: (input: { href: string; path?: string | null; targetPlatform?: string | null }) => boolean | void\n /** Optional new-tab decision callback. Returns true → lib opens in\n * new tab; false → same tab via `navigate`. Hub wires the existing\n * `decideNewTab` logic from use-nav-link.tsx (re-imports the pure\n * helper from lib). Embedders may omit; lib defaults to:\n * same-origin/same-platform → same tab, else new tab. */\n decideNewTab?: (args: { href: string; targetPlatform?: string | null }) => boolean\n }\n /** Optional OG placeholder URL builder. Returns a branded\n * `/api/og-placeholder?...` URL for the given title. Hub wires this\n * to its `buildOgPlaceholderUrl` (resolves CSS-var ODS colors to\n * hex via the static map). Embedders can wire any equivalent that\n * hits their own placeholder route — or omit, in which case entity\n * cards fall back to no placeholder.\n *\n * Pure synchronous function — NOT a hook. Callers wrap with\n * `useMemo`/`useOgPlaceholder` for memoization. */\n resolvePlaceholderUrl?: (\n title: string,\n options?: { site?: string; aspect?: 'wide' | 'square' },\n ) => string\n /** Optional content-URL composer. Returns the platform-aware href +\n * target-platform tuple for a content entity. Hub wires this to its\n * `buildContentURL(type, slug, extractPrimaryPlatform(platforms))`\n * pipeline so the lib catalog/detail views can derive cross-\n * platform hrefs without knowing the hub's platform topology\n * (openmsp.ai / openframe.app / flamingo.run / tmcg).\n *\n * THE single content-href authority for every embeddable surface — page\n * views (onboarding catalog/detail, releases) AND chat cards / chips /\n * search results all resolve content links through this one seam, so a\n * given type lands in the SAME place regardless of where it's rendered.\n * Embedders wire `makeComposeContentUrl({ hostedTypes, contentOrigin })`;\n * omit it and lib views fall back to a same-origin relative path\n * (`buildDefaultHref`).\n *\n * Takes a single `ComposeContentUrlInput`: `type` + `identifier` (page\n * views pass the slug; chat rows pass the id + `externalUrl`, whose path\n * yields the slug for in-app routing) + optional `platforms` /\n * `externalUrl` / `targetPlatform`. */\n composeContentUrl?: ComposeContentUrl\n /** Per-`documentType` doc-viewer targets — the UNIFIED, DYNAMIC replacement for\n * the single `chipBasePlatform` prop. Maps a doc-table documentType\n * (`'markdown'`, `'data_room_doc'`, …) → `{ platform, basePath }` for the PUBLIC\n * doc viewer that hosts it. Doc chips with no `externalUrl` resolve PER ROW to\n * `getBaseUrl(platform)/<basePath>/<path>`, so a chat mixing several doc sources\n * sends EACH to its own home (markdown→flamingo/knowledge-base,\n * data_room_doc→company-hub/data-room) instead of one static fallback. The hub\n * may keep using `chipBasePlatform` (one doc source per platform); embedders that\n * surface multiple doc sources wire this. Threaded into `resolveSourceRowCTA`. */\n docPlatformTargets?: Record<string, { platform: string; basePath: string }>\n /** Chat source / platform identifier — OPTIONAL. The hub sets it from\n * `currentPlatform()`; EMBEDDERS leave it unset and stay platform-agnostic.\n *\n * It is NOT required for chat to work. The wire resolves source server-side\n * (`/docs/chat|search|commands` reject any client `source`); the\n * same-tab-vs-new-tab link decision falls back to an origin comparison when\n * it's absent (`decideNewTab` → `isCrossOriginUrl`); and the localStorage\n * history namespace falls back to a stable constant. Set it only where the\n * client legitimately needs to know its platform a priori — i.e. the hub,\n * where several platforms share related origins so \"same platform\" can't be\n * inferred from a URL alone. */\n source?: string\n // NOTE: No `user` field. The chat's display identity (greeting\n // first-name, etc.) comes from the SERVER-resolved auth via\n // `useChatIdentity()` — the same identity the server uses to\n // authorize requests. Letting embedders pass a client-side `user`\n // would let it desync from the actual auth tier, causing greetings\n // like \"Hey Bob\" while the server treats the session as\n // alice@example.com. Single source of truth: the server.\n}\n\nexport const ChatRuntimeContext = createContext<ChatRuntime | null>(null)\n\n/**\n * Returns the active runtime, or null when no provider is mounted.\n * NULL is a first-class value — it signals \"no chat runtime configured.\"\n * Optional consumers fall back to no-op behavior; strict consumers\n * use `useRequiredChatRuntime` (below).\n */\nexport function useChatRuntime(): ChatRuntime | null {\n return useContext(ChatRuntimeContext)\n}\n\n/**\n * Strict variant used INSIDE the chat panel. Throws if no provider.\n * The hub guarantees one exists by mounting `<HubRuntimeProvider>` at\n * root; the embedded app mounts its own `<ChatRuntimeContext.Provider>`\n * at the tree root. In Jest / Storybook tests that render chat\n * internals directly, wrap with `<HubRuntimeProvider>` (hub defaults)\n * or supply `<ChatRuntimeContext.Provider value={mockedRuntime}>`.\n */\nexport function useRequiredChatRuntime(): ChatRuntime {\n const v = useContext(ChatRuntimeContext)\n if (!v) {\n throw new Error(\n '[chat-runtime] hook called outside a <ChatRuntimeContext.Provider>. ' +\n 'The hub mounts <HubRuntimeProvider> at root — this only fires when ' +\n 'chat internals are rendered above the provider tree. ' +\n 'Fix: ensure the rendering subtree descends from the runtime provider. ' +\n 'In tests/Storybook: wrap with <HubRuntimeProvider> or supply ' +\n 'a <ChatRuntimeContext.Provider value={mockedRuntime}>.',\n )\n }\n return v\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/docs/doc-viewer.tsx","../src/components/persistent-filter-controls.tsx","../src/components/loading/page-layout-skeleton.tsx","../src/components/loading/unified-skeleton.tsx","../src/components/loading/card-skeleton.tsx","../src/components/docs/use-document-tree.ts","../src/components/docs/doc-navigation-context.tsx","../src/components/docs/use-scroll-spy.ts"],"sourcesContent":["\"use client\"\n\nimport React, { useMemo } from \"react\"\nimport { MultiLevelNavigation, MobileNavigationDropdown } from \"../navigation/multi-level-navigation\"\nimport { PageHeading } from \"../layout/page-heading\"\nimport { PersistentSidebar, PersistentMobileDropdown } from \"../persistent-filter-controls\"\nimport { CategorySidebarSkeleton } from \"../loading/page-layout-skeleton\"\nimport { DocSearchBar, useDocSearch } from \"../shared/doc-search\"\nimport { StickySectionNav } from \"../navigation/sticky-section-nav\"\nimport { useDocumentTree } from \"./use-document-tree\"\nimport { useScrollSpy } from \"./use-scroll-spy\"\nimport { useDocNavigation } from \"./doc-navigation-context\"\nimport { findDocNodeByPath } from \"../../utils/doc-tree-nav\"\nimport type { DocContent, DocNode, DocRenderHandlers, DocSourceId } from \"../../types/doc-source\"\n\n/** Color tokens for the doc-viewer chrome. Hub-side `DocViewer` callers share\n * this constant; no need to override per source — the palette is intentionally\n * uniform across knowledge-base + data-room (both use ODS dark tokens). */\nexport const DEFAULT_DOC_VIEWER_PALETTE = {\n background: \"bg-ods-bg\",\n containerBackground: \"transparent\",\n headerText: \"text-ods-text-primary\",\n primaryText: \"text-ods-text-primary\",\n secondaryText: \"text-ods-text-secondary\",\n accent: \"var(--ods-accent)\",\n border: \"border-ods-border\",\n cardBackground: \"bg-ods-card\",\n} as const\n\nexport interface DocViewerProps {\n /**\n * Registry source id (`'openframe-docs'`, `'data-room-docs'`, …). Flowed through\n * `renderContent`'s handlers for `/api/resolve-link` POSTs.\n */\n sourceId: DocSourceId\n\n /**\n * Render the content body. The page shell owns this — it picks the markdown\n * renderer, dispatches PDF/Figma/Sheets/file branches, etc. No renderer\n * interface in between.\n */\n renderContent: (content: DocContent, handlers: DocRenderHandlers) => React.ReactNode\n\n /**\n * Render the loading skeleton. Receives the selected node's `documentType`\n * (undefined while structure is still loading) so the caller can return a\n * markdown-shaped skeleton vs an embed-shaped skeleton.\n */\n renderSkeleton: (documentType: DocNode['documentType']) => React.ReactNode\n\n /**\n * Chat-source identifier — passed in by the page shell from server-side\n * `currentPlatform()`. Lib has no platform context; the page shell is the\n * trusted boundary that wires this. NEVER pass user input here.\n */\n chatSource: string\n\n title: string | React.ReactNode\n /** Override the default ODS palette. Optional — most callers should omit. */\n colorPalette?: typeof DEFAULT_DOC_VIEWER_PALETTE\n className?: string\n\n /** Initial doc path (URL `[...path]`). */\n docPath?: string\n\n /** Sidebar header copy (`'DOCUMENTATION'`, `'DATA ROOM'`). */\n sidebarLabel?: string\n\n /**\n * API endpoint for fetching the document tree structure. Defaults to the\n * dispatcher path `/api/docs/sources/${sourceId}/structure`. Override only\n * if hosting the viewer behind a different route.\n */\n structureEndpoint?: string\n /** Same shape as `structureEndpoint`. Defaults to `/api/docs/sources/${sourceId}/content`. */\n contentEndpoint?: string\n /** Base route path for URL navigation. */\n baseRoute: string\n\n /** Empty state copy when no doc is selected. */\n emptyStateText?: string\n\n /** Whether to render the doc-search bar (bound to chat). */\n showAIChat?: boolean\n\n /** Folder-index filename (default `'README.md'`). */\n folderIndexFile?: string\n}\n\nexport function DocViewer(props: DocViewerProps) {\n return <DocViewerContent {...props} />\n}\n\nfunction DocViewerContent({\n sourceId,\n renderContent,\n renderSkeleton,\n chatSource,\n title,\n colorPalette = DEFAULT_DOC_VIEWER_PALETTE,\n className = \"\",\n docPath,\n sidebarLabel = \"DOCUMENTATION\",\n structureEndpoint,\n contentEndpoint,\n baseRoute,\n emptyStateText,\n showAIChat = false,\n folderIndexFile,\n}: DocViewerProps) {\n // Default endpoints derived from sourceId. Hub callers omit the props in 99%\n // of cases; the override is for embed contexts where the doc-viewer sits\n // behind a non-standard route.\n const resolvedStructureEndpoint =\n structureEndpoint ?? `/api/docs/sources/${sourceId}/structure`\n const resolvedContentEndpoint =\n contentEndpoint ?? `/api/docs/sources/${sourceId}/content`\n const {\n structure,\n selectedPath,\n content,\n isLoadingStructure,\n isLoadingContent,\n error,\n expandedNodes,\n selectNode,\n toggleNode,\n navigateToDoc,\n } = useDocumentTree(\n {\n structureEndpoint: resolvedStructureEndpoint,\n contentEndpoint: resolvedContentEndpoint,\n baseRoute,\n folderIndexFile,\n },\n docPath,\n )\n\n const { activeSection, handleSectionClick } = useScrollSpy(content?.sections)\n\n const docNav = useDocNavigation()\n const docSearch = useDocSearch({\n source: chatSource,\n baseRoute,\n onNavigate: (path) => navigateToDoc(path, { fromInternalLink: true }),\n onInPageSwap: (path) => docNav.navigate(path),\n })\n\n const renderedContent = useMemo(() => {\n if (!content) return null\n return renderContent(content, {\n onInternalLinkClick: navigateToDoc,\n currentPath: selectedPath,\n sourceId,\n })\n }, [content, selectedPath, renderContent, navigateToDoc, sourceId])\n\n // Selected node's documentType drives:\n // - which skeleton the caller renders during fetch (markdown vs embed)\n // - the article max-width + sticky-nav visibility (markdown only)\n // `undefined` documentType is treated as `'markdown'` (per the DocNode\n // discriminator's documented default).\n const selectedNodeDocType =\n selectedPath && structure.length > 0\n ? findDocNodeByPath(selectedPath, structure)?.documentType\n : undefined\n // During loading, the in-flight content's type isn't known yet — fall back\n // to the selected node's type (or markdown if neither is set).\n const activeDocType = content?.documentType ?? selectedNodeDocType\n const isMarkdownContent = !activeDocType || activeDocType === 'markdown'\n const showStickyNav = isMarkdownContent\n\n const stickyNavSections =\n content?.sections?.map((s) => ({ id: s.id, label: s.title })) ?? []\n\n const isColorValue =\n colorPalette.background.startsWith('#') ||\n colorPalette.background.startsWith('rgb') ||\n colorPalette.background.startsWith('var(')\n\n const bgStyle = isColorValue ? { backgroundColor: colorPalette.background } : {}\n const bgClass = !isColorValue ? colorPalette.background : ''\n const containerBgStyle =\n colorPalette.containerBackground !== 'transparent'\n ? { backgroundColor: colorPalette.containerBackground }\n : {}\n\n const defaultEmptyText =\n structure.length > 0\n ? 'Select a document from the sidebar to view'\n : 'No documents yet. Add content from the admin panel.'\n const resolvedEmptyText = emptyStateText || defaultEmptyText\n\n return (\n <section className={`${bgClass} ${className}`} style={bgStyle}>\n <div\n className=\"max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto\"\n style={containerBgStyle}\n >\n <div className=\"flex flex-col gap-6\">\n <div className=\"flex flex-col gap-4\">\n {typeof title === 'string' ? <PageHeading>{title}</PageHeading> : title}\n </div>\n\n {showAIChat && (\n <DocSearchBar\n placeholder={`Search ${sidebarLabel?.toLowerCase() || 'documents'}...`}\n query={docSearch.query}\n onQueryChange={docSearch.setQuery}\n results={docSearch.results}\n isLoading={docSearch.isLoading}\n onResultSelect={docSearch.handleResultSelect}\n showDropdown={docSearch.keepDropdownOpen}\n />\n )}\n\n {error && (\n <div className=\"flex justify-center\">\n <div className=\"rounded-lg border bg-ods-card p-8 text-center max-w-md border-ods-border\">\n <h2 className=\"text-xl font-semibold text-ods-text-primary\">\n Error Loading Documents\n </h2>\n <p className=\"mt-2 text-ods-text-secondary\">{error}. Please try again later.</p>\n </div>\n </div>\n )}\n\n {!error && (\n <div className=\"flex flex-col lg:flex-row gap-6 lg:gap-10 items-start flex-1\">\n <div className=\"w-full lg:w-[320px] lg:shrink-0\">\n <div className=\"lg:sticky lg:top-20\">\n {isLoadingStructure ? (\n <CategorySidebarSkeleton />\n ) : (\n <>\n <PersistentMobileDropdown isLoading={false}>\n <MobileNavigationDropdown\n nodes={structure}\n selectedPath={selectedPath}\n expandedNodes={expandedNodes}\n onNodeClick={selectNode}\n onToggleExpand={toggleNode}\n isLoading={false}\n folderIndexFile={folderIndexFile}\n />\n </PersistentMobileDropdown>\n\n <PersistentSidebar isLoading={false}>\n <div className=\"hidden lg:block\">\n <div className=\"space-y-4\">\n <h3 className=\"text-[14px] font-['Azeret_Mono'] font-semibold uppercase text-ods-text-secondary tracking-[-0.02em] leading-[1.43em]\">\n {sidebarLabel}\n </h3>\n <MultiLevelNavigation\n nodes={structure}\n selectedPath={selectedPath}\n expandedNodes={expandedNodes}\n onNodeClick={selectNode}\n onToggleExpand={toggleNode}\n isLoading={false}\n folderIndexFile={folderIndexFile}\n />\n </div>\n </div>\n </PersistentSidebar>\n </>\n )}\n </div>\n </div>\n\n <div className=\"flex-1 min-w-0 w-full\">\n <div\n className={`grid grid-cols-1 ${\n (showStickyNav && stickyNavSections.length > 0) ||\n isLoadingContent ||\n isLoadingStructure\n ? 'lg:grid-cols-[1fr_280px]'\n : ''\n } gap-8`}\n >\n <div className={`w-full ${isMarkdownContent ? 'max-w-4xl mx-auto' : ''}`}>\n <article className=\"space-y-2\">\n {(isLoadingContent || isLoadingStructure) ? (\n renderSkeleton(selectedNodeDocType)\n ) : !content ? (\n <div className=\"text-center py-16\">\n <p className=\"text-xl text-ods-text-secondary\">{resolvedEmptyText}</p>\n </div>\n ) : (\n renderedContent\n )}\n </article>\n </div>\n\n {(isLoadingContent || isLoadingStructure) && (\n <div className=\"hidden lg:block\">\n <div className=\"sticky top-24\">\n <div className=\"h-[14px] w-28 bg-ods-border rounded animate-pulse mb-5\" />\n <div className=\"space-y-0\">\n {[130, 170, 190, 220, 110, 200, 80, 100, 120, 140, 90].map((w, i) => (\n <div\n key={i}\n className={`py-[13px] pl-3 border-l-2 ${\n i === 0 ? 'border-ods-accent' : 'border-transparent'\n }`}\n >\n <div\n className=\"h-[13px] bg-ods-border rounded animate-pulse\"\n style={{ width: w }}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n\n {showStickyNav &&\n content &&\n stickyNavSections.length > 0 &&\n !isLoadingContent && (\n <div className=\"hidden lg:block\">\n <div className=\"sticky top-24\">\n <h3 className=\"text-[14px] font-['Azeret_Mono'] font-semibold uppercase text-ods-text-secondary tracking-[-0.02em] leading-[1.43em] mb-4\">\n ON THIS PAGE\n </h3>\n <StickySectionNav\n sections={stickyNavSections}\n activeSection={activeSection}\n onSectionClick={handleSectionClick}\n ribbonPosition=\"left\"\n ribbonColor=\"var(--ods-accent)\"\n />\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n </section>\n )\n}\n","\"use client\"\n\nimport { ReactNode } from \"react\"\nimport { cn } from \"../utils/cn\"\n\ninterface PersistentFilterControlsProps {\n /**\n * Whether the filters are currently in a loading state\n */\n isLoading: boolean\n /**\n * The filter control components (search, sidebar, mobile dropdown)\n */\n children: ReactNode\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 prevent pointer events during loading\n */\n preventInteraction?: boolean\n}\n\n/**\n * PersistentFilterControls\n * \n * A wrapper component that keeps filter controls visible during loading states\n * but provides visual feedback that they are temporarily disabled.\n * \n * Features:\n * - Reduces opacity and disables pointer events during loading\n * - Maintains layout and accessibility during loading states\n * - Provides smooth transitions between enabled/disabled states\n * - Preserves keyboard navigation and screen reader functionality\n * - Applies consistent disabled styling across all filter types\n * \n * Usage:\n * ```tsx\n * <PersistentFilterControls isLoading={isLoadingVendors}>\n * <SearchContainer />\n * <CategoryFilterSidebar />\n * <MobileDropdown />\n * </PersistentFilterControls>\n * ```\n */\nexport function PersistentFilterControls({\n isLoading,\n children,\n className,\n disabledOpacity = 0.6,\n transitionDuration = 300,\n preventInteraction = true,\n}: PersistentFilterControlsProps) {\n return (\n <div \n className={cn(\n \"relative transition-all ease-in-out\",\n isLoading && preventInteraction && \"pointer-events-none\",\n className\n )}\n style={{\n opacity: isLoading ? disabledOpacity : 1,\n transitionDuration: `${transitionDuration}ms`\n }}\n role=\"region\"\n aria-label=\"Filter controls\"\n aria-busy={isLoading}\n data-loading={isLoading}\n >\n {/* Loading indicator overlay for screen readers */}\n {isLoading && (\n <div \n className=\"sr-only\" \n role=\"status\" \n aria-live=\"polite\"\n >\n Filters temporarily disabled while loading content\n </div>\n )}\n\n {/* Filter controls with disabled state styling */}\n <div \n className={cn(\n \"transition-all ease-in-out\",\n isLoading && \"cursor-not-allowed\"\n )}\n style={{\n transitionDuration: `${transitionDuration}ms`\n }}\n >\n {children}\n </div>\n\n {/* Visual loading indicator */}\n {isLoading && (\n <div \n className=\"absolute top-2 right-2 z-10\"\n role=\"status\"\n aria-label=\"Loading filters\"\n >\n <div className=\"w-4 h-4 border-2 border-ods-border border-t-[#FFC008] rounded-full animate-spin\" />\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Enhanced SearchContainer wrapper that integrates with persistent loading\n */\ninterface PersistentSearchContainerProps {\n isLoading: boolean\n children: ReactNode\n className?: string\n}\n\nexport function PersistentSearchContainer({\n isLoading,\n children,\n className\n}: PersistentSearchContainerProps) {\n return (\n <PersistentFilterControls \n isLoading={isLoading}\n preventInteraction={false} // Keep search interactive during loading\n className={className}\n >\n {children}\n </PersistentFilterControls>\n )\n}\n\n/**\n * Enhanced sidebar wrapper for desktop filter controls\n */\ninterface PersistentSidebarProps {\n isLoading: boolean\n children: ReactNode\n className?: string\n}\n\nexport function PersistentSidebar({\n isLoading,\n children,\n className\n}: PersistentSidebarProps) {\n return (\n <PersistentFilterControls \n isLoading={isLoading}\n disabledOpacity={0.5}\n className={cn(\"lg:sticky lg:top-20\", className)}\n >\n {children}\n </PersistentFilterControls>\n )\n}\n\n/**\n * Enhanced mobile dropdown wrapper\n */\ninterface PersistentMobileDropdownProps {\n isLoading: boolean\n children: ReactNode\n className?: string\n}\n\nexport function PersistentMobileDropdown({\n isLoading,\n children,\n className\n}: PersistentMobileDropdownProps) {\n return (\n <PersistentFilterControls \n isLoading={isLoading}\n disabledOpacity={0.7}\n className={cn(\"lg:hidden\", className)}\n >\n {children}\n </PersistentFilterControls>\n )\n} ","import type React from \"react\"\nimport { cn } from \"../../utils/cn\"\nimport { UnifiedSkeleton, TextSkeleton, MediaSkeleton, InteractiveSkeleton } from \"./unified-skeleton\"\nimport { CardSkeletonGrid } from \"./card-skeleton\"\n\ninterface PageLayoutSkeletonProps {\n className?: string\n}\n\n/**\n * Announcement bar skeleton that matches the AnnouncementBar component\n */\nexport function AnnouncementBarSkeleton() {\n return (\n <div className=\"bg-[#2A2A2A] relative w-full animate-pulse\">\n <div className=\"flex flex-row items-center relative w-full\">\n <div className=\"box-border flex flex-row gap-4 md:gap-6 items-center justify-start pl-4 md:pl-6 pr-12 md:pr-16 py-3 md:py-4 relative w-full\">\n {/* Logo skeleton */}\n <div className=\"relative shrink-0 w-6 h-6 md:w-8 md:h-8 bg-ods-border rounded\"></div>\n \n {/* Text content skeleton */}\n <div className=\"flex-1 min-w-0 space-y-1 md:space-y-2\">\n <div className=\"h-[14px] md:h-[18px] bg-ods-border rounded w-3/4 max-w-md\"></div>\n <div className=\"h-[12px] md:h-[18px] bg-ods-border rounded w-full max-w-lg hidden md:block\"></div>\n </div>\n \n {/* Close button skeleton */}\n <div className=\"absolute right-2 top-2 w-6 h-6 bg-ods-border rounded\"></div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Header skeleton that matches the ClientOnlyHeader placeholder but with proper animations\n */\nexport function HeaderSkeleton() {\n return (\n <header className=\"sticky top-0 z-40 w-full flex items-center justify-between border-b border-ods-border bg-ods-card backdrop-blur-sm bg-ods-card/95 px-4 md:px-[80px] py-3 md:py-[12px] animate-pulse\">\n {/* Left: Logo skeleton */}\n <div className=\"flex items-center justify-start flex-1 min-w-0\">\n <div className=\"w-[110px] h-[26px] md:w-[137px] md:h-8 bg-[#2A2A2A] rounded\"></div>\n </div>\n\n {/* Center: Navigation skeleton - hidden on mobile, visible on desktop */}\n <nav className=\"hidden md:flex flex-1 basis-1/3 justify-center items-center gap-2 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <InteractiveSkeleton.Button className=\"w-24 h-10\" />\n <InteractiveSkeleton.Button className=\"w-24 h-10\" />\n <InteractiveSkeleton.Button className=\"w-24 h-10\" />\n </div>\n </nav>\n\n {/* Right: Actions skeleton */}\n <div className=\"flex items-center justify-end gap-4 flex-1 min-w-0\">\n {/* Mobile: Show hamburger skeleton */}\n <div className=\"md:hidden\">\n <InteractiveSkeleton.Button className=\"h-10 w-10\" />\n </div>\n \n {/* Desktop: Show action buttons skeletons */}\n <div className=\"hidden md:flex items-center gap-4\">\n <InteractiveSkeleton.Button className=\"w-10 h-10\" />\n <InteractiveSkeleton.Button className=\"w-32 h-10\" />\n <InteractiveSkeleton.Button className=\"w-20 h-10\" />\n </div>\n </div>\n </header>\n )\n}\n\n/**\n * Hero section skeleton for static content areas\n */\nexport function HeroSkeleton() {\n return (\n <section\n className=\"w-full flex flex-col items-center justify-center py-12 px-4 md:py-20 md:px-8 text-center animate-pulse\"\n style={{\n background: 'radial-gradient(circle at 50% 0%, #242323 0%, #1A1A1A 100%)'\n }}\n >\n {/* Title skeleton */}\n <TextSkeleton.Heading className=\"h-12 md:h-20 lg:h-24 mb-4 md:mb-6 max-w-4xl w-full\" />\n \n {/* Subtitle skeleton */}\n <div className=\"space-y-3 mb-8 md:mb-10 max-w-4xl w-full px-2\">\n <TextSkeleton.Body className=\"h-5 md:h-7\" />\n <TextSkeleton.Body className=\"h-5 md:h-7 w-3/4 mx-auto\" />\n </div>\n \n {/* CTA Button skeleton */}\n <InteractiveSkeleton.Button className=\"w-full md:w-64 h-12\" />\n </section>\n )\n}\n\n/**\n * Search container skeleton with filters\n */\nexport function SearchContainerSkeleton({ \n className,\n showFilters = true \n}: PageLayoutSkeletonProps & { showFilters?: boolean }) {\n return (\n <div className={cn(\"space-y-4\", className || \"\")}>\n {/* Search input and button */}\n <div className=\"flex gap-2 md:gap-4\">\n <InteractiveSkeleton.Input className=\"flex-1\" />\n <InteractiveSkeleton.Button className=\"w-32 md:w-40\" />\n </div>\n\n {/* Filter chips */}\n {showFilters && (\n <div className=\"flex gap-2 flex-wrap\">\n <InteractiveSkeleton.Chip />\n <InteractiveSkeleton.Chip className=\"w-24\" />\n <InteractiveSkeleton.Chip className=\"w-16\" />\n <InteractiveSkeleton.Chip className=\"w-20\" />\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Category sidebar skeleton for filtering pages\n */\nexport function CategorySidebarSkeleton({ className }: PageLayoutSkeletonProps) {\n // Matches the actual MultiLevelNavigation sidebar with folder/file icons, README badges, and chevrons\n const items = [\n { type: 'file', width: 'w-20', hasBadge: false },\n { type: 'folder', width: 'w-24', hasBadge: true },\n { type: 'folder', width: 'w-20', hasBadge: true },\n { type: 'folder', width: 'w-24', hasBadge: true },\n { type: 'folder', width: 'w-16', hasBadge: true },\n { type: 'folder', width: 'w-20', hasBadge: true },\n { type: 'folder', width: 'w-28', hasBadge: true },\n { type: 'folder', width: 'w-16', hasBadge: true },\n { type: 'folder', width: 'w-24', hasBadge: true },\n { type: 'file', width: 'w-28' },\n { type: 'file', width: 'w-36' },\n ]\n\n return (\n <div className={cn(\"w-full lg:w-[320px]\", className)}>\n {/* DATA ROOM label */}\n <UnifiedSkeleton className=\"h-[14px] w-24 rounded mb-4\" />\n\n {/* Navigation items — each has card background matching actual MultiLevelNavigation */}\n <div className=\"space-y-1.5\">\n {items.map((item, index) => (\n <div\n key={index}\n className={cn(\n \"flex items-center justify-between py-4 px-4 rounded-lg border border-ods-border min-h-[50px]\",\n index === 0 ? \"bg-ods-accent/20\" : \"bg-ods-card\"\n )}\n >\n <div className=\"flex items-center gap-2.5\">\n <UnifiedSkeleton className=\"w-4 h-4 shrink-0 rounded\" />\n <UnifiedSkeleton className={`h-[14px] ${item.width} rounded`} />\n </div>\n <div className=\"flex items-center gap-2 h-[18px]\">\n {item.hasBadge && <UnifiedSkeleton className=\"h-[18px] w-14 rounded\" />}\n {item.type === 'folder' && <UnifiedSkeleton className=\"w-4 h-4 rounded\" />}\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n}\n\n/**\n * Breadcrumb navigation skeleton\n */\nexport function BreadcrumbSkeleton({ className }: PageLayoutSkeletonProps) {\n return (\n <div className={cn(\"flex items-center space-x-1 mb-6\", className)}>\n <TextSkeleton.Caption className=\"w-16\" />\n <UnifiedSkeleton variant=\"default\" className=\"w-4 h-4 rounded-full\" />\n <TextSkeleton.Caption className=\"w-24\" />\n <UnifiedSkeleton variant=\"default\" className=\"w-4 h-4 rounded-full\" />\n <TextSkeleton.Caption className=\"w-32\" />\n <UnifiedSkeleton variant=\"default\" className=\"w-4 h-4 rounded-full\" />\n <TextSkeleton.Caption className=\"w-24\" />\n </div>\n )\n}\n\n/**\n * Results header skeleton with count and sorting\n */\nexport function ResultsHeaderSkeleton({ className }: PageLayoutSkeletonProps) {\n return (\n <div className={cn(\"flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-6\", className)}>\n <div className=\"space-y-1\">\n <TextSkeleton.Body className=\"w-48\" />\n {/* <TextSkeleton.Caption className=\"w-32\" /> */}\n </div>\n </div>\n )\n}\n\n/**\n * Two-column layout skeleton (sidebar + main content)\n */\nexport function TwoColumnLayoutSkeleton({\n className,\n sidebarContent,\n mainContent,\n sidebarPosition = 'left'\n}: PageLayoutSkeletonProps & {\n sidebarContent?: React.ReactNode\n mainContent?: React.ReactNode\n sidebarPosition?: 'left' | 'right'\n}) {\n const sidebar = sidebarContent || <CategorySidebarSkeleton />\n const main = mainContent || <CardSkeletonGrid count={6} />\n\n return (\n <div className={cn(\n \"grid grid-cols-1 lg:grid-cols-[320px_1fr] gap-6 lg:gap-8\",\n sidebarPosition === 'right' && \"lg:grid-cols-[1fr_320px]\",\n className\n )}>\n {sidebarPosition === 'left' ? (\n <>\n <aside className=\"order-2 lg:order-1\">{sidebar}</aside>\n <main className=\"order-1 lg:order-2\">{main}</main>\n </>\n ) : (\n <>\n <main className=\"order-1\">{main}</main>\n <aside className=\"order-2\">{sidebar}</aside>\n </>\n )}\n </div>\n )\n}\n\n/**\n * Article/blog post layout skeleton\n */\nexport function ArticleLayoutSkeleton({ className }: PageLayoutSkeletonProps) {\n return (\n <article className={cn(\"max-w-4xl mx-auto\", className)}>\n {/* Article header */}\n <header className=\"mb-8 md:mb-12 space-y-4 md:space-y-6\">\n {/* Category/tags */}\n <div className=\"flex gap-2\">\n <InteractiveSkeleton.Chip />\n <InteractiveSkeleton.Chip className=\"w-16\" />\n </div>\n \n {/* Title */}\n <div className=\"space-y-3\">\n <TextSkeleton.Heading className=\"w-full\" />\n <TextSkeleton.Heading className=\"w-3/4\" />\n </div>\n \n {/* Metadata */}\n <div className=\"flex items-center gap-4 pt-4 border-t border-ods-divider\">\n <div className=\"flex items-center gap-2\">\n <MediaSkeleton.Avatar size=\"sm\" />\n <TextSkeleton.Caption className=\"w-24\" />\n </div>\n <TextSkeleton.Caption className=\"w-20\" />\n <TextSkeleton.Caption className=\"w-16\" />\n </div>\n </header>\n\n {/* Featured image */}\n <div className=\"mb-8 md:mb-12\">\n <MediaSkeleton.CardImage className=\"w-full h-64 md:h-96\" />\n </div>\n\n {/* Article content */}\n <div className=\"prose prose-invert max-w-none space-y-6\">\n {/* Paragraphs */}\n {Array.from({ length: 12 }).map((_, index) => (\n <div key={index} className=\"space-y-2\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-5/6\" />\n <TextSkeleton.Body className=\"w-3/4\" />\n </div>\n ))}\n \n {/* Subheading in content */}\n <div className=\"py-4\">\n <TextSkeleton.Subheading className=\"w-2/3 mb-4\" />\n <div className=\"space-y-2\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-4/5\" />\n </div>\n </div>\n </div>\n </article>\n )\n}\n\n/**\n * Vendor Detail Layout Skeleton - Complete vendor detail page structure\n * Matches the refactored vendor detail page with proper platform colors and responsive layout\n */\nexport function VendorDetailLayoutSkeleton({ className }: PageLayoutSkeletonProps) {\n return (\n <main className={cn(\"bg-ods-bg min-h-screen\", className)}>\n <div className=\"max-w-[1920px] mx-auto px-6 md:px-20 py-6 md:py-10\">\n {/* Breadcrumb */}\n <BreadcrumbSkeleton className=\"mb-6\" />\n\n {/* Main Layout Container */}\n <div className=\"flex flex-col lg:flex-row lg:gap-10\">\n {/* Left Content Area */}\n <div className=\"flex-1 min-w-0\">\n \n {/* Vendor Hero Section */}\n <div className=\"mb-10\">\n {/* Header - Logo and Title Side by Side */}\n <div className=\"flex gap-6 mb-6\">\n <div className=\"w-20 h-20 bg-ods-card border border-ods-border rounded-lg animate-pulse flex-shrink-0\"></div>\n \n <div className=\"flex flex-col gap-3\">\n <div className=\"flex flex-col gap-2\">\n {/* Large title skeleton */}\n <div className=\"h-12 md:h-16 lg:h-20 bg-[#2A2A2A] rounded animate-pulse w-80 max-w-full\"></div>\n {/* Category text */}\n <div className=\"h-5 md:h-6 bg-[#2A2A2A] rounded animate-pulse w-32\"></div>\n </div>\n \n {/* Pricing tags */}\n <div className=\"flex items-center gap-2\">\n <div className=\"h-8 bg-[#2A2A2A] rounded animate-pulse w-20\"></div>\n <div className=\"h-8 bg-[#2A2A2A] rounded animate-pulse w-16\"></div>\n </div>\n </div>\n </div>\n\n {/* Vendor Image Display Skeleton */}\n <div className=\"w-full h-[300px] md:h-[400px] lg:h-[500px] bg-ods-card border border-ods-border rounded-lg animate-pulse mb-2\"></div>\n <div className=\"text-center\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-24 mx-auto\"></div>\n </div>\n </div>\n\n {/* Mobile Sidebar - Show on mobile only, positioned after title */}\n <div className=\"lg:hidden mb-10\">\n <div className=\"space-y-4\">\n {/* Deploy Button */}\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n \n {/* Voting Buttons */}\n <div className=\"space-y-2\">\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n </div>\n \n {/* GitHub Score Section */}\n <div className=\"border border-ods-border rounded-lg overflow-hidden\">\n {/* Header */}\n <div className=\"bg-ods-card p-4 flex items-center gap-3\">\n <div className=\"w-8 h-8 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"flex flex-col gap-1\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-20\"></div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-12\"></div>\n </div>\n </div>\n \n {/* Stats */}\n <div className=\"p-4 space-y-3\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <div className=\"w-6 h-6 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"flex items-end gap-1\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-12\"></div>\n <div className=\"h-3 bg-[#2A2A2A] rounded animate-pulse w-8\"></div>\n </div>\n </div>\n ))}\n </div>\n </div>\n \n {/* Action Buttons */}\n <div className=\"space-y-3\">\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n </div>\n </div>\n </div>\n\n {/* Alternatives Container */}\n <div className=\"flex flex-col gap-6 mb-20\">\n <div className=\"h-8 md:h-10 bg-[#2A2A2A] rounded animate-pulse w-48\"></div>\n \n {/* Open Source Alternatives */}\n <div className=\"flex flex-col gap-4\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-48\"></div>\n <div className=\"flex flex-wrap gap-2\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 bg-ods-card border border-ods-border rounded-lg animate-pulse w-24\"></div>\n ))}\n </div>\n </div>\n\n {/* Commercial Alternatives */}\n <div className=\"flex flex-col gap-4\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-52\"></div>\n <div className=\"flex flex-wrap gap-2\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"h-10 bg-ods-card border border-ods-border rounded-lg animate-pulse w-28\"></div>\n ))}\n </div>\n </div>\n </div>\n\n {/* About Section */}\n <div className=\"flex flex-col gap-6 mb-20\">\n <div className=\"h-8 md:h-10 bg-[#2A2A2A] rounded animate-pulse w-64\"></div>\n \n <div className=\"bg-ods-card border border-ods-border rounded-lg p-6 md:p-8\">\n <div className=\"space-y-4\">\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-5/6\"></div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-4/5\"></div>\n </div>\n </div>\n </div>\n\n {/* Key Features Section */}\n <div className=\"flex flex-col gap-6 mb-20\">\n <div className=\"h-8 md:h-10 bg-[#2A2A2A] rounded animate-pulse w-72\"></div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"bg-ods-card border border-ods-border rounded-lg p-4 flex gap-4\">\n <div className=\"w-12 h-12 bg-[#2A2A2A] rounded flex-shrink-0 animate-pulse\"></div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-3/4\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse\"></div>\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Pros and Cons Section */}\n <div className=\"flex flex-col gap-6 mb-20\">\n <div className=\"h-8 md:h-10 bg-[#2A2A2A] rounded animate-pulse w-80\"></div>\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Pros Column */}\n <div className=\"flex flex-col gap-6\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-16\"></div>\n {Array.from({ length: 3 }).map((_, i) => (\n <div key={i} className=\"bg-ods-card border border-ods-border rounded-lg p-4 flex gap-3\">\n <div className=\"w-12 h-12 bg-ods-bg border border-ods-border rounded flex-shrink-0 animate-pulse\"></div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-2/3\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse\"></div>\n </div>\n </div>\n ))}\n </div>\n {/* Cons Column */}\n <div className=\"flex flex-col gap-6\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-16\"></div>\n {Array.from({ length: 3 }).map((_, i) => (\n <div key={i} className=\"bg-ods-card border border-ods-border rounded-lg p-4 flex gap-3\">\n <div className=\"w-12 h-12 bg-ods-bg border border-ods-border rounded flex-shrink-0 animate-pulse\"></div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-5 bg-[#2A2A2A] rounded animate-pulse w-2/3\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse\"></div>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Alternatives Section */}\n <div className=\"flex flex-col gap-6 mb-20\">\n <div className=\"h-8 md:h-10 bg-[#2A2A2A] rounded animate-pulse w-72\"></div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"bg-ods-card border border-ods-border rounded-lg p-6 space-y-4\">\n {/* Header */}\n <div className=\"flex items-start gap-4\">\n <div className=\"w-16 h-16 bg-ods-bg border border-ods-border rounded-lg animate-pulse flex-shrink-0\"></div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-3/4\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-1/2\"></div>\n </div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-20\"></div>\n </div>\n \n {/* Description */}\n <div className=\"space-y-2\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-5/6\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-4/5\"></div>\n </div>\n \n {/* Footer */}\n <div className=\"flex items-center justify-between pt-2 border-t border-ods-border\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-16\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-20\"></div>\n </div>\n </div>\n ))}\n </div>\n {/* Show All Button */}\n <div className=\"h-12 bg-[#2A2A2A] rounded-md animate-pulse\"></div>\n </div>\n\n {/* Comments Section */}\n <div className=\"flex flex-col gap-6 mb-20\">\n <div className=\"h-8 md:h-10 bg-[#2A2A2A] rounded animate-pulse w-64\"></div>\n \n {/* Comment Form Skeleton */}\n <div className=\"space-y-3\">\n <div className=\"flex gap-3\">\n <div className=\"flex-1 bg-ods-bg border border-ods-border rounded-lg overflow-hidden\">\n {/* Title Section */}\n <div className=\"border-b border-ods-border p-3\">\n <div className=\"h-3 bg-[#2A2A2A] rounded animate-pulse w-8 mb-2\"></div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-full\"></div>\n </div>\n \n {/* Description Section */}\n <div className=\"p-3\">\n <div className=\"h-3 bg-[#2A2A2A] rounded animate-pulse w-16 mb-2\"></div>\n <div className=\"space-y-2\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-full\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-3/4\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-1/2\"></div>\n </div>\n </div>\n </div>\n \n {/* Send Button */}\n <div className=\"flex items-stretch\">\n <div className=\"h-[120px] w-20 bg-[#2A2A2A] rounded-lg animate-pulse\"></div>\n </div>\n </div>\n </div>\n \n {/* Sample Comment Cards */}\n <div className=\"space-y-4\">\n {Array.from({ length: 3 }).map((_, i) => (\n <div key={i} className=\"bg-ods-card border border-ods-border rounded-lg p-4\">\n {/* Comment Header */}\n <div className=\"flex flex-col gap-3 mb-3 min-[420px]:flex-row min-[420px]:items-center min-[420px]:justify-between min-[420px]:gap-2\">\n <div className=\"flex items-center gap-2\">\n {/* User Info */}\n <div className=\"flex items-center gap-2 bg-ods-card border border-ods-border rounded-lg py-2 px-3\">\n <div className=\"w-8 h-8 bg-[#2A2A2A] rounded-lg animate-pulse\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-20\"></div>\n </div>\n \n {/* Timestamp */}\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-12\"></div>\n </div>\n </div>\n \n {/* Comment Content */}\n <div className=\"space-y-2\">\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-2/3\"></div>\n <div className=\"space-y-1\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-full\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-4/5\"></div>\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-3/5\"></div>\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n\n {/* Right Sidebar - Desktop Only */}\n <div className=\"hidden lg:block w-[290px] flex-shrink-0\">\n <div className=\"space-y-4\">\n {/* Deploy Button */}\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n \n {/* Voting Buttons */}\n <div className=\"space-y-2\">\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n </div>\n \n {/* GitHub Score Section */}\n <div className=\"border border-ods-border rounded-lg overflow-hidden\">\n {/* Header */}\n <div className=\"bg-ods-card p-4 flex items-center gap-3\">\n <div className=\"w-8 h-8 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"flex flex-col gap-1\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-20\"></div>\n <div className=\"h-6 bg-[#2A2A2A] rounded animate-pulse w-12\"></div>\n </div>\n </div>\n \n {/* Stats */}\n <div className=\"p-4 space-y-3\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-3\">\n <div className=\"w-6 h-6 bg-[#2A2A2A] rounded animate-pulse\"></div>\n <div className=\"flex items-end gap-1\">\n <div className=\"h-4 bg-[#2A2A2A] rounded animate-pulse w-12\"></div>\n <div className=\"h-3 bg-[#2A2A2A] rounded animate-pulse w-8\"></div>\n </div>\n </div>\n ))}\n </div>\n </div>\n \n {/* Action Buttons */}\n <div className=\"space-y-3\">\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n <div className=\"h-12 bg-ods-card border border-ods-border rounded-lg animate-pulse\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </main>\n )\n}\n\n/**\n * Stats/features section skeleton for homepage\n */\nexport function StatsSectionSkeleton({ \n className,\n columns = 3 \n}: PageLayoutSkeletonProps & { columns?: number }) {\n return (\n <div className={cn(\n \"grid gap-6 mb-12 md:mb-16\",\n columns === 3 && \"grid-cols-1 md:grid-cols-3\",\n columns === 4 && \"grid-cols-1 md:grid-cols-2 lg:grid-cols-4\",\n className\n )}>\n {Array.from({ length: columns }).map((_, i) => (\n <div key={i} className=\"bg-ods-card border border-ods-border rounded-lg p-6\">\n <div className=\"space-y-4\">\n <MediaSkeleton.Icon size=\"lg\" />\n <TextSkeleton.Subheading className=\"w-3/4\" />\n <div className=\"space-y-2\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-3/4\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n )\n}\n\n\n/**\n * Blog Card Grid Skeleton - Always displays exactly 4 blog card skeletons\n * Used for consistent blog page layout with predictable height\n */\nexport function BlogCardGridSkeleton({ className }: PageLayoutSkeletonProps) {\n return (\n <div className={cn(\"space-y-6\", className)}>\n <CardSkeletonGrid \n count={4}\n variant=\"blog\"\n className=\"grid-cols-1 md:grid-cols-2\"\n />\n </div>\n )\n}\n\n/**\n * Vendor Grid Skeleton - Always displays exactly 12 vendor card skeletons\n * Used for consistent vendor page layout with predictable height\n */\nexport function VendorGridSkeleton({ className }: PageLayoutSkeletonProps) {\n return (\n <div className={cn(\"space-y-6\", className)}>\n <CardSkeletonGrid \n count={12}\n variant=\"vendor\"\n className=\"grid-cols-1 md:grid-cols-2 lg:grid-cols-3\"\n />\n </div>\n )\n}\n\n/**\n * Slack Community Section Skeleton\n * Matches SlackCommunity component structure with title, channel list, and chat interface\n */\nexport function SlackCommunitySkeleton() {\n return (\n <section \n className=\"w-full bg-ods-bg px-4 md:px-20 lg:px-20 py-12 md:py-20\"\n aria-label=\"Slack Community Loading\"\n >\n {/* Frame 651 Container */}\n <div className=\"w-full flex flex-col gap-4 md:gap-6\">\n \n {/* Title Skeleton */}\n <div className=\"w-full\">\n <div className=\"h-8 md:h-12 lg:h-14 bg-[#2A2A2A] rounded-lg animate-pulse max-w-md\"></div>\n </div>\n\n {/* Content Area - Channel List + Chat Interface */}\n <div className=\"w-full flex flex-col lg:flex-row lg:justify-end lg:items-start gap-4 md:gap-6 min-w-0\">\n \n {/* Channel List Skeleton */}\n <div className=\"w-full lg:w-[290px] lg:max-w-[290px] bg-ods-bg border border-ods-border rounded flex flex-col flex-shrink-0 overflow-hidden animate-pulse\">\n <div className=\"p-4 space-y-4\">\n {/* Header */}\n <div className=\"flex flex-col gap-2\">\n <div className=\"flex items-center justify-between\">\n <div className=\"h-4 bg-[#2A2A2A] rounded w-24\"></div>\n <div className=\"h-3 bg-[#2A2A2A] rounded w-16\"></div>\n </div>\n <div className=\"h-3 bg-[#2A2A2A] rounded w-32\"></div>\n </div>\n\n {/* Channels */}\n <div className=\"flex flex-col gap-1\">\n <div className=\"h-3 bg-[#2A2A2A] rounded w-16 mb-2\"></div>\n {Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-2 px-2 py-3 min-h-[48px]\">\n <div className=\"w-4 h-4 bg-[#2A2A2A] rounded\"></div>\n <div className=\"flex-1\">\n <div className=\"h-3 bg-[#2A2A2A] rounded w-24 mb-1\"></div>\n <div className=\"h-2 bg-[#2A2A2A] rounded w-16\"></div>\n </div>\n </div>\n ))}\n </div>\n\n {/* Stats */}\n <div className=\"border-t border-ods-border pt-3 mt-2\">\n <div className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <div className=\"h-3 bg-[#2A2A2A] rounded w-16\"></div>\n <div className=\"h-3 bg-[#2A2A2A] rounded w-8\"></div>\n </div>\n <div className=\"flex justify-between\">\n <div className=\"h-3 bg-[#2A2A2A] rounded w-20\"></div>\n <div className=\"h-3 bg-[#2A2A2A] rounded w-8\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {/* Chat Interface Skeleton */}\n <div className=\"flex-1 min-h-[450px] h-[450px] md:min-h-[500px] md:h-[500px] lg:min-h-[600px] lg:h-[600px] bg-ods-card border border-ods-border rounded-lg flex flex-col min-w-0 animate-pulse\">\n \n {/* Header */}\n <div className=\"flex items-center justify-between p-4 md:p-6 border-b border-ods-border bg-ods-bg h-[56px] md:h-[60px] flex-shrink-0\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-3 md:h-4 bg-[#2A2A2A] rounded w-3 md:w-4\"></div>\n <div className=\"h-3 md:h-4 bg-[#2A2A2A] rounded w-24 md:w-32\"></div>\n </div>\n <div className=\"h-3 bg-[#2A2A2A] rounded w-12 md:w-16\"></div>\n </div>\n\n {/* Messages */}\n <div className=\"flex-1 bg-ods-bg p-4 md:p-6 space-y-3 md:space-y-4 overflow-hidden min-h-[280px] md:min-h-[320px] lg:min-h-[420px]\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"flex gap-2 md:gap-3\">\n <div className=\"w-10 h-10 md:w-12 md:h-12 bg-[#2A2A2A] rounded-lg flex-shrink-0\"></div>\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <div className=\"h-3 bg-[#2A2A2A] rounded w-16 md:w-20\"></div>\n <div className=\"h-3 bg-[#2A2A2A] rounded w-12 md:w-16\"></div>\n </div>\n <div className=\"h-3 md:h-4 bg-[#2A2A2A] rounded w-full\"></div>\n </div>\n </div>\n ))}\n </div>\n\n {/* Input Area */}\n <div className=\"p-4 md:p-6 border-t border-ods-border bg-ods-card h-[72px] md:h-[80px] flex-shrink-0\">\n <div className=\"flex items-end gap-3 justify-center md:justify-end\">\n <div className=\"h-10 md:h-12 bg-[#2A2A2A] rounded-lg w-28 md:w-32\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n );\n} ","import type React from \"react\"\nimport { cn } from \"../../utils/cn\"\n\ninterface UnifiedSkeletonProps extends React.HTMLAttributes<HTMLDivElement> {\n className?: string\n variant?: 'default' | 'text' | 'circular' | 'rectangular'\n width?: string | number\n height?: string | number\n /**\n * Disable animation for users who prefer reduced motion\n */\n animate?: boolean\n /**\n * Accessibility label for screen readers\n */\n 'aria-label'?: string\n}\n\n/**\n * UnifiedSkeleton Component\n * \n * Base skeleton component with consistent styling across the application.\n * \n * Design Specifications:\n * - Color: #2A2A2A base with #3A3A3A pulse highlight\n * - Animation: 1.5s pulse duration, ease-in-out timing\n * - Responsive: Mobile-first approach with proper scaling\n * - Accessibility: ARIA labels and reduced motion support\n * \n * @param variant - Shape variant: default (rounded), text (rounded-sm), circular, rectangular\n * @param width - Custom width (defaults to full width)\n * @param height - Custom height (defaults to auto based on variant)\n * @param animate - Enable/disable animation (respects prefers-reduced-motion)\n * @param aria-label - Accessibility label for screen readers\n */\nexport function UnifiedSkeleton({\n className,\n variant = 'default',\n width,\n height,\n animate = true,\n 'aria-label': ariaLabel,\n ...props\n}: UnifiedSkeletonProps) {\n const baseClasses = \"bg-[#2A2A2A]\"\n \n const variantClasses = {\n default: \"rounded-md\",\n text: \"rounded-sm\",\n circular: \"rounded-full\",\n rectangular: \"rounded-none\"\n }\n \n const defaultHeights = {\n default: \"h-4\",\n text: \"h-4\", \n circular: \"h-12 w-12\",\n rectangular: \"h-4\"\n }\n \n const animationClasses = animate \n ? \"animate-pulse motion-reduce:animate-none\" \n : \"\"\n \n const style: React.CSSProperties = {\n width: width,\n height: height,\n }\n \n return (\n <div\n className={cn(\n baseClasses,\n variantClasses[variant],\n !height && !style.height && defaultHeights[variant],\n !width && !style.width && variant !== 'circular' && \"w-full\",\n animationClasses,\n className\n )}\n style={style}\n role=\"status\"\n aria-label={ariaLabel || \"Loading content\"}\n {...props}\n />\n )\n}\n\n/**\n * Text skeleton variants for consistent typography loading\n */\nexport const TextSkeleton = {\n /**\n * Large heading skeleton (h1, h2)\n */\n Heading: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"text\" \n className={cn(\"h-8 md:h-10 lg:h-12\", className)} \n aria-label=\"Loading heading\"\n {...props} \n />\n ),\n \n /**\n * Medium heading skeleton (h3, h4)\n */\n Subheading: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"text\" \n className={cn(\"h-6 md:h-7 lg:h-8\", className)} \n aria-label=\"Loading subheading\"\n {...props} \n />\n ),\n \n /**\n * Body text skeleton\n */\n Body: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"text\" \n className={cn(\"h-4 md:h-5\", className)} \n aria-label=\"Loading text\"\n {...props} \n />\n ),\n \n /**\n * Small text skeleton (captions, metadata)\n */\n Caption: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"text\" \n className={cn(\"h-3 md:h-4\", className)} \n aria-label=\"Loading caption\"\n {...props} \n />\n ),\n}\n\n/**\n * Interactive element skeletons\n */\nexport const InteractiveSkeleton = {\n /**\n * Button skeleton with proper touch targets\n */\n Button: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"default\" \n className={cn(\"h-10 md:h-12 w-32 md:w-40\", className)} \n aria-label=\"Loading button\"\n {...props} \n />\n ),\n \n /**\n * Input field skeleton\n */\n Input: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"default\" \n className={cn(\"h-10 md:h-12 w-full\", className)} \n aria-label=\"Loading input field\"\n {...props} \n />\n ),\n \n /**\n * Filter chip skeleton\n */\n Chip: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton \n variant=\"default\" \n className={cn(\"h-8 w-20 rounded-full\", className)} \n aria-label=\"Loading filter\"\n {...props} \n />\n ),\n}\n\n/**\n * Media skeletons for images and icons\n */\nexport const MediaSkeleton = {\n /**\n * Avatar/profile image skeleton\n */\n Avatar: ({ size = 'md', className, ...props }: Omit<UnifiedSkeletonProps, 'variant'> & { size?: 'sm' | 'md' | 'lg' }) => {\n const sizeClasses = {\n sm: \"h-8 w-8\",\n md: \"h-12 w-12\", \n lg: \"h-16 w-16\"\n }\n \n return (\n <UnifiedSkeleton \n variant=\"circular\" \n className={cn(sizeClasses[size], className)} \n aria-label=\"Loading profile image\"\n {...props} \n />\n )\n },\n \n /**\n * Card image skeleton (1200×630 OG aspect — matches entity-cards default\n * so loading state never causes a height shift when card data arrives)\n */\n CardImage: ({ className, ...props }: Omit<UnifiedSkeletonProps, 'variant'>) => (\n <UnifiedSkeleton\n variant=\"default\"\n className={cn(\"aspect-[1200/630] w-full\", className)}\n aria-label=\"Loading image\"\n {...props}\n />\n ),\n \n /**\n * Icon skeleton\n */\n Icon: ({ size = 'md', className, ...props }: Omit<UnifiedSkeletonProps, 'variant'> & { size?: 'sm' | 'md' | 'lg' }) => {\n const sizeClasses = {\n sm: \"h-4 w-4\",\n md: \"h-6 w-6\",\n lg: \"h-8 w-8\"\n }\n \n return (\n <UnifiedSkeleton \n variant=\"default\" \n className={cn(sizeClasses[size], className)} \n aria-label=\"Loading icon\"\n {...props} \n />\n )\n },\n} ","import type React from \"react\"\nimport { cn } from \"../../utils/cn\"\nimport { UnifiedSkeleton, TextSkeleton, MediaSkeleton, InteractiveSkeleton } from \"./unified-skeleton\"\n\ninterface CardSkeletonProps {\n className?: string\n /**\n * Card layout variant\n */\n variant?: 'vendor' | 'blog' | 'category' | 'alternative'\n /**\n * Show action buttons area\n */\n showActions?: boolean\n /**\n * Show metadata footer\n */\n showMetadata?: boolean\n /** Optional tailwind classes to override the card container background & border */\n containerClassName?: string\n}\n \n/**\n * Unified card skeleton component for consistent card loading states\n * \n * Supports different card types used across the application:\n * - vendor: Vendor cards with logo, title, description, and actions\n * - blog: Blog post cards with image, title, summary, and metadata \n * - category: Category cards with icon, title, and description\n * - alternative: Alternative vendor cards in comparison lists\n */\nexport function CardSkeleton({\n className,\n containerClassName,\n variant = 'vendor',\n showActions = true,\n showMetadata = true,\n ...props\n}: CardSkeletonProps) {\n const cardContent = {\n vendor: <VendorCardContent showActions={showActions} showMetadata={showMetadata} />,\n blog: <BlogCardContent showActions={showActions} showMetadata={showMetadata} />,\n category: <CategoryCardContent />,\n alternative: <AlternativeCardContent showActions={showActions} />\n }\n\n return (\n <div \n className={cn(\n containerClassName || \"bg-ods-card border border-ods-border\",\n \"rounded-lg overflow-hidden\",\n // Flex layouts for certain variants\n variant === 'blog' && \"h-full flex flex-col\",\n variant === 'vendor' && \"h-full flex flex-col\",\n className\n )}\n role=\"status\"\n aria-label={`Loading ${variant} card`}\n {...props}\n >\n {cardContent[variant]}\n </div>\n )\n}\n\n/**\n * Vendor card skeleton content - matches exact VendorCard structure\n */\nfunction VendorCardContent({ showActions, showMetadata }: { showActions: boolean; showMetadata: boolean }) {\n return (\n <div className=\"p-4 gap-3 flex flex-col\">\n {/* Header Section - Row layout matching actual VendorCard */}\n <div className=\"flex items-start gap-3 w-full\">\n {/* Logo Frame - 60px width fixed, matching actual structure */}\n <div className=\"w-[60px] h-[60px] bg-ods-bg border border-ods-border rounded-lg p-2 flex items-center justify-center flex-shrink-0\">\n <MediaSkeleton.Avatar size=\"sm\" className=\"w-11 h-11\" />\n </div>\n \n {/* Text Container - Column layout, matching actual structure */}\n <div className=\"flex-1 flex flex-col justify-center py-2 min-w-0 space-y-1\">\n {/* Title - Single line with proper width */}\n <TextSkeleton.Subheading className=\"w-3/4\" />\n {/* Category - Single line, shorter */}\n <TextSkeleton.Caption className=\"w-1/2\" />\n </div>\n </div>\n\n {/* Description Section - Fixed 48px height matching actual VendorCard */}\n <div className=\"w-full h-12 overflow-hidden flex items-center\">\n <div className=\"space-y-1 w-full\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-2/3\" />\n </div>\n </div>\n\n {/* Footer Section - Responsive layout matching actual structure */}\n <div className=\"flex items-center justify-between gap-2 w-full min-w-0\">\n {/* Stats Container - Flexible width, no overflow */}\n <div className=\"flex items-center gap-3 md:gap-4 min-w-0 flex-shrink\">\n {/* OpenMSP Score skeleton */}\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n <MediaSkeleton.Icon size=\"sm\" className=\"w-5 h-5\" />\n <TextSkeleton.Caption className=\"w-8\" />\n </div>\n \n {/* GitHub Stats skeleton */}\n <div className=\"flex items-center gap-1 flex-shrink-0\">\n <MediaSkeleton.Icon size=\"sm\" className=\"w-5 h-5\" />\n <TextSkeleton.Caption className=\"w-10\" />\n </div>\n </div>\n\n {/* Tag Section - Contained within card boundaries */}\n <div className=\"flex-shrink-0\">\n <div className=\"flex items-center gap-1.5 bg-ods-bg border border-ods-border rounded px-2.5 py-1.5\">\n <div className=\"w-4 h-4 bg-ods-border rounded-sm flex items-center justify-center flex-shrink-0\">\n <MediaSkeleton.Icon size=\"sm\" className=\"w-2.5 h-2.5\" />\n </div>\n <TextSkeleton.Caption className=\"w-16\" />\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Blog card skeleton content - matches fixed height structure\n */\nfunction BlogCardContent({ showActions, showMetadata }: { showActions: boolean; showMetadata: boolean }) {\n return (\n <>\n {/* Image Section — OG 1200×630 aspect (matches blog-card.tsx loaded state) */}\n <div className=\"blog-card-image-container relative w-full aspect-[1200/630] overflow-hidden bg-ods-bg\">\n <MediaSkeleton.CardImage />\n </div>\n \n {/* Content - Fixed height structure to match BlogCard */}\n <div className=\"p-4 flex flex-col flex-grow\">\n {/* Title Section - Fixed 2 lines with vertical centering */}\n <div className=\"mb-3 flex items-center min-h-[50.4px] md:min-h-[56px] lg:min-h-[61.6px]\">\n <div className=\"space-y-1 w-full\">\n <TextSkeleton.Subheading className=\"w-full\" />\n <TextSkeleton.Subheading className=\"w-3/4\" />\n </div>\n </div>\n \n {/* Chips Section - Fixed single line height */}\n <div className=\"mb-3 flex gap-2 h-[28px] items-center\">\n <InteractiveSkeleton.Chip className=\"w-16\" />\n <InteractiveSkeleton.Chip className=\"w-12\" />\n </div>\n \n {/* Description Section - Fixed 2 lines with vertical centering */}\n <div className=\"mb-3 flex items-center min-h-[42px] md:min-h-[45px] lg:min-h-[48px]\">\n <div className=\"space-y-1 w-full\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-1/2\" />\n </div>\n </div>\n\n {/* Actions - only if requested */}\n {showActions && (\n <div className=\"pt-2\">\n <InteractiveSkeleton.Button className=\"w-24 h-8\" />\n </div>\n )}\n \n {/* Metadata footer - Matches BlogMeta horizontal layout */}\n {showMetadata && (\n <div className=\"mt-auto\">\n <div className=\"flex items-center justify-between gap-4 pt-4 border-t border-ods-border\">\n {/* Author section - matches AuthorMeta */}\n <div className=\"flex items-center gap-2 min-w-0 flex-1\">\n <MediaSkeleton.Avatar size=\"sm\" />\n <TextSkeleton.Caption className=\"w-16\" />\n </div>\n \n {/* Date and reading time section - matches BlogMeta right side */}\n <div className=\"flex items-center gap-3 text-[#767676] shrink-0\">\n <TextSkeleton.Caption className=\"w-12\" />\n {/* Separator dot */}\n <div className=\"w-1 h-1 bg-[#767676] rounded-full\"></div>\n <TextSkeleton.Caption className=\"w-16\" />\n </div>\n </div>\n </div>\n )}\n </div>\n </>\n )\n}\n\n/**\n * Category card skeleton content\n */\nfunction CategoryCardContent() {\n return (\n <div className=\"p-4 md:p-6 space-y-4\">\n {/* Icon grid */}\n <div className=\"flex gap-2 md:gap-3\">\n {Array.from({ length: 6 }).map((_, index) => (\n <MediaSkeleton.Icon \n key={index}\n size=\"lg\"\n className=\"flex-shrink-0\"\n />\n ))}\n </div>\n\n {/* Content */}\n <div className=\"space-y-2\">\n <div className=\"space-y-1\">\n <TextSkeleton.Subheading className=\"w-3/4\" />\n <TextSkeleton.Caption className=\"w-1/2\" />\n </div>\n \n <div className=\"flex items-start md:items-end justify-between gap-4 md:gap-6\">\n <div className=\"flex-1 space-y-2\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-2/3\" />\n </div>\n \n <InteractiveSkeleton.Button className=\"w-10 h-10 md:w-12 md:h-12 flex-shrink-0\" />\n </div>\n </div>\n </div>\n )\n}\n\n/**\n * Alternative card skeleton content (for vendor alternatives/comparisons)\n */\nfunction AlternativeCardContent({ showActions }: { showActions: boolean }) {\n return (\n <div className=\"flex items-start gap-4 p-4\">\n <MediaSkeleton.Avatar size=\"md\" className=\"flex-shrink-0\" />\n \n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <TextSkeleton.Subheading className=\"w-1/3\" />\n <TextSkeleton.Caption className=\"w-16\" />\n </div>\n \n <div className=\"space-y-1\">\n <TextSkeleton.Body className=\"w-full\" />\n <TextSkeleton.Body className=\"w-5/6\" />\n </div>\n\n {showActions && (\n <div className=\"pt-2\">\n <InteractiveSkeleton.Button className=\"w-20 h-8\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/**\n * Grid of card skeletons for loading lists\n */\nexport function CardSkeletonGrid({\n count = 6,\n variant = 'vendor',\n className,\n containerClassName,\n ...props\n}: {\n count?: number\n variant?: CardSkeletonProps['variant']\n className?: string\n containerClassName?: string\n} & Omit<CardSkeletonProps, 'variant'>) {\n return (\n <div \n className={cn(\n \"grid gap-4 md:gap-6\",\n // Responsive grid based on card type\n variant === 'vendor' && \"grid-cols-1 md:grid-cols-2 lg:grid-cols-3\",\n variant === 'blog' && \"grid-cols-1 md:grid-cols-2\",\n variant === 'category' && \"grid-cols-1 md:grid-cols-2 lg:grid-cols-4\",\n variant === 'alternative' && \"grid-cols-1\",\n className\n )}\n role=\"status\"\n aria-label={`Loading ${count} ${variant} cards`}\n >\n {Array.from({ length: count }, (_, index) => (\n <CardSkeleton \n key={index} \n variant={variant}\n containerClassName={containerClassName}\n {...props}\n />\n ))}\n </div>\n )\n} ","\"use client\"\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport type { DocNode, DocContent } from '../../types/doc-source'\nimport {\n stripFolderIndexFromPath,\n findDocNodeByPath,\n getDocAncestorNodeIds,\n DEFAULT_FOLDER_INDEX_FILE,\n} from '../../utils/doc-tree-nav'\nimport { useDocNavigation } from './doc-navigation-context'\nimport { scrollElementIntoView } from '../../utils/scroll-into-view'\nimport { navigateSamePageHash, HUB_HEADER_OFFSET_PX } from '../../utils/same-page-hash-nav'\n\nfunction scrollToContent() {\n const article = document.querySelector('article') as HTMLElement | null\n if (article) {\n scrollElementIntoView(article, { headerOffset: HUB_HEADER_OFFSET_PX })\n } else {\n window.scrollTo({ top: 0, behavior: 'smooth' })\n }\n}\n\nexport interface UseDocumentTreeConfig {\n /** API endpoint for fetching the document tree structure */\n structureEndpoint: string\n /** API endpoint for fetching document content */\n contentEndpoint: string\n /** Base route path for URL navigation (e.g., '/knowledge-base', '/data-room') */\n baseRoute: string\n /** Folder-index filename (defaults to 'README.md'). */\n folderIndexFile?: string\n}\n\n/**\n * Generic hook for document tree navigation and content fetching.\n * Drives DocViewer across all doc-source consumers.\n *\n * Client-only: structure + content fetches run in parallel on first mount.\n * No SSR pre-population — the previous SSR path required a Supabase admin\n * client (service role key) and silently fell back to client fetches on\n * envs where the key wasn't set; the parallel client fetches keep behavior\n * uniform across local + prod (latency ~= max(structure, content), not sum).\n */\nexport function useDocumentTree(\n config: UseDocumentTreeConfig,\n initialPath?: string,\n) {\n const { structureEndpoint, contentEndpoint, baseRoute } = config\n const folderIndexFile = config.folderIndexFile ?? DEFAULT_FOLDER_INDEX_FILE\n\n const cleanInitialPath = stripFolderIndexFromPath(\n initialPath?.replace(/\\/$/, '') || '',\n folderIndexFile,\n )\n\n const [structure, setStructure] = useState<DocNode[]>([])\n const [selectedPath, setSelectedPath] = useState<string>(cleanInitialPath)\n const [content, setContent] = useState<DocContent | null>(null)\n const [isLoadingStructure, setIsLoadingStructure] = useState(true)\n const [isLoadingContent, setIsLoadingContent] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [expandedNodes, setExpandedNodes] = useState<Set<string>>(new Set())\n const [isInitialized, setIsInitialized] = useState(false)\n const lastFetchedPath = useRef<string | null>(null)\n\n const normalizedBaseRoute = baseRoute.replace(/\\/$/, '')\n\n const selectedPathRef = useRef(selectedPath)\n selectedPathRef.current = selectedPath\n\n const docNavigation = useDocNavigation()\n\n useEffect(() => {\n const handlePopState = () => {\n const pathname = window.location.pathname\n let pathFromUrl = ''\n\n if (pathname === normalizedBaseRoute || pathname === `${normalizedBaseRoute}/`) {\n pathFromUrl = ''\n } else if (pathname.startsWith(`${normalizedBaseRoute}/`)) {\n pathFromUrl = pathname.substring(`${normalizedBaseRoute}/`.length)\n }\n\n pathFromUrl = stripFolderIndexFromPath(pathFromUrl, folderIndexFile)\n\n if (pathFromUrl !== selectedPathRef.current) {\n setSelectedPath(pathFromUrl)\n if (pathFromUrl) {\n const parentPath = pathFromUrl.includes('/')\n ? pathFromUrl.substring(0, pathFromUrl.lastIndexOf('/'))\n : pathFromUrl\n setExpandedNodes(new Set(getDocAncestorNodeIds(parentPath)))\n }\n setTimeout(() => {\n scrollToContent()\n }, 150)\n }\n }\n\n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }, [normalizedBaseRoute, folderIndexFile])\n\n useEffect(() => {\n if (!isInitialized) {\n // Kick off the speculative content fetch IN PARALLEL with the structure\n // fetch — the two endpoints are independent and most landing pages have\n // a root README (the default folder-index). If the structure ends up\n // pointing at a different path (e.g. knowledge-base falls back to the\n // first-folder README because there's no root README), the content\n // useEffect issues the correct fetch after structure arrives — the\n // speculative result silently no-ops (the content state update gets\n // overwritten by the targeted fetch).\n const speculativeContentPath = cleanInitialPath || folderIndexFile\n lastFetchedPath.current = speculativeContentPath\n fetchContent(speculativeContentPath)\n fetchStructure()\n setIsInitialized(true)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isInitialized])\n\n useEffect(() => {\n if (selectedPath === null || selectedPath === undefined) return\n if (!structure || structure.length === 0) return\n\n let pathToFetch: string | null = null\n\n if (selectedPath === '') {\n pathToFetch = folderIndexFile\n } else {\n const node = findDocNodeByPath(selectedPath, structure)\n\n if (node && node.type === 'folder' && !node.hasReadme) {\n return\n }\n\n pathToFetch = selectedPath\n if (node && node.type === 'folder' && node.hasReadme) {\n pathToFetch = `${selectedPath}/${folderIndexFile}`\n }\n }\n\n if (pathToFetch === lastFetchedPath.current) {\n return\n }\n\n if (pathToFetch) {\n lastFetchedPath.current = pathToFetch\n fetchContent(pathToFetch)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedPath, structure, folderIndexFile])\n\n const fetchStructure = async () => {\n try {\n setIsLoadingStructure(true)\n setError(null)\n\n const response = await fetch(structureEndpoint)\n\n if (!response.ok) {\n throw new Error('Failed to load documentation structure')\n }\n\n const result = await response.json()\n\n if (result.success && result.data) {\n setStructure(result.data)\n\n if (cleanInitialPath) {\n const pathForExpansion = cleanInitialPath.includes('.')\n ? cleanInitialPath.substring(0, cleanInitialPath.lastIndexOf('/'))\n : cleanInitialPath\n if (pathForExpansion) {\n setExpandedNodes(new Set(getDocAncestorNodeIds(pathForExpansion)))\n }\n } else if (result.data.length > 0) {\n const hasRootReadme = result.data.some(\n (node: DocNode) => node.type === 'file' && node.path === folderIndexFile\n )\n\n if (!hasRootReadme) {\n const firstNode = result.data[0]\n if (firstNode.type === 'folder') {\n setExpandedNodes(new Set([firstNode.id]))\n if (firstNode.hasReadme) {\n setSelectedPath(firstNode.path)\n window.history.replaceState({}, '', `${normalizedBaseRoute}/${firstNode.path}`)\n }\n }\n }\n }\n } else {\n setError('Failed to load documentation structure')\n }\n } catch (err) {\n console.error('Error fetching documentation structure:', err)\n setError(err instanceof Error ? err.message : 'Failed to load documentation')\n } finally {\n setIsLoadingStructure(false)\n }\n }\n\n const fetchContent = async (path: string) => {\n try {\n setIsLoadingContent(true)\n // Don't clear `error` here — if a previous fetch set an error and this\n // is a stale/speculative call that gets superseded, the guard below\n // returns early without writing to state. Clearing error here would\n // briefly flicker the user-visible error message.\n\n const response = await fetch(`${contentEndpoint}?path=${encodeURIComponent(path)}`)\n\n // Request-id guard: between awaits, `lastFetchedPath.current` may have\n // been bumped by a newer fetch (the structure-arrives auto-select issues\n // a more-targeted fetch while the speculative one is in flight). Bail\n // BEFORE writing to state — otherwise the late 404 of the speculative\n // fetch overwrites the targeted fetch's good content with null.\n if (path !== lastFetchedPath.current) return\n\n if (!response.ok) {\n if (response.status === 404) {\n const result = await response.json().catch(() => ({}))\n if (path !== lastFetchedPath.current) return\n // Landing-page silent fallback: when the user lands on the source's\n // root URL and there's no root `README.md` (knowledge-base case),\n // the speculative fetch 404s — surface an empty state instead of\n // an error banner. The structure-arrives auto-select will fire\n // a targeted fetch for the first-folder README on the next render.\n if (path === folderIndexFile && selectedPath === '') {\n setError(null)\n setContent(null)\n return\n }\n setError(result.error || 'Documentation file not found')\n setContent(null)\n return\n }\n throw new Error('Failed to load documentation content')\n }\n\n const result = await response.json()\n if (path !== lastFetchedPath.current) return\n setError(null)\n\n if (result.success && result.data) {\n if (result.redirect && result.correctPath !== undefined) {\n const correctedPath = result.correctPath\n setSelectedPath(correctedPath)\n window.history.replaceState({}, '', `${normalizedBaseRoute}/${correctedPath}`)\n setContent(result.data)\n } else {\n setContent(result.data)\n }\n } else {\n setError(result.error || 'Failed to load content')\n setContent(null)\n }\n } catch (err) {\n if (path !== lastFetchedPath.current) return\n console.error('Error fetching documentation content:', err)\n setError(err instanceof Error ? err.message : 'Failed to load content')\n setContent(null)\n } finally {\n // Only clear loading state if THIS fetch is still the active one — a\n // superseded speculative shouldn't flip the spinner off while the\n // targeted fetch is still in flight.\n if (path === lastFetchedPath.current) setIsLoadingContent(false)\n }\n }\n\n const toggleNode = useCallback((nodeId: string) => {\n setExpandedNodes(prev => {\n const next = new Set(prev)\n if (next.has(nodeId)) {\n next.delete(nodeId)\n } else {\n next.add(nodeId)\n }\n return next\n })\n }, [])\n\n // Structural minimum the body uses — `id`, `path`, `type`, `hasReadme`.\n // Widening from `DocNode` lets the navigation components (which carry the\n // narrower `NavigationNode` row shape) pass their own node back without the\n // cross-type `as` cast. Both DocNode and NavigationNode satisfy this.\n const selectNode = useCallback((node: Pick<DocNode, 'id' | 'path' | 'type' | 'hasReadme'>) => {\n if (node.type === 'folder') {\n setExpandedNodes(prev => {\n if (prev.has(node.id)) {\n const ancestorIds = getDocAncestorNodeIds(node.path)\n ancestorIds.pop()\n return new Set(ancestorIds)\n } else {\n return new Set(getDocAncestorNodeIds(node.path))\n }\n })\n\n if (node.hasReadme) {\n lastFetchedPath.current = null\n setSelectedPath(node.path)\n window.history.pushState({}, '', `${normalizedBaseRoute}/${node.path}`)\n setTimeout(() => {\n scrollToContent()\n }, 150)\n } else {\n setSelectedPath(node.path)\n }\n } else {\n const lastSlash = node.path.lastIndexOf('/')\n if (lastSlash > 0) {\n const parentPath = node.path.substring(0, lastSlash)\n setExpandedNodes(new Set(getDocAncestorNodeIds(parentPath)))\n }\n\n lastFetchedPath.current = null\n setSelectedPath(node.path)\n window.history.pushState({}, '', `${normalizedBaseRoute}/${node.path}`)\n setTimeout(() => {\n scrollToContent()\n }, 150)\n }\n }, [normalizedBaseRoute])\n\n const navigateToDoc = useCallback((path: string, options?: { expandFolder?: boolean; fromInternalLink?: boolean }) => {\n const hashIndex = path.indexOf('#')\n const anchor = hashIndex !== -1 ? path.substring(hashIndex) : ''\n const cleanPath = path.replace(/\\/$/, '').split('#')[0]\n\n // Same-doc-different-anchor shortcut. Content is already mounted, so we\n // don't need the 300ms \"wait-for-fetch\" bandaid — the canonical helper\n // owns pushState + synthetic `hashchange` (so any in-doc TOC / accordion\n // bound to the URL hash re-renders) + the anchoring-proof tween in one\n // sync call. `headerOffset: HUB_HEADER_OFFSET_PX` matches the cross-doc path below so\n // anchors land BELOW the docs sticky header on every same-doc internal\n // link click. Cross-doc nav (different cleanPath) falls through to the\n // existing fetch-then-scroll path below.\n //\n // We pass the BARE-hash form to the helper rather than reconstructing\n // a full `${normalizedBaseRoute}/${cleanPath}${anchor}` path: the\n // helper's pathname check compares against `window.location.pathname`,\n // which carries the FOLDER-INDEX-STRIPPED form (`/docs/foo` for\n // `foo/README.md`, `/docs` for the root index). Handing it `cleanPath`\n // — the raw resolved path — produces e.g. `/docs/foo/README.md` and\n // the compare fails → helper returns false → silent dead-click. The\n // bare-hash form sidesteps that entirely: the helper reconstructs\n // `pathname + search + hash` from `window.location`, so the compare\n // is trivially equal. Covers bare `#anchor` links (resolve to\n // `cleanPath=''`) AND folder-index links (`foo/README.md` resolving\n // to the current `/docs/foo`).\n // Bare-hash internal links (`[Click](#section)`) come in as\n // `path === '#section'`, so `cleanPath` becomes `''` and the naive\n // strip-then-compare misses the same-doc shortcut on every NON-root\n // doc (selectedPath is e.g. `'foo/bar'`, not `''`). For that case the\n // current doc IS the same-doc target by definition — short-circuit\n // pathForSelection to the current selection so the shortcut fires.\n const pathForSelection =\n anchor && options?.fromInternalLink && cleanPath === ''\n ? selectedPathRef.current\n : stripFolderIndexFromPath(cleanPath, folderIndexFile)\n if (\n anchor &&\n options?.fromInternalLink &&\n pathForSelection === selectedPathRef.current\n ) {\n navigateSamePageHash(anchor, { headerOffset: HUB_HEADER_OFFSET_PX })\n return\n }\n\n const scrollAfterNav = () => {\n if (anchor) {\n setTimeout(() => {\n const el = document.getElementById(anchor.substring(1))\n if (el) {\n scrollElementIntoView(el, { headerOffset: HUB_HEADER_OFFSET_PX })\n } else {\n scrollToContent()\n }\n }, 300)\n } else {\n setTimeout(() => {\n scrollToContent()\n }, 150)\n }\n }\n\n if (options?.expandFolder) {\n lastFetchedPath.current = null\n setSelectedPath(cleanPath)\n window.history.pushState({}, '', `${normalizedBaseRoute}/${cleanPath}${anchor}`)\n\n const pathParts = cleanPath.split('/')\n const nodeIdsToExpand: string[] = []\n let currentPath = ''\n\n for (const part of pathParts) {\n currentPath = currentPath ? `${currentPath}-${part}` : part\n nodeIdsToExpand.push(currentPath.toLowerCase())\n }\n\n setExpandedNodes(new Set(nodeIdsToExpand))\n\n if (options?.fromInternalLink) {\n scrollAfterNav()\n }\n\n setError(null)\n return\n }\n\n // `pathForSelection` was already computed above (inside the\n // same-doc-anchor shortcut check); reuse it here for cross-doc nav.\n const urlPath = pathForSelection\n\n lastFetchedPath.current = null\n setSelectedPath(pathForSelection)\n window.history.pushState({}, '', `${normalizedBaseRoute}/${urlPath}${anchor}`)\n\n if (options?.fromInternalLink) {\n scrollAfterNav()\n }\n\n const pathParts = cleanPath.split('/')\n const parentIds: string[] = []\n let currentPath = ''\n const partsToProcess = cleanPath.includes('.') ? pathParts.slice(0, -1) : pathParts\n\n for (const part of partsToProcess) {\n currentPath = currentPath ? `${currentPath}-${part}` : part\n parentIds.push(currentPath.toLowerCase())\n }\n\n if (parentIds.length > 0) {\n setExpandedNodes(new Set(parentIds))\n }\n }, [structure, normalizedBaseRoute, folderIndexFile])\n\n useEffect(() => {\n return docNavigation.register({\n baseRoute: normalizedBaseRoute,\n findNodeByPath: (path) => {\n const clean = stripFolderIndexFromPath(path.replace(/\\/$/, '').split('#')[0], folderIndexFile)\n return findDocNodeByPath(clean, structure) ?? null\n },\n selectNode,\n })\n }, [docNavigation, normalizedBaseRoute, structure, selectNode, folderIndexFile])\n\n return {\n structure,\n selectedPath,\n setSelectedPath,\n content,\n isLoadingStructure,\n isLoadingContent,\n error,\n expandedNodes,\n toggleNode,\n selectNode,\n navigateToDoc,\n refetch: fetchStructure,\n setExpandedNodes,\n }\n}\n\nexport type { DocNode, DocContent }\n","\"use client\"\n\nimport React, { createContext, useCallback, useContext, useRef, type ReactNode } from 'react'\nimport type { DocNode } from '../../types/doc-source'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface DocNavigator {\n /** Base route this navigator owns (e.g., '/knowledge-base', '/data-room'). */\n baseRoute: string\n /** Look up a node in the tree by its storage path (e.g., 'openframe-cli/README.md'). */\n findNodeByPath: (path: string) => DocNode | null\n /** Navigate to a node using the same flow as a sidebar menu click. */\n selectNode: (node: DocNode) => void\n}\n\ninterface DocNavigationContextValue {\n /**\n * Register a navigator for a `baseRoute`. Returns a cleanup function that\n * removes the navigator from the map IF the slot still owns it (StrictMode-safe).\n * Duplicate registration for the same baseRoute logs a console.warn and replaces.\n */\n register: (nav: DocNavigator) => () => void\n /**\n * Navigate to a path inside whichever registered navigator owns its baseRoute prefix.\n * Returns true if handled, false otherwise (caller falls back to opening in a new tab).\n */\n navigate: (path: string) => boolean\n /** Whether ANY navigator is currently mounted. */\n isAvailable: () => boolean\n}\n\n// =============================================================================\n// Context\n// =============================================================================\n\nconst DocNavigationContext = createContext<DocNavigationContextValue | null>(null)\n\n/**\n * Bridges `useDocumentTree` instances (deep in the page tree) to `GlobalAskAI`\n * (high up near the root) without global events or URL parsing.\n *\n * Multi-navigator design: a `Map<baseRoute, DocNavigator>` lets multiple\n * viewers coexist (nested viewers, dual-pane scenarios). Today only one\n * viewer mounts at a time, so behavior is unchanged.\n */\nexport function DocNavigationProvider({ children }: { children: ReactNode }) {\n const navMap = useRef<Map<string, DocNavigator>>(new Map())\n\n const register = useCallback((nav: DocNavigator) => {\n const existing = navMap.current.get(nav.baseRoute)\n if (existing && existing !== nav) {\n // eslint-disable-next-line no-console\n console.warn(\n `[DocNavigationContext] duplicate registration for ${nav.baseRoute}; replacing`,\n )\n }\n navMap.current.set(nav.baseRoute, nav)\n return () => {\n // StrictMode-safe identity check: only delete if THIS nav still owns the slot.\n if (navMap.current.get(nav.baseRoute) === nav) {\n navMap.current.delete(nav.baseRoute)\n }\n }\n }, [])\n\n const findOwningNavigator = useCallback((path: string): DocNavigator | null => {\n // First try: a navigator whose baseRoute is a prefix of `path`. Walks the\n // longest match first so nested routes win over a parent.\n const baseRoutes = Array.from(navMap.current.keys()).sort((a, b) => b.length - a.length)\n for (const baseRoute of baseRoutes) {\n if (path === baseRoute || path.startsWith(`${baseRoute}/`)) {\n return navMap.current.get(baseRoute) ?? null\n }\n }\n // Fallback: a single navigator handles any path (existing behavior — chat\n // chips emit storage paths, not absolute URLs, so prefix matching misses).\n if (navMap.current.size === 1) {\n return navMap.current.values().next().value ?? null\n }\n return null\n }, [])\n\n const navigate = useCallback(\n (path: string): boolean => {\n const nav = findOwningNavigator(path)\n if (!nav) return false\n const node = nav.findNodeByPath(path)\n if (!node) return false\n nav.selectNode(node)\n return true\n },\n [findOwningNavigator],\n )\n\n const isAvailable = useCallback(() => navMap.current.size > 0, [])\n\n const value = React.useMemo(\n () => ({ register, navigate, isAvailable }),\n [register, navigate, isAvailable],\n )\n\n return <DocNavigationContext.Provider value={value}>{children}</DocNavigationContext.Provider>\n}\n\n// =============================================================================\n// Hooks\n// =============================================================================\n\nexport function useDocNavigation(): DocNavigationContextValue {\n const ctx = useContext(DocNavigationContext)\n if (!ctx) {\n // Safe no-op fallback when used outside the provider.\n return {\n register: () => () => {},\n navigate: () => false,\n isAvailable: () => false,\n }\n }\n return ctx\n}\n","\"use client\"\n\nimport { useState, useRef, useCallback, useEffect, useMemo } from \"react\"\nimport { scrollElementIntoView } from \"../../utils/scroll-into-view\"\n\n// Single source for the sticky-chrome height. Used for BOTH the scroll target\n// offset (where a clicked section lands) AND the active-section detection\n// threshold (where the scroll listener flips highlight). They must match —\n// previously 100 vs 150 caused a 50px window where the indicator jumped to\n// the next section even though that section's top was still below the\n// clicked one's resting offset.\nconst SCROLL_OFFSET = 100\n\ninterface ScrollSpySection {\n id: string\n title?: string\n level?: number\n}\n\ninterface UseScrollSpyReturn {\n activeSection: string\n handleSectionClick: (sectionId: string) => void\n}\n\n/**\n * Shared scroll spy hook for tracking active section based on scroll position.\n * Used by DocViewer for sticky section navigation.\n */\nexport function useScrollSpy(sections: ScrollSpySection[] | undefined): UseScrollSpyReturn {\n const [activeSection, setActiveSection] = useState(\"\")\n const isScrollingFromClick = useRef(false)\n\n const sectionIds = useMemo(\n () => sections?.map(s => s.id).join(',') ?? '',\n [sections]\n )\n const stableSections = useRef(sections)\n if (sectionIds !== (stableSections.current?.map(s => s.id).join(',') ?? '')) {\n stableSections.current = sections\n }\n\n const handleSectionClick = useCallback((sectionId: string) => {\n const targetElement = document.getElementById(sectionId)\n if (!targetElement) return\n\n isScrollingFromClick.current = true\n setActiveSection(sectionId)\n\n scrollElementIntoView(targetElement, { headerOffset: SCROLL_OFFSET })\n\n setTimeout(() => {\n isScrollingFromClick.current = false\n }, 800)\n }, [])\n\n useEffect(() => {\n const currentSections = stableSections.current\n if (!currentSections || currentSections.length === 0) return\n\n const handleScroll = () => {\n if (isScrollingFromClick.current) return\n\n const scrollPosition = window.scrollY + SCROLL_OFFSET\n let currentSection = currentSections[0]?.id || \"\"\n\n for (let i = currentSections.length - 1; i >= 0; i--) {\n const element = document.getElementById(currentSections[i].id)\n if (element && scrollPosition >= element.offsetTop) {\n currentSection = currentSections[i].id\n break\n }\n }\n\n setActiveSection((prev) => (prev !== currentSection ? currentSection : prev))\n }\n\n let scrollTimer: ReturnType<typeof setTimeout>\n const throttledScroll = () => {\n clearTimeout(scrollTimer)\n scrollTimer = setTimeout(handleScroll, 100)\n }\n\n window.addEventListener(\"scroll\", throttledScroll)\n handleScroll()\n\n return () => {\n window.removeEventListener(\"scroll\", throttledScroll)\n clearTimeout(scrollTimer)\n }\n }, [sectionIds])\n\n return { activeSection, handleSectionClick }\n}\n"],"mappings":"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","names":["useMemo","jsx","jsx","jsxs","Fragment","jsx","jsxs","jsxs","jsx","Fragment","useCallback","useRef","jsx","useRef","result","useCallback","pathParts","currentPath","useState","useRef","useCallback","useEffect","useState","useRef","useCallback","useEffect","Fragment","jsx","jsxs","useMemo"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-ETACGX2A.cjs","../src/components/unified-pagination.tsx","../src/components/empty-state.tsx","../src/components/shared/dev-section/dev-section-view.tsx","../src/components/shared/dev-section/dev-section-page.tsx","../src/components/shared/dev-section/dev-card-row.tsx","../src/components/shared/delivery/delivery-row.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-ETACGX2A.cjs","sourcesContent":[null,"\"use client\"\n\nimport { useRouter, useSearchParams, usePathname } from \"../embed-shims/next-navigation\"\nimport { Pagination } from \"./pagination\"\n\ninterface UnifiedPaginationProps {\n currentPage: number\n totalPages: number\n onPageChange?: (page: number) => void\n className?: string\n}\n\nexport function UnifiedPagination({ \n currentPage, \n totalPages, \n onPageChange,\n className = \"mt-8 flex justify-center w-full\"\n}: UnifiedPaginationProps) {\n const router = useRouter()\n const searchParams = useSearchParams()\n const pathname = usePathname()\n\n const handlePageChange = (page: number) => {\n // Preserve current scroll position\n const currentScrollY = window.scrollY\n \n // Call the callback to update local state (prevents reload)\n if (onPageChange) {\n onPageChange(page)\n }\n \n // Update URL for bookmarking without navigation\n const params = new URLSearchParams(searchParams.toString())\n params.set(\"page\", page.toString())\n \n // Update URL without navigation (for bookmarking support)\n const newUrl = `${pathname}?${params.toString()}`\n window.history.replaceState(null, '', newUrl)\n \n // Restore scroll position after a brief delay to allow content to render\n setTimeout(() => {\n window.scrollTo({\n top: currentScrollY,\n behavior: 'instant' // Instant to prevent any scroll animation\n })\n }, 0)\n }\n\n // Don't render pagination if there's only one page\n if (totalPages <= 1) return null\n\n return (\n <div className={className}>\n <Pagination\n currentPage={currentPage}\n totalPages={totalPages}\n onPageChange={handlePageChange}\n />\n </div>\n )\n} ","\"use client\";\n\nimport { Search, FileText, Package } from \"lucide-react\"\nimport { Button } from \"./ui/button\"\nimport { useRouter } from \"../embed-shims/next-navigation\"\n\nexport interface EmptyStateProps {\n type: 'vendors' | 'posts' | 'search' | 'generic'\n title?: string\n description?: string\n showBackButton?: boolean\n onGoBack?: () => void\n backButtonText?: string\n // New CTA properties\n showCTA?: boolean\n ctaText?: string\n onCtaClick?: () => void\n ctaVariant?: 'primary' | 'secondary'\n}\n\nexport function EmptyState({\n type,\n title,\n description,\n showBackButton = false,\n onGoBack,\n backButtonText = \"Go Back\",\n showCTA = true,\n ctaText,\n onCtaClick,\n ctaVariant = 'primary'\n}: EmptyStateProps) {\n const router = useRouter()\n\n // Default content based on type\n const getDefaultContent = () => {\n switch (type) {\n case 'vendors':\n return {\n icon: <Package className=\"w-full h-full\" />,\n title: \"No vendors found\",\n description: \"We couldn't find any vendors matching your criteria. Try adjusting your filters or search terms.\"\n }\n case 'posts':\n return {\n icon: <FileText className=\"w-full h-full\" />,\n title: \"No articles found\",\n description: \"We couldn't find any articles matching your criteria. Try different categories, tags, or search terms.\"\n }\n case 'search':\n return {\n icon: <Search className=\"w-full h-full\" />,\n title: \"No results found\",\n description: \"Your search didn't return any results. Try different keywords or browse our categories.\"\n }\n default:\n return {\n icon: <Search className=\"w-full h-full\" />,\n title: \"Nothing found\",\n description: \"We couldn't find what you're looking for. Try adjusting your search or filters.\"\n }\n }\n }\n\n // Smart CTA logic based on context\n const getSmartCTA = () => {\n // If custom CTA is provided, use it\n if (ctaText && onCtaClick) {\n return {\n text: ctaText,\n action: onCtaClick\n }\n }\n\n // Check if we're on the client side\n const isClient = typeof window !== 'undefined'\n const currentPath = isClient ? window.location.pathname : ''\n\n // Smart defaults based on type and context\n switch (type) {\n case 'search':\n return {\n text: \"Reset Filters\",\n action: () => {\n if (isClient) {\n // Try to reset search by clearing URL params and refreshing\n const url = new URL(window.location.href)\n url.search = ''\n router.push(url.pathname)\n }\n }\n }\n case 'posts':\n // If we're on blog/community pages, reset blog filters\n if (currentPath.includes('/blog')) {\n return {\n text: \"Reset Filters\",\n action: () => {\n if (isClient) {\n // Reset blog search and filters by clearing URL params\n const url = new URL(window.location.href)\n url.search = ''\n router.push(url.pathname)\n }\n }\n }\n } else if (currentPath.includes('/profile')) {\n return {\n text: \"Browse Vendors\",\n action: () => router.push('/vendors')\n }\n }\n return {\n text: \"View All Posts\",\n action: () => router.push('/blog')\n }\n case 'vendors':\n // If we're in profile or other pages, direct to main content\n if (currentPath.includes('/profile')) {\n return {\n text: \"Browse Vendors\",\n action: () => router.push('/vendors')\n }\n } else if (currentPath.includes('/vendors') || currentPath.includes('/margin-increase/compare')) {\n return {\n text: \"Reset Filters\",\n action: () => {\n if (isClient) {\n // Reset vendor search and filters by clearing URL params\n const url = new URL(window.location.href)\n url.search = ''\n router.push(url.pathname)\n }\n }\n }\n }\n return {\n text: \"Browse Vendors\",\n action: () => router.push('/vendors')\n }\n default:\n return {\n text: \"Browse Vendors\",\n action: () => router.push('/vendors')\n }\n }\n }\n\n const defaultContent = getDefaultContent()\n const displayTitle = title || defaultContent.title\n const displayDescription = description || defaultContent.description\n const smartCTA = getSmartCTA()\n\n return (\n <div className=\"flex flex-col items-center justify-center py-6 md:py-16 px-6 text-center\">\n {/* Icon */}\n <div className=\"mb-3 md:mb-6 flex items-center justify-center\">\n <div className=\"rounded-full bg-ods-card p-3 md:p-6 border border-ods-border\">\n <div className=\"w-8 h-8 md:w-16 md:h-16 text-ods-text-secondary flex items-center justify-center\">\n {defaultContent.icon}\n </div>\n </div>\n </div>\n\n {/* Title */}\n <h2 className=\"mb-2 md:mb-3 text-lg md:text-xl font-semibold font-['DM_Sans'] text-ods-text-primary tracking-[-0.02em]\">\n {displayTitle}\n </h2>\n\n {/* Description */}\n <p className=\"mb-4 md:mb-8 max-w-md text-sm font-medium font-['DM_Sans'] text-ods-text-secondary leading-[1.43em]\">\n {displayDescription}\n </p>\n\n {/* Smart CTA Button */}\n {showCTA && smartCTA && (\n <div className=\"w-full max-w-xs mb-3\">\n <Button\n onClick={smartCTA.action}\n className={ctaVariant === 'primary'\n ? \"w-full bg-ods-accent text-ods-text-on-accent hover:bg-ods-accent-hover transition-all duration-150 font-['DM_Sans'] font-medium\"\n : \"w-full bg-transparent border border-ods-border text-ods-text-primary hover:border-ods-accent hover:text-ods-accent transition-all duration-150 font-['DM_Sans'] font-medium\"\n }\n >\n {smartCTA.text}\n </Button>\n </div>\n )}\n\n {/* Optional Back Button */}\n {showBackButton && onGoBack && (\n <div className=\"w-full max-w-xs\">\n <Button\n onClick={onGoBack}\n variant=\"outline\"\n className=\"w-full transition-all duration-150 font-['DM_Sans'] font-medium\"\n >\n {backButtonText}\n </Button>\n </div>\n )}\n </div>\n )\n} ","'use client';\n\n/**\n * DevSectionView — the canonical chrome for ANY dev-center section\n * (Roadmap / Delivery / Releases). One component, used in BOTH:\n *\n * - tabbed `/roadmap-and-releases` (compact title mode, no `hero`)\n * - full-page `/roadmap`, `/bug-fixes-and-enhancements`, `/releases`\n * (hero mode with icon + description + back link)\n *\n * Owns: title rendering, the inline search input, the filter pill row,\n * and the URL-param wiring that connects both. The list `children`\n * receive a clean URL contract — they read `?<paramKey>=...` via\n * `useSearchParams()` and refetch on change. No duplicated controls.\n */\n\nimport type { ReactNode } from 'react';\nimport { useState, useEffect } from 'react';\nimport { useRouter, useSearchParams, usePathname } from '../../../embed-shims';\nimport { SearchInput } from '../../ui';\nimport { StatusFilterComponent } from '../../features';\nimport {\n OPENFRAME_DEV_SECTIONS,\n type OpenframeDevSectionKey,\n} from '../../../utils/dev-sections/openframe-dev-sections';\n\nexport interface DevSectionViewProps {\n /** Which section to render — drives title, search, and filter\n * config via the `OPENFRAME_DEV_SECTIONS` registry. */\n sectionKey: OpenframeDevSectionKey;\n /** When set, renders the rich page-level hero (icon + h1 + description).\n * Omit for the compact tab-context heading. */\n hero?: {\n /** Pre-rendered icon JSX. Server components render the icon themselves\n * and pass the element here — function references can't cross the\n * server→client boundary, but React elements can. */\n icon: ReactNode;\n /** Hero title. Falls back to `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`\n * when omitted, so embedders can override the (OpenFrame-specific) default\n * copy without forking the registry. */\n title?: string;\n description: string;\n };\n /** Optional slot rendered BETWEEN the hero and the search/filter\n * controls. Use this for an entry-action surface that should sit\n * above the list (e.g. the Help Center's \"Open a new ticket\" form).\n * The slot is wrapped in the same `gap-10` flex column so spacing\n * matches the surrounding chrome — callers should NOT add their\n * own top/bottom margin. Renders `null` (no DOM) when omitted. */\n preControls?: ReactNode;\n /** The page-specific list body. Reads URL params written by this\n * component (search input + filter pills). */\n children: ReactNode;\n}\n\nexport function DevSectionView({ sectionKey, hero, preControls, children }: DevSectionViewProps) {\n const section = OPENFRAME_DEV_SECTIONS[sectionKey];\n const router = useRouter();\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n const search = section.search;\n const filter = section.filter;\n\n const currentSearch = search ? searchParams.get(search.paramKey) || '' : '';\n const currentFilterValue = filter\n ? searchParams.get(filter.paramKey) || filter.defaultValue\n : '';\n\n // Controlled search-input state — input commits to the URL only on\n // Enter (not on every keystroke), preserving the legacy behavior.\n // Lazy init from URL avoids a brief flash of stale value on first\n // paint after URL-driven re-render (e.g. tab switch).\n const [searchValue, setSearchValue] = useState(() => currentSearch);\n useEffect(() => {\n setSearchValue(currentSearch);\n }, [currentSearch]);\n\n const handleSearchSubmit = (value: string) => {\n if (!search) return;\n const params = new URLSearchParams(searchParams.toString());\n if (value.trim()) params.set(search.paramKey, value.trim());\n else params.delete(search.paramKey);\n router.replace(`${pathname}?${params.toString()}`, { scroll: false });\n };\n\n const handleFilterChange = (value: string) => {\n if (!filter) return;\n const params = new URLSearchParams(searchParams.toString());\n if (value === filter.defaultValue) params.delete(filter.paramKey);\n else params.set(filter.paramKey, value);\n router.replace(`${pathname}?${params.toString()}`, { scroll: false });\n };\n\n return (\n <div className=\"w-full flex flex-col gap-10\">\n {hero ? (\n <div className=\"space-y-4\">\n <h1 className=\"text-h1 tracking-[-1.12px] text-ods-text-primary flex items-center gap-3\">\n {hero.icon}\n {hero.title ?? section.hero.title}\n </h1>\n <p className=\"font-['DM_Sans'] font-medium text-[18px] leading-[28px] text-ods-text-secondary max-w-3xl\">\n {hero.description}\n </p>\n </div>\n ) : (\n <div className=\"flex items-center justify-between w-full\">\n <h2 className=\"font-['Azeret_Mono'] font-semibold text-[32px] md:text-[40px] lg:text-[48px] leading-[40px] md:leading-[48px] lg:leading-[56px] text-ods-text-primary tracking-[-0.64px] md:tracking-[-0.8px] lg:tracking-[-0.96px]\">\n {section.hero.title}\n <span className=\"text-ods-accent\">:</span>\n </h2>\n </div>\n )}\n\n {preControls}\n\n {(search || filter) && (\n <div className=\"space-y-4\">\n {search && (\n <SearchInput\n showDropdown={false}\n placeholder={search.placeholder}\n value={searchValue}\n onChange={setSearchValue}\n onSubmit={handleSearchSubmit}\n />\n )}\n {filter && (\n <StatusFilterComponent\n selectedStatus={currentFilterValue}\n onStatusChange={handleFilterChange}\n statusOptions={[...filter.options]}\n />\n )}\n </div>\n )}\n\n {children}\n </div>\n );\n}\n","'use client';\n\n/**\n * DevSectionPage — full-page wrapper for a dev-center section\n * (`/roadmap`, `/bug-fixes-and-enhancements`, `/releases`).\n *\n * Mounts the lib's canonical `PageLayout` directly (no in-app wrapper)\n * so the back-button affordance stays in lockstep with whatever the\n * design system ships — any future lib change to BackButton / TitleBlock\n * propagates automatically.\n *\n * Composition: `PageShell` → `PageLayout` (back-to-home wired) →\n * `DevSectionView` (icon hero + search + filter pills) → list body.\n *\n * Adding a new section is one entry in `OPENFRAME_DEV_SECTIONS` plus a\n * single-line page file mounting this factory with the new key.\n */\n\nimport type { ReactNode } from 'react';\nimport { useRouter } from '../../../embed-shims/next-navigation';\nimport { PageShell, PageLayout } from '../../ui';\nimport { DevSectionView } from './dev-section-view';\nimport {\n OPENFRAME_DEV_SECTIONS,\n type OpenframeDevSectionKey,\n} from '../../../utils/dev-sections/openframe-dev-sections';\n\nconst SECTION_HERO_ICON_CLASS = 'h-10 w-10 text-ods-accent';\n\nexport interface DevSectionPageProps {\n sectionKey: OpenframeDevSectionKey;\n /** The page-specific list body (e.g. `<RoadmapList />`). */\n children: ReactNode;\n /** Optional slot rendered BETWEEN the hero and search/filter — see\n * `DevSectionView.preControls`. Used by surfaces that want an entry\n * action (e.g. Help Center's \"Open a new ticket\" form) above the\n * controls instead of below them. */\n preControls?: ReactNode;\n /** Back-button config — same shape as `LegalDocumentPage` /\n * `ReleaseDetailPage`. Pass `false` to hide entirely. Default\n * `{ label: 'Back to home', href: '/' }` — embedders whose \"home\" isn't `/`\n * should override `href`, or pass `false` if the embed has no home page. */\n backButton?: { label?: string; href?: string } | false;\n /** Override the hero title. Defaults to the (OpenFrame-specific) copy in\n * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`. Set this to brand the\n * section for a non-OpenFrame embed. */\n title?: string;\n /** Override the hero subtitle/description. Defaults to\n * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.description`. */\n subtitle?: string;\n}\n\nexport function DevSectionPage({\n sectionKey,\n children,\n preControls,\n backButton,\n title,\n subtitle,\n}: DevSectionPageProps) {\n const router = useRouter();\n const section = OPENFRAME_DEV_SECTIONS[sectionKey];\n const Icon = section.icon;\n\n // Back-button config — mirrors LegalDocumentPage / ReleaseDetailPage.\n // Default: { label: 'Back to home', href: '/' }. Pass `false` to hide.\n // After `backButton &&` narrowing, inner type is `{ label?, href? } |\n // undefined`; don't re-compare to `false` (TS2367).\n const backCfg =\n backButton === false\n ? undefined\n : {\n label: (backButton ? backButton.label : undefined) ?? 'Back to home',\n onClick: () => router.push((backButton ? backButton.href : undefined) ?? '/'),\n };\n\n return (\n <PageShell>\n <PageLayout backButton={backCfg}>\n <DevSectionView\n sectionKey={sectionKey}\n hero={{\n icon: <Icon className={SECTION_HERO_ICON_CLASS} />,\n title,\n description: subtitle ?? section.hero.description,\n }}\n preControls={preControls}\n >\n {children}\n </DevSectionView>\n </PageLayout>\n </PageShell>\n );\n}\n","'use client';\n\n/**\n * Shared row chrome for any `DevSectionPage` list (delivery, tickets,\n * future sections). One source of truth for the layout that every\n * dev-section card row uses:\n * left column → title (h3) / subtitle (h5 uppercase) / description\n * (h4 line-clamp-3), each in a fixed min-height block\n * so rows align across the grid\n * right column → caller-supplied stacked badges\n *\n * Surface stays small on purpose — `rightBadges` is a `ReactNode` so\n * the caller decides how many badges (delivery: 2, tickets: 1-2,\n * future: anything). No behavior baked in: the caller wraps the row\n * in a `<div>` (static, like delivery) or `<button>` (clickable, like\n * tickets) and renders the row content via this component.\n *\n * Pair with `DevCardRowSkeletonList` for the loading state — the\n * skeleton mirrors the same min-heights so the in-flight UI doesn't\n * shift the layout when real data lands.\n *\n * NOTE: the ticket conversation row is NOT here — it renders the shared\n * `<ChatMessageRow>` (`components/chat/chat-message-row.tsx`), the SAME\n * component the OpenMSP Slack-community feed uses, so the two surfaces stay\n * pixel-identical by construction.\n */\n\nimport type { ReactNode } from 'react';\n\nexport interface DevCardRowContentProps {\n title: string;\n /** Single-line uppercase metadata (e.g. \"UPDATED today, #4271, Code review\"). */\n subtitle: string;\n /** 3-line description block. Empty string renders the fallback. */\n description: string;\n /** Fallback copy when `description` is empty. Defaults to a generic\n * string; ticket / delivery surfaces override. */\n emptyDescription?: string;\n /** Right column — caller renders its own stacked badges. */\n rightBadges: ReactNode;\n}\n\nexport function DevCardRowContent({\n title,\n subtitle,\n description,\n emptyDescription = 'No description provided',\n rightBadges,\n}: DevCardRowContentProps) {\n return (\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n <div className=\"min-h-[24px] flex items-center\">\n <h3 className=\"text-h3 text-ods-text-primary tracking-[-0.36px] flex-1 line-clamp-2 md:truncate break-words\">\n {title}\n </h3>\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <p className=\"text-h5 text-ods-text-secondary uppercase tracking-[-0.28px] truncate\">\n {subtitle}\n </p>\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <p className=\"text-h4 text-ods-text-secondary line-clamp-3 break-words\">\n {description || emptyDescription}\n </p>\n </div>\n </div>\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n {rightBadges}\n </div>\n </div>\n );\n}\n\n/**\n * Skeleton rendering for a single row — the bars mirror the same\n * min-heights as `DevCardRowContent` so the loading→loaded swap\n * doesn't reflow.\n */\nexport function DevCardRowSkeleton() {\n return (\n <div className=\"border-b border-ods-border last:border-b-0 p-[12px] md:p-[16px]\">\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n <div className=\"min-h-[24px] flex items-center\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-1/2\" />\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <div className=\"flex-1 space-y-1\">\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-full\" />\n <div className=\"h-[20px] bg-ods-border rounded animate-pulse w-2/3\" />\n </div>\n </div>\n </div>\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n <div className=\"h-[32px] w-[100px] bg-ods-border rounded animate-pulse\" />\n <div className=\"h-[32px] w-[120px] bg-ods-border rounded animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * The standard \"5 skeleton rows inside a bordered card\" loading state\n * used by every list shell. Both delivery (`delivery-table.tsx`) and\n * tickets (`tickets-list.tsx`) mount this directly.\n */\nexport function DevCardRowSkeletonList({ rows = 5 }: { rows?: number }) {\n return (\n <div className=\"bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full\">\n {Array.from({ length: rows }, (_, i) => (\n <DevCardRowSkeleton key={i} />\n ))}\n </div>\n );\n}\n","'use client'\n\n/**\n * `<DeliveryRow />` — canonical single-row presentation for a ClickUp\n * delivery item.\n *\n * Single source of truth: both the `/bug-fixes-and-enhancements` page\n * (via `DeliveryTable`) AND the linked-delivery card on a HubSpot ticket\n * (via `TicketLinkedDeliveryCard`) compose this primitive. Visual parity\n * across those two surfaces is the design goal — the user reads the\n * card on their ticket and recognises it as a row from the public\n * delivery list.\n *\n * Behaviors:\n * - `href` set → outer element is an `<a>`, the whole row becomes\n * clickable (used by the linked-card surface to deep-link into\n * `/bug-fixes-and-enhancements?focus=<id>`).\n * - `id` set → outer element gets that DOM id so the consuming page\n * can `scrollIntoView` to it when the URL carries `?focus=<id>`.\n * - `highlighted` true → brief accent border + background pulse\n * (`animate-flash-focus` keyframe defined in `tailwind.config.ts`).\n * - `caption` set → small uppercase label rendered above the title\n * (\"LINKED DELIVERY\" on the ticket-side variant). Omitted on the\n * standard list rendering.\n */\n\nimport * as React from 'react'\nimport Link from '../../../embed-shims/next-link'\nimport { StatusBadge } from '../../ui/status-badge'\nimport { getStatusColorScheme } from '../../../utils'\nimport {\n type DeliveryItem,\n TASK_TYPE_LABELS,\n TASK_TYPE_TEXT_COLORS,\n} from '../../../types/delivery'\nimport { cn } from '../../../utils/cn'\n\n/** Same heuristic as DeliveryTable's local helper. Inlined so the row\n * primitive owns its complete rendering contract. */\nfunction getRelativeTime(timestamp: number): string {\n const now = Date.now()\n const diff = now - timestamp\n const days = Math.floor(diff / (1000 * 60 * 60 * 24))\n const weeks = Math.floor(days / 7)\n const months = Math.floor(days / 30)\n if (months > 0) return months === 1 ? 'last month' : `${months} months ago`\n if (weeks > 0) return weeks === 1 ? 'last week' : `${weeks} weeks ago`\n if (days > 0) return days === 1 ? 'yesterday' : `${days} days ago`\n return 'today'\n}\n\nexport interface DeliveryRowProps {\n item: DeliveryItem\n /** When set, the row becomes a clickable anchor. The ticket-side\n * linked-card composes this from `buildDevSectionUrl('delivery', id)`\n * which carries `?search=<id>` — the delivery list filters to that\n * exact task on landing (canonical deep-link mechanism, same one\n * the chat-inline delivery card uses). */\n href?: string\n /** Small uppercase caption rendered above the title. Used by the\n * linked-delivery card variant (\"LINKED DELIVERY\"). */\n caption?: string\n /** DOM `id` applied to the row's outer element. `DeliveryTable`\n * always sets `delivery-<external_id>` so chat-card deep-links\n * (`?search=<id>#delivery-<id>`) and the ticket linked-card path\n * both have a target for `useScrollToHash` to scroll to. Always\n * paired with `scroll-mt-24` on the outer element so the row lands\n * BELOW the sticky chrome after the scroll. */\n id?: string\n className?: string\n}\n\nexport function DeliveryRow({\n item,\n href,\n caption,\n id,\n className,\n}: DeliveryRowProps) {\n const taskType = item.taskType as keyof typeof TASK_TYPE_LABELS\n const typeBadgeLabel = TASK_TYPE_LABELS[taskType] || 'TASK'\n const typeBadgeTextColor = TASK_TYPE_TEXT_COLORS[taskType] || ''\n const statusBadgeScheme = getStatusColorScheme(item.status)\n const relativeTime = getRelativeTime(item.dateUpdated)\n const subtitle = `ACTIVE ${relativeTime}${item.listNames.length > 0 ? `, ${item.listNames.join(', ')}` : ''}, ${item.id}`\n\n const inner = (\n <div className=\"flex flex-col md:flex-row items-start justify-between gap-[12px] md:gap-[16px] w-full\">\n {/* Left: caption (optional) + title + subtitle + description */}\n <div className=\"flex-1 min-w-0 w-full md:w-auto flex flex-col gap-[12px] md:gap-[16px]\">\n {caption && (\n <p className=\"text-xs font-medium uppercase tracking-wider text-ods-text-secondary\">\n {caption}\n </p>\n )}\n <div className=\"min-h-[24px] md:min-h-[24px] flex items-center\">\n <h3 className=\"text-h3 text-ods-text-primary tracking-[-0.36px] flex-1 line-clamp-2 md:truncate break-words\">\n {item.title}\n </h3>\n </div>\n <div className=\"min-h-[20px] flex items-center\">\n <p className=\"text-h5 text-ods-text-secondary uppercase tracking-[-0.28px] truncate\">\n {subtitle}\n </p>\n </div>\n <div className=\"min-h-[72px] flex items-center\">\n <p className=\"text-h4 text-ods-text-secondary line-clamp-3 break-words\">\n {item.description || 'No description provided'}\n </p>\n </div>\n </div>\n\n {/* Right: status + task-type badges */}\n <div className=\"flex-shrink-0 self-start flex flex-col gap-2\">\n <StatusBadge\n text={item.status.toUpperCase()}\n colorScheme={statusBadgeScheme}\n variant=\"card\"\n className=\"border border-ods-border\"\n />\n <StatusBadge\n text={typeBadgeLabel}\n variant=\"card\"\n className={`border border-ods-border ${typeBadgeTextColor}`}\n />\n </div>\n </div>\n )\n\n const baseClass = cn(\n 'block p-[12px] md:p-[16px] no-underline text-inherit transition-colors duration-150',\n // `scroll-mt-24` is paid for whether `id` is set or not (it's a\n // single Tailwind utility, no runtime cost). Keeping it\n // unconditional means a future caller adding `id` doesn't also\n // have to remember to ask for the offset.\n 'scroll-mt-24',\n href && 'hover:bg-ods-bg-hover cursor-pointer',\n className,\n )\n\n if (href) {\n // `Link` is the env-aware embed-shim — delegates to `next/link` on\n // a Next.js host (soft RSC nav, back-button restores the previous\n // page's React state intact), falls back to a plain `<a>` on\n // non-Next embedders. A raw `<a href>` was hard-navigating +\n // losing TanStack-Query state on back, leaving /tickets stuck on\n // its skeleton.\n return (\n <Link href={href} id={id} className={baseClass} prefetch={false}>\n {inner}\n </Link>\n )\n }\n\n return <div id={id} className={baseClass}>{inner}</div>\n}\n"]}