@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
@@ -0,0 +1,110 @@
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import { useRouter } from '../../embed-shims/next-navigation'
5
+ import { PageShell } from './article-detail-layout'
6
+ import { PageLayout } from './page-layout'
7
+ import { PageHeader, type PageHeaderProps } from './page-header'
8
+
9
+ /**
10
+ * `<PageWithHeader>` — the canonical 4-layer unified-chrome wrapper that
11
+ * every public lib/hub page now uses (Knowledge Hub, Roadmap, FAQs,
12
+ * Authors, Blog, Vendors, Case Studies, Interviews, Investor Updates,
13
+ * /webinars-/podcasts-/events, hub admin-but-public pages, …).
14
+ *
15
+ * Renders the same JSX tree every consumer was hand-rolling individually
16
+ * before this helper landed:
17
+ *
18
+ * PageShell ← bg-ods-bg, min-h-screen, max-w-[1920px],
19
+ * page-shell-px/pt/pb gutters
20
+ * PageLayout backButton ← TitleBlock → PageHeader #1 (back-btn row
21
+ * only, pt-l + mb-l)
22
+ * div.w-full.flex-col.gap-10 ← matches DevSectionView's hero container
23
+ * PageHeader title/subtitle ← PageHeader #2 (noTopPadding +
24
+ * titleIcon accentDot noBottomMargin so PageLayout's gap-l owns
25
+ * noTopPadding the spacing between #1 and #2)
26
+ * noBottomMargin
27
+ * children ← page-specific body (search, lists, forms)
28
+ *
29
+ * Why this exists: 15+ pages copy-pasted the EXACT same nesting + the
30
+ * EXACT same `noTopPadding + noBottomMargin + accentDot` triplet on the
31
+ * inner `<PageHeader>`. Forgetting one of those flags collapsed the gap
32
+ * to ~8px (the FAQs-vs-Onboarding-Guides bug). Centralizing the chain
33
+ * here makes the spacing invariant compiler-enforced — consumers can't
34
+ * pick the wrong combination.
35
+ *
36
+ * **Back button is config-driven by default.** Consumers don't have to
37
+ * thread `useRouter()` + `() => router.push('/')` themselves anymore —
38
+ * the helper supplies a sane default (`{ label: 'Back to home', href: '/' }`).
39
+ * Hosts whose `/` lives elsewhere should pass their own `href`. Pass
40
+ * `backButton={false}` to suppress entirely (the lib's own
41
+ * `<DocsHubPage>` does this on the platform-home docs landing).
42
+ */
43
+ export interface PageWithHeaderProps {
44
+ /** Title — passed straight to inner `<PageHeader>`. */
45
+ title?: string
46
+ /** Inline icon rendered before the title (lucide / SVG node). Wrap with
47
+ * `SECTION_HERO_ICON_CLASS` at the call site, or use the lib's
48
+ * `<PageHeader>`'s `titleIcon` ReactNode signature directly. */
49
+ titleIcon?: PageHeaderProps['titleIcon']
50
+ /** Subtitle (1-2 lines, auto-clamped to 2 + min-h-[56px]). */
51
+ subtitle?: string
52
+ /** Render yellow accent dot after the title (default true — matches every
53
+ * unified surface). Pass `false` to opt out. */
54
+ accentDot?: boolean
55
+ /** Back-button config. Defaults to `{ label: 'Back to home', href: '/' }`.
56
+ * Pass `false` to hide entirely (platform-home docs landings). */
57
+ backButton?: { label?: string; href?: string } | false
58
+ /** Optional image (entity-image-style — onboarding guides etc.). */
59
+ image?: PageHeaderProps['image']
60
+ /** Optional actions slot (right side of header). */
61
+ actions?: React.ReactNode
62
+ /** Page body — search bars, lists, forms, sections. Rendered INSIDE the
63
+ * gap-10 flex column so it shares gutters + vertical rhythm with the
64
+ * header. */
65
+ children: React.ReactNode
66
+ /** Extra class applied to the gap-10 content wrapper. */
67
+ contentClassName?: string
68
+ }
69
+
70
+ export function PageWithHeader({
71
+ title,
72
+ titleIcon,
73
+ subtitle,
74
+ accentDot = true,
75
+ backButton,
76
+ image,
77
+ actions,
78
+ children,
79
+ contentClassName,
80
+ }: PageWithHeaderProps) {
81
+ const router = useRouter()
82
+
83
+ const backCfg =
84
+ backButton === false
85
+ ? undefined
86
+ : {
87
+ label: backButton?.label ?? 'Back to home',
88
+ onClick: () => router.push(backButton?.href ?? '/'),
89
+ }
90
+
91
+ return (
92
+ <PageShell>
93
+ <PageLayout backButton={backCfg}>
94
+ <div className={`w-full flex flex-col gap-10${contentClassName ? ` ${contentClassName}` : ''}`}>
95
+ <PageHeader
96
+ title={title}
97
+ titleIcon={titleIcon}
98
+ subtitle={subtitle}
99
+ accentDot={accentDot}
100
+ image={image}
101
+ actions={actions}
102
+ noTopPadding
103
+ noBottomMargin
104
+ />
105
+ {children}
106
+ </div>
107
+ </PageLayout>
108
+ </PageShell>
109
+ )
110
+ }
@@ -1,15 +1,29 @@
1
1
  'use client'
