@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,15 @@
1
+ /**
2
+ * String constants shared by page-header consumers (`<PageHeader>`,
3
+ * `<DevSectionPage>`, `<DocsHubPage>` callers). Lives in `utils/` —
4
+ * NOT `'use client'` — so server modules (e.g. the hub's
5
+ * `lib/docs/hub-docs-presets.tsx` that builds preset JSX with
6
+ * `<Icon className={SECTION_HERO_ICON_CLASS} />`) can import the raw
7
+ * string. Importing the constant from a `'use client'` module turns it
8
+ * into a Next.js client-reference proxy in server contexts, which
9
+ * lucide's internal `mergeClasses` then calls `.trim()` on and crashes.
10
+ */
11
+ /** Tailwind class applied uniformly to every section-hero / page-header
12
+ * icon across the lib (Roadmap Map, Releases Rocket, Knowledge Hub
13
+ * BookOpen, Data Room Building2, …). Yellow accent color, 40x40. */
14
+ export declare const SECTION_HERO_ICON_CLASS = "h-10 w-10 text-ods-accent";
15
+ //# sourceMappingURL=page-header-constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-header-constants.d.ts","sourceRoot":"","sources":["../../src/utils/page-header-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;qEAEqE;AACrE,eAAO,MAAM,uBAAuB,8BAA8B,CAAA"}
@@ -0,0 +1,29 @@
1
+ interface CacheOptions {
2
+ platform: 'reddit' | 'twitter';
3
+ url: string;
4
+ apiEndpoint: string;
5
+ }
6
+ export declare class SocialEmbedCache {
7
+ private static instance;
8
+ static getInstance(): SocialEmbedCache;
9
+ getFromMemory(url: string): any | null;
10
+ setInMemory(url: string, data: any): void;
11
+ getFromServer(options: CacheOptions): Promise<any | null>;
12
+ private updateServerCache;
13
+ setInServer(options: CacheOptions, data: any): Promise<void>;
14
+ fetchDirect(options: CacheOptions & {
15
+ directFetcher: () => Promise<any>;
16
+ }): Promise<any | null>;
17
+ fetchWithHierarchy(options: CacheOptions & {
18
+ dataValidator: (data: any) => boolean;
19
+ onDataUpdate: (data: any) => void;
20
+ onError: (error: string) => void;
21
+ onLoading: (loading: boolean) => void;
22
+ }): Promise<void>;
23
+ private scheduleAsyncRefresh;
24
+ private backgroundRefresh;
25
+ private silentBackgroundRefresh;
26
+ }
27
+ export declare const socialCache: SocialEmbedCache;
28
+ export {};
29
+ //# sourceMappingURL=social-embed-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social-embed-cache.d.ts","sourceRoot":"","sources":["../../src/utils/social-embed-cache.ts"],"names":[],"mappings":"AAmBA,UAAU,YAAY;IACpB,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmB;IAE1C,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAQtC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI;IAKtC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAKnC,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;YAoBjD,iBAAiB;IAezB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B5D,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG;QACxC,aAAa,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;KAClC,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAajB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG;QAC/C,aAAa,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;QACtC,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;QAClC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QACjC,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;KACvC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmIjB,OAAO,CAAC,oBAAoB;YAgCd,iBAAiB;IAiD/B,OAAO,CAAC,uBAAuB;CAgDhC;AAED,eAAO,MAAM,WAAW,kBAAiC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flamingo-stack/openframe-frontend-core",
3
- "version": "0.0.295",
3
+ "version": "0.0.296-snapshot.20260621021605",
4
4
  "description": "Shared design system and components for all Flamingo platforms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -78,6 +78,12 @@
78
78
  "require": "./dist/components/contact/index.cjs",
79
79
  "default": "./dist/components/contact/index.js"
80
80
  },
