@flamingo-stack/openframe-frontend-core 0.0.296 → 0.0.297-snapshot.20260621022511

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 (278) 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-D3LEFMOA.cjs → chunk-54KNMC2R.cjs} +3 -3
  5. package/dist/{chunk-D3LEFMOA.cjs.map → chunk-54KNMC2R.cjs.map} +1 -1
  6. package/dist/{chunk-WHMATDVP.js → chunk-7YNG257E.js} +9 -15
  7. package/dist/{chunk-WHMATDVP.js.map → chunk-7YNG257E.js.map} +1 -1
  8. package/dist/chunk-FBWXMMRB.cjs +2 -0
  9. package/dist/chunk-FBWXMMRB.cjs.map +1 -0
  10. package/dist/{chunk-IE6OU3WQ.cjs → chunk-FQOTC3UU.cjs} +318 -16
  11. package/dist/chunk-FQOTC3UU.cjs.map +1 -0
  12. package/dist/{chunk-MBFWU2EM.js → chunk-G6JDD3LU.js} +6 -10
  13. package/dist/{chunk-MBFWU2EM.js.map → chunk-G6JDD3LU.js.map} +1 -1
  14. package/dist/{chunk-6GCI7JOE.js → chunk-GAVVRWQM.js} +8 -7
  15. package/dist/{chunk-6GCI7JOE.js.map → chunk-GAVVRWQM.js.map} +1 -1
  16. package/dist/chunk-GZ4C3XW6.js +2 -0
  17. package/dist/chunk-GZ4C3XW6.js.map +1 -0
  18. package/dist/chunk-IDMNFM4N.js +144 -0
  19. package/dist/chunk-IDMNFM4N.js.map +1 -0
  20. package/dist/{chunk-LCNMR277.js → chunk-IZ7JSBFP.js} +1 -1
  21. package/dist/chunk-IZ7JSBFP.js.map +1 -0
  22. package/dist/{chunk-EL6QLAWX.js → chunk-JALO4TAZ.js} +357 -55
  23. package/dist/chunk-JALO4TAZ.js.map +1 -0
  24. package/dist/{chunk-ZP4AVIZP.js → chunk-JGA5KBUO.js} +4 -6
  25. package/dist/{chunk-ZP4AVIZP.js.map → chunk-JGA5KBUO.js.map} +1 -1
  26. package/dist/{chunk-GLLDTKZK.cjs → chunk-JUBR24HC.cjs} +7 -9
  27. package/dist/chunk-JUBR24HC.cjs.map +1 -0
  28. package/dist/{chunk-AQOWFSMB.cjs → chunk-L6PSSIUQ.cjs} +1 -1
  29. package/dist/chunk-L6PSSIUQ.cjs.map +1 -0
  30. package/dist/chunk-MFAKIJJU.cjs +144 -0
  31. package/dist/chunk-MFAKIJJU.cjs.map +1 -0
  32. package/dist/{chunk-YIGPRLQY.cjs → chunk-MVTHRLDI.cjs} +21 -20
  33. package/dist/chunk-MVTHRLDI.cjs.map +1 -0
  34. package/dist/{chunk-XREEV72C.cjs → chunk-N3V434AN.cjs} +19 -25
  35. package/dist/chunk-N3V434AN.cjs.map +1 -0
  36. package/dist/{chunk-5P3B2LZW.js → chunk-N67N3OWW.js} +8 -14
  37. package/dist/{chunk-5P3B2LZW.js.map → chunk-N67N3OWW.js.map} +1 -1
  38. package/dist/{chunk-EYEW6PTA.cjs → chunk-NVSAPIIA.cjs} +358 -118
  39. package/dist/chunk-NVSAPIIA.cjs.map +1 -0
  40. package/dist/{chunk-X6BV7MB7.cjs → chunk-OW3Q7DSU.cjs} +37 -31
  41. package/dist/chunk-OW3Q7DSU.cjs.map +1 -0
  42. package/dist/{chunk-3ZXUQQL4.js → chunk-PI4WSYQV.js} +2 -2
  43. package/dist/{chunk-X647HY3F.cjs → chunk-Q22JYCQK.cjs} +33 -37
  44. package/dist/chunk-Q22JYCQK.cjs.map +1 -0
  45. package/dist/{chunk-QHIXS3W2.cjs → chunk-S6DXA35S.cjs} +11591 -2106
  46. package/dist/chunk-S6DXA35S.cjs.map +1 -0
  47. package/dist/{chunk-OY7OF7E7.js → chunk-TNF3DTTC.js} +30 -24
  48. package/dist/chunk-TNF3DTTC.js.map +1 -0
  49. package/dist/{chunk-K2PFPBMF.js → chunk-UFVKCF2C.js} +15050 -5565
  50. package/dist/chunk-UFVKCF2C.js.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-E4XABBSU.js → chunk-YFZ7R4Z4.js} +338 -98
  54. package/dist/chunk-YFZ7R4Z4.js.map +1 -0
  55. package/dist/{chunk-W6M2FLLT.cjs → chunk-ZZOZKXQM.cjs} +40 -46
  56. package/dist/chunk-ZZOZKXQM.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/guide-welcome.d.ts.map +1 -1
  66. package/dist/components/chat/index.cjs +8 -18
  67. package/dist/components/chat/index.cjs.map +1 -1
  68. package/dist/components/chat/index.js +75 -85
  69. package/dist/components/contact/index.cjs +8 -15
  70. package/dist/components/contact/index.cjs.map +1 -1
  71. package/dist/components/contact/index.js +7 -14
  72. package/dist/components/docs/doc-viewer.d.ts +39 -2
  73. package/dist/components/docs/doc-viewer.d.ts.map +1 -1
  74. package/dist/components/docs/docs-hub-page.d.ts +46 -0
  75. package/dist/components/docs/docs-hub-page.d.ts.map +1 -0
  76. package/dist/components/docs/index.cjs +17 -9
  77. package/dist/components/docs/index.cjs.map +1 -1
  78. package/dist/components/docs/index.d.ts +4 -0
  79. package/dist/components/docs/index.d.ts.map +1 -1
  80. package/dist/components/docs/index.js +16 -8
  81. package/dist/components/docs/skeletons.d.ts +32 -0
  82. package/dist/components/docs/skeletons.d.ts.map +1 -0
  83. package/dist/components/docs/use-docs-resolve-link.d.ts +20 -0
  84. package/dist/components/docs/use-docs-resolve-link.d.ts.map +1 -0
  85. package/dist/components/docs/use-document-tree.d.ts.map +1 -1
  86. package/dist/components/embeds/embed-container.d.ts +37 -0
  87. package/dist/components/embeds/embed-container.d.ts.map +1 -0
  88. package/dist/components/embeds/embed-iframe.d.ts.map +1 -1
  89. package/dist/components/embeds/file-download-card.d.ts +18 -0
  90. package/dist/components/embeds/file-download-card.d.ts.map +1 -0
  91. package/dist/components/embeds/index.cjs +38 -15
  92. package/dist/components/embeds/index.cjs.map +1 -1
  93. package/dist/components/embeds/index.d.ts +8 -0
  94. package/dist/components/embeds/index.d.ts.map +1 -1
  95. package/dist/components/embeds/index.js +40 -17
  96. package/dist/components/embeds/linkedin-embed-client.d.ts +8 -0
  97. package/dist/components/embeds/linkedin-embed-client.d.ts.map +1 -0
  98. package/dist/components/embeds/markdown-image.d.ts +5 -0
  99. package/dist/components/embeds/markdown-image.d.ts.map +1 -0
  100. package/dist/components/embeds/reddit-embed-client.d.ts +7 -0
  101. package/dist/components/embeds/reddit-embed-client.d.ts.map +1 -0
  102. package/dist/components/embeds/rich-markdown-runtime.d.ts +46 -0
  103. package/dist/components/embeds/rich-markdown-runtime.d.ts.map +1 -0
  104. package/dist/components/embeds/twitter-embed-client.d.ts +8 -0
  105. package/dist/components/embeds/twitter-embed-client.d.ts.map +1 -0
  106. package/dist/components/faq/index.cjs +9 -16
  107. package/dist/components/faq/index.cjs.map +1 -1
  108. package/dist/components/faq/index.js +8 -15
  109. package/dist/components/features/index.cjs +8 -16
  110. package/dist/components/features/index.cjs.map +1 -1
  111. package/dist/components/features/index.js +24 -32
  112. package/dist/components/index.cjs +257 -452
  113. package/dist/components/index.cjs.map +1 -1
  114. package/dist/components/index.js +781 -976
  115. package/dist/components/index.js.map +1 -1
  116. package/dist/components/layout/page-header.d.ts +78 -0
  117. package/dist/components/layout/page-header.d.ts.map +1 -0
  118. package/dist/components/layout/page-layout.d.ts +10 -1
  119. package/dist/components/layout/page-layout.d.ts.map +1 -1
  120. package/dist/components/layout/page-with-header.d.ts +67 -0
  121. package/dist/components/layout/page-with-header.d.ts.map +1 -0
  122. package/dist/components/layout/title-block.d.ts +17 -1
  123. package/dist/components/layout/title-block.d.ts.map +1 -1
  124. package/dist/components/navigation/index.cjs +7 -15
  125. package/dist/components/navigation/index.cjs.map +1 -1
  126. package/dist/components/navigation/index.js +9 -17
  127. package/dist/components/onboarding-guides/index.cjs +35 -36
  128. package/dist/components/onboarding-guides/index.cjs.map +1 -1
  129. package/dist/components/onboarding-guides/index.js +13 -14
  130. package/dist/components/onboarding-guides/index.js.map +1 -1
  131. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts +1 -1
  132. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  133. package/dist/components/related-content/index.cjs +9 -16
  134. package/dist/components/related-content/index.cjs.map +1 -1
  135. package/dist/components/related-content/index.js +8 -15
  136. package/dist/components/shared/dev-section/dev-section-page.d.ts +9 -0
  137. package/dist/components/shared/dev-section/dev-section-page.d.ts.map +1 -1
  138. package/dist/components/shared/dev-section/dev-section-view.d.ts.map +1 -1
  139. package/dist/components/shared/dev-section/index.d.ts +1 -1
  140. package/dist/components/shared/dev-section/index.d.ts.map +1 -1
  141. package/dist/components/shared/doc-search/use-doc-search.d.ts.map +1 -1
  142. package/dist/components/shared/legal-document/legal-document-page.d.ts.map +1 -1
  143. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  144. package/dist/components/tickets/index.cjs +100 -112
  145. package/dist/components/tickets/index.cjs.map +1 -1
  146. package/dist/components/tickets/index.js +20 -32
  147. package/dist/components/tickets/index.js.map +1 -1
  148. package/dist/components/ui/file-manager/index.cjs +50 -52
  149. package/dist/components/ui/file-manager/index.cjs.map +1 -1
  150. package/dist/components/ui/file-manager/index.js +4 -6
  151. package/dist/components/ui/file-manager/index.js.map +1 -1
  152. package/dist/components/ui/index.cjs +13 -19
  153. package/dist/components/ui/index.cjs.map +1 -1
  154. package/dist/components/ui/index.d.ts +2 -0
  155. package/dist/components/ui/index.d.ts.map +1 -1
  156. package/dist/components/ui/index.js +133 -139
  157. package/dist/components/ui/release-changelog-section.d.ts +6 -2
  158. package/dist/components/ui/release-changelog-section.d.ts.map +1 -1
  159. package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
  160. package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
  161. package/dist/components/ui/simple-markdown-renderer.d.ts +2 -8
  162. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
  163. package/dist/contexts/chat-runtime-context.d.ts +14 -0
  164. package/dist/contexts/chat-runtime-context.d.ts.map +1 -1
  165. package/dist/contexts/index.cjs +3 -3
  166. package/dist/contexts/index.js +5 -5
  167. package/dist/embed-shims/index.cjs +3 -3
  168. package/dist/embed-shims/index.cjs.map +1 -1
  169. package/dist/embed-shims/index.js +4 -4
  170. package/dist/hooks/index.cjs +4 -9
  171. package/dist/hooks/index.cjs.map +1 -1
  172. package/dist/hooks/index.js +6 -11
  173. package/dist/index.cjs +14 -20
  174. package/dist/index.cjs.map +1 -1
  175. package/dist/index.js +362 -368
  176. package/dist/types/doc-source.d.ts +31 -1
  177. package/dist/types/doc-source.d.ts.map +1 -1
  178. package/dist/utils/index.cjs +4 -0
  179. package/dist/utils/index.cjs.map +1 -1
  180. package/dist/utils/index.d.ts +1 -0
  181. package/dist/utils/index.d.ts.map +1 -1
  182. package/dist/utils/index.js +4 -1
  183. package/dist/utils/index.js.map +1 -1
  184. package/dist/utils/page-header-constants.d.ts +15 -0
  185. package/dist/utils/page-header-constants.d.ts.map +1 -0
  186. package/dist/utils/social-embed-cache.d.ts +29 -0
  187. package/dist/utils/social-embed-cache.d.ts.map +1 -0
  188. package/package.json +7 -1
  189. package/src/components/case-studies/index.ts +4 -0
  190. package/src/components/case-studies/share-experience-section.tsx +185 -0
  191. package/src/components/chat/embeddable-chat.tsx +1 -1
  192. package/src/components/chat/guide-welcome.tsx +12 -3
  193. package/src/components/docs/doc-viewer.tsx +111 -19
  194. package/src/components/docs/docs-hub-page.tsx +149 -0
  195. package/src/components/docs/index.ts +17 -0
  196. package/src/components/docs/skeletons.tsx +138 -0
  197. package/src/components/docs/use-docs-resolve-link.ts +52 -0
  198. package/src/components/docs/use-document-tree.ts +21 -0
  199. package/src/components/embeds/embed-container.tsx +80 -0
  200. package/src/components/embeds/embed-iframe.tsx +7 -9
  201. package/src/components/embeds/file-download-card.tsx +54 -0
  202. package/src/components/embeds/index.ts +30 -0
  203. package/src/components/embeds/linkedin-embed-client.tsx +100 -0
  204. package/src/components/embeds/markdown-image.tsx +88 -0
  205. package/src/components/embeds/og-link-preview.tsx +13 -13
  206. package/src/components/embeds/reddit-embed-client.tsx +550 -0
  207. package/src/components/embeds/rich-markdown-runtime.tsx +79 -0
  208. package/src/components/embeds/twitter-embed-client.tsx +308 -0
  209. package/src/components/layout/page-header.tsx +182 -0
  210. package/src/components/layout/page-layout.tsx +14 -1
  211. package/src/components/layout/page-with-header.tsx +110 -0
  212. package/src/components/layout/title-block.tsx +40 -62
  213. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +3 -3
  214. package/src/components/shared/dev-section/dev-section-page.tsx +9 -1
  215. package/src/components/shared/dev-section/dev-section-view.tsx +14 -9
  216. package/src/components/shared/dev-section/index.ts +1 -1
  217. package/src/components/shared/doc-search/use-doc-search.ts +7 -3
  218. package/src/components/shared/legal-document/legal-document-page.tsx +2 -2
  219. package/src/components/shared/product-release/release-detail-page.tsx +6 -4
  220. package/src/components/ui/index.ts +2 -0
  221. package/src/components/ui/release-changelog-section.tsx +7 -2
  222. package/src/components/ui/rich-markdown-renderer.tsx +1203 -0
  223. package/src/components/ui/simple-markdown-renderer.tsx +7 -11
  224. package/src/contexts/chat-runtime-context.tsx +14 -0
  225. package/src/types/doc-source.ts +33 -1
  226. package/src/utils/index.ts +1 -0
  227. package/src/utils/page-header-constants.ts +15 -0
  228. package/src/utils/social-embed-cache.ts +391 -0
  229. package/dist/chunk-26PKDALD.js +0 -2379
  230. package/dist/chunk-26PKDALD.js.map +0 -1
  231. package/dist/chunk-3MCHAFHB.js +0 -89
  232. package/dist/chunk-3MCHAFHB.js.map +0 -1
  233. package/dist/chunk-5E2HOSSH.cjs.map +0 -1
  234. package/dist/chunk-66AANIOC.cjs +0 -619
  235. package/dist/chunk-66AANIOC.cjs.map +0 -1
  236. package/dist/chunk-6JINAOI7.cjs +0 -311
  237. package/dist/chunk-6JINAOI7.cjs.map +0 -1
  238. package/dist/chunk-7RIYT7ZH.js.map +0 -1
  239. package/dist/chunk-AQOWFSMB.cjs.map +0 -1
  240. package/dist/chunk-BOCFIKYS.cjs +0 -3009
  241. package/dist/chunk-BOCFIKYS.cjs.map +0 -1
  242. package/dist/chunk-D652TJBQ.js +0 -3009
  243. package/dist/chunk-D652TJBQ.js.map +0 -1
  244. package/dist/chunk-E4XABBSU.js.map +0 -1
  245. package/dist/chunk-EL6QLAWX.js.map +0 -1
  246. package/dist/chunk-EYEW6PTA.cjs.map +0 -1
  247. package/dist/chunk-FQJK446R.js +0 -1606
  248. package/dist/chunk-FQJK446R.js.map +0 -1
  249. package/dist/chunk-GLLDTKZK.cjs.map +0 -1
  250. package/dist/chunk-IE6OU3WQ.cjs.map +0 -1
  251. package/dist/chunk-J54Z3OCR.cjs +0 -1606
  252. package/dist/chunk-J54Z3OCR.cjs.map +0 -1
  253. package/dist/chunk-K2PFPBMF.js.map +0 -1
  254. package/dist/chunk-KXCRGTRN.cjs +0 -2379
  255. package/dist/chunk-KXCRGTRN.cjs.map +0 -1
  256. package/dist/chunk-LCNMR277.js.map +0 -1
  257. package/dist/chunk-LFGGF7OT.cjs +0 -449
  258. package/dist/chunk-LFGGF7OT.cjs.map +0 -1
  259. package/dist/chunk-M2OCXTNT.js +0 -311
  260. package/dist/chunk-M2OCXTNT.js.map +0 -1
  261. package/dist/chunk-ME4EVDFP.js +0 -619
  262. package/dist/chunk-ME4EVDFP.js.map +0 -1
  263. package/dist/chunk-OQ6X7ZOC.js +0 -449
  264. package/dist/chunk-OQ6X7ZOC.js.map +0 -1
  265. package/dist/chunk-OY7OF7E7.js.map +0 -1
  266. package/dist/chunk-POKKCWKF.js +0 -354
  267. package/dist/chunk-POKKCWKF.js.map +0 -1
  268. package/dist/chunk-QHIXS3W2.cjs.map +0 -1
  269. package/dist/chunk-TFSYSWPS.cjs +0 -89
  270. package/dist/chunk-TFSYSWPS.cjs.map +0 -1
  271. package/dist/chunk-W6M2FLLT.cjs.map +0 -1
  272. package/dist/chunk-X647HY3F.cjs.map +0 -1
  273. package/dist/chunk-X6BV7MB7.cjs.map +0 -1
  274. package/dist/chunk-XREEV72C.cjs.map +0 -1
  275. package/dist/chunk-YETA25JW.cjs +0 -354
  276. package/dist/chunk-YETA25JW.cjs.map +0 -1
  277. package/dist/chunk-YIGPRLQY.cjs.map +0 -1
  278. /package/dist/{chunk-3ZXUQQL4.js.map → chunk-PI4WSYQV.js.map} +0 -0