2
2
 
3
3
  import React from 'react'
4
- import { cn } from '../../utils/cn'
5
4
  import type { ActionsMenuGroup } from '../ui/actions-menu'
6
- import { EntityImage } from '../ui/entity-image'
7
5
  import { PageActions, type PageActionButton } from '../ui/page-actions'
8
- import { BackButton } from './back-button'
6
+ import { PageHeader } from './page-header'
9
7
 
8
+ /**
9
+ * `<TitleBlock>` — thin adapter over `<PageHeader>` that turns the
10
+ * `actions: PageActionButton[]` / `menuActions` / `selector` API into
11
+ * a `ReactNode` slot that PageHeader can render. Kept as a separate
12
+ * component for backwards compatibility (`PageLayout` consumes it,
13
+ * external callers may too) — all the DOM/CSS lives in PageHeader.
14
+ *
15
+ * If a new consumer doesn't need the `PageActions` wiring, prefer
16
+ * `<PageHeader>` directly.
17
+ */
10
18
  export interface TitleBlockProps {
11
19
  title?: string
12
20
  subtitle?: string
21
+ /** Inline icon rendered before the title text (e.g. HelpCircle on /faqs,
22
+ * BookOpen on /knowledge-base, Map on /roadmap). Forwarded verbatim to
23
+ * `<PageHeader>`. */
24
+ titleIcon?: React.ReactNode
25
+ /** Yellow accent dot after the title — same flag as PageHeader. */
26
+ accentDot?: boolean
13
27
  image?: { src: string; alt?: string }
14
28
  backButton?: { label?: string; onClick: () => void }
15
29
  actions?: PageActionButton[]
@@ -29,6 +43,8 @@ export interface TitleBlockProps {
29
43
  export function TitleBlock({
30
44
  title,
31
45
  subtitle,
46
+ titleIcon,
47
+ accentDot,
32
48
  image,
33
49
  backButton,
34
50
  actions,
@@ -40,67 +56,29 @@ export function TitleBlock({
40
56
  }: TitleBlockProps) {
41
57
  const hasActions = actions && actions.length > 0
42
58
  const hasMenuActions = !!menuActions && menuActions.some(g => g.items.length > 0)
59
+ const hasActionsSlot = hasActions || hasMenuActions || !!selector
43
60
 
44
- return (
45
- <div
46
- className={cn(
47
- 'flex items-end justify-between gap-[var(--spacing-system-m)]',
48
- 'md:flex-col md:items-start md:justify-start lg:flex-row lg:items-end lg:justify-between',
49
- 'pt-[var(--spacing-system-l)]',
50
- variant === 'card'
51
- ? cn(
52
- 'bg-ods-card border-b border-ods-border',
53
- 'px-[var(--spacing-system-l)] pb-[var(--spacing-system-l)]',
54
- 'md:bg-transparent md:border-b-0',
55
- 'md:px-0 md:pb-0',
56
- 'md:mb-[var(--spacing-system-l)]',
57
- )
58
- : 'mb-[var(--spacing-system-l)]',
59
- className,
60
- )}
61
- >
62
- <div className="flex flex-col gap-[var(--spacing-system-xs)] flex-1 min-w-0">
63
- {backButton && (
64
- <BackButton
65
- onClick={backButton.onClick}
66
- label={backButton.label}
67
- className="hidden md:inline-flex"
68
- />
69
- )}
70
- {(image || subtitle) ? (
71
- <div className="flex items-center gap-[var(--spacing-system-m)] min-w-0 w-full">
72
- {image && (
73
- <EntityImage
74
- src={image.src}
75
- alt={image.alt}
76
- fallbackText={image.alt || title}
77
- />
78
- )}
79
- <div className="flex flex-col justify-center min-w-0 flex-1">
80
- {title && (
81
- <h1 className="text-h2 text-ods-text-primary truncate" title={title}>{title}</h1>
82
- )}
83
- {subtitle && (
84
- <p className="text-h6 text-ods-text-secondary truncate" title={subtitle}>{subtitle}</p>
85
- )}
86
- </div>
87
- </div>
88
- ) : (
89
- title && <h1 className="text-h2 text-ods-text-primary">{title}</h1>
90
- )}
91
- </div>
61
+ const actionsNode = hasActionsSlot ? (
62
+ <PageActions
63
+ variant={actionsVariant}
64
+ actions={actions ?? []}
65
+ menuActions={menuActions}
66
+ selector={selector}
67
+ />
68
+ ) : undefined
92
69
 
93
- {(hasActions || hasMenuActions || selector) && (
94
- <div className="flex gap-2 items-center shrink-0">
95
- <PageActions
96
- variant={actionsVariant}
97
- actions={actions ?? []}
98
- menuActions={menuActions}
99
- selector={selector}
100
- />
101
- </div>
102
- )}
103
- </div>
70
+ return (
71
+ <PageHeader
72
+ title={title}
73
+ titleIcon={titleIcon}
74
+ subtitle={subtitle}
75
+ accentDot={accentDot}
76
+ image={image}
77
+ backButton={backButton}
78
+ actions={actionsNode}
79
+ variant={variant}
80
+ className={className}
81
+ />
104
82
  )
105
83
  }
106
84
 
@@ -29,7 +29,7 @@ import { EntityVideoSection } from '../features/entity-video-section'
29
29
  import { VideoBitesDisplay } from '../features/video-bites-display'
30
30
  import { useVideoWarmup } from '../features/use-video-warmup'
31
31
  import { getCaptionsUrl } from '../features/captions-url'
32
- import { SimpleMarkdownRenderer } from '../ui/simple-markdown-renderer'
32
+ import { RichMarkdownRenderer } from '../ui/rich-markdown-renderer'
33
33
  import { EntityTagBadges } from '../features/entity-tag-badges'
34
34
  import { LoadError } from '../ui/error-state'
35
35
  import { ArticleAuthorByline } from '../shared/article-author-byline'
@@ -62,7 +62,7 @@ export interface OnboardingGuideDetailViewProps {
62
62
  * byline renders nothing below the name when the bio is empty. */
63
63
  fallbackBio?: string | null
64
64
  /** Optional markdown renderer override. Defaults to lib
65
- * `<SimpleMarkdownRenderer>`. */
65
+ * `<RichMarkdownRenderer>`. */
66
66
  MarkdownRenderer?: ComponentType<{ content: string }>
67
67
  /** Optional per-row related-card renderer override. */
68
68
  renderRelatedCard?: (guide: OnboardingGuide) => ReactNode
@@ -83,7 +83,7 @@ export function OnboardingGuideDetailView({
83
83
  slug,
84
84
  guideEndpoint,
85
85
  related = [],
86
- MarkdownRenderer = SimpleMarkdownRenderer,
86
+ MarkdownRenderer = RichMarkdownRenderer,
87
87
  renderRelatedCard,
88
88
  backHref,
89
89
  backLabel = 'Back to Getting Started',
@@ -25,7 +25,15 @@ import {
25
25
  type OpenframeDevSectionKey,
26
26
  } from '../../../utils/dev-sections/openframe-dev-sections';
27
27
 
28
- const SECTION_HERO_ICON_CLASS = 'h-10 w-10 text-ods-accent';
28
+ /** Re-export the constant so existing dev-section call sites keep their
29
+ * old import path. The canonical home is `src/utils/page-header-constants.ts`
30
+ * (NOT a `'use client'` module) so server modules can import it without
31
+ * Next.js turning it into a client reference proxy — that proxy is what
32
+ * blew up lucide's `mergeClasses().trim()` when used as
33
+ * `<Icon className={SECTION_HERO_ICON_CLASS} />` inside a hub
34
+ * server-component preset. */
35
+ import { SECTION_HERO_ICON_CLASS } from '../../../utils/page-header-constants';
36
+ export { SECTION_HERO_ICON_CLASS };
29
37
 
30
38
  export interface DevSectionPageProps {
31
39
  sectionKey: OpenframeDevSectionKey;
@@ -19,6 +19,7 @@ import { useState, useEffect } from 'react';
19
19
  import { useRouter, useSearchParams, usePathname } from '../../../embed-shims';
20
20
  import { SearchInput } from '../../ui';
21
21
  import { StatusFilterComponent } from '../../features';
22
+ import { PageHeader } from '../../layout/page-header';
22
23
  import {
23
24
  OPENFRAME_DEV_SECTIONS,
24
25
  type OpenframeDevSectionKey,
@@ -95,15 +96,19 @@ export function DevSectionView({ sectionKey, hero, preControls, children }: DevS
95
96
  return (
96
97
  <div className="w-full flex flex-col gap-10">
97
98
  {hero ? (
98
- <div className="space-y-4">
99
- <h1 className="text-h1 tracking-[-1.12px] text-ods-text-primary flex items-center gap-3">
100
- {hero.icon}
101
- {hero.title ?? section.hero.title}
102
- </h1>
103
- <p className="font-['DM_Sans'] font-medium text-[18px] leading-[28px] text-ods-text-secondary max-w-3xl">
104
- {hero.description}
105
- </p>
106
- </div>
99
+ // Render through the shared `<PageHeader>` primitive so the dev-
100
+ // section hero (Releases, Roadmap, Onboarding catalog) and the
101
+ // docs-hub hero (Knowledge Hub, Data Room) are LITERALLY the same
102
+ // component rendering the same DOM/CSS. `noBottomMargin` because
103
+ // the parent `gap-10` already supplies the spacing to the next
104
+ // sibling (preControls / search / filter).
105
+ <PageHeader
106
+ title={hero.title ?? section.hero.title}
107
+ titleIcon={hero.icon}
108
+ subtitle={hero.description}
109
+ noBottomMargin
110
+ noTopPadding
111
+ />
107
112
  ) : (
108
113
  <div className="flex items-center justify-between w-full">
109
114
  <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]">
@@ -1,5 +1,5 @@
1
1
  export { DevSectionView, type DevSectionViewProps } from './dev-section-view';
2
- export { DevSectionPage, type DevSectionPageProps } from './dev-section-page';
2
+ export { DevSectionPage, type DevSectionPageProps, SECTION_HERO_ICON_CLASS } from './dev-section-page';
3
3
  export {
4
4
  DevCardRowContent,
5
5
  DevCardRowSkeleton,
@@ -80,7 +80,7 @@ export function useDocSearch(config: UseDocSearchConfig) {
80
80
  onNavigate,
81
81
  tableIds,
82
82
  onInPageSwap,
83
- searchEndpoint = '/api/docs/search',
83
+ searchEndpoint,
84
84
  } = config
85
85
  const tableIdsKey = tableIds && tableIds.length > 0 ? tableIds.join(',') : ''
86
86
 
@@ -88,7 +88,11 @@ export function useDocSearch(config: UseDocSearchConfig) {
88
88
  // Optional chat-runtime read — when present and mode='embed' the
89
89
  // search-result row click short-circuits to a new-tab open against
90
90
  // the absolutized URL. Null/host preserves today's behavior.
91
+ // Also used as the proxy-prefix fallback for `searchEndpoint`, matching
92
+ // how tickets resolves `findTicketUrl`.
91
93
  const runtime = useChatRuntime()
94
+ const resolvedSearchEndpoint =
95
+ searchEndpoint ?? runtime?.endpoints.docsSearchUrl ?? '/api/docs/search'
92
96
 
93
97
  const [query, setQuery] = useState('')
94
98
  const [results, setResults] = useState<SearchResult[]>([])
@@ -114,7 +118,7 @@ export function useDocSearch(config: UseDocSearchConfig) {
114
118
  })
115
119
  if (tableIdsKey) params.set('tableIds', tableIdsKey)
116
120
 
117
- const response = await fetch(`${searchEndpoint}?${params.toString()}`)
121
+ const response = await fetch(`${resolvedSearchEndpoint}?${params.toString()}`)
118
122
  if (!response.ok) {
119
123
  throw new Error(`Search request failed: ${response.status}`)
120
124
  }
@@ -142,7 +146,7 @@ export function useDocSearch(config: UseDocSearchConfig) {
142
146
  return () => {
143
147
  cancelled = true
144
148
  }
145
- }, [debouncedQuery, source, tableIdsKey, searchEndpoint])
149
+ }, [debouncedQuery, source, tableIdsKey, resolvedSearchEndpoint])
146
150
 
147
151
  // Derived loading state — single source of truth for "should the
148
152
  // dropdown show 'Loading...' instead of 'No results found'":
@@ -19,7 +19,7 @@
19
19
 
20
20
  import type { ComponentType } from 'react';
21
21
  import { PageShell, PageLayout, PageHeading } from '../../ui';
22
- import { SimpleMarkdownRenderer } from '../../ui/simple-markdown-renderer';
22
+ import { RichMarkdownRenderer } from '../../ui/rich-markdown-renderer';
23
23
  import { useRouter } from '../../../embed-shims/next-navigation';
24
24
  import { useLegalDocs, type LegalDocument } from './use-legal-docs';
25
25
  import { formatLegalDate } from '../../../utils/format';
@@ -76,7 +76,7 @@ export function LegalDocumentPage({
76
76
  initialData = null,
77
77
  initialLastUpdatedLabel = null,
78
78
  apiEndpoint,
79
- MarkdownRenderer = SimpleMarkdownRenderer,
79
+ MarkdownRenderer = RichMarkdownRenderer,
80
80
  backButton,
81
81
  }: LegalDocumentPageProps) {
82
82
  const router = useRouter();
@@ -10,6 +10,7 @@ import { Card, CardContent } from '../../ui/card';
10
10
  import { PageShell } from '../../layout/article-detail-layout';
11
11
  import { PageLayout } from '../../layout/page-layout';
12
12
  import { ReleaseChangelogSection } from '../../ui/release-changelog-section';
13
+ import { RichMarkdownRenderer } from '../../ui/rich-markdown-renderer';
13
14
  import { EntityTagBadges } from '../../features/entity-tag-badges';
14
15
  import { EntityMetadataAuthorCell } from '../../chat/entity-cards/entity-author-card';
15
16
  import type { EntityAuthor } from '../../../types/entity-author';
@@ -109,10 +110,11 @@ export interface ReleaseDetailPageProps {
109
110
  authorHref?: string;
110
111
  }
111
112
 
112
- // Default simple markdown renderer (just renders as text)
113
- function DefaultMarkdownRenderer({ content }: MarkdownRendererProps) {
114
- return <div className="whitespace-pre-wrap">{content}</div>;
115
- }
113
+ // Default renderer = the lib's `RichMarkdownRenderer` so out-of-the-box
114
+ // release pages get full rich-link previews, embedded media, social cards,
115
+ // etc. Hosts that want a different rendering (or a Supabase-aware preset)
116
+ // override via the `MarkdownRenderer` prop.
117
+ const DefaultMarkdownRenderer = RichMarkdownRenderer;
116
118
 
117
119
  export function ReleaseDetailPage({
118
120
  authorHref,
@@ -106,13 +106,15 @@ interface HalfOptions {
106
106
  prefetch?: boolean
107
107
  onClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>
108
108
  disabled?: boolean
109
+ /** Stretch this half to fill the group's free space (the icon half stays fixed-width). */
110
+ grow?: boolean
109
111
  type?: "button" | "submit" | "reset"
110
112
  ariaLabel?: string
111
113
  children: React.ReactNode
112
114
  }
113
115
 
114
- function Half({ variant, size, side, href, openInNewTab, prefetch, onClick, disabled, type = "button", ariaLabel, children }: HalfOptions) {
115
- const classes = splitHalfVariants({ variant, size, side })
116
+ function Half({ variant, size, side, href, openInNewTab, prefetch, onClick, disabled, grow, type = "button", ariaLabel, children }: HalfOptions) {
117
+ const classes = cn(splitHalfVariants({ variant, size, side }), grow && "flex-1")
116
118
 
117
119
  if (href) {
118
120
  return (
@@ -183,6 +185,7 @@ const SplitButton = React.forwardRef<HTMLDivElement, SplitButtonProps>(function
183
185
  prefetch={prefetch}
184
186
  onClick={onClick}
185
187
  disabled={disabled || mainDisabled}
188
+ grow={fullWidth}
186
189
  type={type}
187
190
  ariaLabel={ariaLabel}
188
191
  >
@@ -61,6 +61,7 @@ export * from './hover-dropdown'
61
61
  export * from '../chat'
62
62
  export * from '../layout/list-page-layout'
63
63
  export * from '../layout/page-container'
64
+ export * from '../layout/page-header'
64
65
  export * from '../layout/page-heading'
65
66
  export * from '../layout/page-layout'
66
67
  export * from '../layout/article-detail-layout'
@@ -160,5 +161,6 @@ export * from './ticket-attachments-list'
160
161
  export * from './ticket-note-card'
161
162
  export * from './ticket-notes-section'
162
163
  export * from './simple-markdown-renderer'
164
+ export { RichMarkdownRenderer, type RichMarkdownRendererProps } from './rich-markdown-renderer'
163
165
  export * from './filter-pill-row'
164
166
 
@@ -3,6 +3,7 @@
3
3
  import React, { useState, useRef, useEffect } from 'react';
4
4
  import { Badge } from './badge';
5
5
  import { ChevronDown } from 'lucide-react';
6
+ import { RichMarkdownRenderer } from './rich-markdown-renderer';
6
7
  import type { ChangelogEntry } from '../../types/product-release';
7
8
 
8
9
  interface ReleaseChangelogSectionProps {
@@ -31,7 +32,11 @@ interface ReleaseChangelogSectionProps {
31
32
  * visual taxonomy across catalog and detail. Inherits the title's color
32
33
  * (secondary for normal sections, red for breaking). */
33
34
  icon?: React.ReactNode;
34
- SimpleMarkdownRenderer: React.ComponentType<{ content: string }>;
35
+ /** Markdown renderer for each entry's description. Optional defaults to
36
+ * the lib's `RichMarkdownRenderer` so changelog rich-link previews
37
+ * (YouTube, OG cards, etc.) work out of the box. Hosts that already
38
+ * wrap with a Supabase-aware preset can keep passing their own. */
39
+ SimpleMarkdownRenderer?: React.ComponentType<{ content: string }>;
35
40
  }
36
41
 
37
42
  // Collapsed height for the preview-first mode. ~120px shows the first
@@ -47,7 +52,7 @@ export function ReleaseChangelogSection({
47
52
  defaultCollapsed = true,
48
53
  previewFirst = false,
49
54
  icon,
50
- SimpleMarkdownRenderer
55
+ SimpleMarkdownRenderer = RichMarkdownRenderer,
51
56
  }: ReleaseChangelogSectionProps) {
52
57
  const [collapsed, setCollapsed] = useState(collapsible ? defaultCollapsed : false);
53
58
  const [previewExpanded, setPreviewExpanded] = useState(false);