81
+ "./components/case-studies": {
82
+ "types": "./dist/components/case-studies/index.d.ts",
83
+ "import": "./dist/components/case-studies/index.js",
84
+ "require": "./dist/components/case-studies/index.cjs",
85
+ "default": "./dist/components/case-studies/index.js"
86
+ },
81
87
  "./schemas/contact-schema": {
82
88
  "types": "./dist/schemas/contact-schema.d.ts",
83
89
  "import": "./dist/schemas/contact-schema.js",
@@ -0,0 +1,4 @@
1
+ export {
2
+ ShareExperienceSection,
3
+ type ShareExperienceSectionProps,
4
+ } from './share-experience-section'
@@ -0,0 +1,185 @@
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import { BenefitCard, BenefitCardGrid } from '../ui'
5
+ import {
6
+ G2Icon,
7
+ CapterraIcon,
8
+ TrustpilotIcon,
9
+ GetAppIcon,
10
+ } from '../icons'
11
+ import {
12
+ ContactForm,
13
+ type ContactFormProps,
14
+ } from '../contact'
15
+
16
+ /**
17
+ * `<ShareExperienceSection>` — the case-studies "Share Your Experience"
18
+ * CTA block.
19
+ *
20
+ * Hub usage: rendered inside the unified case-studies chrome (between
21
+ * the search bar and the case-study card grid) so it stays at the same
22
+ * y-offset + gutters as the rest of the page content.
23
+ *
24
+ * Embedders mount this anywhere they like. The inner `<ContactForm>`
25
+ * submits through the AMBIENT `EndpointsRuntime.contactUrl` (same proxy
26
+ * seam every other embed-aware form uses), so embedders behind a
27
+ * `/content` reverse proxy get a working submission with no per-call-
28
+ * site wiring.
29
+ *
30
+ * **Copy is overridable.** Every string is a prop with a sensible
31
+ * "Flamingo case-studies" default so the hub keeps its existing copy
32
+ * verbatim; embedders override what they need.
33
+ *
34
+ * **Contact-form integration is configurable.** Hub auto-resolves
35
+ * `userId` / `helpCategoryOptions` / `rdtCid` / `onSubmitSuccess` from
36
+ * its app context and passes them down via `contactFormProps`;
37
+ * embedders without that context just omit them. All other
38
+ * `ContactFormProps` are forwarded too, so the host can adjust prefill,
39
+ * success redirect, hidden fields, etc. without forking this component.
40
+ */
41
+ export interface ShareExperienceSectionProps {
42
+ /** Override the section heading. JSX so the host can break lines /
43
+ * colorize the accent the same way the hub does. Default: the
44
+ * hub's two-line "Share Your Experience / with Fellow MSPs:" copy. */
45
+ title?: React.ReactNode
46
+ /** Override the lead paragraph. Default: the hub's review-incentive
47
+ * copy referencing Flamingo. Pass a brand-neutral string in embeds. */
48
+ subtitle?: React.ReactNode
49
+ /** Override the "How it works?" sub-heading. */
50
+ howItWorksTitle?: React.ReactNode
51
+ /** Override the "How it works?" body copy. */
52
+ howItWorksBody?: React.ReactNode
53
+ /** Forwarded to the inner `<ContactForm>`. The hub passes its
54
+ * auto-resolved `userId` / `helpCategoryOptions` / `rdtCid` /
55
+ * `onSubmitSuccess` here. Embedders pass overrides like
56
+ * `successRedirectUrl` or extra prefill copy. */
57
+ contactFormProps?: Partial<ContactFormProps>
58
+ className?: string
59
+ }
60
+
61
+ const DEFAULT_TITLE: React.ReactNode = (
62
+ <>
63
+ Share Your Experience
64
+ <br />
65
+ with Fellow MSPs<span className="text-ods-accent">:</span>
66
+ </>
67
+ )
68
+
69
+ const DEFAULT_SUBTITLE: React.ReactNode = (
70
+ <>
71
+ We know your time is valuable. When you leave an honest review about
72
+ your Flamingo experience, we&apos;d like to thank you with a gift
73
+ certificate – not as payment for a review, but as appreciation for
74
+ the time you invest in helping other MSPs make informed decisions.
75
+ </>
76
+ )
77
+
78
+ const DEFAULT_HOW_IT_WORKS_TITLE: React.ReactNode = (
79
+ <>
80
+ How it works<span className="text-ods-accent">?</span>
81
+ </>
82
+ )
83
+
84
+ const DEFAULT_HOW_IT_WORKS_BODY: React.ReactNode = (
85
+ <>
86
+ Share your name and email with us, and we&apos;ll reach out to guide
87
+ you through the review process and arrange your thank-you gift
88
+ certificate.
89
+ </>
90
+ )
91
+
92
+ /** Defaults that match the hub's existing /case-studies behavior. Host
93
+ * overrides via `contactFormProps` win over these. */
94
+ const DEFAULT_CONTACT_FORM_PROPS = {
95
+ prefilledReason: 'I want to do a case study',
96
+ prefilledMessage: 'I want to do a case study',
97
+ hideFields: ['companySize', 'referralSource', 'helpCategory', 'message'] as ContactFormProps['hideFields'],
98
+ title: '',
99
+ subtitle: '',
100
+ footerText: '',
101
+ noBorder: true,
102
+ noPadding: true,
103
+ buttonVariant: 'outline' as ContactFormProps['buttonVariant'],
104
+ buttonClassName: 'w-full',
105
+ successToastMessage: "Thank you! We'll reach out to schedule your case study.",
106
+ } satisfies Partial<ContactFormProps>
107
+
108
+ export function ShareExperienceSection({
109
+ title = DEFAULT_TITLE,
110
+ subtitle = DEFAULT_SUBTITLE,
111
+ howItWorksTitle = DEFAULT_HOW_IT_WORKS_TITLE,
112
+ howItWorksBody = DEFAULT_HOW_IT_WORKS_BODY,
113
+ contactFormProps,
114
+ className,
115
+ }: ShareExperienceSectionProps = {}) {
116
+ return (
117
+ <section className={`flex flex-col gap-10${className ? ` ${className}` : ''}`}>
118
+ <div className="text-ods-text-primary">
119
+ <h2 className="text-h1 text-ods-text-primary">{title}</h2>
120
+ <p className="text-h4 mt-6 max-w-[765px]">{subtitle}</p>
121
+ </div>
122
+
123
+ <div className="bg-ods-background border border-ods-border rounded-md p-10">
124
+ <div className="flex flex-col gap-10">
125
+ <div className="flex flex-col gap-6 text-ods-text-primary">
126
+ <h3 className="text-h2">{howItWorksTitle}</h3>
127
+ <p className="text-h4">{howItWorksBody}</p>
128
+ </div>
129
+
130
+ <BenefitCardGrid columns={4}>
131
+ <BenefitCard
132
+ icon={
133
+ <div className="bg-ods-background border border-ods-border rounded-md p-2 w-12 h-12 flex items-center justify-center">
134
+ <G2Icon width={24} height={24} />
135
+ </div>
136
+ }
137
+ title="G2"
138
+ description="g2.com"
139
+ variant="auth-figma"
140
+ />
141
+ <BenefitCard
142
+ icon={
143
+ <div className="bg-ods-background border border-ods-border rounded-md p-2 w-12 h-12 flex items-center justify-center">
144
+ <CapterraIcon width={24} height={24} />
145
+ </div>
146
+ }
147
+ title="Capterra"
148
+ description="capterra.com"
149
+ variant="auth-figma"
150
+ />
151
+ <BenefitCard
152
+ icon={
153
+ <div className="bg-ods-background border border-ods-border rounded-md p-2 w-12 h-12 flex items-center justify-center">
154
+ <TrustpilotIcon width={24} height={24} />
155
+ </div>
156
+ }
157
+ title="TrustPilot"
158
+ description="trustpilot.com"
159
+ variant="auth-figma"
160
+ />
161
+ <BenefitCard
162
+ icon={
163
+ <div className="bg-ods-background border border-ods-border rounded-md p-2 w-12 h-12 flex items-center justify-center">
164
+ <GetAppIcon width={24} height={24} />
165
+ </div>
166
+ }
167
+ title="GetApp"
168
+ description="getapp.com"
169
+ variant="auth-figma"
170
+ />
171
+ </BenefitCardGrid>
172
+
173
+ {/* Submission proxies through ambient EndpointsRuntime.contactUrl —
174
+ * no per-call-site wiring needed for embeds behind a reverse
175
+ * proxy. Hub's auto-resolving wrapper passes userId / rdtCid /
176
+ * onSubmitSuccess via `contactFormProps`. */}
177
+ <ContactForm
178
+ {...DEFAULT_CONTACT_FORM_PROPS}
179
+ {...contactFormProps}
180
+ />
181
+ </div>
182
+ </div>
183
+ </section>
184
+ )
185
+ }
@@ -12,16 +12,14 @@ import type { ConnectionIndicatorProps, ChatContainerProps, ChatHeaderProps } fr
12
12
  const ConnectionIndicator: React.FC<ConnectionIndicatorProps> = ({ status }) => {
13
13
  const getStatusStyles = () => {
14
14
  switch (status) {
15
- // ODS attention tokens — same scheme used by the rest of the chat
16
- // shell (StatusBadge, error toast, etc.). Hex Tailwind palette
17
- // (`bg-green-500` / `bg-red-500`) would diverge from the theme and
18
- // is forbidden by the host's design-token policy.
15
+ // ODS semantic status tokens — preset-defined utilities aliasing the same
16
+ // green/yellow/red as the raw ods-attention palette (which is CSS-vars only).
19
17
  case 'connected':
20
- return 'bg-ods-attention-green-success'
18
+ return 'bg-ods-success'
21
19
  case 'connecting':
22
- return 'bg-ods-attention-yellow-warning animate-pulse'
20
+ return 'bg-ods-warning animate-pulse'
23
21
  case 'disconnected':
24
- return 'bg-ods-attention-red-error'
22
+ return 'bg-ods-error'
25
23
  default:
26
24
  return 'bg-ods-text-tertiary'
27
25
  }
@@ -1127,7 +1127,7 @@ function EmbeddableChatInner({
1127
1127
  // falls back to the platform default here) — instead it sets a truthy baseRoute +
1128
1128
  // `chipBasePlatform` so doc chips with no externalUrl resolve cross-platform to that
1129
1129
  // platform's public knowledge hub (`getBaseUrl(chipBasePlatform)/knowledge-base/…`),
1130
- // exactly like the hub's openframe config (baseRoute:'/', chipBasePlatform:'flamingo').
1130
+ // exactly like the hub's openframe config (baseRoute:'/', chipBasePlatform:'openframe').
1131
1131
  const resolvedBaseRoute =
1132
1132
  baseRoute || (source === 'flamingo' ? '/knowledge-base' : '/data-room')
1133
1133
 
@@ -2,51 +2,69 @@
2
2
 
3
3
  import { forwardRef, useState } from "react"
4
4
  import { cn } from "../../utils/cn"
5
- import { AlertCircle, ChevronDown } from "lucide-react"
5
+ import { AlertCircleIcon } from "../icons-v2-generated"
6
+ import { ExpandChevron } from "./expand-chevron"
7
+ import { useCollapsible } from "./hooks/use-collapsible"
6
8
  import type { ErrorMessageDisplayProps } from "./types"
7
9
 
10
+ const iconTint = {
11
+ error: "text-ods-error",
12
+ warning: "text-ods-warning",
13
+ info: "text-ods-text-secondary",
14
+ } as const
15
+
8
16
  const ErrorMessageDisplay = forwardRef<HTMLDivElement, ErrorMessageDisplayProps>(
9
- ({ className, title, details, ...props }, ref) => {
10
- const [isExpanded, setIsExpanded] = useState(false)
17
+ ({ className, title, details, type = "error", ...props }, ref) => {
18
+ const [expanded, setExpanded] = useState(false)
19
+ const { innerRef, containerStyle } = useCollapsible({ expanded })
20
+ const hasDetails = Boolean(details)
11
21
 
12
22
  return (
13
23
  <div
14
24
  ref={ref}
15
25
  className={cn(
16
- "bg-[var(--ods-attention-red-error-secondary)] border border-ods-error rounded-[6px] p-3 mb-2 flex items-start gap-3",
26
+ "bg-ods-card rounded-md p-[var(--spacing-system-xsf)] mb-[var(--spacing-system-xsf)]",
17
27
  className
18
28
  )}
19
29
  {...props}
20
30
  >
21
- <AlertCircle className="w-6 h-6 text-white flex-shrink-0 mt-0.5" />
22
- <div className="flex flex-col gap-0.5 min-w-0 flex-1">
23
- <div className="flex items-center justify-between gap-2">
24
- <span className="text-lg leading-6 text-white font-['DM_Sans'] font-medium">
25
- {title}
26
- </span>
27
- {details && (
28
- <button
29
- type="button"
30
- onClick={() => setIsExpanded(prev => !prev)}
31
- className="flex-shrink-0 p-0.5 rounded hover:bg-white/10 transition-colors"
32
- aria-expanded={isExpanded}
33
- aria-label={isExpanded ? "Collapse error details" : "Expand error details"}
34
- >
35
- <ChevronDown
36
- className={cn(
37
- "w-5 h-5 text-white transition-transform duration-200",
38
- isExpanded && "rotate-180"
39
- )}
40
- />
41
- </button>
31
+ <button
32
+ type="button"
33
+ onClick={hasDetails ? () => setExpanded(prev => !prev) : undefined}
34
+ aria-expanded={hasDetails ? expanded : undefined}
35
+ aria-label={
36
+ hasDetails ? (expanded ? "Collapse details" : "Expand details") : undefined
37
+ }
38
+ disabled={!hasDetails}
39
+ className={cn(
40
+ "flex w-full items-center gap-[var(--spacing-system-xsf)] text-left",
41
+ hasDetails ? "cursor-pointer" : "cursor-default"
42
+ )}
43
+ >
44
+ <AlertCircleIcon size={16} className={cn("shrink-0", iconTint[type])} />
45
+ <span
46
+ className={cn(
47
+ "min-w-0 flex-1 font-mono text-sm font-medium uppercase leading-5 tracking-[-0.28px]",
48
+ expanded ? "text-ods-text-primary" : "truncate text-ods-text-secondary"
42
49
  )}
50
+ >
51
+ {title}
52
+ </span>
53
+ {hasDetails && <ExpandChevron expanded={expanded} />}
54
+ </button>
55
+
56
+ {hasDetails && (
57
+ <div style={containerStyle}>
58
+ <div
59
+ ref={innerRef}
60
+ className="px-[var(--spacing-system-lf)] pt-[var(--spacing-system-xsf)]"
61
+ >
62
+ <p className="text-sm font-medium leading-5 text-ods-text-primary">
63
+ {details}
64
+ </p>
65
+ </div>
43
66
  </div>
44
- {details && isExpanded && (
45
- <span className="text-sm leading-5 text-white font-['DM_Sans'] font-medium mt-1">
46
- {details}
47
- </span>
48
- )}
49
- </div>
67
+ )}
50
68
  </div>
51
69
  )
52
70
  }
@@ -448,6 +448,8 @@ export interface ApprovalRequestMessageProps extends HTMLAttributes<HTMLDivEleme
448
448
  export interface ErrorMessageDisplayProps extends HTMLAttributes<HTMLDivElement> {
449
449
  title: string
450
450
  details?: string
451
+ /** Severity — drives the icon tint. Defaults to `error`. */
452
+ type?: 'error' | 'warning' | 'info'
451
453
  }
452
454
 
453
455
  // ========== Context Compaction Display Props ==========
@@ -2,7 +2,10 @@
2
2
 
3
3
  import React, { useMemo } from "react"
4
4
  import { MultiLevelNavigation, MobileNavigationDropdown } from "../navigation/multi-level-navigation"
5
- import { PageHeading } from "../layout/page-heading"
5
+ import { PageHeader } from "../layout/page-header"
6
+ import { PageLayout } from "../layout/page-layout"
7
+ import { PageShell } from "../layout/article-detail-layout"
8
+ import { useRouter } from "../../embed-shims/next-navigation"
6
9
  import { PersistentSidebar, PersistentMobileDropdown } from "../persistent-filter-controls"
7
10
  import { CategorySidebarSkeleton } from "../loading/page-layout-skeleton"
8
11
  import { DocSearchBar, useDocSearch } from "../shared/doc-search"
@@ -12,6 +15,7 @@ import { useScrollSpy } from "./use-scroll-spy"
12
15
  import { useDocNavigation } from "./doc-navigation-context"
13
16
  import { findDocNodeByPath } from "../../utils/doc-tree-nav"
14
17
  import type { DocContent, DocNode, DocRenderHandlers, DocSourceId } from "../../types/doc-source"
18
+ import { useDocsResolveLink } from "./use-docs-resolve-link"
15
19
 
16
20
  /** Color tokens for the doc-viewer chrome. Hub-side `DocViewer` callers share
17
21
  * this constant; no need to override per source — the palette is intentionally
@@ -30,7 +34,7 @@ export const DEFAULT_DOC_VIEWER_PALETTE = {
30
34
  export interface DocViewerProps {
31
35
  /**
32
36
  * Registry source id (`'openframe-docs'`, `'data-room-docs'`, …). Flowed through
33
- * `renderContent`'s handlers for `/api/resolve-link` POSTs.
37
+ * `renderContent`'s handlers for `/api/docs/resolve-link` POSTs.
34
38
  */
35
39
  sourceId: DocSourceId
36
40
 
@@ -55,7 +59,24 @@ export interface DocViewerProps {
55
59
  */
56
60
  chatSource: string
57
61
 
58
- title: string | React.ReactNode
62
+ /** Page title rendered via the shared `<PageHeader>` primitive
63
+ * so the doc-viewer chrome matches every other lib page
64
+ * (DevSectionPage / LegalDocumentPage / OnboardingGuideDetailView)
65
+ * pixel-for-pixel. ReactNode is intentionally not supported here —
66
+ * every consumer renders the same typography. */
67
+ title?: string
68
+ /** Optional icon rendered inline before the title text — same slot
69
+ * `<DevSectionView>`'s hero uses (Map for Roadmap, Rocket for Releases,
70
+ * etc.). Pass a pre-rendered React element styled with
71
+ * `SECTION_HERO_ICON_CLASS` (`h-10 w-10 text-ods-accent`) for visual
72
+ * parity with other lib pages. */
73
+ titleIcon?: React.ReactNode
74
+ /** Subtitle (h6, secondary text) rendered beneath the title. */
75
+ subtitle?: string
76
+ /** Render a yellow accent dot (`.`) after the title — same flag as
77
+ * the hub's legacy `<AdminPageHeader accentDot>` so the docs-hub
78
+ * surface keeps its existing accent styling after the migration. */
79
+ accentDot?: boolean
59
80
  /** Override the default ODS palette. Optional — most callers should omit. */
60
81
  colorPalette?: typeof DEFAULT_DOC_VIEWER_PALETTE
61
82
  className?: string
@@ -74,6 +95,17 @@ export interface DocViewerProps {
74
95
  structureEndpoint?: string
75
96
  /** Same shape as `structureEndpoint`. Defaults to `/api/docs/sources/${sourceId}/content`. */
76
97
  contentEndpoint?: string
98
+ /** RAG-search endpoint that backs the in-source search bar (when `showAIChat`
99
+ * is on). Defaults to `/api/docs/search`. Override for proxy-prefix embeds —
100
+ * same injectability pattern as `structureEndpoint` / `contentEndpoint`. */
101
+ searchEndpoint?: string
102
+ /** POST internal-link resolver. The viewer threads an async `onResolveLink`
103
+ * into `renderContent`'s `handlers` that posts `{ link, currentPath, source }`
104
+ * here. Defaults to `/api/docs/resolve-link`. Override for proxy-prefix embeds —
105
+ * same injectability pattern as `structureEndpoint` / `contentEndpoint` /
106
+ * `searchEndpoint`, with `ChatRuntime.endpoints.docsResolveLinkUrl` as a
107
+ * runtime fallback (prop → runtime → default). */
108
+ resolveLinkEndpoint?: string
77
109
  /** Base route path for URL navigation. */
78
110
  baseRoute: string
79
111
 
@@ -85,6 +117,13 @@ export interface DocViewerProps {
85
117
 
86
118
  /** Folder-index filename (default `'README.md'`). */
87
119
  folderIndexFile?: string
120
+
121
+ /** Back-button shown above the title. Mirrors `<DevSectionPage>` /
122
+ * `<HelpCenterList>` / `<LegalDocumentPage>` so every embeddable surface
123
+ * shares the same chrome. Defaults to `{ label: 'Back to home', href: '/' }`.
124
+ * Pass `false` to hide; pass `{ href: '/docs' }` etc. when the embed's
125
+ * home isn't `/`. */
126
+ backButton?: { label?: string; href?: string } | false
88
127
  }
89
128
 
90
129
  export function DocViewer(props: DocViewerProps) {
@@ -97,16 +136,22 @@ function DocViewerContent({
97
136
  renderSkeleton,
98
137
  chatSource,
99
138
  title,
139
+ titleIcon,
140
+ subtitle,
141
+ accentDot,
100
142
  colorPalette = DEFAULT_DOC_VIEWER_PALETTE,
101
143
  className = "",
102
144
  docPath,
103
145
  sidebarLabel = "DOCUMENTATION",
104
146
  structureEndpoint,
105
147
  contentEndpoint,
148
+ searchEndpoint,
149
+ resolveLinkEndpoint,
106
150
  baseRoute,
107
151
  emptyStateText,
108
152
  showAIChat = false,
109
153
  folderIndexFile,
154
+ backButton,
110
155
  }: DocViewerProps) {
111
156
  // Default endpoints derived from sourceId. Hub callers omit the props in 99%
112
157
  // of cases; the override is for embed contexts where the doc-viewer sits
@@ -115,6 +160,13 @@ function DocViewerContent({
115
160
  structureEndpoint ?? `/api/docs/sources/${sourceId}/structure`
116
161
  const resolvedContentEndpoint =
117
162
  contentEndpoint ?? `/api/docs/sources/${sourceId}/content`
163
+ // Resolve-link endpoint chain (prop → ChatRuntime.endpoints → hub default)
164
+ // + the full fetch + JSON-parse pipeline live in `useDocsResolveLink`.
165
+ // Keeping it factored out as a proper hook makes the contract reusable
166
+ // by any embedder rendering doc content outside `<DocViewer>` (custom
167
+ // markdown renderers, link-resolver previews, etc.) and keeps this
168
+ // component focused on layout + state.
169
+ const resolveLink = useDocsResolveLink(sourceId, resolveLinkEndpoint)
118
170
  const {
119
171
  structure,
120
172
  selectedPath,
@@ -139,9 +191,23 @@ function DocViewerContent({
139
191
  const { activeSection, handleSectionClick } = useScrollSpy(content?.sections)
140
192
 
141
193
  const docNav = useDocNavigation()
194
+
195
+ // Back-button config — mirrors `<DevSectionPage>` so the docs surface
196
+ // matches every other embeddable page's chrome. Default target is `/`
197
+ // (the embed's home); pass `backButton: false` to hide entirely, or
198
+ // override the href when the embed's home isn't `/`.
199
+ const router = useRouter()
200
+ const backCfg =
201
+ backButton === false
202
+ ? null
203
+ : {
204
+ label: backButton?.label ?? 'Back to home',
205
+ onClick: () => router.push(backButton?.href ?? '/'),
206
+ }
142
207
  const docSearch = useDocSearch({
143
208
  source: chatSource,
144
209
  baseRoute,
210
+ searchEndpoint,
145
211
  onNavigate: (path) => navigateToDoc(path, { fromInternalLink: true }),
146
212
  onInPageSwap: (path) => docNav.navigate(path),
147
213
  })
@@ -152,8 +218,9 @@ function DocViewerContent({
152
218
  onInternalLinkClick: navigateToDoc,
153
219
  currentPath: selectedPath,
154
220
  sourceId,
221
+ onResolveLink: resolveLink,
155
222
  })
156
- }, [content, selectedPath, renderContent, navigateToDoc, sourceId])
223
+ }, [content, selectedPath, renderContent, navigateToDoc, sourceId, resolveLink])
157
224
 
158
225
  // Selected node's documentType drives:
159
226
  // - which skeleton the caller renders during fetch (markdown vs embed)
@@ -192,15 +259,32 @@ function DocViewerContent({
192
259
  const resolvedEmptyText = emptyStateText || defaultEmptyText
193
260
 
194
261
  return (
195
- <section className={`${bgClass} ${className}`} style={bgStyle}>
196
- <div
197
- className="max-w-[1920px] px-6 md:px-20 py-6 md:py-10 mx-auto"
198
- style={containerBgStyle}
199
- >
200
- <div className="flex flex-col gap-6">
201
- <div className="flex flex-col gap-4">
202
- {typeof title === 'string' ? <PageHeading>{title}</PageHeading> : title}
203
- </div>
262
+ // STRUCTURAL UNIFICATION: render through the IDENTICAL wrapper chain
263
+ // `<DevSectionPage>` uses (PageShell → PageLayout → `gap-10 flex-col`),
264
+ // not a hand-rolled custom container with similar-looking spacing.
265
+ // PageLayout owns the back-button row; the inner `gap-10` div +
266
+ // `<PageHeader noTopPadding noBottomMargin>` renders the title section
267
+ // the same way `<DevSectionView>`'s hero does. This is the only way to
268
+ // guarantee /knowledge-base sits at pixel-identical vertical rhythm to
269
+ // /roadmap / /releases / /onboarding-guides same components, same DOM,
270
+ // not "same CSS classes that look similar on paper."
271
+ //
272
+ // `colorPalette` / `className` / `bgStyle` flow through PageShell's
273
+ // contentClassName + an inner style-passthrough wrapper so legacy
274
+ // palette overrides still apply (none in the codebase today; the API
275
+ // surface is preserved).
276
+ <PageShell contentClassName={`${bgClass} ${className}`}>
277
+ <div style={{ ...bgStyle, ...containerBgStyle }}>
278
+ <PageLayout backButton={backCfg ?? undefined}>
279
+ <div className="w-full flex flex-col gap-10">
280
+ <PageHeader
281
+ title={title}
282
+ titleIcon={titleIcon}
283
+ subtitle={subtitle}
284
+ accentDot={accentDot}
285
+ noTopPadding
286
+ noBottomMargin
287
+ />
204
288
 
205
289
  {showAIChat && (
206
290
  <DocSearchBar
@@ -271,9 +355,16 @@ function DocViewerContent({
271
355
  <div className="flex-1 min-w-0 w-full">
272
356
  <div
273
357
  className={`grid grid-cols-1 ${
274
- (showStickyNav && stickyNavSections.length > 0) ||
275
- isLoadingContent ||
276
- isLoadingStructure
358
+ // "On this page" right column only makes sense for
359
+ // MARKDOWN content (PDFs / Sheets / Figma / file have no
360
+ // sections to navigate to). Gating the grid template on
361
+ // `isMarkdownContent` also suppresses the section-skeleton
362
+ // bars during embed loads — the user-reported "skeleton
363
+ // shouldn't be on file pages" bug.
364
+ isMarkdownContent &&
365
+ ((showStickyNav && stickyNavSections.length > 0) ||
366
+ isLoadingContent ||
367
+ isLoadingStructure)
277
368
  ? 'lg:grid-cols-[1fr_280px]'
278
369
  : ''
279
370
  } gap-8`}
@@ -292,7 +383,7 @@ function DocViewerContent({
292
383
  </article>
293
384
  </div>
294
385
 
295
- {(isLoadingContent || isLoadingStructure) && (
386
+ {isMarkdownContent && (isLoadingContent || isLoadingStructure) && (
296
387
  <div className="hidden lg:block">
297
388
  <div className="sticky top-24">
298
389
  <div className="h-[14px] w-28 bg-ods-border rounded animate-pulse mb-5" />
@@ -338,8 +429,9 @@ function DocViewerContent({
338
429
  </div>
339
430
  </div>
340
431
  )}
341
- </div>
432
+ </div>
433
+ </PageLayout>
342
434
  </div>
343
- </section>
435
+ </PageShell>
344
436
  )
345
437
  }