@@ -6,13 +6,13 @@
6
6
 
7
7
 
8
8
 
9
- var _chunkJ54Z3OCRcjs = require('./chunk-J54Z3OCR.cjs');
9
+ var _chunkS6DXA35Scjs = require('./chunk-S6DXA35S.cjs');
10
10
 
11
11
 
12
- var _chunkYETA25JWcjs = require('./chunk-YETA25JW.cjs');
12
+ var _chunkWMSTJAZTcjs = require('./chunk-WMSTJAZT.cjs');
13
13
 
14
14
 
15
- var _chunkAQOWFSMBcjs = require('./chunk-AQOWFSMB.cjs');
15
+ var _chunkL6PSSIUQcjs = require('./chunk-L6PSSIUQ.cjs');
16
16
 
17
17
 
18
18
  var _chunkG7UE6RKVcjs = require('./chunk-G7UE6RKV.cjs');
@@ -33,7 +33,7 @@ function DocSearchResultRow({
33
33
  }) {
34
34
  const docType = _optionalChain([result, 'access', _ => _.metadata, 'optionalAccess', _2 => _2.documentType]) || void 0;
35
35
  const sourceRepo = _optionalChain([result, 'access', _3 => _3.metadata, 'optionalAccess', _4 => _4.sourceRepo]) || void 0;
36
- const { Icon: SourceIcon, label: iconLabel } = _chunkJ54Z3OCRcjs.resolveSourceIcon.call(void 0, {
36
+ const { Icon: SourceIcon, label: iconLabel } = _chunkS6DXA35Scjs.resolveSourceIcon.call(void 0, {
37
37
  sourceRepo,
38
38
  documentType: docType
39
39
  });
@@ -76,7 +76,7 @@ function DocSearchBar({
76
76
  renderResult
77
77
  }) {
78
78
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
79
- _chunkJ54Z3OCRcjs.SearchInput,
79
+ _chunkS6DXA35Scjs.SearchInput,
80
80
  {
81
81
  placeholder,
82
82
  value: query,
@@ -185,7 +185,7 @@ function resolveSearchResultAction(result, source, runtimeMode) {
185
185
  const externalUrl = meta.externalUrl;
186
186
  if (externalUrl) {
187
187
  const targetPlatform = meta.targetPlatform;
188
- const isNewTab = _chunkJ54Z3OCRcjs.decideNewTab.call(void 0, {
188
+ const isNewTab = _chunkS6DXA35Scjs.decideNewTab.call(void 0, {
189
189
  href: externalUrl,
190
190
  targetPlatform,
191
191
  surface: "useUnifiedNav",
@@ -221,15 +221,16 @@ function useDocSearch(config) {
221
221
  onNavigate,
222
222
  tableIds,
223
223
  onInPageSwap,
224
- searchEndpoint = "/api/docs/search"
224
+ searchEndpoint
225
225
  } = config;
226
226
  const tableIdsKey = tableIds && tableIds.length > 0 ? tableIds.join(",") : "";
227
227
  const router = _chunkG7UE6RKVcjs.useRouter.call(void 0, );
228
- const runtime = _chunkAQOWFSMBcjs.useChatRuntime.call(void 0, );
228
+ const runtime = _chunkL6PSSIUQcjs.useChatRuntime.call(void 0, );
229
+ const resolvedSearchEndpoint = _nullishCoalesce(_nullishCoalesce(searchEndpoint, () => ( _optionalChain([runtime, 'optionalAccess', _10 => _10.endpoints, 'access', _11 => _11.docsSearchUrl]))), () => ( "/api/docs/search"));
229
230
  const [query, setQuery] = _react.useState.call(void 0, "");
230
231
  const [results, setResults] = _react.useState.call(void 0, []);
231
232
  const [isFetching, setIsFetching] = _react.useState.call(void 0, false);
232
- const debouncedQuery = _chunkYETA25JWcjs.useDebounce.call(void 0, query, 300);
233
+ const debouncedQuery = _chunkWMSTJAZTcjs.useDebounce.call(void 0, query, 300);
233
234
  _react.useEffect.call(void 0, () => {
234
235
  if (!debouncedQuery || debouncedQuery.trim().length < 2) {
235
236
  setResults([]);
@@ -246,7 +247,7 @@ function useDocSearch(config) {
246
247
  limit: "10"
247
248
  });
248
249
  if (tableIdsKey) params.set("tableIds", tableIdsKey);
249
- const response = await fetch(`${searchEndpoint}?${params.toString()}`);
250
+ const response = await fetch(`${resolvedSearchEndpoint}?${params.toString()}`);
250
251
  if (!response.ok) {
251
252
  throw new Error(`Search request failed: ${response.status}`);
252
253
  }
@@ -270,7 +271,7 @@ function useDocSearch(config) {
270
271
  return () => {
271
272
  cancelled = true;
272
273
  };
273
- }, [debouncedQuery, source, tableIdsKey, searchEndpoint]);
274
+ }, [debouncedQuery, source, tableIdsKey, resolvedSearchEndpoint]);
274
275
  const isLoading = query.trim().length >= 2 && (query !== debouncedQuery || isFetching);
275
276
  const [keepOpen, setKeepOpen] = _react.useState.call(void 0, false);
276
277
  const handleResultSelect = _react.useCallback.call(void 0,
@@ -278,15 +279,15 @@ function useDocSearch(config) {
278
279
  const action = resolveSearchResultAction(
279
280
  result,
280
281
  source,
281
- _optionalChain([runtime, 'optionalAccess', _10 => _10.navigation, 'access', _11 => _11.mode])
282
+ _optionalChain([runtime, 'optionalAccess', _12 => _12.navigation, 'access', _13 => _13.mode])
282
283
  );
283
284
  const wantsNewTab = modifiers && (modifiers.metaKey || modifiers.ctrlKey || modifiers.shiftKey || modifiers.altKey || typeof modifiers.button === "number" && modifiers.button !== 0);
284
285
  switch (action.kind) {
285
286
  case "navigate-same-tab": {
286
- if (_optionalChain([runtime, 'optionalAccess', _12 => _12.navigation, 'access', _13 => _13.mode]) === "embed") {
287
+ if (_optionalChain([runtime, 'optionalAccess', _14 => _14.navigation, 'access', _15 => _15.mode]) === "embed") {
287
288
  setKeepOpen(true);
288
- const targetPlatform = _nullishCoalesce(_optionalChain([result, 'access', _14 => _14.metadata, 'optionalAccess', _15 => _15.targetPlatform]), () => ( null));
289
- _chunkJ54Z3OCRcjs.resolveExternalNavigation.call(void 0, {
289
+ const targetPlatform = _nullishCoalesce(_optionalChain([result, 'access', _16 => _16.metadata, 'optionalAccess', _17 => _17.targetPlatform]), () => ( null));
290
+ _chunkS6DXA35Scjs.resolveExternalNavigation.call(void 0, {
290
291
  href: action.href,
291
292
  targetPlatform,
292
293
  runtime
@@ -295,18 +296,18 @@ function useDocSearch(config) {
295
296
  }
296
297
  if (wantsNewTab) {
297
298
  setKeepOpen(true);
298
- window.open(action.href, "_blank", _chunkJ54Z3OCRcjs.NEW_TAB_FEATURES);
299
+ window.open(action.href, "_blank", _chunkS6DXA35Scjs.NEW_TAB_FEATURES);
299
300
  return;
300
301
  }
301
302
  setKeepOpen(false);
302
303
  const path = baseRoute && action.href.startsWith(`${baseRoute}/`) ? action.href.slice(baseRoute.length + 1) : null;
303
- if (path && _optionalChain([onInPageSwap, 'optionalCall', _16 => _16(path)])) return;
304
- router.push(_chunkJ54Z3OCRcjs.stripSameOriginToPath.call(void 0, action.href));
304
+ if (path && _optionalChain([onInPageSwap, 'optionalCall', _18 => _18(path)])) return;
305
+ router.push(_chunkS6DXA35Scjs.stripSameOriginToPath.call(void 0, action.href));
305
306
  return;
306
307
  }
307
308
  case "navigate-new-tab":
308
309
  setKeepOpen(true);
309
- window.open(action.href, "_blank", _chunkJ54Z3OCRcjs.NEW_TAB_FEATURES);
310
+ window.open(action.href, "_blank", _chunkS6DXA35Scjs.NEW_TAB_FEATURES);
310
311
  return;
311
312
  case "ask-ai":
312
313
  setKeepOpen(false);
@@ -345,4 +346,4 @@ function useDocSearch(config) {
345
346
 
346
347
 
347
348
  exports.formatRelativePath = formatRelativePath; exports.DocSearchResultRow = DocSearchResultRow; exports.DocSearchBar = DocSearchBar; exports.mapDocSearchResults = mapDocSearchResults; exports.resolveSearchResultAction = resolveSearchResultAction; exports.useDocSearch = useDocSearch;
348
- //# sourceMappingURL=chunk-YIGPRLQY.cjs.map
349
+ //# sourceMappingURL=chunk-MVTHRLDI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-MVTHRLDI.cjs","../src/components/shared/doc-search/format-relative-path.ts","../src/components/shared/doc-search/doc-search-result-row.tsx","../src/components/shared/doc-search/doc-search-bar.tsx","../src/components/shared/doc-search/map-doc-search-results.ts","../src/components/shared/doc-search/resolve-search-result-action.ts","../src/components/shared/doc-search/use-doc-search.ts"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-MVTHRLDI.cjs","sourcesContent":[null,"/**\n * Format a full document path as a breadcrumb trail.\n * Shows parent folders only (excludes the last segment / filename).\n *\n * @example\n * formatRelativePath('openframe-oss-tenant/architecture/api-controllers.md')\n * // → 'Openframe oss tenant / Architecture'\n */\nexport function formatRelativePath(fullPath: string): string {\n if (!fullPath) return ''\n const segments = fullPath.replace(/\\.md$/, '').split('/')\n // Show only parent path (exclude the filename itself since the title already shows it)\n const parentSegments = segments.length > 1 ? segments.slice(0, -1) : segments\n return parentSegments\n .map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1).replace(/-/g, ' '))\n .join(' / ')\n}\n","'use client'\n\n/**\n * Single row in the `<SearchInput>` dropdown — the standard layout\n * used by every doc-search-backed surface (company-hub data-room\n * search bar, onboarding-guide catalog search, …). Single source of\n * truth for the row appearance so search dropdowns are visually\n * identical everywhere.\n *\n * Resolves the source icon via the same `resolveSourceIcon()`\n * registry the inline chat-card refs use, so a row pointing at e.g.\n * an onboarding-guide surfaces the SAME `<GraduationCap>` glyph the\n * chat card surfaces — no cross-surface drift.\n */\n\nimport { resolveSourceIcon } from '../../chat/utils/source-row-cta'\nimport { formatRelativePath } from './format-relative-path'\n\n/**\n * Minimal result shape this row renders. Compatible with any\n * doc-search hook whose result type exposes `{ title?, path?,\n * metadata? }`. The two hub consumers (onboarding-guide catalog,\n * data-room sidebar) both satisfy this shape via their\n * `useDocSearch` hook result.\n */\nexport interface DocSearchResultRowEntry {\n title?: string\n path?: string\n metadata?: Record<string, unknown>\n}\n\nexport interface DocSearchResultRowProps {\n result: DocSearchResultRowEntry\n isHighlighted: boolean\n}\n\nexport function DocSearchResultRow({\n result,\n isHighlighted,\n}: DocSearchResultRowProps) {\n const docType = (result.metadata?.documentType as string) || undefined\n const sourceRepo = (result.metadata?.sourceRepo as string) || undefined\n const { Icon: SourceIcon, label: iconLabel } = resolveSourceIcon({\n sourceRepo,\n documentType: docType,\n })\n const isGroup = result.metadata?.isGroup as boolean | undefined\n\n return (\n <div className=\"flex items-center gap-3 w-full min-w-0\">\n <span\n className=\"flex-shrink-0 text-ods-text-secondary\"\n title={iconLabel}\n >\n <SourceIcon className=\"size-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div\n className={`text-sm font-medium leading-5 truncate ${\n isHighlighted ? 'text-ods-accent' : 'text-ods-text-primary'\n }`}\n >\n {result.title || result.path}\n </div>\n {!isGroup && result.path?.includes('/') && (\n <div className=\"text-xs leading-4 text-ods-text-secondary truncate mt-0.5\">\n {formatRelativePath(result.path)}\n </div>\n )}\n </div>\n </div>\n )\n}\n","'use client'\n\n/**\n * `<DocSearchBar>` — the canonical RAG-search dropdown surface.\n *\n * Mounted by every doc-search consumer (data-room sidebar, onboarding-\n * guide catalog, and any future surface that needs typeahead against\n * `/api/docs/search`). Wraps `<SearchInput>` with the lib's standard\n * `<DocSearchResultRow>` so the dropdown looks identical everywhere.\n *\n * ## Why a presentation component, not a \"search bar that owns its\n * own hook\"\n *\n * The data-fetching hook (`useDocSearch`) lives hub-side because it\n * depends on hub-only context (`useDocNavigation`, the rag-table-\n * config registry, the hub's `decideNewTab` helper). Moving the hook\n * would cascade ~5 more file migrations into the lib.\n *\n * Instead, the hook stays hub-side and callers pass its result into\n * this component as plain props. Both consumers shrink to ~5 lines.\n */\n\nimport type { ReactNode } from 'react'\nimport { SearchInput, type SearchResult } from '../../ui/search-input'\nimport { DocSearchResultRow } from './doc-search-result-row'\n\nexport interface DocSearchBarProps {\n placeholder: string\n query: string\n onQueryChange: (value: string) => void\n /** Hook-fetched results. Reuses the lib's `<SearchInput>` `SearchResult`\n * shape directly so callers don't translate. */\n results: SearchResult[]\n isLoading: boolean\n /** Result selection handler. Mirrors `<SearchInput>` — the second\n * `modifiers` argument is preserved so cmd-click / shift-click on\n * a result row still forces new-tab behavior. Hub `useDocSearch`\n * reads these to short-circuit to `window.open()`. */\n onResultSelect: (\n result: SearchResult,\n modifiers?: {\n metaKey?: boolean\n ctrlKey?: boolean\n shiftKey?: boolean\n altKey?: boolean\n button?: number\n },\n ) => void\n /** Lets the caller's hook force the dropdown open after a recent\n * internal action (e.g. result navigation). `undefined` falls back\n * to `<SearchInput>`'s built-in focus/hover heuristics. */\n showDropdown?: boolean\n /** Defaults to 2 — matches the existing data-room and onboarding-\n * guide consumers. Override only if a surface needs different\n * typeahead semantics. */\n minQueryLength?: number\n /** Defaults to 0 — both existing consumers debounce inside the\n * hook, not the input. */\n debounceMs?: number\n className?: string\n /** Optional row-renderer override. Defaults to the lib's standard\n * `<DocSearchResultRow>` (source icon + title + path breadcrumb).\n * Override only when a surface needs custom row chrome. */\n renderResult?: (result: SearchResult, isHighlighted: boolean) => ReactNode\n}\n\nexport function DocSearchBar({\n placeholder,\n query,\n onQueryChange,\n results,\n isLoading,\n onResultSelect,\n showDropdown,\n minQueryLength = 2,\n debounceMs = 0,\n className = 'w-full',\n renderResult,\n}: DocSearchBarProps) {\n return (\n <SearchInput\n placeholder={placeholder}\n value={query}\n onChange={onQueryChange}\n results={results}\n isLoading={isLoading}\n onResultSelect={onResultSelect}\n showDropdown={showDropdown || undefined}\n debounceMs={debounceMs}\n minQueryLength={minQueryLength}\n className={className}\n renderResult={\n renderResult ??\n ((result, isHighlighted) => (\n <DocSearchResultRow result={result} isHighlighted={isHighlighted} />\n ))\n }\n />\n )\n}\n","/**\n * Map RAG `/api/docs/search` wire results into the `<DocSearchBar>`\n * dropdown's row shape, collapsing entity-table rows into grouped\n * results so the dropdown lists ONE \"Cap Table (12 records)\" row\n * instead of 12 individual rows.\n *\n * Pure transform — no telemetry, no navigation, no React deps. Lifted\n * from the hub's `hooks/use-docs.ts:mapDocSearchResults` (the hub's\n * `traceCompose` call was hub-only telemetry and is intentionally\n * dropped — callers that want logging can wrap this helper).\n */\n\nimport type { SearchResult } from '../../ui/search-input'\nimport type { DocSearchResult } from './types'\n\n/** Source repos that should be collapsed into grouped results in the search bar.\n * Only financial tables (all rows link to the same admin page).\n * Content tables (blog, webinar, podcast, etc.) stay individual since each has a unique URL. */\nconst SEARCH_GROUP_REPOS = new Set([\n 'financial-cap-table',\n 'financial-kpis',\n 'financial-pnl',\n 'financial-balance-sheet',\n 'financial-cash-flow',\n])\n\nconst ENTITY_LABELS: Record<string, string> = {\n 'financial-cap-table': 'Cap Table',\n 'financial-kpis': 'Financial KPIs',\n 'financial-pnl': 'Profit & Loss',\n 'financial-balance-sheet': 'Balance Sheets',\n 'financial-cash-flow': 'Cash Flow',\n 'blog-posts': 'Blog Posts',\n 'product-releases': 'Product Releases',\n 'case-studies': 'Case Studies',\n webinars: 'Webinars',\n events: 'Events',\n podcasts: 'Podcasts',\n}\n\nexport function mapDocSearchResults(docs: DocSearchResult[]): SearchResult[] {\n const entityGroups = new Map<string, DocSearchResult[]>()\n // Track insertion order — groups appear where the FIRST row of that\n // repo appeared in the response.\n const order: Array<\n { type: 'entity'; repo: string } | { type: 'doc'; doc: DocSearchResult }\n > = []\n const seenRepos = new Set<string>()\n\n for (const doc of docs) {\n if (doc.sourceRepo && SEARCH_GROUP_REPOS.has(doc.sourceRepo)) {\n const group = entityGroups.get(doc.sourceRepo) || []\n group.push(doc)\n entityGroups.set(doc.sourceRepo, group)\n if (!seenRepos.has(doc.sourceRepo)) {\n seenRepos.add(doc.sourceRepo)\n order.push({ type: 'entity', repo: doc.sourceRepo })\n }\n } else {\n order.push({ type: 'doc', doc })\n }\n }\n\n const results: SearchResult[] = []\n for (const entry of order) {\n if (entry.type === 'entity') {\n const rows = entityGroups.get(entry.repo)!\n const label = ENTITY_LABELS[entry.repo] || entry.repo\n results.push({\n id: `group-${entry.repo}`,\n title: `${label} (${rows.length} ${rows.length === 1 ? 'record' : 'records'})`,\n path: rows[0].path,\n type: 'file',\n metadata: {\n documentType: rows[0].documentType,\n externalUrl: rows[0].externalUrl,\n sourceRepo: entry.repo,\n id: rows[0].entityId,\n isGroup: true,\n items: rows.map((r) => ({\n name: r.name,\n externalUrl: r.externalUrl,\n id: r.entityId,\n sourceRepo: r.sourceRepo,\n documentType: r.documentType,\n })),\n },\n })\n } else {\n const doc = entry.doc\n const isNonMarkdown = doc.documentType && doc.documentType !== 'markdown'\n results.push({\n id: doc.path,\n title: doc.name,\n description: isNonMarkdown ? doc.name : doc.snippet,\n path: doc.path,\n type: doc.type,\n metadata: {\n matchType: doc.matchType,\n ...(doc.documentType ? { documentType: doc.documentType } : {}),\n ...(doc.externalUrl ? { externalUrl: doc.externalUrl } : {}),\n ...(doc.targetPlatform != null\n ? { targetPlatform: doc.targetPlatform }\n : {}),\n ...(doc.sourceRepo ? { sourceRepo: doc.sourceRepo } : {}),\n ...(doc.entityId ? { id: doc.entityId } : {}),\n },\n })\n }\n }\n\n return results\n}\n","/**\n * Resolve what should happen when the user picks a search result.\n * Returns one of five typed actions so the caller is a single switch.\n *\n * Resolution order:\n * 1. `externalUrl` present → use `decideNewTab` to choose same-tab vs\n * new-tab against the row's `targetPlatform`.\n * 2. Row has `id` + `sourceRepo` + `documentType` → synth an Ask-AI\n * action (entity drill-in via primary key, no URL).\n * 3. Row has only `path` → legacy navigation fallback.\n * 4. Nothing actionable → noop.\n *\n * Lifted from the hub's `hooks/use-docs.ts:resolveSearchResultAction`.\n * Pure — no React, no telemetry.\n */\n\nimport type { SearchResult } from '../../ui/search-input'\nimport type { ChatRef } from '../../chat/chat-ref.types'\nimport { decideNewTab } from '../../chat/utils/decide-new-tab'\n\nexport type SearchResultAction =\n | { kind: 'navigate-same-tab'; href: string }\n | { kind: 'navigate-new-tab'; href: string }\n | { kind: 'ask-ai'; detail: { source: string; ref: ChatRef } }\n | { kind: 'route'; path: string }\n | { kind: 'noop' }\n\nexport function resolveSearchResultAction(\n result: SearchResult,\n source: string,\n runtimeMode?: 'host' | 'embed',\n): SearchResultAction {\n const meta = result.metadata ?? {}\n const externalUrl = meta.externalUrl as string | undefined\n if (externalUrl) {\n // Same pure helper `useNavLink` and `useUnifiedNav` call — single\n // decision rule across cards, chips, and autocomplete rows. Thread\n // the caller's `source` as `currentSource` so the platform-vs-\n // platform comparison matches the hub's pre-migration behavior.\n const targetPlatform = meta.targetPlatform as string | null | undefined\n const isNewTab = decideNewTab({\n href: externalUrl,\n targetPlatform,\n surface: 'useUnifiedNav',\n runtimeMode,\n currentSource: source,\n })\n return isNewTab\n ? { kind: 'navigate-new-tab', href: externalUrl }\n : { kind: 'navigate-same-tab', href: externalUrl }\n }\n const rowId = meta.id as string | undefined\n const sourceRepo = meta.sourceRepo as string | undefined\n const documentType = meta.documentType as string | undefined\n if (rowId && sourceRepo && documentType) {\n return {\n kind: 'ask-ai',\n detail: {\n source,\n ref: { type: documentType, id: rowId, title: result.title, url: null },\n },\n }\n }\n if (result.path) {\n return { kind: 'route', path: result.path }\n }\n return { kind: 'noop' }\n}\n","'use client'\n\n/**\n * `useDocSearch` — debounced RAG-search hook against `/api/docs/search`.\n *\n * Pure fetch + navigation glue. Embedders can mount this directly\n * (any host with a reverse-proxy that exposes `/api/docs/search` will\n * work). Hub callers wire it into the lib `<DocSearchBar>` for the\n * canonical typeahead dropdown.\n *\n * ## What moved from hub to lib\n *\n * Lifted from `multi-platform-hub/hooks/use-docs.ts:useDocSearch`. Two\n * hub-only concerns are now optional injection points instead of\n * direct imports:\n *\n * - `useDocNavigation()` (hub's in-page doc-tree swap) → optional\n * `onInPageSwap?: (path: string) => boolean` config callback. When\n * present and returns true, the hook treats a same-origin result\n * click as \"handled in-page\"; when absent or returns false, the\n * hook falls back to `onNavigate(path)` (`router.push` on hub,\n * `window.location.assign` on bare embedders).\n * - `traceCompose` (hub-only telemetry) → dropped. The lib has no\n * equivalent runtime-context yet; bring it back when there is one.\n *\n * Everything else (debounce, `useChatRuntime` for embed-mode short-\n * circuit, embed-shim router, the action-resolver + result-mapper) is\n * now lib-resident.\n */\n\nimport { useState, useEffect, useCallback } from 'react'\nimport { useRouter } from '../../../embed-shims'\nimport { useDebounce } from '../../../hooks/ui/use-debounce'\nimport { useChatRuntime } from '../../../contexts/chat-runtime-context'\nimport type { SearchResult } from '../../ui/search-input'\nimport {\n resolveExternalNavigation,\n stripSameOriginToPath,\n NEW_TAB_FEATURES,\n} from '../../chat/utils/chat-nav-resolution'\nimport type { DocSearchResult } from './types'\nimport { mapDocSearchResults } from './map-doc-search-results'\nimport { resolveSearchResultAction } from './resolve-search-result-action'\n\nexport interface UseDocSearchConfig {\n /** Discriminator passed to `/api/docs/search?source=` (e.g.\n * `'openframe'`). Embedders set it to whatever discriminator their\n * reverse-proxy expects. */\n source: string\n /** Base route prefix this search lives under (e.g. `'/onboarding-guides'`).\n * When a result's href starts with `${baseRoute}/`, the hook\n * attempts the optional in-page swap path before falling through\n * to a full nav. */\n baseRoute: string\n /** Imperative navigation fallback. Called when no override\n * (in-page swap, new-tab) applies. Hub callers pass\n * `(path) => router.push(path)`; embedders pass an equivalent. */\n onNavigate: (path: string) => void\n /** Optional `RagTableConfig.id` list to narrow the search to specific\n * tables (e.g. `['onboarding-guides']`). Forwarded to\n * `/api/docs/search?tableIds=…` which intersects with the source's\n * standing set. */\n tableIds?: string[]\n /** Optional in-page swap callback. When the result's href is under\n * `baseRoute` AND this callback returns true, the hook treats the\n * click as handled in-page (no router push). Hub's\n * `<DocumentationSection>` wires this to\n * `useDocNavigation().navigate(path)`. */\n onInPageSwap?: (path: string) => boolean\n /** Optional endpoint override. Defaults to `'/api/docs/search'`\n * (the hub's reverse-proxy route). Embedders with a different\n * path can override. */\n searchEndpoint?: string\n}\n\nexport function useDocSearch(config: UseDocSearchConfig) {\n const {\n source,\n baseRoute,\n onNavigate,\n tableIds,\n onInPageSwap,\n searchEndpoint,\n } = config\n const tableIdsKey = tableIds && tableIds.length > 0 ? tableIds.join(',') : ''\n\n const router = useRouter()\n // Optional chat-runtime read — when present and mode='embed' the\n // search-result row click short-circuits to a new-tab open against\n // the absolutized URL. Null/host preserves today's behavior.\n // Also used as the proxy-prefix fallback for `searchEndpoint`, matching\n // how tickets resolves `findTicketUrl`.\n const runtime = useChatRuntime()\n const resolvedSearchEndpoint =\n searchEndpoint ?? runtime?.endpoints.docsSearchUrl ?? '/api/docs/search'\n\n const [query, setQuery] = useState('')\n const [results, setResults] = useState<SearchResult[]>([])\n const [isFetching, setIsFetching] = useState(false)\n const debouncedQuery = useDebounce(query, 300)\n\n useEffect(() => {\n if (!debouncedQuery || debouncedQuery.trim().length < 2) {\n setResults([])\n setIsFetching(false)\n return\n }\n\n let cancelled = false\n\n async function fetchResults() {\n setIsFetching(true)\n try {\n const params = new URLSearchParams({\n q: debouncedQuery,\n source,\n limit: '10',\n })\n if (tableIdsKey) params.set('tableIds', tableIdsKey)\n\n const response = await fetch(`${resolvedSearchEndpoint}?${params.toString()}`)\n if (!response.ok) {\n throw new Error(`Search request failed: ${response.status}`)\n }\n\n const json = await response.json()\n\n if (!cancelled && json.success && Array.isArray(json.data)) {\n const mapped = mapDocSearchResults(json.data as DocSearchResult[])\n setResults(mapped)\n }\n } catch (error) {\n console.error('Doc search error:', error)\n if (!cancelled) {\n setResults([])\n }\n } finally {\n if (!cancelled) {\n setIsFetching(false)\n }\n }\n }\n\n fetchResults()\n\n return () => {\n cancelled = true\n }\n }, [debouncedQuery, source, tableIdsKey, resolvedSearchEndpoint])\n\n // Derived loading state — single source of truth for \"should the\n // dropdown show 'Loading...' instead of 'No results found'\":\n const isLoading =\n query.trim().length >= 2 && (query !== debouncedQuery || isFetching)\n\n // Track whether dropdown should stay open (external link opened in new tab).\n const [keepOpen, setKeepOpen] = useState(false)\n\n const handleResultSelect = useCallback(\n (\n result: SearchResult,\n modifiers?: {\n metaKey?: boolean\n ctrlKey?: boolean\n shiftKey?: boolean\n altKey?: boolean\n button?: number\n },\n ) => {\n const action = resolveSearchResultAction(\n result,\n source,\n runtime?.navigation.mode,\n )\n // Modifier / non-primary mouse click → force new tab regardless of\n // same-tab/new-tab decision. The dropdown row is a `<div>`, not an\n // `<a target=\"_blank\">`, so the browser doesn't background-tab\n // natively on cmd-click. Honor it explicitly here for parity with\n // the anchor-based surfaces (cards, chips, related-content). Plain\n // Enter from the keyboard passes `modifiers === undefined`.\n const wantsNewTab =\n modifiers &&\n (modifiers.metaKey ||\n modifiers.ctrlKey ||\n modifiers.shiftKey ||\n modifiers.altKey ||\n (typeof modifiers.button === 'number' && modifiers.button !== 0))\n switch (action.kind) {\n case 'navigate-same-tab': {\n // Embed-mode short-circuit — autocomplete row clicked while\n // the chat panel is hosted inside an embedding app.\n if (runtime?.navigation.mode === 'embed') {\n setKeepOpen(true)\n const targetPlatform =\n (result.metadata?.targetPlatform as string | null | undefined) ?? null\n resolveExternalNavigation({\n href: action.href,\n targetPlatform,\n runtime,\n }).open()\n return\n }\n if (wantsNewTab) {\n setKeepOpen(true)\n window.open(action.href, '_blank', NEW_TAB_FEATURES)\n return\n }\n // Same-origin click:\n // 1. If the href is under the current doc-tree's baseRoute AND\n // an `onInPageSwap` callback is wired AND returns true →\n // consider in-page swap handled.\n // 2. Otherwise → embed-shim `router.push()` (soft RSC nav on\n // Next.js hosts, window.location.assign on bare hosts).\n setKeepOpen(false)\n const path =\n baseRoute && action.href.startsWith(`${baseRoute}/`)\n ? action.href.slice(baseRoute.length + 1)\n : null\n if (path && onInPageSwap?.(path)) return\n router.push(stripSameOriginToPath(action.href))\n return\n }\n case 'navigate-new-tab':\n // Cross-origin (e.g. clicking a flamingo.run release from\n // product-hub) — open in a new tab. Keep dropdown open so the\n // user can pick another result without re-searching.\n setKeepOpen(true)\n window.open(action.href, '_blank', NEW_TAB_FEATURES)\n return\n case 'ask-ai':\n // Row is searchable-but-not-openable (cap_table positions,\n // financial-kpi snapshots, anything backed by\n // `resolveUrl: () => null`). Dispatch a CustomEvent that\n // GlobalAskAI listens for — opens chat + drills via\n // `entityIdFilter` (primary-key only, same as inline-card Ask).\n setKeepOpen(false)\n window.dispatchEvent(\n new CustomEvent('ask-ai:open-with-ref', { detail: action.detail }),\n )\n return\n case 'route':\n // Final fallback: legacy navigation by path. Hits when a row\n // has neither URL nor pk metadata — a mapper/API regression.\n setKeepOpen(false)\n onNavigate(action.path)\n return\n case 'noop':\n return\n }\n },\n [onNavigate, source, baseRoute, router, onInPageSwap, runtime],\n )\n\n // Reset keepOpen when query changes.\n useEffect(() => {\n setKeepOpen(false)\n }, [query])\n\n return {\n query,\n setQuery,\n results,\n isLoading,\n handleResultSelect,\n keepDropdownOpen: keepOpen,\n }\n}\n"]}
@@ -1,29 +1,23 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
2
 
3
3
 
4
- var _chunkD3LEFMOAcjs = require('./chunk-D3LEFMOA.cjs');
4
+ var _chunk54KNMC2Rcjs = require('./chunk-54KNMC2R.cjs');
5
5
 
6
6
 
7
- var _chunkQHIXS3W2cjs = require('./chunk-QHIXS3W2.cjs');
8
7
 
9
8
 
10
9
 
11
10
 
12
11
 
13
12
 
14
- var _chunkKXCRGTRNcjs = require('./chunk-KXCRGTRN.cjs');
15
13
 
16
14
 
15
+ var _chunkS6DXA35Scjs = require('./chunk-S6DXA35S.cjs');
17
16
 
18
17
 
19
- var _chunkLFGGF7OTcjs = require('./chunk-LFGGF7OT.cjs');
20
18
 
21
19
 
22
-
23
- var _chunkBOCFIKYScjs = require('./chunk-BOCFIKYS.cjs');
24
-
25
-
26
- var _chunkJ54Z3OCRcjs = require('./chunk-J54Z3OCR.cjs');
20
+ var _chunkWMSTJAZTcjs = require('./chunk-WMSTJAZT.cjs');
27
21
 
28
22
 
29
23
 
@@ -74,7 +68,7 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
74
68
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
75
69
  "div",
76
70
  {
77
- id: _chunkKXCRGTRNcjs.faqItemAnchor.call(void 0, item.id),
71
+ id: _chunkS6DXA35Scjs.faqItemAnchor.call(void 0, item.id),
78
72
  className: _chunkFIG2RKZFcjs.cn.call(void 0, "group scroll-mt-24 transition-colors hover:bg-[#1E1E1E]", isOpen ? "bg-ods-bg" : "bg-transparent"),
79
73
  children: [
80
74
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
@@ -94,7 +88,7 @@ function FaqAccordion({ items, defaultOpenIds = [] }) {
94
88
  children: [
95
89
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "min-w-0 pr-4", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: item.question }) }),
96
90
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-shrink-0", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
97
- _chunkQHIXS3W2cjs.ChevronButton,
91
+ _chunkS6DXA35Scjs.ChevronButton,
98
92
  {
99
93
  "aria-label": isOpen ? "Collapse question" : "Expand question",
100
94
  size: "md",
@@ -154,7 +148,7 @@ function buildFaqJsonLdFromFaqs(faqs, opts = {}) {
154
148
 
155
149
  var DEFAULT_HEADING_TEXT = "Frequently Asked Questions";
156
150
  function buildFaqsUrl(entityType, entityId, minResults, apiBaseUrl = "") {
157
- return _chunkKXCRGTRNcjs.buildSuggestionUrl.call(void 0, "/api/faqs", { apiBaseUrl, entityType, entityId, count: minResults });
151
+ return _chunkS6DXA35Scjs.buildSuggestionUrl.call(void 0, "/api/faqs", { apiBaseUrl, entityType, entityId, count: minResults });
158
152
  }
159
153
  function groupFaqsBySection(faqs) {
160
154
  const order = [];
@@ -170,7 +164,7 @@ function groupFaqsBySection(faqs) {
170
164
  }
171
165
  let group = byName.get(name);
172
166
  if (!group) {
173
- group = { section: name, slug: _chunkKXCRGTRNcjs.faqSectionSlug.call(void 0, name), items: [] };
167
+ group = { section: name, slug: _chunkS6DXA35Scjs.faqSectionSlug.call(void 0, name), items: [] };
174
168
  byName.set(name, group);
175
169
  order.push(name);
176
170
  }
@@ -210,7 +204,7 @@ function GroupedFaqList({
210
204
  }
211
205
  if (bestId) setActiveSlug(bestId);
212
206
  },
213
- { rootMargin: `-${_chunkLFGGF7OTcjs.STICKY_HEADER_OFFSET_PX}px 0px -55% 0px`, threshold: 0 }
207
+ { rootMargin: `-${_chunkWMSTJAZTcjs.STICKY_HEADER_OFFSET_PX}px 0px -55% 0px`, threshold: 0 }
214
208
  );
215
209
  for (const group of navGroups) {
216
210
  const el = group.slug ? document.getElementById(group.slug) : null;
@@ -220,7 +214,7 @@ function GroupedFaqList({
220
214
  }, [slugKey]);
221
215
  const [hashTarget, setHashTarget] = _react.useState.call(void 0, null);
222
216
  _react.useEffect.call(void 0, () => {
223
- const refresh = () => setHashTarget(_chunkKXCRGTRNcjs.parseFaqHash.call(void 0, window.location.hash));
217
+ const refresh = () => setHashTarget(_chunkS6DXA35Scjs.parseFaqHash.call(void 0, window.location.hash));
224
218
  refresh();
225
219
  window.addEventListener("hashchange", refresh);
226
220
  return () => window.removeEventListener("hashchange", refresh);
@@ -238,16 +232,16 @@ function GroupedFaqList({
238
232
  const accordionKeySuffix = _optionalChain([hashTarget, 'optionalAccess', _8 => _8.kind]) === "item" ? `item:${hashTarget.rawId}` : "default";
239
233
  _react.useEffect.call(void 0, () => {
240
234
  if (!hashTarget) return;
241
- const elId = hashTarget.kind === "item" ? _chunkKXCRGTRNcjs.faqItemAnchor.call(void 0, hashTarget.rawId) : hashTarget.slug;
235
+ const elId = hashTarget.kind === "item" ? _chunkS6DXA35Scjs.faqItemAnchor.call(void 0, hashTarget.rawId) : hashTarget.slug;
242
236
  const el = document.getElementById(elId);
243
- if (el) _chunkLFGGF7OTcjs.scrollElementIntoView.call(void 0, el, { headerOffset: _chunkLFGGF7OTcjs.STICKY_HEADER_OFFSET_PX });
237
+ if (el) _chunkWMSTJAZTcjs.scrollElementIntoView.call(void 0, el, { headerOffset: _chunkWMSTJAZTcjs.STICKY_HEADER_OFFSET_PX });
244
238
  if (hashTarget.kind === "section") setActiveSlug(hashTarget.slug);
245
239
  }, [hashTarget]);
246
240
  const handleJump = _react.useCallback.call(void 0,
247
241
  (e, slug) => {
248
242
  e.preventDefault();
249
- _chunkLFGGF7OTcjs.navigateSamePageHash.call(void 0, "#" + slug, {
250
- headerOffset: _chunkLFGGF7OTcjs.STICKY_HEADER_OFFSET_PX,
243
+ _chunkWMSTJAZTcjs.navigateSamePageHash.call(void 0, "#" + slug, {
244
+ headerOffset: _chunkWMSTJAZTcjs.STICKY_HEADER_OFFSET_PX,
251
245
  history: "replace"
252
246
  });
253
247
  },
@@ -279,7 +273,7 @@ function GroupedFaqList({
279
273
  id: _nullishCoalesce(group.slug, () => ( void 0)),
280
274
  className: "scroll-mt-24 space-y-4",
281
275
  children: [
282
- group.section && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CategoryHeading, { className: _chunkJ54Z3OCRcjs.SECTION_HEADING_CLASS, children: group.section }),
276
+ group.section && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, CategoryHeading, { className: _chunkS6DXA35Scjs.SECTION_HEADING_CLASS, children: group.section }),
283
277
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
284
278
  FaqAccordion,
285
279
  {
@@ -320,10 +314,10 @@ function FaqSection({
320
314
  () => initialFaqs ? { faqs: initialFaqs } : void 0,
321
315
  [initialFaqs]
322
316
  );
323
- const { data, isLoading, error } = _chunkD3LEFMOAcjs.useSelfFetch.call(void 0, url, { initialData });
317
+ const { data, isLoading, error } = _chunk54KNMC2Rcjs.useSelfFetch.call(void 0, url, { initialData });
324
318
  const faqs = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _11 => _11.faqs]), () => ( []));
325
319
  const groups = _react.useMemo.call(void 0, () => faqs.length > 0 ? groupFaqsBySection(faqs) : [], [faqs]);
326
- const headingNode = heading === void 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunkJ54Z3OCRcjs.SECTION_HEADING_CLASS, children: DEFAULT_HEADING_TEXT }) : heading;
320
+ const headingNode = heading === void 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: _chunkS6DXA35Scjs.SECTION_HEADING_CLASS, children: DEFAULT_HEADING_TEXT }) : heading;
327
321
  if (error) return null;
328
322
  if (!isLoading && faqs.length === 0) return null;
329
323
  if (isLoading && faqs.length === 0) {
@@ -339,7 +333,7 @@ function FaqSection({
339
333
  "script",
340
334
  {
341
335
  type: "application/ld+json",
342
- dangerouslySetInnerHTML: { __html: _chunkKXCRGTRNcjs.serializeJsonLd.call(void 0, schema) }
336
+ dangerouslySetInnerHTML: { __html: _chunkS6DXA35Scjs.serializeJsonLd.call(void 0, schema) }
343
337
  }
344
338
  )
345
339
  ] });
@@ -362,7 +356,7 @@ function FaqDocumentPage({
362
356
  label: _nullishCoalesce(_optionalChain([backButton, 'optionalAccess', _12 => _12.label]), () => ( "Back to home")),
363
357
  onClick: () => router.push(_nullishCoalesce(_optionalChain([backButton, 'optionalAccess', _13 => _13.href]), () => ( "/")))
364
358
  };
365
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBOCFIKYScjs.PageShell, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBOCFIKYScjs.PageLayout, { title, subtitle, backButton: backCfg, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
359
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkS6DXA35Scjs.PageShell, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkS6DXA35Scjs.PageLayout, { title, subtitle, backButton: backCfg, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
366
360
  FaqSection,
367
361
  {
368
362
  heading: null,
@@ -379,4 +373,4 @@ function FaqDocumentPage({
379
373
 
380
374
 
381
375
  exports.FaqAccordion = FaqAccordion; exports.FaqSection = FaqSection; exports.FaqDocumentPage = FaqDocumentPage;
382
- //# sourceMappingURL=chunk-XREEV72C.cjs.map
376
+ //# sourceMappingURL=chunk-N3V434AN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-N3V434AN.cjs","../src/components/faq/faq-section.tsx","../src/components/faq-accordion.tsx","../src/components/faq/json-ld.ts","../src/components/faq/faq-document-page.tsx"],"names":["jsx","jsxs"],"mappings":"AAAA,6rBAAY;AACZ;AACE;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;AC3BA,8BAAiE;AD6BjE;AACA;AE9BA;AAEA,uCAAA,CAAA;AAoEY,+CAAA;AArDZ,IAAM,kBAAA,EAAoB,CAAC,MAAA,EAAA,GAAoB;AAC7C,EAAA,MAAM,IAAA,EAAM,2BAAA,IAAkC,CAAA;AAC9C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,EAAA,EAAI,6BAAA,KAAsB,CAAA;AAExD,EAAA,MAAM,QAAA,EAAU,gCAAA,CAAY,EAAA,GAAM;AAChC,IAAA,GAAA,CAAI,GAAA,CAAI,OAAA,EAAS;AACf,MAAA,MAAM,OAAA,EAAS,GAAA,CAAI,OAAA,CAAQ,YAAA;AAC3B,MAAA,YAAA,CAAa,CAAA,EAAA;AACf,IAAA;AACG,EAAA;AAGW,EAAA;AACF,IAAA;AACF,MAAA;AACH,IAAA;AACQ,MAAA;AACf,IAAA;AACU,EAAA;AAEE,EAAA;AAChB;AAEgB;AACE,EAAA;AAEA,EAAA;AACH,IAAA;AACI,MAAA;AACA,MAAA;AACC,MAAA;AACP,MAAA;AACR,IAAA;AACH,EAAA;AAGE,EAAA;AAEmB,IAAA;AACF,IAAA;AAGX,IAAA;AAAC,MAAA;AAAA,MAAA;AAOK,QAAA;AACO,QAAA;AAGX,QAAA;AAAA,0BAAA;AAAC,YAAA;AAAA,YAAA;AACM,cAAA;AACL,cAAA;AACA,cAAA;AACA,cAAA;AACM,gBAAA;AACA,kBAAA;AACF,kBAAA;AACF,gBAAA;AACF,cAAA;AACA,cAAA;AACA,cAAA;AAEA,cAAA;AAAA,gCAAA;AAKA,gCAAA;AACG,kBAAA;AAAA,kBAAA;AACC,oBAAA;AACA,oBAAA;AACA,oBAAA;AACA,oBAAA;AACA,oBAAA;AAAY,kBAAA;AAEhB,gBAAA;AAAA,cAAA;AAAA,YAAA;AACF,UAAA;AAEA,0BAAA;AAAC,YAAA;AAAA,YAAA;AACC,cAAA;AACA,cAAA;AAEA,cAAA;AAEA,YAAA;AACF,UAAA;AAAA,QAAA;AAAA,MAAA;AA9CU,MAAA;AA+CZ,IAAA;AAGN,EAAA;AAEJ;AFEoB;AACA;AC1GpB;AD4GoB;AACA;AGtGC;AACf;AAGU;AACP,EAAA;AACO,IAAA;AACH,IAAA;AACE,IAAA;AACE,IAAA;AACI,IAAA;AACnB,EAAA;AACF;AAEgB;AACP,EAAA;AACF,IAAA;AACS,IAAA;AACD,MAAA;AACC,MAAA;AACV,MAAA;AACW,QAAA;AACC,QAAA;AACZ,MAAA;AACA,IAAA;AACJ,EAAA;AACF;AHqGoB;AACA;AC2GN;AA5MR;AAKG;AAMA,EAAA;AACT;AAcS;AACkB,EAAA;AACV,EAAA;AACX,EAAA;AACc,EAAA;AACQ,IAAA;AACX,IAAA;AACF,IAAA;AACJ,MAAA;AACS,MAAA;AACd,MAAA;AACF,IAAA;AACY,IAAA;AACA,IAAA;AACA,MAAA;AACC,MAAA;AACA,MAAA;AACb,IAAA;AACY,IAAA;AACd,EAAA;AACe,EAAA;AACI,EAAA;AACZ,EAAA;AACT;AAMM;AACY;AAYT;AACP,EAAA;AACA,EAAA;AASC;AACK,EAAA;AACY,EAAA;AACX,EAAA;AAGS,EAAA;AAMA,EAAA;AACA,IAAA;AACD,IAAA;AACP,IAAA;AACS,MAAA;AACA,QAAA;AACH,UAAA;AACI,UAAA;AACA,UAAA;AACZ,QAAA;AACI,QAAA;AACA,QAAA;AACQ,QAAA;AACA,UAAA;AACR,YAAA;AACA,YAAA;AACF,UAAA;AACF,QAAA;AACY,QAAA;AACd,MAAA;AACc,MAAA;AAChB,IAAA;AACW,IAAA;AACE,MAAA;AACH,MAAA;AACV,IAAA;AACa,IAAA;AAGH,EAAA;AAYL,EAAA;AACS,EAAA;AACE,IAAA;AACR,IAAA;AACD,IAAA;AACM,IAAA;AACV,EAAA;AAIC,EAAA;AACY,IAAA;AACV,IAAA;AACS,IAAA;AACJ,IAAA;AACG,MAAA;AACH,MAAA;AACX,IAAA;AACc,IAAA;AACJ,EAAA;AAON,EAAA;AAGU,EAAA;AACG,IAAA;AAEf,IAAA;AACS,IAAA;AACH,IAAA;AACO,IAAA;AACF,EAAA;AAkBT,EAAA;AACsD,IAAA;AACtD,MAAA;AACF,MAAA;AACE,QAAA;AACS,QAAA;AACV,MAAA;AACH,IAAA;AACC,IAAA;AACH,EAAA;AAGE,EAAA;AACa,IAAA;AAGC,MAAA;AAEJ,MAAA;AAAC,QAAA;AAAA,QAAA;AAEW,UAAA;AACV,UAAA;AACU,UAAA;AACV,UAAA;AACE,YAAA;AACA,YAAA;AAGF,UAAA;AAEC,UAAA;AAAM,QAAA;AAXI,QAAA;AAYb,MAAA;AAGN,IAAA;AAEFA,oBAAAA;AAEgB,MAAA;AAEV,MAAA;AAAC,QAAA;AAAA,QAAA;AAEW,UAAA;AACV,UAAA;AAEC,UAAA;AAAM,YAAA;AAGP,4BAAA;AAAC,cAAA;AAAA,cAAA;AAKC,gBAAA;AACA,gBAAA;AAA8C,cAAA;AAFnC,cAAA;AAGb,YAAA;AAAA,UAAA;AAAA,QAAA;AAdK,QAAA;AAeP,MAAA;AAGN,IAAA;AACF,EAAA;AAEJ;AAES;AAEL,EAAA;AACEA,oBAAAA;AACAA,oBAAAA;AAGM,sBAAA;AACA,sBAAA;AAGN,IAAA;AACF,EAAA;AAEJ;AAuB2B;AACzB,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACa,EAAA;AACb,EAAA;AACA,EAAA;AACA,EAAA;AACa,EAAA;AACK;AACN,EAAA;AAGN,EAAA;AACG,IAAA;AACK,IAAA;AACd,EAAA;AACc,EAAA;AAED,EAAA;AAEE,EAAA;AAKT,EAAA;AAKY,EAAA;AACA,EAAA;AACD,EAAA;AAEb,IAAA;AAIJ,EAAA;AAEe,EAAA;AAGb,EAAA;AACEC,oBAAAA;AACG,MAAA;AACD,sBAAA;AACF,IAAA;AAEE,IAAA;AAAC,MAAA;AAAA,MAAA;AACM,QAAA;AAIL,QAAA;AAA2D,MAAA;AAC7D,IAAA;AAEJ,EAAA;AAEJ;ADzDoB;AACA;AIvUpB;AA4CQD;AAxBQ;AACN,EAAA;AACR,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACuB;AACR,EAAA;AAKb,EAAA;AAGa,IAAA;AACQ,IAAA;AACjB,EAAA;AAGJ,EAAA;AAEK,IAAA;AAAA,IAAA;AACU,MAAA;AACT,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAAA,IAAA;AAGN,EAAA;AAEJ;AJ6SoB;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-N3V434AN.cjs","sourcesContent":[null,"\"use client\"\n\nimport React, { useCallback, useEffect, useMemo, useState } from 'react'\nimport type { Faq } from '../../types/faq'\nimport { FaqAccordion, type FaqItem } from '../faq-accordion'\nimport { useSelfFetch } from '../../hooks/use-self-fetch'\nimport { buildSuggestionUrl } from '../../utils/suggestion-url'\nimport { serializeJsonLd } from '../../utils/common'\nimport { scrollElementIntoView } from '../../utils/scroll-into-view'\nimport { navigateSamePageHash, STICKY_HEADER_OFFSET_PX } from '../../utils/same-page-hash-nav'\nimport { faqSectionSlug, faqItemAnchor, parseFaqHash, type FaqHashTarget } from '../../utils/faq-anchor'\nimport { cn } from '../../utils/cn'\nimport { buildFaqJsonLdFromFaqs, type FaqSchemaOptions } from './json-ld'\nimport { SECTION_HEADING_CLASS } from '../layout/page-heading'\n\nexport interface FaqSectionProps {\n /**\n * SSR hydrate. When provided, the hook skips the first client fetch (per\n * useSelfFetch contract). The consuming server page resolves FAQs then drills\n * them into this prop — the lib never re-fetches what the host already gated on.\n */\n initialFaqs?: Faq[]\n /** Both required together for entity-attached FAQs; partial → bare /api/faqs. */\n entityType?: string\n entityId?: number | string\n /**\n * Heading node above the grouped list. `undefined` → default\n * `<h2>`\"Frequently Asked Questions\". `null` → no heading (the host page\n * owns the `<h1>`, as the standalone /faqs surface does). A React node lets a\n * platform drill its own <PageHeading> without the lib referencing platform\n * state. Also drives category nesting so the document outline stays correct:\n * `null` → categories render `<h2>` (directly under the page `<h1>`);\n * otherwise categories render `<h3>` beneath this heading.\n */\n heading?: React.ReactNode | null\n /** Inject FAQPage schema.org JSON-LD as a <script>. Off by default so embeds\n * don't emit duplicate schema. */\n emitJsonLd?: boolean\n /** Overrides for the JSON-LD's name/description/url. */\n jsonLd?: FaqSchemaOptions\n className?: string\n /** Maps to /api/faqs `?count=` (the 5-tier fill target). Absent → param\n * not sent (server default applies). */\n minResults?: number\n /** Fetch-URL prefix for third-party embeds / reverse proxies\n * ('' = same-origin relative). */\n apiBaseUrl?: string\n}\n\nconst DEFAULT_HEADING_TEXT = 'Frequently Asked Questions'\n\n/** URL composition shared with RelatedContentSection (`buildSuggestionUrl`)\n * — byte-identical to the historical `buildFaqsUrl` output when\n * `minResults`/`apiBaseUrl` are absent. */\nfunction buildFaqsUrl(\n entityType?: string,\n entityId?: number | string,\n minResults?: number,\n apiBaseUrl = '',\n): string {\n return buildSuggestionUrl('/api/faqs', { apiBaseUrl, entityType, entityId, count: minResults })\n}\n\ninterface FaqGroup {\n /** null → the uncategorized bucket: no heading, no jump pill, rendered last. */\n section: string | null\n slug: string | null\n items: FaqItem[]\n}\n\n/** Group FAQs by `faq.section`, preserving the server's first-seen\n * (display_order) order for BOTH the section order and the rows within each\n * section. The uncategorized bucket (blank/missing section) sinks to the end\n * since it renders without a heading. Items carry NO badge here — the `<h2>`\n * IS the category, so a per-row chip would be redundant. */\nfunction groupFaqsBySection(faqs: Faq[]): FaqGroup[] {\n const order: string[] = []\n const byName = new Map<string, FaqGroup>()\n let uncategorized: FaqGroup | null = null\n for (const faq of faqs) {\n const item: FaqItem = { id: faq.id, question: faq.question, answer: faq.answer }\n const name = faq.section?.trim()\n if (!name) {\n if (!uncategorized) uncategorized = { section: null, slug: null, items: [] }\n uncategorized.items.push(item)\n continue\n }\n let group = byName.get(name)\n if (!group) {\n group = { section: name, slug: faqSectionSlug(name), items: [] }\n byName.set(name, group)\n order.push(name)\n }\n group.items.push(item)\n }\n const groups = order.map((name) => byName.get(name)!)\n if (uncategorized) groups.push(uncategorized)\n return groups\n}\n\n\n/** Map key for the uncategorized bucket — `group.slug` is null for it, so\n * every per-group map (default-open ids, accordion keys) uses this sentinel\n * to keep the lookup typed. */\nconst UNCATEGORIZED_KEY = '__uncategorized__'\nconst groupKey = (g: FaqGroup): string => g.slug ?? UNCATEGORIZED_KEY\n\n/**\n * Grouped FAQ layout: a category jump-nav above stacked `<h2>` category\n * sections (each its own accordion). Isolated into its own component so the\n * scroll-spy hooks only mount in grouped mode — `FaqSection`'s own hooks stay\n * unconditional.\n *\n * The pills are real `<a href=\"#slug\">` anchors (crawlable in-page links,\n * deep-linkable, work without JS); the click handler upgrades the jump to the\n * cancellation-proof `scrollElementIntoView` tween and syncs the URL hash.\n */\nfunction GroupedFaqList({\n groups,\n categoryHeadingAs,\n}: {\n groups: FaqGroup[]\n /** Heading tag for each category, so the document outline nests correctly\n * under whatever owns the heading above this block: `h2` on the standalone\n * page (the page owns the `<h1>`), `h3` beneath an embed's `<h2>` title.\n * The VISUAL is `SECTION_HEADING_CLASS` either way, so categories look\n * identical on every surface. */\n categoryHeadingAs: 'h2' | 'h3'\n}) {\n const CategoryHeading = categoryHeadingAs\n const navGroups = useMemo(() => groups.filter((g) => g.slug), [groups])\n const [activeSlug, setActiveSlug] = useState<string | null>(navGroups[0]?.slug ?? null)\n // Identity-stable key for the section set so the observer re-binds only when\n // the categories actually change (not on every parent re-render).\n const slugKey = navGroups.map((g) => g.slug).join('|')\n\n // Scroll-spy: mark the pill for the category currently at the top of the\n // viewport. rootMargin drops the trigger line just below the sticky header\n // and ignores the bottom ~55% so \"active\" is the section being read, not the\n // next one peeking in.\n useEffect(() => {\n if (navGroups.length < 2) return\n const tops = new Map<string, number>()\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n const id = (entry.target as HTMLElement).id\n if (entry.isIntersecting) tops.set(id, entry.boundingClientRect.top)\n else tops.delete(id)\n }\n let bestId: string | null = null\n let bestTop = Number.POSITIVE_INFINITY\n for (const [id, top] of tops) {\n if (top < bestTop) {\n bestTop = top\n bestId = id\n }\n }\n if (bestId) setActiveSlug(bestId)\n },\n { rootMargin: `-${STICKY_HEADER_OFFSET_PX}px 0px -55% 0px`, threshold: 0 },\n )\n for (const group of navGroups) {\n const el = group.slug ? document.getElementById(group.slug) : null\n if (el) observer.observe(el)\n }\n return () => observer.disconnect()\n // slugKey encodes the section set; re-observe only when it changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [slugKey])\n\n // ─── Hash dispatch — `/faqs#faq-item-<id>` or `/faqs#faq-<section-slug>` ──\n // Tracks the current hash so:\n // 1. an item-kind hash seeds `defaultOpenIds` on the matching accordion\n // (auto-expands the cited question);\n // 2. either kind triggers the cancellation-proof tween scroll with the\n // sticky-header offset (native browser hash scroll runs once, ignores\n // our offset — re-running the tween puts the target in the right spot).\n // Listens to `hashchange` so back/forward replays the same behavior. SSR-\n // safe: initial null state matches the server render; the first effect\n // tick on the client updates it.\n const [hashTarget, setHashTarget] = useState<FaqHashTarget | null>(null)\n useEffect(() => {\n const refresh = () => setHashTarget(parseFaqHash(window.location.hash))\n refresh()\n window.addEventListener('hashchange', refresh)\n return () => window.removeEventListener('hashchange', refresh)\n }, [])\n\n // Per-group default-open set when the hash points at an item. The map key\n // matches `groupKey(group)` so the render-time lookup is O(1) per group.\n const defaultOpenByGroupKey = useMemo(() => {\n if (hashTarget?.kind !== 'item') return null\n const targetId = hashTarget.rawId\n const result = new Map<string, (string | number)[]>()\n for (const group of groups) {\n const hit = group.items.find((i) => String(i.id) === targetId)\n if (hit) result.set(groupKey(group), [hit.id])\n }\n return result.size > 0 ? result : null\n }, [groups, hashTarget])\n\n // Accordion is uncontrolled — `defaultOpenIds` is only consumed at mount,\n // so a new item hash needs a remount to honor it. Keying off the item-id\n // suffix triggers exactly the remount we need (and stays stable when the\n // hash points at a section, so category navigation never disturbs the\n // accordion's open state).\n const accordionKeySuffix =\n hashTarget?.kind === 'item' ? `item:${hashTarget.rawId}` : 'default'\n\n useEffect(() => {\n if (!hashTarget) return\n const elId =\n hashTarget.kind === 'item' ? faqItemAnchor(hashTarget.rawId) : hashTarget.slug\n const el = document.getElementById(elId)\n if (el) scrollElementIntoView(el, { headerOffset: STICKY_HEADER_OFFSET_PX })\n if (hashTarget.kind === 'section') setActiveSlug(hashTarget.slug)\n }, [hashTarget])\n\n // Category pill click. `navigateSamePageHash` owns the entire transition:\n // replaceState → synthetic `hashchange` → `scrollElementIntoView` tween\n // with `STICKY_HEADER_OFFSET_PX` so the section heading lands BELOW the\n // sticky category nav on the FIRST tween (covers the same-target\n // re-click case, where the `hashTarget` effect at L214 is a no-op\n // because the state reference is equal). For DIFFERENT-target clicks\n // the helper's synthetic `hashchange` re-fires that effect, which\n // re-scrolls with the same offset and cancels this tween (singleton)\n // — both paths land at the same position. The effect is still\n // required for back/forward + direct URL edits, where the helper\n // isn't in the call chain.\n //\n // `history: 'replace'` matches the pre-helper behavior: category pills\n // are a TOC, not a navigation step, so the Back button leaves the\n // FAQ page in one step regardless of how many categories the user\n // clicked through.\n const handleJump = useCallback(\n (e: React.MouseEvent<HTMLAnchorElement>, slug: string) => {\n e.preventDefault()\n navigateSamePageHash('#' + slug, {\n headerOffset: STICKY_HEADER_OFFSET_PX,\n history: 'replace',\n })\n },\n [],\n )\n\n return (\n <div className=\"space-y-8\">\n {navGroups.length > 1 && (\n <nav aria-label=\"FAQ categories\" className=\"flex flex-wrap gap-2\">\n {navGroups.map((group) => {\n const isActive = group.slug === activeSlug\n return (\n <a\n key={group.slug}\n href={`#${group.slug}`}\n aria-current={isActive ? 'true' : undefined}\n onClick={(e) => handleJump(e, group.slug as string)}\n className={cn(\n \"rounded-full border px-4 py-2 text-sm font-medium font-['DM_Sans'] transition-colors\",\n isActive\n ? 'border-ods-text-primary bg-ods-card text-ods-text-primary'\n : 'border-ods-border bg-ods-card text-ods-text-secondary hover:border-ods-text-secondary hover:text-ods-text-primary',\n )}\n >\n {group.section}\n </a>\n )\n })}\n </nav>\n )}\n <div className=\"space-y-10\">\n {groups.map((group) => {\n const key = groupKey(group)\n return (\n <section\n key={key}\n id={group.slug ?? undefined}\n className=\"scroll-mt-24 space-y-4\"\n >\n {group.section && (\n <CategoryHeading className={SECTION_HEADING_CLASS}>{group.section}</CategoryHeading>\n )}\n <FaqAccordion\n // Re-key on item-hash changes so the remount picks up the new\n // `defaultOpenIds` (the accordion is uncontrolled). Stable for\n // section hashes — category navigation doesn't disturb state.\n key={`${key}:${accordionKeySuffix}`}\n items={group.items}\n defaultOpenIds={defaultOpenByGroupKey?.get(key)}\n />\n </section>\n )\n })}\n </div>\n </div>\n )\n}\n\nfunction FaqSkeleton() {\n return (\n <div className=\"space-y-8 animate-pulse\">\n <div className=\"h-12 md:h-14 w-2/3 rounded bg-ods-border\" />\n <div className=\"rounded-3xl border border-ods-border overflow-hidden bg-ods-card divide-y divide-ods-border w-full\">\n {Array.from({ length: 8 }).map((_, idx) => (\n <div key={idx} className=\"flex items-center justify-between px-6 md:px-8 py-6\">\n <div className=\"h-6 w-5/6 rounded bg-ods-border\" />\n <div className=\"h-10 w-10 rounded-md bg-ods-border\" />\n </div>\n ))}\n </div>\n </div>\n )\n}\n\ninterface FaqsResponse {\n faqs: Faq[]\n}\n\n/**\n * The FAQ display surface — ONE rendering on every host: the list grouped by\n * `faq.section` (each category a heading + its own accordion, with a category\n * jump-nav once there are 2+ categories). There is no flat/ungrouped mode and\n * no page-vs-embedded shell fork; the standalone /faqs page and every embed\n * render through this single path, so they cannot drift.\n *\n * - Standalone /faqs page: pass `initialFaqs` (SSR) + `heading={null}` (the\n * page owns the <h1>) + `emitJsonLd` with `jsonLd` overrides for SEO.\n * - Per-entity embed: pass `entityType` + `entityId` (no `initialFaqs`); the\n * hook self-fetches `GET /api/faqs`, and `heading` is this block's own <h2>.\n *\n * CONTRACT: the consuming app MUST implement `GET /api/faqs`. On a fetch error\n * (or zero FAQs) the component renders nothing so the host page isn't\n * disfigured. The host always supplies the page shell — this renders a bare\n * <section>.\n */\nexport function FaqSection({\n initialFaqs,\n entityType,\n entityId,\n heading,\n emitJsonLd = false,\n jsonLd,\n className,\n minResults,\n apiBaseUrl = '',\n}: FaqSectionProps) {\n const url = buildFaqsUrl(entityType, entityId, minResults, apiBaseUrl)\n // Memoized — useSelfFetch re-syncs on [initialData]; a fresh per-render\n // wrapper object would setState-loop under re-rendering parents.\n const initialData = useMemo<FaqsResponse | undefined>(\n () => (initialFaqs ? { faqs: initialFaqs } : undefined),\n [initialFaqs],\n )\n const { data, isLoading, error } = useSelfFetch<FaqsResponse>(url, { initialData })\n\n const faqs = data?.faqs ?? []\n // Grouped before the early returns so the hook order stays stable.\n const groups = useMemo(() => (faqs.length > 0 ? groupFaqsBySection(faqs) : []), [faqs])\n\n // `undefined` → default <h2> title; `null` → the host page owns the <h1>, so\n // no title renders here. `heading === null` also makes the category headings\n // <h2> (directly under the page <h1>); otherwise they nest as <h3>.\n const headingNode =\n heading === undefined ? <h2 className={SECTION_HEADING_CLASS}>{DEFAULT_HEADING_TEXT}</h2> : heading\n\n // Degrade silently — never show an error banner or an empty section shell\n // where FAQs would be (host pages and the standalone surface both rely on it).\n if (error) return null\n if (!isLoading && faqs.length === 0) return null\n if (isLoading && faqs.length === 0) {\n return (\n <div className={className}>\n <FaqSkeleton />\n </div>\n )\n }\n\n const schema = emitJsonLd ? buildFaqJsonLdFromFaqs(faqs, jsonLd) : null\n\n return (\n <>\n <section className={className ?? 'space-y-10'}>\n {headingNode}\n <GroupedFaqList groups={groups} categoryHeadingAs={heading === null ? 'h2' : 'h3'} />\n </section>\n {schema && (\n <script\n type=\"application/ld+json\"\n // eslint-disable-next-line react/no-danger\n // serializeJsonLd, NOT raw JSON.stringify — FAQ answers are\n // admin-entered; an embedded \"</script>\" must not break the tag.\n dangerouslySetInnerHTML={{ __html: serializeJsonLd(schema) }}\n />\n )}\n </>\n )\n}\n","\"use client\"\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react'\nimport { ChevronButton } from './ui/chevron-button'\nimport { cn } from \"../utils/cn\"\nimport { faqItemAnchor } from \"../utils/faq-anchor\"\n\nexport interface FaqItem {\n id: number | string\n question: string\n answer: string\n}\n\ninterface FaqAccordionProps {\n items: FaqItem[]\n defaultOpenIds?: (number | string)[]\n}\n\n// Utility to measure scrollHeight outside render cycle\nconst useMeasuredHeight = (isOpen: boolean) => {\n const ref = useRef<HTMLDivElement | null>(null)\n const [maxHeight, setMaxHeight] = useState<string>('0px')\n\n const measure = useCallback(() => {\n if (ref.current) {\n const height = ref.current.scrollHeight\n setMaxHeight(`${height}px`)\n }\n }, [])\n\n // Update height only when section is open\n useEffect(() => {\n if (isOpen) {\n measure()\n } else {\n setMaxHeight('0px')\n }\n }, [isOpen, measure])\n\n return { ref, maxHeight }\n}\n\nexport function FaqAccordion({ items, defaultOpenIds = [] }: FaqAccordionProps) {\n const [openSet, setOpenSet] = useState<Set<string | number>>(new Set(defaultOpenIds))\n\n const toggle = (id: string | number) => {\n setOpenSet(prev => {\n const next = new Set(prev)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n return (\n <div className=\"rounded-3xl border border-ods-border divide-y divide-ods-border bg-ods-card overflow-hidden\">\n {items.map(item => {\n const isOpen = openSet.has(item.id)\n const { ref, maxHeight } = useMeasuredHeight(isOpen)\n\n return (\n <div\n key={item.id}\n // Per-row anchor — chat citation chips (`/faqs#faq-item-<id>`) land\n // here via native browser hash scroll AND via `FaqSection`'s tween\n // dispatch. `scroll-mt-24` keeps the row header below the 96px\n // sticky nav offset (matches `<section>`'s scroll-margin for\n // category anchors).\n id={faqItemAnchor(item.id)}\n className={cn('group scroll-mt-24 transition-colors hover:bg-[#1E1E1E]', isOpen ? 'bg-ods-bg' : 'bg-transparent')}\n >\n {/* Header */}\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => toggle(item.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggle(item.id);\n }\n }}\n aria-expanded={isOpen}\n className=\"flex w-full items-center justify-between px-6 md:px-8 py-6 text-left focus:outline-none transition-colors cursor-pointer\"\n >\n <div className=\"min-w-0 pr-4\">\n <h3>\n {item.question}\n </h3>\n </div>\n <div className=\"flex-shrink-0\">\n <ChevronButton\n aria-label={isOpen ? 'Collapse question' : 'Expand question'}\n size=\"md\"\n isExpanded={isOpen}\n backgroundColor=\"transparent\"\n borderColor=\"#3A3A3A\"\n />\n </div>\n </div>\n {/* Content wrapper with max-height animation */}\n <div\n style={{ maxHeight, transition: 'max-height 0.35s ease-in-out, opacity 0.35s ease-in-out', opacity: isOpen ? 1 : 0 }}\n className=\"overflow-hidden group-hover:bg-[#1E1E1E]/30\"\n >\n <div ref={ref} className=\"px-6 md:px-8 pb-6 text-ods-text-primary text-h4\">\n {item.answer}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n )\n} ","/**\n * Pure FAQ JSON-LD builder. No React, no client-only deps — safe to import from\n * Server Components via the `./components/faq` subpath export. (Do NOT import\n * through the root `./components` barrel — that barrel is `\"use client\"` and\n * dragging it into a Server Component would force the client graph into the\n * server.)\n *\n * The hub used to harcode `name`/`description` here for OpenMSP; in the lib we\n * accept overrides so every embedder can supply its own platform branding.\n */\nimport type { Faq } from '../../types/faq'\n\nexport interface FaqSchemaOptions {\n name?: string\n description?: string\n url?: string\n}\n\nconst DEFAULT_NAME = 'Frequently Asked Questions'\nconst DEFAULT_DESCRIPTION =\n 'Answers to common questions.'\n\nexport function baseFaqSchema(opts: FaqSchemaOptions = {}) {\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n name: opts.name ?? DEFAULT_NAME,\n description: opts.description ?? DEFAULT_DESCRIPTION,\n ...(opts.url ? { url: opts.url } : {}),\n } as const\n}\n\nexport function buildFaqJsonLdFromFaqs(faqs: Faq[], opts: FaqSchemaOptions = {}) {\n return {\n ...baseFaqSchema(opts),\n mainEntity: faqs.map((faq) => ({\n '@type': 'Question',\n name: faq.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: faq.answer,\n },\n })),\n } as const\n}\n","'use client';\n\n/**\n * FaqDocumentPage — the full `/faqs` page with chrome, so embedders drop in\n * ONE component instead of hand-assembling `PageShell` + `PageLayout` around the\n * bare `<FaqSection>`. Mirrors `LegalDocumentPage` / `DevSectionPage`: the\n * page-level layout lives in the lib, the host passes only config + a back button.\n *\n * `<FaqSection heading={null}>` lets `PageLayout`'s `TitleBlock` own the heading +\n * back button; it self-fetches `${apiBaseUrl}/api/faqs` via the authed\n * `useSelfFetch`, and renders nothing on a fetch error or zero FAQs.\n */\n\nimport { PageShell, PageLayout } from '../ui';\nimport { useRouter } from '../../embed-shims/next-navigation';\nimport { FaqSection } from './faq-section';\n\nexport interface FaqDocumentPageProps {\n /** Page title (PageLayout TitleBlock). Default \"FAQs\". */\n title?: string;\n /** Subtitle under the title. */\n subtitle?: string;\n /** Back-button config — same pattern as `DevSectionPage` / `LegalDocumentPage`.\n * Pass `false` to hide. Default `{ label: 'Back to home', href: '/' }`. */\n backButton?: { label?: string; href?: string } | false;\n /** Base URL `FaqSection` appends `/api/faqs` to (reverse-proxy embedders). */\n apiBaseUrl?: string;\n /** Optional entity scoping forwarded to `FaqSection`. */\n entityType?: string;\n entityId?: number | string;\n /** Minimum FAQ count before the section renders (forwarded to `FaqSection`). */\n minResults?: number;\n}\n\nexport function FaqDocumentPage({\n title = 'FAQs',\n subtitle,\n backButton,\n apiBaseUrl,\n entityType,\n entityId,\n minResults,\n}: FaqDocumentPageProps) {\n const router = useRouter();\n\n // Back-button config — mirrors LegalDocumentPage/DevSectionPage. Hide entirely\n // when the caller passes `false` (embed-mode where the host owns nav chrome).\n const backCfg =\n backButton === false\n ? undefined\n : {\n label: backButton?.label ?? 'Back to home',\n onClick: () => router.push(backButton?.href ?? '/'),\n };\n\n return (\n <PageShell>\n <PageLayout title={title} subtitle={subtitle} backButton={backCfg}>\n <FaqSection\n heading={null}\n apiBaseUrl={apiBaseUrl}\n entityType={entityType}\n entityId={entityId}\n minResults={minResults}\n />\n </PageLayout>\n </PageShell>\n );\n}\n"]}
@@ -1,29 +1,23 @@
1
1
  "use client";
2
2
  import {
3
3
  useSelfFetch
4
- } from "./chunk-3ZXUQQL4.js";
5
- import {
6
- ChevronButton
7
- } from "./chunk-K2PFPBMF.js";
4
+ } from "./chunk-PI4WSYQV.js";
8
5
  import {
6
+ ChevronButton,
7
+ PageLayout,
8
+ PageShell,
9
+ SECTION_HEADING_CLASS,
9
10
  buildSuggestionUrl,
10
11
  faqItemAnchor,
11
12
  faqSectionSlug,
12
13
  parseFaqHash,
13
14
  serializeJsonLd
14
- } from "./chunk-26PKDALD.js";
15
+ } from "./chunk-UFVKCF2C.js";
15
16
  import {
16
17
  STICKY_HEADER_OFFSET_PX,
17
18
  navigateSamePageHash,
18
19
  scrollElementIntoView
19
- } from "./chunk-OQ6X7ZOC.js";
20
- import {
21
- PageLayout,
22
- PageShell
23
- } from "./chunk-D652TJBQ.js";
24
- import {
25
- SECTION_HEADING_CLASS
26
- } from "./chunk-FQJK446R.js";
20
+ } from "./chunk-2QG57XOJ.js";
27
21
  import {
28
22
  init_next_navigation,
29
23
  useRouter
@@ -379,4 +373,4 @@ export {
379
373
  FaqSection,
380
374
  FaqDocumentPage
381
375
  };
382
- //# sourceMappingURL=chunk-5P3B2LZW.js.map
376
+ //# sourceMappingURL=chunk-N67N3OWW.js.map