@flamingo-stack/openframe-frontend-core 0.0.204 → 0.0.205

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 (509) hide show
  1. package/dist/{chunk-4CWSZPXH.cjs → chunk-24KCAECR.cjs} +9 -9
  2. package/dist/{chunk-4CWSZPXH.cjs.map → chunk-24KCAECR.cjs.map} +1 -1
  3. package/dist/chunk-27APPAJN.cjs +24 -0
  4. package/dist/chunk-27APPAJN.cjs.map +1 -0
  5. package/dist/{chunk-UC43NICZ.cjs → chunk-664KA5FI.cjs} +2 -35
  6. package/dist/chunk-664KA5FI.cjs.map +1 -0
  7. package/dist/chunk-6RZYJICV.cjs +24 -0
  8. package/dist/chunk-6RZYJICV.cjs.map +1 -0
  9. package/dist/chunk-7L4DWM7P.js +24 -0
  10. package/dist/chunk-7L4DWM7P.js.map +1 -0
  11. package/dist/chunk-BZFW3FOF.cjs +21 -0
  12. package/dist/chunk-BZFW3FOF.cjs.map +1 -0
  13. package/dist/{chunk-N57KWHDB.js → chunk-CIPO6DXK.js} +5 -5
  14. package/dist/chunk-EL5YVPD5.js +21 -0
  15. package/dist/chunk-EL5YVPD5.js.map +1 -0
  16. package/dist/{chunk-ARQ4XP64.cjs → chunk-FDCFI7YT.cjs} +40080 -31492
  17. package/dist/chunk-FDCFI7YT.cjs.map +1 -0
  18. package/dist/chunk-G7UE6RKV.cjs +121 -0
  19. package/dist/chunk-G7UE6RKV.cjs.map +1 -0
  20. package/dist/{chunk-25LVV26X.cjs → chunk-JUZGUQMX.cjs} +178 -50
  21. package/dist/chunk-JUZGUQMX.cjs.map +1 -0
  22. package/dist/{chunk-SZPJ5R5B.js → chunk-KSOOKNBG.js} +1 -34
  23. package/dist/chunk-KSOOKNBG.js.map +1 -0
  24. package/dist/{chunk-RMB5DVED.cjs → chunk-KUZGEA7U.cjs} +83 -66
  25. package/dist/chunk-KUZGEA7U.cjs.map +1 -0
  26. package/dist/chunk-LXC6P2EO.js +63 -0
  27. package/dist/chunk-LXC6P2EO.js.map +1 -0
  28. package/dist/chunk-MJNXIEV2.js +24 -0
  29. package/dist/chunk-MJNXIEV2.js.map +1 -0
  30. package/dist/{chunk-CPXLQ57U.js → chunk-MVGGXOFA.js} +37 -20
  31. package/dist/chunk-MVGGXOFA.js.map +1 -0
  32. package/dist/{chunk-LY34ORX6.js → chunk-O55ZUAX7.js} +39920 -31332
  33. package/dist/chunk-O55ZUAX7.js.map +1 -0
  34. package/dist/chunk-OHPI2HRK.js +47 -0
  35. package/dist/chunk-OHPI2HRK.js.map +1 -0
  36. package/dist/chunk-PLJLE4A4.js +121 -0
  37. package/dist/chunk-PLJLE4A4.js.map +1 -0
  38. package/dist/{chunk-XGL5FKIK.js → chunk-SCN5WFIZ.js} +148 -20
  39. package/dist/chunk-SCN5WFIZ.js.map +1 -0
  40. package/dist/chunk-WBR7H6E3.cjs +47 -0
  41. package/dist/chunk-WBR7H6E3.cjs.map +1 -0
  42. package/dist/chunk-XL4V2PYG.cjs +63 -0
  43. package/dist/chunk-XL4V2PYG.cjs.map +1 -0
  44. package/dist/components/announcement-bar.d.ts.map +1 -1
  45. package/dist/components/chat/chat-attachment-bar.d.ts +66 -0
  46. package/dist/components/chat/chat-attachment-bar.d.ts.map +1 -0
  47. package/dist/components/chat/chat-container.d.ts +21 -1
  48. package/dist/components/chat/chat-container.d.ts.map +1 -1
  49. package/dist/components/chat/chat-input.d.ts.map +1 -1
  50. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  51. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  52. package/dist/components/chat/chat-panel-context.d.ts +9 -0
  53. package/dist/components/chat/chat-panel-context.d.ts.map +1 -0
  54. package/dist/components/chat/chat-ticket-list.d.ts +1 -1
  55. package/dist/components/chat/chat-ticket-list.d.ts.map +1 -1
  56. package/dist/components/chat/embeddable-chat.d.ts +42 -0
  57. package/dist/components/chat/embeddable-chat.d.ts.map +1 -0
  58. package/dist/components/chat/entity-cards/admin-content-card.d.ts +34 -0
  59. package/dist/components/chat/entity-cards/admin-content-card.d.ts.map +1 -0
  60. package/dist/components/chat/entity-cards/block-card.d.ts.map +1 -0
  61. package/dist/components/chat/entity-cards/blog-card.d.ts +30 -0
  62. package/dist/components/chat/entity-cards/blog-card.d.ts.map +1 -0
  63. package/dist/components/chat/entity-cards/blog-image-placeholder.d.ts +26 -0
  64. package/dist/components/chat/entity-cards/blog-image-placeholder.d.ts.map +1 -0
  65. package/dist/components/chat/entity-cards/campaign-card-admin.d.ts +33 -0
  66. package/dist/components/chat/entity-cards/campaign-card-admin.d.ts.map +1 -0
  67. package/dist/components/chat/entity-cards/case-study-card.d.ts +20 -0
  68. package/dist/components/chat/entity-cards/case-study-card.d.ts.map +1 -0
  69. package/dist/components/chat/entity-cards/chat-ticket-item.d.ts.map +1 -0
  70. package/dist/components/chat/{chat-video-entity-card.d.ts → entity-cards/chat-video-entity-card.d.ts} +1 -1
  71. package/dist/components/chat/entity-cards/chat-video-entity-card.d.ts.map +1 -0
  72. package/dist/components/chat/entity-cards/customer-interview-card.d.ts +19 -0
  73. package/dist/components/chat/entity-cards/customer-interview-card.d.ts.map +1 -0
  74. package/dist/components/chat/entity-cards/data-room-doc-card.d.ts +47 -0
  75. package/dist/components/chat/entity-cards/data-room-doc-card.d.ts.map +1 -0
  76. package/dist/components/chat/entity-cards/dispatch.d.ts +119 -0
  77. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -0
  78. package/dist/components/chat/entity-cards/entity-author-card.d.ts +87 -0
  79. package/dist/components/chat/entity-cards/entity-author-card.d.ts.map +1 -0
  80. package/dist/components/chat/entity-cards/generic-entity-card.d.ts +42 -0
  81. package/dist/components/chat/entity-cards/generic-entity-card.d.ts.map +1 -0
  82. package/dist/components/chat/entity-cards/github-activity-card.d.ts +37 -0
  83. package/dist/components/chat/entity-cards/github-activity-card.d.ts.map +1 -0
  84. package/dist/components/chat/entity-cards/hubspot-ticket-card.d.ts +28 -0
  85. package/dist/components/chat/entity-cards/hubspot-ticket-card.d.ts.map +1 -0
  86. package/dist/components/chat/entity-cards/index.d.ts +32 -0
  87. package/dist/components/chat/entity-cards/index.d.ts.map +1 -0
  88. package/dist/components/chat/entity-cards/investor-update-card.d.ts +19 -0
  89. package/dist/components/chat/entity-cards/investor-update-card.d.ts.map +1 -0
  90. package/dist/components/chat/entity-cards/onboarding-guide-card.d.ts +20 -0
  91. package/dist/components/chat/entity-cards/onboarding-guide-card.d.ts.map +1 -0
  92. package/dist/components/chat/entity-cards/product-release-card-defaults.d.ts +21 -0
  93. package/dist/components/chat/entity-cards/product-release-card-defaults.d.ts.map +1 -0
  94. package/dist/components/chat/entity-cards/product-release-card.d.ts +12 -0
  95. package/dist/components/chat/entity-cards/product-release-card.d.ts.map +1 -0
  96. package/dist/components/chat/entity-cards/program-card-defaults.d.ts +32 -0
  97. package/dist/components/chat/entity-cards/program-card-defaults.d.ts.map +1 -0
  98. package/dist/components/chat/entity-cards/program-card.d.ts +37 -0
  99. package/dist/components/chat/entity-cards/program-card.d.ts.map +1 -0
  100. package/dist/components/chat/entity-cards/roadmap-card.d.ts +28 -0
  101. package/dist/components/chat/entity-cards/roadmap-card.d.ts.map +1 -0
  102. package/dist/components/chat/entity-cards/roadmap-vote-button.d.ts +12 -0
  103. package/dist/components/chat/entity-cards/roadmap-vote-button.d.ts.map +1 -0
  104. package/dist/components/chat/entity-cards/slack-message-card.d.ts +28 -0
  105. package/dist/components/chat/entity-cards/slack-message-card.d.ts.map +1 -0
  106. package/dist/components/chat/entity-cards/task-type-icon.d.ts +6 -0
  107. package/dist/components/chat/entity-cards/task-type-icon.d.ts.map +1 -0
  108. package/dist/components/chat/hooks/index.d.ts +10 -0
  109. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  110. package/dist/components/chat/hooks/use-chat-attachment-image-gallery.d.ts +5 -0
  111. package/dist/components/chat/hooks/use-chat-attachment-image-gallery.d.ts.map +1 -0
  112. package/dist/components/chat/hooks/use-chat-attachments.d.ts +33 -0
  113. package/dist/components/chat/hooks/use-chat-attachments.d.ts.map +1 -0
  114. package/dist/components/chat/hooks/use-chat-card-item.d.ts +7 -0
  115. package/dist/components/chat/hooks/use-chat-card-item.d.ts.map +1 -0
  116. package/dist/components/chat/hooks/use-chat-identity.d.ts +44 -0
  117. package/dist/components/chat/hooks/use-chat-identity.d.ts.map +1 -0
  118. package/dist/components/chat/hooks/use-chat.d.ts +30 -0
  119. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -0
  120. package/dist/components/chat/hooks/use-close-on-navigation.d.ts +2 -0
  121. package/dist/components/chat/hooks/use-close-on-navigation.d.ts.map +1 -0
  122. package/dist/components/chat/hooks/use-embedded-chat.d.ts +174 -0
  123. package/dist/components/chat/hooks/use-embedded-chat.d.ts.map +1 -0
  124. package/dist/components/chat/hooks/use-proxied-image-url.d.ts +18 -0
  125. package/dist/components/chat/hooks/use-proxied-image-url.d.ts.map +1 -0
  126. package/dist/components/chat/hooks/use-slash-commands.d.ts +32 -0
  127. package/dist/components/chat/hooks/use-slash-commands.d.ts.map +1 -0
  128. package/dist/components/chat/hooks/use-sse.d.ts +57 -0
  129. package/dist/components/chat/hooks/use-sse.d.ts.map +1 -0
  130. package/dist/components/chat/index.cjs +393 -0
  131. package/dist/components/chat/index.cjs.map +1 -0
  132. package/dist/components/chat/index.d.ts +5 -3
  133. package/dist/components/chat/index.d.ts.map +1 -1
  134. package/dist/components/chat/index.js +393 -0
  135. package/dist/components/chat/index.js.map +1 -0
  136. package/dist/components/chat/nav-link-anchor-via-runtime.d.ts +33 -0
  137. package/dist/components/chat/nav-link-anchor-via-runtime.d.ts.map +1 -0
  138. package/dist/components/chat/source-action-button.d.ts +39 -0
  139. package/dist/components/chat/source-action-button.d.ts.map +1 -0
  140. package/dist/components/chat/types/chat.types.d.ts +36 -0
  141. package/dist/components/chat/types/chat.types.d.ts.map +1 -1
  142. package/dist/components/chat/types/component.types.d.ts +56 -11
  143. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  144. package/dist/components/chat/types/entities/blog.d.ts +14 -0
  145. package/dist/components/chat/types/entities/blog.d.ts.map +1 -0
  146. package/dist/components/chat/types/entities/case-study.d.ts +10 -0
  147. package/dist/components/chat/types/entities/case-study.d.ts.map +1 -0
  148. package/dist/components/chat/types/entities/content-ref.d.ts +23 -0
  149. package/dist/components/chat/types/entities/content-ref.d.ts.map +1 -0
  150. package/dist/components/chat/types/entities/customer-interview.d.ts +10 -0
  151. package/dist/components/chat/types/entities/customer-interview.d.ts.map +1 -0
  152. package/dist/components/chat/types/entities/data-room-doc.d.ts +37 -0
  153. package/dist/components/chat/types/entities/data-room-doc.d.ts.map +1 -0
  154. package/dist/components/chat/types/entities/github-activity.d.ts +29 -0
  155. package/dist/components/chat/types/entities/github-activity.d.ts.map +1 -0
  156. package/dist/components/chat/types/entities/hubspot-ticket.d.ts +39 -0
  157. package/dist/components/chat/types/entities/hubspot-ticket.d.ts.map +1 -0
  158. package/dist/components/chat/types/entities/index.d.ts +28 -0
  159. package/dist/components/chat/types/entities/index.d.ts.map +1 -0
  160. package/dist/components/chat/types/entities/investor-update.d.ts +83 -0
  161. package/dist/components/chat/types/entities/investor-update.d.ts.map +1 -0
  162. package/dist/components/chat/types/entities/onboarding-guide.d.ts +79 -0
  163. package/dist/components/chat/types/entities/onboarding-guide.d.ts.map +1 -0
  164. package/dist/components/chat/types/entities/program-types.d.ts +303 -0
  165. package/dist/components/chat/types/entities/program-types.d.ts.map +1 -0
  166. package/dist/components/chat/types/entities/roadmap-item.d.ts +41 -0
  167. package/dist/components/chat/types/entities/roadmap-item.d.ts.map +1 -0
  168. package/dist/components/chat/types/entities/slack-message.d.ts +28 -0
  169. package/dist/components/chat/types/entities/slack-message.d.ts.map +1 -0
  170. package/dist/components/chat/types/index.d.ts +1 -0
  171. package/dist/components/chat/types/index.d.ts.map +1 -1
  172. package/dist/components/chat/utils/agent-status-message.d.ts +18 -0
  173. package/dist/components/chat/utils/agent-status-message.d.ts.map +1 -0
  174. package/dist/components/chat/utils/auto-continuation-directive.d.ts +38 -0
  175. package/dist/components/chat/utils/auto-continuation-directive.d.ts.map +1 -0
  176. package/dist/components/chat/utils/chat-attachment-markdown.d.ts +114 -0
  177. package/dist/components/chat/utils/chat-attachment-markdown.d.ts.map +1 -0
  178. package/dist/components/chat/utils/chat-authed-fetch.d.ts +13 -0
  179. package/dist/components/chat/utils/chat-authed-fetch.d.ts.map +1 -0
  180. package/dist/components/chat/utils/chat-nav-resolution.d.ts +72 -0
  181. package/dist/components/chat/utils/chat-nav-resolution.d.ts.map +1 -0
  182. package/dist/components/chat/utils/chat-proxy-auth-storage.d.ts +43 -0
  183. package/dist/components/chat/utils/chat-proxy-auth-storage.d.ts.map +1 -0
  184. package/dist/components/chat/utils/chip-action-class.d.ts +16 -0
  185. package/dist/components/chat/utils/chip-action-class.d.ts.map +1 -0
  186. package/dist/components/chat/utils/chip-styles.d.ts +32 -0
  187. package/dist/components/chat/utils/chip-styles.d.ts.map +1 -0
  188. package/dist/components/chat/utils/clickup-task-type-utils.d.ts +38 -0
  189. package/dist/components/chat/utils/clickup-task-type-utils.d.ts.map +1 -0
  190. package/dist/components/chat/utils/compact-card-classes.d.ts +50 -0
  191. package/dist/components/chat/utils/compact-card-classes.d.ts.map +1 -0
  192. package/dist/components/chat/utils/decide-new-tab.d.ts +39 -0
  193. package/dist/components/chat/utils/decide-new-tab.d.ts.map +1 -0
  194. package/dist/components/chat/utils/external-app-urls.d.ts +14 -0
  195. package/dist/components/chat/utils/external-app-urls.d.ts.map +1 -0
  196. package/dist/components/chat/utils/flatten-assistant-content.d.ts +25 -0
  197. package/dist/components/chat/utils/flatten-assistant-content.d.ts.map +1 -0
  198. package/dist/components/chat/utils/icon-registry.d.ts +67 -0
  199. package/dist/components/chat/utils/icon-registry.d.ts.map +1 -0
  200. package/dist/components/chat/utils/index.d.ts +21 -0
  201. package/dist/components/chat/utils/index.d.ts.map +1 -1
  202. package/dist/components/chat/utils/is-cross-origin-url.d.ts +22 -0
  203. package/dist/components/chat/utils/is-cross-origin-url.d.ts.map +1 -0
  204. package/dist/components/chat/utils/nav-anchor-props.d.ts +54 -0
  205. package/dist/components/chat/utils/nav-anchor-props.d.ts.map +1 -0
  206. package/dist/components/chat/utils/nav-click-handler.d.ts +51 -0
  207. package/dist/components/chat/utils/nav-click-handler.d.ts.map +1 -0
  208. package/dist/components/chat/utils/scroll-anchor.d.ts +30 -0
  209. package/dist/components/chat/utils/scroll-anchor.d.ts.map +1 -0
  210. package/dist/components/chat/utils/slash-dispatch-utils.d.ts +109 -0
  211. package/dist/components/chat/utils/slash-dispatch-utils.d.ts.map +1 -0
  212. package/dist/components/chat/utils/source-icons.d.ts +8 -0
  213. package/dist/components/chat/utils/source-icons.d.ts.map +1 -0
  214. package/dist/components/chat/utils/source-row-cta.d.ts +111 -0
  215. package/dist/components/chat/utils/source-row-cta.d.ts.map +1 -0
  216. package/dist/components/features/figma-prototype-viewer.d.ts.map +1 -1
  217. package/dist/components/features/index.cjs +12 -6
  218. package/dist/components/features/index.cjs.map +1 -1
  219. package/dist/components/features/index.js +11 -5
  220. package/dist/components/features/video.d.ts.map +1 -1
  221. package/dist/components/icons/index.cjs +3 -3
  222. package/dist/components/icons/index.js +2 -2
  223. package/dist/components/index.cjs +274 -8
  224. package/dist/components/index.cjs.map +1 -1
  225. package/dist/components/index.js +273 -7
  226. package/dist/components/interactive-wrapper.d.ts +3 -3
  227. package/dist/components/navigation/index.cjs +12 -6
  228. package/dist/components/navigation/index.cjs.map +1 -1
  229. package/dist/components/navigation/index.js +11 -5
  230. package/dist/components/resizable.d.ts +1 -1
  231. package/dist/components/shared/product-release/product-release-card-skeleton.d.ts +1 -1
  232. package/dist/components/shared/product-release/product-release-card-skeleton.d.ts.map +1 -1
  233. package/dist/components/shared/product-release/product-release-card.d.ts +19 -12
  234. package/dist/components/shared/product-release/product-release-card.d.ts.map +1 -1
  235. package/dist/components/shared/product-release/release-detail-page.d.ts +2 -4
  236. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  237. package/dist/components/ui/button/button.d.ts +13 -0
  238. package/dist/components/ui/button/button.d.ts.map +1 -1
  239. package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
  240. package/dist/components/ui/entity-image.d.ts.map +1 -1
  241. package/dist/components/ui/file-manager/index.cjs +71 -70
  242. package/dist/components/ui/file-manager/index.cjs.map +1 -1
  243. package/dist/components/ui/file-manager/index.js +6 -5
  244. package/dist/components/ui/file-manager/index.js.map +1 -1
  245. package/dist/components/ui/hover-dropdown.d.ts +66 -0
  246. package/dist/components/ui/hover-dropdown.d.ts.map +1 -0
  247. package/dist/components/ui/index.cjs +276 -6
  248. package/dist/components/ui/index.cjs.map +1 -1
  249. package/dist/components/ui/index.d.ts +1 -0
  250. package/dist/components/ui/index.d.ts.map +1 -1
  251. package/dist/components/ui/index.js +278 -8
  252. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -1
  253. package/dist/components/ui/square-avatar.d.ts.map +1 -1
  254. package/dist/contexts/chat-runtime-context.d.ts +109 -0
  255. package/dist/contexts/chat-runtime-context.d.ts.map +1 -0
  256. package/dist/contexts/endpoints-runtime-context.d.ts +28 -0
  257. package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -0
  258. package/dist/contexts/index.cjs +30 -0
  259. package/dist/contexts/index.cjs.map +1 -0
  260. package/dist/contexts/index.d.ts +26 -0
  261. package/dist/contexts/index.d.ts.map +1 -0
  262. package/dist/contexts/index.js +30 -0
  263. package/dist/contexts/index.js.map +1 -0
  264. package/dist/contexts/use-outer-or-default.d.ts +29 -0
  265. package/dist/contexts/use-outer-or-default.d.ts.map +1 -0
  266. package/dist/embed-shims/index.cjs +51 -0
  267. package/dist/embed-shims/index.cjs.map +1 -0
  268. package/dist/embed-shims/index.d.ts +31 -0
  269. package/dist/embed-shims/index.d.ts.map +1 -0
  270. package/dist/embed-shims/index.js +51 -0
  271. package/dist/embed-shims/index.js.map +1 -0
  272. package/dist/embed-shims/next-dynamic.cjs +12 -0
  273. package/dist/embed-shims/next-dynamic.cjs.map +1 -0
  274. package/dist/embed-shims/next-dynamic.d.ts +47 -0
  275. package/dist/embed-shims/next-dynamic.d.ts.map +1 -0
  276. package/dist/embed-shims/next-dynamic.js +12 -0
  277. package/dist/embed-shims/next-dynamic.js.map +1 -0
  278. package/dist/embed-shims/next-image.cjs +12 -0
  279. package/dist/embed-shims/next-image.cjs.map +1 -0
  280. package/dist/embed-shims/next-image.d.ts +28 -0
  281. package/dist/embed-shims/next-image.d.ts.map +1 -0
  282. package/dist/embed-shims/next-image.js +12 -0
  283. package/dist/embed-shims/next-image.js.map +1 -0
  284. package/dist/embed-shims/next-link.cjs +14 -0
  285. package/dist/embed-shims/next-link.cjs.map +1 -0
  286. package/dist/embed-shims/next-link.d.ts +22 -0
  287. package/dist/embed-shims/next-link.d.ts.map +1 -0
  288. package/dist/embed-shims/next-link.js +14 -0
  289. package/dist/embed-shims/next-link.js.map +1 -0
  290. package/dist/embed-shims/next-navigation.cjs +30 -0
  291. package/dist/embed-shims/next-navigation.cjs.map +1 -0
  292. package/dist/embed-shims/next-navigation.d.ts +46 -0
  293. package/dist/embed-shims/next-navigation.d.ts.map +1 -0
  294. package/dist/embed-shims/next-navigation.js +30 -0
  295. package/dist/embed-shims/next-navigation.js.map +1 -0
  296. package/dist/hooks/index.cjs +10 -4
  297. package/dist/hooks/index.cjs.map +1 -1
  298. package/dist/hooks/index.d.ts +2 -0
  299. package/dist/hooks/index.d.ts.map +1 -1
  300. package/dist/hooks/index.js +9 -3
  301. package/dist/hooks/use-access-code-integration.d.ts +48 -0
  302. package/dist/hooks/use-access-code-integration.d.ts.map +1 -0
  303. package/dist/hooks/use-contact-submission.d.ts.map +1 -1
  304. package/dist/hooks/use-og-placeholder.d.ts +31 -0
  305. package/dist/hooks/use-og-placeholder.d.ts.map +1 -0
  306. package/dist/hooks/use-toast.d.ts +1 -1
  307. package/dist/index.cjs +367 -7
  308. package/dist/index.cjs.map +1 -1
  309. package/dist/index.js +378 -18
  310. package/dist/types/index.cjs.map +1 -1
  311. package/dist/types/index.js.map +1 -1
  312. package/dist/utils/access-code-client.d.ts +21 -37
  313. package/dist/utils/access-code-client.d.ts.map +1 -1
  314. package/dist/utils/cn.d.ts +0 -27
  315. package/dist/utils/cn.d.ts.map +1 -1
  316. package/dist/utils/color-analysis.d.ts +33 -0
  317. package/dist/utils/color-analysis.d.ts.map +1 -0
  318. package/dist/utils/date-formatters.d.ts +16 -5
  319. package/dist/utils/date-formatters.d.ts.map +1 -1
  320. package/dist/utils/fetch-priority.d.ts +3 -0
  321. package/dist/utils/fetch-priority.d.ts.map +1 -0
  322. package/dist/utils/format.d.ts +192 -1
  323. package/dist/utils/format.d.ts.map +1 -1
  324. package/dist/utils/image-proxy.d.ts +67 -2
  325. package/dist/utils/image-proxy.d.ts.map +1 -1
  326. package/dist/utils/index.cjs +1274 -155
  327. package/dist/utils/index.cjs.map +1 -1
  328. package/dist/utils/index.d.ts +19 -3
  329. package/dist/utils/index.d.ts.map +1 -1
  330. package/dist/utils/index.js +1200 -157
  331. package/dist/utils/index.js.map +1 -1
  332. package/dist/utils/local-storage-adapter.d.ts +46 -0
  333. package/dist/utils/local-storage-adapter.d.ts.map +1 -0
  334. package/dist/utils/source-icons.d.ts +78 -0
  335. package/dist/utils/source-icons.d.ts.map +1 -0
  336. package/package.json +29 -2
  337. package/src/components/announcement-bar.tsx +26 -4
  338. package/src/components/categories-cart.tsx +1 -1
  339. package/src/components/chat/chat-attachment-bar.tsx +323 -0
  340. package/src/components/chat/chat-container.tsx +39 -5
  341. package/src/components/chat/chat-input.tsx +7 -1
  342. package/src/components/chat/chat-message-enhanced.tsx +32 -22
  343. package/src/components/chat/chat-message-list.tsx +53 -4
  344. package/src/components/chat/chat-panel-context.tsx +37 -0
  345. package/src/components/chat/chat-ticket-list.tsx +1 -1
  346. package/src/components/chat/embeddable-chat.tsx +1106 -0
  347. package/src/components/chat/entity-cards/admin-content-card.tsx +155 -0
  348. package/src/components/chat/entity-cards/blog-card.tsx +259 -0
  349. package/src/components/chat/entity-cards/blog-image-placeholder.tsx +52 -0
  350. package/src/components/chat/entity-cards/campaign-card-admin.tsx +113 -0
  351. package/src/components/chat/entity-cards/case-study-card.tsx +192 -0
  352. package/src/components/chat/{chat-ticket-item.tsx → entity-cards/chat-ticket-item.tsx} +2 -2
  353. package/src/components/chat/{chat-video-entity-card.tsx → entity-cards/chat-video-entity-card.tsx} +2 -2
  354. package/src/components/chat/entity-cards/customer-interview-card.tsx +211 -0
  355. package/src/components/chat/entity-cards/data-room-doc-card.tsx +120 -0
  356. package/src/components/chat/entity-cards/dispatch.tsx +1093 -0
  357. package/src/components/chat/entity-cards/entity-author-card.tsx +193 -0
  358. package/src/components/chat/entity-cards/generic-entity-card.tsx +144 -0
  359. package/src/components/chat/entity-cards/github-activity-card.tsx +305 -0
  360. package/src/components/chat/entity-cards/hubspot-ticket-card.tsx +205 -0
  361. package/src/components/chat/entity-cards/index.ts +125 -0
  362. package/src/components/chat/entity-cards/investor-update-card.tsx +150 -0
  363. package/src/components/chat/entity-cards/onboarding-guide-card.tsx +326 -0
  364. package/src/components/chat/entity-cards/product-release-card-defaults.ts +57 -0
  365. package/src/components/chat/entity-cards/product-release-card.tsx +19 -0
  366. package/src/components/chat/entity-cards/program-card-defaults.ts +62 -0
  367. package/src/components/chat/entity-cards/program-card.tsx +451 -0
  368. package/src/components/chat/entity-cards/roadmap-card.tsx +356 -0
  369. package/src/components/chat/entity-cards/roadmap-vote-button.tsx +54 -0
  370. package/src/components/chat/entity-cards/slack-message-card.tsx +182 -0
  371. package/src/components/chat/entity-cards/task-type-icon.tsx +60 -0
  372. package/src/components/chat/hooks/index.ts +22 -0
  373. package/src/components/chat/hooks/use-chat-attachment-image-gallery.tsx +114 -0
  374. package/src/components/chat/hooks/use-chat-attachments.ts +429 -0
  375. package/src/components/chat/hooks/use-chat-card-item.ts +102 -0
  376. package/src/components/chat/hooks/use-chat-identity.ts +139 -0
  377. package/src/components/chat/hooks/use-chat.ts +501 -0
  378. package/src/components/chat/hooks/use-close-on-navigation.ts +87 -0
  379. package/src/components/chat/hooks/use-embedded-chat.ts +1023 -0
  380. package/src/components/chat/hooks/use-proxied-image-url.ts +31 -0
  381. package/src/components/chat/hooks/use-slash-commands.ts +106 -0
  382. package/src/components/chat/hooks/use-sse.ts +143 -0
  383. package/src/components/chat/index.ts +30 -4
  384. package/src/components/chat/nav-link-anchor-via-runtime.tsx +72 -0
  385. package/src/components/chat/source-action-button.tsx +120 -0
  386. package/src/components/chat/types/chat.types.ts +61 -0
  387. package/src/components/chat/types/component.types.ts +57 -11
  388. package/src/components/chat/types/entities/blog.ts +27 -0
  389. package/src/components/chat/types/entities/case-study.ts +14 -0
  390. package/src/components/chat/types/entities/content-ref.ts +23 -0
  391. package/src/components/chat/types/entities/customer-interview.ts +15 -0
  392. package/src/components/chat/types/entities/data-room-doc.ts +37 -0
  393. package/src/components/chat/types/entities/github-activity.ts +36 -0
  394. package/src/components/chat/types/entities/hubspot-ticket.ts +39 -0
  395. package/src/components/chat/types/entities/index.ts +28 -0
  396. package/src/components/chat/types/entities/investor-update.ts +100 -0
  397. package/src/components/chat/types/entities/onboarding-guide.ts +101 -0
  398. package/src/components/chat/types/entities/program-types.ts +433 -0
  399. package/src/components/chat/types/entities/roadmap-item.ts +42 -0
  400. package/src/components/chat/types/entities/slack-message.ts +28 -0
  401. package/src/components/chat/types/index.ts +1 -0
  402. package/src/components/chat/utils/agent-status-message.ts +52 -0
  403. package/src/components/chat/utils/auto-continuation-directive.ts +70 -0
  404. package/src/components/chat/utils/chat-attachment-markdown.ts +190 -0
  405. package/src/components/chat/utils/chat-authed-fetch.ts +73 -0
  406. package/src/components/chat/utils/chat-nav-resolution.ts +151 -0
  407. package/src/components/chat/utils/chat-proxy-auth-storage.ts +148 -0
  408. package/src/components/chat/utils/chip-action-class.ts +19 -0
  409. package/src/components/chat/utils/chip-styles.ts +51 -0
  410. package/src/components/chat/utils/clickup-task-type-utils.ts +59 -0
  411. package/src/components/chat/utils/compact-card-classes.ts +97 -0
  412. package/src/components/chat/utils/decide-new-tab.ts +57 -0
  413. package/src/components/chat/utils/external-app-urls.ts +19 -0
  414. package/src/components/chat/utils/flatten-assistant-content.ts +35 -0
  415. package/src/components/chat/utils/icon-registry.ts +297 -0
  416. package/src/components/chat/utils/index.ts +133 -0
  417. package/src/components/chat/utils/is-cross-origin-url.ts +28 -0
  418. package/src/components/chat/utils/nav-anchor-props.ts +78 -0
  419. package/src/components/chat/utils/nav-click-handler.ts +81 -0
  420. package/src/components/chat/utils/scroll-anchor.ts +35 -0
  421. package/src/components/chat/utils/slash-dispatch-utils.ts +183 -0
  422. package/src/components/chat/utils/source-icons.ts +14 -0
  423. package/src/components/chat/utils/source-row-cta.ts +215 -0
  424. package/src/components/empty-state.tsx +1 -1
  425. package/src/components/features/board/ticket-card.tsx +1 -1
  426. package/src/components/features/figma-prototype-viewer.tsx +2 -1
  427. package/src/components/features/media-gallery-manager.tsx +1 -1
  428. package/src/components/features/parallax-image-showcase.tsx +1 -1
  429. package/src/components/features/release-media-manager.tsx +1 -1
  430. package/src/components/features/seo-editor-preview.tsx +1 -1
  431. package/src/components/features/video.tsx +54 -3
  432. package/src/components/footer-waitlist-button.tsx +1 -1
  433. package/src/components/navigation/header.tsx +1 -1
  434. package/src/components/shared/onboarding/onboarding-step-card.tsx +1 -1
  435. package/src/components/shared/product-release/product-release-card-skeleton.tsx +8 -44
  436. package/src/components/shared/product-release/product-release-card.tsx +31 -116
  437. package/src/components/shared/product-release/release-detail-page.tsx +12 -16
  438. package/src/components/ui/actions-menu.tsx +1 -1
  439. package/src/components/ui/button/button.tsx +41 -11
  440. package/src/components/ui/button/split-button.tsx +1 -1
  441. package/src/components/ui/dashboard-info-card.tsx +2 -3
  442. package/src/components/ui/data-table/data-table-row.tsx +1 -1
  443. package/src/components/ui/entity-image.tsx +2 -8
  444. package/src/components/ui/hover-dropdown.tsx +258 -0
  445. package/src/components/ui/image-gallery-modal.tsx +1 -1
  446. package/src/components/ui/index.ts +1 -0
  447. package/src/components/ui/markdown-editor.tsx +1 -1
  448. package/src/components/ui/more-actions-menu.tsx +1 -1
  449. package/src/components/ui/organization-card.tsx +1 -1
  450. package/src/components/ui/simple-markdown-renderer.tsx +53 -5
  451. package/src/components/ui/square-avatar.tsx +3 -12
  452. package/src/components/ui/tab-navigation.tsx +1 -1
  453. package/src/components/ui/table/table-row.tsx +1 -1
  454. package/src/components/unified-filter-logic.tsx +1 -1
  455. package/src/components/unified-pagination.tsx +1 -1
  456. package/src/components/user-summary-stub.tsx +1 -1
  457. package/src/components/vendor-display-button.tsx +1 -1
  458. package/src/components/vendor-icon.tsx +1 -1
  459. package/src/contexts/chat-runtime-context.tsx +163 -0
  460. package/src/contexts/endpoints-runtime-context.tsx +68 -0
  461. package/src/contexts/index.ts +38 -0
  462. package/src/contexts/use-outer-or-default.ts +42 -0
  463. package/src/embed-shims/index.ts +42 -0
  464. package/src/embed-shims/next-dynamic.tsx +70 -0
  465. package/src/embed-shims/next-image.tsx +114 -0
  466. package/src/embed-shims/next-link.tsx +91 -0
  467. package/src/embed-shims/next-navigation.tsx +201 -0
  468. package/src/hooks/index.ts +9 -0
  469. package/src/hooks/state/use-api-params.ts +1 -1
  470. package/src/hooks/state/use-query-params.ts +1 -1
  471. package/src/hooks/use-access-code-integration.ts +107 -0
  472. package/src/hooks/use-contact-submission.ts +7 -3
  473. package/src/hooks/use-og-placeholder.ts +45 -0
  474. package/src/stories/OnboardingStepCard.stories.tsx +140 -0
  475. package/src/styles/chat-animations.css +65 -0
  476. package/src/styles/index.css +1 -0
  477. package/src/utils/access-code-client.ts +32 -75
  478. package/src/utils/cn.ts +0 -65
  479. package/src/utils/color-analysis.ts +205 -0
  480. package/src/utils/date-formatters.ts +54 -11
  481. package/src/utils/fetch-priority.ts +41 -0
  482. package/src/utils/format.ts +525 -1
  483. package/src/utils/image-proxy.ts +127 -7
  484. package/src/utils/index.ts +145 -5
  485. package/src/utils/local-storage-adapter.ts +105 -0
  486. package/src/utils/source-icons.ts +219 -0
  487. package/dist/chunk-25LVV26X.cjs.map +0 -1
  488. package/dist/chunk-ARQ4XP64.cjs.map +0 -1
  489. package/dist/chunk-CPXLQ57U.js.map +0 -1
  490. package/dist/chunk-LY34ORX6.js.map +0 -1
  491. package/dist/chunk-RMB5DVED.cjs.map +0 -1
  492. package/dist/chunk-SZPJ5R5B.js.map +0 -1
  493. package/dist/chunk-UC43NICZ.cjs.map +0 -1
  494. package/dist/chunk-XGL5FKIK.js.map +0 -1
  495. package/dist/components/chat/block-card.d.ts.map +0 -1
  496. package/dist/components/chat/chat-ticket-item.d.ts.map +0 -1
  497. package/dist/components/chat/chat-video-entity-card.d.ts.map +0 -1
  498. package/dist/utils/dynamic-icons.d.ts +0 -26
  499. package/dist/utils/dynamic-icons.d.ts.map +0 -1
  500. package/dist/utils/format-relative-time.d.ts +0 -21
  501. package/dist/utils/format-relative-time.d.ts.map +0 -1
  502. package/src/utils/.dynamic-icons.md +0 -30
  503. package/src/utils/.format-relative-time.md +0 -36
  504. package/src/utils/dynamic-icons.tsx +0 -120
  505. package/src/utils/format-relative-time.ts +0 -52
  506. /package/dist/{chunk-N57KWHDB.js.map → chunk-CIPO6DXK.js.map} +0 -0
  507. /package/dist/components/chat/{block-card.d.ts → entity-cards/block-card.d.ts} +0 -0
  508. /package/dist/components/chat/{chat-ticket-item.d.ts → entity-cards/chat-ticket-item.d.ts} +0 -0
  509. /package/src/components/chat/{block-card.tsx → entity-cards/block-card.tsx} +0 -0
@@ -0,0 +1,1106 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * EmbeddableChat — lib-portable port of the hub's `<GlobalAskAI>` component.
5
+ *
6
+ * Drops every hub-only import (auth-provider, useNavLink, currentPlatform,
7
+ * tableIdForDocumentType, rag-table-config, etc.) and routes ALL navigation
8
+ * + identity decisions through `useRequiredChatRuntime()`. Host wires the
9
+ * runtime once at root (HubRuntimeProvider in the hub, custom provider in
10
+ * embedders); this component reads from it everywhere.
11
+ *
12
+ * Diff summary vs hub original:
13
+ * - `useAuth()` → `useRequiredChatRuntime().user` (greeting + identity only;
14
+ * the requireAuth render gate is dropped — hub's wrapper handles it).
15
+ * - `currentPlatform()` → `useRequiredChatRuntime().source`.
16
+ * - `useNavLink`/`NavLinkAnchor` → chip-anchor rewrite via
17
+ * `handleChatNavClick` + lib's `NavLinkAnchorViaRuntime`.
18
+ * - `useDocChat(source)` → `useEmbeddedChat()` (reads source from runtime).
19
+ * - `tableIdForDocumentType` import deleted (dead per audit).
20
+ * - `renderChatInlineEntityCard` imported from lib's entity-cards barrel.
21
+ * - `useCloseOnNavigation` signature is `(close, pathname)` — pass `null`
22
+ * because lib has no `usePathname()` and embedders own that decision.
23
+ * - All other hub utilities (chat-attachment-bar, slash-commands fetcher,
24
+ * icon registry, chip-styles, click-utils, etc.) re-resolved against
25
+ * the equivalent lib modules.
26
+ *
27
+ * Public surface — `<EmbeddableChat />` taking the same prop bundle the hub
28
+ * shell passes (minus `requireAuth`, which the hub's wrapper handles), plus
29
+ * an optional `extras` opt-in for the chat-card dispatch helpers that need
30
+ * host-supplied builders (program configs, product-release prop builder).
31
+ */
32
+
33
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
34
+ import { useControllableState } from '@radix-ui/react-use-controllable-state'
35
+ import * as Dialog from '@radix-ui/react-dialog'
36
+ import { usePreventScroll } from '@react-aria/overlays'
37
+ import { isIOS } from '@react-aria/utils'
38
+ import { VisuallyHidden } from '@radix-ui/react-visually-hidden'
39
+ import { FileText, MessageSquare } from 'lucide-react'
40
+
41
+ import { Button } from '../ui/button'
42
+ import { Card } from '../ui/card'
43
+ import { HoverDropdown, type HoverDropdownItem } from '../ui/hover-dropdown'
44
+ import { MingoIcon } from '../icons'
45
+
46
+ import { ChatHeader, ChatFooter } from './chat-container'
47
+ import { ChatInput } from './chat-input'
48
+ import { ChatMessageList } from './chat-message-list'
49
+ import { ModelDisplay } from './model-display'
50
+ import { SourceActionButton } from './source-action-button'
51
+ import { NavLinkAnchorViaRuntime } from './nav-link-anchor-via-runtime'
52
+ import { ChatAttachmentAddButton, ChatAttachmentChipStrip } from './chat-attachment-bar'
53
+ import { renderChatInlineEntityCard } from './entity-cards/dispatch'
54
+ import type { ChatCardDispatchExtras } from './entity-cards/dispatch'
55
+
56
+ import { useRequiredChatRuntime } from '../../contexts/chat-runtime-context'
57
+ import { useEmbeddedChat, type ChatSource } from './hooks/use-embedded-chat'
58
+ import { useChatAttachments } from './hooks/use-chat-attachments'
59
+ import { useChatAttachmentImageGallery } from './hooks/use-chat-attachment-image-gallery'
60
+ import { useChatIdentity } from './hooks/use-chat-identity'
61
+ import { useCloseOnNavigation } from './hooks/use-close-on-navigation'
62
+ import { fetchSlashCommands, type SlashCommandSummary } from './hooks/use-slash-commands'
63
+
64
+ import type { ChatRef } from './chat-ref.types'
65
+ import type { ChatInputRef, SlashCommandActionId } from './types/component.types'
66
+ import type { Message } from './types/message.types'
67
+
68
+ import { formatChatAttachmentMarkdownForBubble } from './utils/chat-attachment-markdown'
69
+ import { handleChatNavClick } from './utils/nav-click-handler'
70
+ import { computeIsNewTab, newTabAnchorAttrs } from './utils/nav-anchor-props'
71
+ import { resolveHrefForRuntime } from './utils/chat-nav-resolution'
72
+ import { ChatPanelContext, type ChatPanelHandle } from './chat-panel-context'
73
+ import { resolveSourceRowCTA } from './utils/source-row-cta'
74
+ import { chatChipClass } from './utils/chip-styles'
75
+ import { CHIP_ACTION_BUTTON_CLASS } from './utils/chip-action-class'
76
+ import { getIconComponent } from './utils/icon-registry'
77
+ import { getSourceIconName } from './utils/source-icons'
78
+ import { formatSingularLookupInvocation } from './utils/slash-dispatch-utils'
79
+
80
+
81
+ // =============================================================================
82
+ // Types
83
+ // =============================================================================
84
+
85
+ /** Lib-side type alias kept inline to avoid leaking the (deprecated) hub
86
+ * `rag-table-config` `DocSource` import. The chat source is always a
87
+ * string id; `useEmbeddedChat` reads it from `runtime.source`. */
88
+ type DocSource = string
89
+
90
+ export interface EmbeddableChatProps {
91
+ /** Base route for in-app doc chip nav (e.g. `/knowledge-base`). When
92
+ * omitted, defaults are derived from `runtime.source`. */
93
+ baseRoute?: string
94
+ /** When the embedder doesn't host a `[...path]` route to render markdown
95
+ * chips against, set this to a platform that does. Chips with
96
+ * `externalUrl: null` resolve to `getBaseUrl(chipBasePlatform) +
97
+ * '/knowledge-base/' + path` and open in a new tab. */
98
+ chipBasePlatform?: string
99
+ /** DB-driven list of enabled RAG table ids (chip catalog filter).
100
+ * Prefetched server-side by the embedder's wrapper. Empty/null → no
101
+ * empty-state chips render. */
102
+ enabledRagTableIds?: ReadonlyArray<string> | null
103
+ /** DB-backed empty-state greeting. Falls back to a generic greeting. */
104
+ emptyStateGreeting?: string | null
105
+ /** DB-backed starter prompts (chips below the greeting). */
106
+ suggestedQueries?: ReadonlyArray<string> | null
107
+ /** Controlled-mode open state. When provided, `onOpenChange` MUST also
108
+ * be provided. Uncontrolled mode is the default. */
109
+ open?: boolean
110
+ /** Controlled-mode change handler. Required when `open` is provided. */
111
+ onOpenChange?: (open: boolean) => void
112
+ /** Initial open state for uncontrolled mode. Ignored if `open` is set. */
113
+ defaultOpen?: boolean
114
+ /** Render the built-in floating "Ask AI" trigger. Defaults to `true`. */
115
+ showInternalTrigger?: boolean
116
+ /** Optional builders for chat-card types whose props live in hub-land
117
+ * (programs + product_release). Forwarded straight to
118
+ * `renderChatInlineEntityCard`. */
119
+ extras?: ChatCardDispatchExtras
120
+ /** Optional callback used by `useEmbeddedChat`'s `displayRef` /
121
+ * `discussRef` flow to translate an LLM document type into the
122
+ * registry table id for entity-id-filtered retrieval. */
123
+ tableIdForDocumentType?: (documentType: string) => string | null
124
+ }
125
+
126
+ // =============================================================================
127
+ // Chip href resolution
128
+ // =============================================================================
129
+
130
+ /** Tiny inline replacement for hub's `formatRelativePath`. */
131
+ const formatRelativePath = (p: string): string =>
132
+ p.replace(/^\/+/, '').replace(/\/+$/, '')
133
+
134
+ /**
135
+ * Fallback fan-out when the model didn't cite any source. Show the top-N
136
+ * retrieved sources instead of zero chips. Mirrors Perplexity's behavior.
137
+ */
138
+ const FALLBACK_TOP_RETRIEVED = 3
139
+
140
+ // =============================================================================
141
+ // Slash-command dispatch
142
+ // =============================================================================
143
+
144
+ /**
145
+ * Single source of truth for slash-command action dispatch. Maps an action
146
+ * id to the corresponding chat-input-ref mutation. Same vocabulary as the
147
+ * hub original.
148
+ */
149
+ function dispatchSlashCommandAction(
150
+ actionId: SlashCommandActionId,
151
+ cmdId: string,
152
+ ref: React.MutableRefObject<ChatInputRef | null>,
153
+ ): void {
154
+ const input = ref.current
155
+ if (!input) return
156
+ switch (actionId) {
157
+ case 'browse':
158
+ input.submit(formatSingularLookupInvocation(cmdId))
159
+ return
160
+ case 'search':
161
+ input.setValue(`/${cmdId} `)
162
+ return
163
+ case 'find': {
164
+ const text = `/${cmdId} ""`
165
+ input.setValueAndCursor(text, text.length - 1)
166
+ return
167
+ }
168
+ case 'display': {
169
+ const text = `/${cmdId} display ""`
170
+ input.setValueAndCursor(text, text.length - 1)
171
+ return
172
+ }
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Resolve the icon for a source chip. `src.sourceRepo` is the
178
+ * `RagTableConfig.id` — looked up via `SOURCE_ICON_NAMES`, then
179
+ * resolved via `ICON_REGISTRY`. Falls back to `<FileText/>` so a
180
+ * misconfigured chip doesn't crash render.
181
+ */
182
+ function resolveChipIcon(src: ChatSource): React.ReactNode {
183
+ const iconName = getSourceIconName(src.sourceRepo)
184
+ const Icon = iconName ? getIconComponent(iconName) : FileText
185
+ return <Icon className="h-3.5 w-3.5" />
186
+ }
187
+
188
+ // =============================================================================
189
+ // SourceChip
190
+ // =============================================================================
191
+
192
+ /**
193
+ * Source chip — TWO affordances per the hub original:
194
+ * 1. Open: click → chip's URL (in-app for docs, new-tab for external).
195
+ * 2. Ask: small `MessageSquare` icon-button on the right → fires
196
+ * `onDiscuss(ref)` to drill into THIS row via `entityIdFilter`.
197
+ */
198
+ function SourceChip({
199
+ src,
200
+ baseRoute,
201
+ chipBasePlatform,
202
+ onClose,
203
+ onDiscuss,
204
+ }: {
205
+ src: ChatSource
206
+ baseRoute: string
207
+ chipBasePlatform?: string
208
+ onClose: () => void
209
+ onDiscuss?: (ref: ChatRef) => void
210
+ }) {
211
+ const runtime = useRequiredChatRuntime()
212
+ // Single CTA resolver — same icon, same href chain, same ChatRef
213
+ // synthesis the inline card and search-result paths use.
214
+ const cta = resolveSourceRowCTA(
215
+ {
216
+ sourceRepo: src.sourceRepo,
217
+ documentType: src.documentType,
218
+ id: src.id,
219
+ title: src.name,
220
+ externalUrl: src.externalUrl,
221
+ targetPlatform: src.targetPlatform ?? null,
222
+ path: src.path,
223
+ },
224
+ { baseRoute, chipBasePlatform, currentPlatform: runtime.source },
225
+ )
226
+ const Icon = cta.icon
227
+ const icon = <Icon className="h-3.5 w-3.5" />
228
+ const chipClass = chatChipClass({ tone: 'secondary' })
229
+
230
+ // Single source for the new-tab decision — same `computeIsNewTab` the
231
+ // inline cards consume via `ChatCardLoader`.
232
+ const decideTab = (href: string | null, targetPlatform: string | null) =>
233
+ computeIsNewTab(runtime, href, targetPlatform)
234
+
235
+ // Grouped source — hover/click reveals a dropdown with Open + Ask per row.
236
+ if (src.items && src.items.length > 0) {
237
+ const dropdownItems: HoverDropdownItem[] = src.items.map((item) => {
238
+ const itemCta = resolveSourceRowCTA(
239
+ {
240
+ sourceRepo: src.sourceRepo,
241
+ documentType: item.documentType,
242
+ id: item.id,
243
+ title: item.name,
244
+ externalUrl: item.externalUrl,
245
+ targetPlatform: item.targetPlatform ?? null,
246
+ path: item.path,
247
+ },
248
+ { baseRoute, chipBasePlatform, currentPlatform: runtime.source },
249
+ )
250
+ const ItemIcon = itemCta.icon
251
+ return {
252
+ label: item.name,
253
+ icon: <ItemIcon className="h-3.5 w-3.5" />,
254
+ href: itemCta.href ?? undefined,
255
+ targetPlatform: itemCta.targetPlatform,
256
+ path: item.path ?? null,
257
+ secondaryAction:
258
+ onDiscuss && itemCta.askable && itemCta.chatRef
259
+ ? {
260
+ icon: <MessageSquare />,
261
+ label: `Ask about ${item.name}`,
262
+ onClick: () => onDiscuss(itemCta.chatRef!),
263
+ }
264
+ : undefined,
265
+ }
266
+ })
267
+
268
+ return (
269
+ <HoverDropdown
270
+ items={dropdownItems}
271
+ renderAnchor={({ href, targetPlatform, path, className, children }) => (
272
+ <NavLinkAnchorViaRuntime
273
+ href={href}
274
+ targetPlatform={targetPlatform ?? null}
275
+ path={path}
276
+ className={className}
277
+ >
278
+ {children}
279
+ </NavLinkAnchorViaRuntime>
280
+ )}
281
+ >
282
+ <span className={`${chipClass} cursor-pointer`}>
283
+ {icon}
284
+ <span className="truncate max-w-[160px]">
285
+ [{src.index}] {src.name}
286
+ </span>
287
+ </span>
288
+ </HoverDropdown>
289
+ )
290
+ }
291
+
292
+ // Single-row chip helpers — wrap whatever clickable element each branch
293
+ // produces with the trailing Ask `SourceActionButton`.
294
+ const displayName = src.name || formatRelativePath(src.path)
295
+ const chipBody = (label: string) => (
296
+ <>
297
+ {icon}
298
+ <span className="truncate max-w-[160px]">
299
+ [{src.index}] {label}
300
+ </span>
301
+ </>
302
+ )
303
+ const wrapChip = (clickable: React.ReactNode) => (
304
+ <span className="inline-flex items-center">
305
+ {clickable}
306
+ <SourceActionButton
307
+ chatRef={cta.chatRef}
308
+ onDiscuss={onDiscuss}
309
+ density="inline"
310
+ className="ml-1 -mr-1"
311
+ />
312
+ </span>
313
+ )
314
+
315
+ // Click handler that routes through chat-runtime AND closes the chat
316
+ // panel ONLY on same-tab navigation. New-tab clicks leave the chat
317
+ // open so the user keeps their context while reading the new tab.
318
+ // Modifier-clicks pass through to the browser's native behavior.
319
+ // Single close model: matches `ChatCardNavWrap` for inline cards.
320
+ const buildClickHandler = (href: string, path: string | null | undefined, targetPlatform: string | null, isNewTab: boolean) =>
321
+ (e: React.MouseEvent<HTMLAnchorElement>) => {
322
+ const handled = handleChatNavClick(e, runtime, { href, path, targetPlatform })
323
+ if (handled && !isNewTab && onClose) onClose()
324
+ }
325
+
326
+ // Entity chip with a resolved external URL. `resolveHrefForRuntime`
327
+ // applies embed-mode origin prefix so modifier-click / hover-preview
328
+ // / right-click "copy link" all land on the hub origin.
329
+ if (cta.href && !!src.externalUrl) {
330
+ const resolvedHref = resolveHrefForRuntime(cta.href, runtime)
331
+ const isNewTab = decideTab(resolvedHref, cta.targetPlatform ?? null)
332
+ return wrapChip(
333
+ <a
334
+ href={resolvedHref}
335
+ {...newTabAnchorAttrs(isNewTab)}
336
+ onClick={buildClickHandler(resolvedHref, null, cta.targetPlatform ?? null, isNewTab)}
337
+ className={chipClass}
338
+ >
339
+ {chipBody(src.name)}
340
+ </a>,
341
+ )
342
+ }
343
+
344
+ // In-app navigable doc (markdown / data-room PDF).
345
+ if (cta.href) {
346
+ const resolvedHref = resolveHrefForRuntime(cta.href, runtime)
347
+ const isNewTab = decideTab(resolvedHref, cta.targetPlatform ?? null)
348
+ return wrapChip(
349
+ <a
350
+ href={resolvedHref}
351
+ {...newTabAnchorAttrs(isNewTab)}
352
+ onClick={buildClickHandler(resolvedHref, src.path, cta.targetPlatform ?? null, isNewTab)}
353
+ className={chipClass}
354
+ title={displayName}
355
+ >
356
+ {chipBody(displayName)}
357
+ </a>,
358
+ )
359
+ }
360
+
361
+ // No openable destination — static label + Ask affordance only.
362
+ return wrapChip(
363
+ <span className={`${chipClass} cursor-default`} title={displayName}>
364
+ {chipBody(displayName)}
365
+ </span>,
366
+ )
367
+ }
368
+
369
+ /**
370
+ * Cited-by-default source chip strip (Perplexity / ChatGPT pattern).
371
+ * Cited rows render first; uncited tail hidden behind an expander.
372
+ */
373
+ function SourceChips({
374
+ cited,
375
+ uncited,
376
+ baseRoute,
377
+ chipBasePlatform,
378
+ onClose,
379
+ onDiscuss,
380
+ }: {
381
+ cited: ChatSource[]
382
+ uncited: ChatSource[]
383
+ baseRoute: string
384
+ chipBasePlatform?: string
385
+ onClose: () => void
386
+ onDiscuss?: (ref: ChatRef) => void
387
+ }) {
388
+ const [expanded, setExpanded] = useState(false)
389
+
390
+ if (cited.length === 0) {
391
+ const fallback = uncited.slice(0, FALLBACK_TOP_RETRIEVED)
392
+ if (fallback.length === 0) return null
393
+ return (
394
+ <div className="flex flex-col gap-1.5 mt-2 pt-2 border-t border-ods-border">
395
+ <span className="text-[11px] text-ods-text-muted uppercase tracking-wider font-medium">
396
+ Top retrieved sources
397
+ </span>
398
+ <div className="flex flex-wrap gap-1.5">
399
+ {fallback.map((src) => (
400
+ <SourceChip
401
+ key={src.index}
402
+ src={src}
403
+ baseRoute={baseRoute}
404
+ chipBasePlatform={chipBasePlatform}
405
+ onClose={onClose}
406
+ onDiscuss={onDiscuss}
407
+ />
408
+ ))}
409
+ </div>
410
+ </div>
411
+ )
412
+ }
413
+
414
+ const hiddenCount = uncited.length
415
+ const hasOverflow = hiddenCount > 0
416
+
417
+ return (
418
+ <div className="flex flex-col gap-1.5 mt-2 pt-2 border-t border-ods-border">
419
+ <span className="text-[11px] text-ods-text-muted uppercase tracking-wider font-medium">
420
+ Sources
421
+ </span>
422
+ <div
423
+ className={`flex flex-wrap gap-1.5 ${expanded ? 'max-h-[200px] overflow-y-auto' : ''}`}
424
+ >
425
+ {cited.map((src) => (
426
+ <SourceChip
427
+ key={src.index}
428
+ src={src}
429
+ baseRoute={baseRoute}
430
+ chipBasePlatform={chipBasePlatform}
431
+ onClose={onClose}
432
+ onDiscuss={onDiscuss}
433
+ />
434
+ ))}
435
+ {expanded &&
436
+ uncited.map((src) => (
437
+ <SourceChip
438
+ key={src.index}
439
+ src={src}
440
+ baseRoute={baseRoute}
441
+ chipBasePlatform={chipBasePlatform}
442
+ onClose={onClose}
443
+ onDiscuss={onDiscuss}
444
+ />
445
+ ))}
446
+ {hasOverflow && (
447
+ <button
448
+ onClick={() => setExpanded(!expanded)}
449
+ className="inline-flex items-center gap-1 px-2 py-0.5 rounded border text-[11px] cursor-pointer transition-colors bg-ods-card border-ods-accent text-ods-accent hover:bg-ods-accent/10"
450
+ aria-expanded={expanded}
451
+ aria-label={
452
+ expanded
453
+ ? 'Show fewer sources'
454
+ : `Show ${hiddenCount} additional retrieved sources`
455
+ }
456
+ >
457
+ {expanded
458
+ ? 'Show less'
459
+ : `+${hiddenCount} more retrieved ${
460
+ hiddenCount === 1 ? 'source' : 'sources'
461
+ }`}
462
+ </button>
463
+ )}
464
+ </div>
465
+ </div>
466
+ )
467
+ }
468
+
469
+ // =============================================================================
470
+ // Component
471
+ // =============================================================================
472
+
473
+ /**
474
+ * EmbeddableChat — the floating "Ask AI" button + Mingo chat panel.
475
+ * Lib-portable port of the hub's `<GlobalAskAI>`.
476
+ */
477
+ export function EmbeddableChat(props: EmbeddableChatProps) {
478
+ return <EmbeddableChatInner {...props} />
479
+ }
480
+
481
+ function EmbeddableChatInner({
482
+ baseRoute,
483
+ chipBasePlatform,
484
+ enabledRagTableIds = null,
485
+ emptyStateGreeting = null,
486
+ suggestedQueries = null,
487
+ open,
488
+ onOpenChange,
489
+ defaultOpen,
490
+ showInternalTrigger = true,
491
+ extras,
492
+ tableIdForDocumentType,
493
+ }: EmbeddableChatProps) {
494
+ const runtime = useRequiredChatRuntime()
495
+ const source = runtime.source as DocSource
496
+ const commandsUrl = runtime.endpoints.commandsUrl
497
+ // Server-resolved identity — drives the greeting first-name AND the
498
+ // attachment capability flag. Single source of truth: the chat-identity
499
+ // endpoint returns BOTH the auth tier and the resolved user, so the
500
+ // displayed name always matches who the server thinks the user is.
501
+ const { attachmentsEnabled, user: identityUser } = useChatIdentity()
502
+ const viewUrlPrefix = runtime.endpoints.attachmentViewUrlPrefix
503
+
504
+ // Dev-only warning when only one of `open` / `onOpenChange` is set.
505
+ useEffect(() => {
506
+ if (process.env.NODE_ENV !== 'production') {
507
+ const hasOpen = open !== undefined
508
+ const hasHandler = onOpenChange !== undefined
509
+ if (hasOpen !== hasHandler) {
510
+ console.warn(
511
+ '[EmbeddableChat] `open` and `onOpenChange` must both be provided ' +
512
+ 'for controlled mode, or both omitted for uncontrolled. ' +
513
+ `Provided: open=${hasOpen}, onOpenChange=${hasHandler}.`,
514
+ )
515
+ }
516
+ }
517
+ }, [open, onOpenChange])
518
+
519
+ const [isOpen = false, setIsOpen] = useControllableState<boolean>({
520
+ prop: open,
521
+ defaultProp: defaultOpen ?? false,
522
+ onChange: onOpenChange,
523
+ })
524
+
525
+ // Suppress chat input auto-focus on touch devices (avoids popping the
526
+ // on-screen keyboard during the bottom-sheet open animation).
527
+ const [autoFocusInput] = useState(() => {
528
+ if (typeof window === 'undefined') return false
529
+ return !window.matchMedia('(pointer: coarse)').matches
530
+ })
531
+
532
+ // iOS scroll-lock — react-aria's two-layer approach plus body fixed-pos.
533
+ // See hub original for the long mechanism comment; logic copied verbatim.
534
+ usePreventScroll({ isDisabled: !isOpen })
535
+
536
+ const navigatingAwayRef = useRef(false)
537
+
538
+ useEffect(() => {
539
+ if (!isOpen) return
540
+ if (typeof window === 'undefined') return
541
+ if (!isIOS()) return
542
+
543
+ navigatingAwayRef.current = false
544
+
545
+ const scrollY = window.scrollY
546
+ const openPathname = window.location.pathname
547
+ const body = document.body
548
+ const prev = {
549
+ position: body.style.position,
550
+ top: body.style.top,
551
+ width: body.style.width,
552
+ }
553
+ body.style.position = 'fixed'
554
+ body.style.top = `-${scrollY}px`
555
+ body.style.width = '100%'
556
+
557
+ return () => {
558
+ body.style.position = prev.position
559
+ body.style.top = prev.top
560
+ body.style.width = prev.width
561
+ if (navigatingAwayRef.current) return
562
+ if (window.location.pathname !== openPathname) return
563
+ window.scrollTo(0, scrollY)
564
+ }
565
+ }, [isOpen])
566
+
567
+ // Imperative handle to the chat input.
568
+ const chatInputRef = useRef<ChatInputRef | null>(null)
569
+
570
+ // Async lookup of the slash-command registry for the current source.
571
+ const [commandsById, setCommandsById] = useState<Map<string, SlashCommandSummary>>(
572
+ () => new Map(),
573
+ )
574
+ const [commandsLoaded, setCommandsLoaded] = useState(false)
575
+ useEffect(() => {
576
+ if (!isOpen) return
577
+ let cancelled = false
578
+ const ctrl = new AbortController()
579
+ setCommandsLoaded(false)
580
+ fetchSlashCommands('', ctrl.signal, commandsUrl)
581
+ .then((commands) => {
582
+ if (cancelled) return
583
+ const map = new Map<string, SlashCommandSummary>()
584
+ for (const cmd of commands) map.set(cmd.id, cmd)
585
+ setCommandsById(map)
586
+ setCommandsLoaded(true)
587
+ })
588
+ .catch((err) => {
589
+ if (!cancelled && (err as Error)?.name !== 'AbortError') {
590
+ console.warn('[embeddable-chat] failed to fetch slash commands:', err)
591
+ setCommandsLoaded(true)
592
+ }
593
+ })
594
+ return () => {
595
+ cancelled = true
596
+ ctrl.abort()
597
+ }
598
+ }, [isOpen, source, commandsUrl])
599
+
600
+ // Slash-command autocomplete config — passed to <ChatInput>.
601
+ const slashCommandsProp = useMemo(
602
+ () => ({
603
+ fetchCommands: (prefix: string, signal?: AbortSignal) =>
604
+ fetchSlashCommands(prefix, signal, commandsUrl),
605
+ resolveSourceIcon: (sourceId: string) => {
606
+ const iconName = getSourceIconName(sourceId)
607
+ if (!iconName) return undefined
608
+ return { Icon: getIconComponent(iconName), label: sourceId }
609
+ },
610
+ onAction: (cmd: SlashCommandSummary, actionId: SlashCommandActionId) => {
611
+ dispatchSlashCommandAction(actionId, cmd.id, chatInputRef)
612
+ },
613
+ }),
614
+ [commandsUrl],
615
+ )
616
+
617
+ // After the open animation finishes we clear the CSS `transform` so
618
+ // `position: fixed` descendants escape the panel's containing block.
619
+ const [animationRest, setAnimationRest] = useState(false)
620
+ useEffect(() => {
621
+ if (!isOpen) setAnimationRest(false)
622
+ }, [isOpen])
623
+
624
+ // Greeting first-name comes from the SERVER-resolved identity (single
625
+ // source of truth — never a client-injected runtime field). Resolution
626
+ // order:
627
+ // 1. `identityUser.firstName` — dedicated optional field from the
628
+ // identity webservice (populated via `X-Chat-First-Name` for
629
+ // bearer-act-as, or by the hub's profile lookup for cookie sessions).
630
+ // 2. `identityUser.name.split(' ')[0]` — legacy fallback for sources
631
+ // that only return a full name. Empty-string-safe (`?.`-chain).
632
+ // 3. `undefined` — anon, loading, or no name available → greeting
633
+ // collapses to the no-name variant `Hey, I'm Mingo`.
634
+ // We coalesce an empty string to `undefined` so the JSX `userName ? …`
635
+ // branch treats `''` the same as missing — embedders that send
636
+ // `firstName: ''` shouldn't render `Hey , I'm Mingo`.
637
+ const userName =
638
+ (identityUser?.firstName?.trim() ||
639
+ identityUser?.name?.split(' ')[0]?.trim()) ||
640
+ undefined
641
+
642
+ const {
643
+ messages: rawMessages,
644
+ isLoading: chatLoading,
645
+ sendMessage,
646
+ discussRef,
647
+ stopMessage,
648
+ clearMessages,
649
+ currentProvider,
650
+ currentModelLabel,
651
+ currentContextWindowMaxTokens,
652
+ currentInputTokens,
653
+ currentOutputTokens,
654
+ currentCacheHitRatePct,
655
+ currentUsageBreakdown,
656
+ displayRef,
657
+ } = useEmbeddedChat({ tableIdForDocumentType })
658
+
659
+ // Chat-attachment hooks (v2 attachment feature).
660
+ const {
661
+ attachments: stagedAttachments,
662
+ readyAttachments,
663
+ hasInflightUploads,
664
+ addFiles: addAttachmentFiles,
665
+ removeAttachment,
666
+ clear: clearAttachments,
667
+ } = useChatAttachments()
668
+ const { panelRef: galleryPanelRef, modal: galleryModal } =
669
+ useChatAttachmentImageGallery()
670
+
671
+ // Resolve base route. Hub default mapping: flamingo → /knowledge-base,
672
+ // anything else → /data-room. Embedders can override per platform.
673
+ const resolvedBaseRoute =
674
+ baseRoute || (source === 'flamingo' ? '/knowledge-base' : '/data-room')
675
+
676
+ const handleClose = useCallback(() => setIsOpen(false), [setIsOpen])
677
+
678
+ const handleNavigationClose = useCallback(() => {
679
+ navigatingAwayRef.current = true
680
+ setIsOpen(false)
681
+ }, [setIsOpen])
682
+
683
+ // Stable panel-level handle for descendants (inline cards via
684
+ // `ChatCardNavWrap` and markdown-body links via
685
+ // `NavLinkAnchorViaRuntime`) to close the panel after same-tab
686
+ // navigation. Uses `handleNavigationClose` (not `handleClose`) so the
687
+ // iOS scroll-restore cleanup effect at the bottom of this file sees
688
+ // `navigatingAwayRef.current = true` and skips restoring the chat's
689
+ // saved scroll position over the freshly-loaded destination page.
690
+ // Source chips already route through `handleNavigationClose`; inline
691
+ // cards now match. New-tab clicks never invoke `closeChat`.
692
+ const chatPanelHandle = useMemo<ChatPanelHandle>(
693
+ () => ({ closeChat: handleNavigationClose }),
694
+ [handleNavigationClose],
695
+ )
696
+
697
+ // Host-provided renderer for inline entity cards — routes through the
698
+ // shared dispatcher in lib's `entity-cards/dispatch.tsx`.
699
+ const renderEntityCard = useCallback(
700
+ (reference: ChatRef): React.ReactNode =>
701
+ renderChatInlineEntityCard(reference, {
702
+ onDiscuss: discussRef,
703
+ onDisplay: displayRef,
704
+ baseRoute: resolvedBaseRoute,
705
+ chipBasePlatform,
706
+ extras,
707
+ }),
708
+ [discussRef, displayRef, resolvedBaseRoute, chipBasePlatform, extras],
709
+ )
710
+
711
+ // Map docMessages → lib's Message type, forwarding chatRefs + scrollAnchor.
712
+ const messages: Message[] = useMemo(
713
+ () =>
714
+ rawMessages.map((m) => ({
715
+ id: m.id,
716
+ role: m.role as 'user' | 'assistant',
717
+ name: m.role === 'assistant' ? 'Mingo AI' : 'You',
718
+ content: m.segments && m.segments.length > 0 ? m.segments : m.content,
719
+ timestamp: new Date(),
720
+ assistantType: m.role === 'assistant' ? ('mingo' as const) : undefined,
721
+ ...(m.chatRefs ? { chatRefs: m.chatRefs } : {}),
722
+ ...(m.scrollAnchor ? { scrollAnchor: m.scrollAnchor } : {}),
723
+ })),
724
+ [rawMessages],
725
+ )
726
+
727
+ const handleSend = useCallback(
728
+ (text: string) => {
729
+ // Append chat-attachment markdown lines to the user's bubble.
730
+ let augmentedText = text
731
+ if (readyAttachments.length > 0) {
732
+ const markdown = readyAttachments
733
+ .map((att) => formatChatAttachmentMarkdownForBubble(att, viewUrlPrefix))
734
+ .join('')
735
+ augmentedText = `${text}${markdown}`
736
+ }
737
+ sendMessage(augmentedText, {
738
+ ...(readyAttachments.length > 0
739
+ ? { pendingAttachments: readyAttachments }
740
+ : {}),
741
+ })
742
+ if (readyAttachments.length > 0) {
743
+ clearAttachments()
744
+ }
745
+ },
746
+ [sendMessage, readyAttachments, viewUrlPrefix, clearAttachments],
747
+ )
748
+
749
+ const handleNewChat = useCallback(() => {
750
+ clearMessages()
751
+ }, [clearMessages])
752
+
753
+ const handleOpen = useCallback(() => setIsOpen(true), [setIsOpen])
754
+
755
+ // Close on every pathname change. Lib has no `usePathname()`, so we pass
756
+ // `null` — embedders that want pathname-driven close can wrap this
757
+ // component with their own close-on-navigation effect. The hub mounts
758
+ // `<HubRuntimeProvider>` which already supplies this behavior on the
759
+ // outer shell, so the chat panel close is a single concern here.
760
+ useCloseOnNavigation(handleNavigationClose, null)
761
+
762
+ // Listen for cross-component "open chat with this row" events. Fired by
763
+ // search bars when the user clicks a result with no public URL.
764
+ useEffect(() => {
765
+ const handler = (e: Event) => {
766
+ const detail = (
767
+ e as CustomEvent<{
768
+ source: string
769
+ ref: { type: string; id: string; title: string; url: string | null }
770
+ }>
771
+ ).detail
772
+ if (!detail || detail.source !== source) return
773
+ setIsOpen(true)
774
+ setTimeout(() => discussRef(detail.ref as ChatRef), 0)
775
+ }
776
+ window.addEventListener('ask-ai:open-with-ref', handler)
777
+ return () => window.removeEventListener('ask-ai:open-with-ref', handler)
778
+ }, [source, discussRef, setIsOpen])
779
+
780
+ const hasMessages = messages.length > 0
781
+ const sourceLabel = source === 'flamingo' ? 'Knowledge Base' : 'Data Room'
782
+ const greetingText =
783
+ emptyStateGreeting || `Ask me anything about ${sourceLabel.toLowerCase()}.`
784
+
785
+ // Empty-state chip grid — derived directly from the fetched slash commands.
786
+ const enabledSet = useMemo(
787
+ () => (enabledRagTableIds ? new Set<string>(enabledRagTableIds) : null),
788
+ [enabledRagTableIds],
789
+ )
790
+ const chipCommands = useMemo(() => {
791
+ const out: SlashCommandSummary[] = []
792
+ for (const cmd of commandsById.values()) {
793
+ if (cmd.displayOrder === undefined) continue
794
+ if (enabledSet && cmd.primarySourceId && !enabledSet.has(cmd.primarySourceId))
795
+ continue
796
+ out.push(cmd)
797
+ }
798
+ out.sort((a, b) => {
799
+ const ao = a.displayOrder ?? Number.POSITIVE_INFINITY
800
+ const bo = b.displayOrder ?? Number.POSITIVE_INFINITY
801
+ if (ao !== bo) return ao - bo
802
+ return a.id.localeCompare(b.id)
803
+ })
804
+ return out
805
+ }, [commandsById, enabledSet])
806
+
807
+ // Find sources for the last assistant message; split into cited / uncited.
808
+ const lastAssistantMsg = [...rawMessages].reverse().find((m) => m.role === 'assistant')
809
+ const lastSources = useMemo(() => {
810
+ if (chatLoading) return undefined
811
+ const sources = lastAssistantMsg?.sources
812
+ if (!sources || sources.length === 0) return undefined
813
+ const content = lastAssistantMsg?.content || ''
814
+ const citationOrder = [...content.matchAll(/\[(\d+)\]/g)].map((m) =>
815
+ parseInt(m[1], 10),
816
+ )
817
+ const seenOrder = new Map<number, number>()
818
+ citationOrder.forEach((idx) => {
819
+ if (!seenOrder.has(idx)) seenOrder.set(idx, seenOrder.size)
820
+ })
821
+ const cited = sources
822
+ .filter((s) => seenOrder.has(s.index))
823
+ .sort((a, b) => (seenOrder.get(a.index) ?? 0) - (seenOrder.get(b.index) ?? 0))
824
+ const uncited = sources.filter((s) => !seenOrder.has(s.index))
825
+ return { cited, uncited }
826
+ }, [lastAssistantMsg, chatLoading])
827
+
828
+
829
+ return (
830
+ <>
831
+ {/* Floating "Ask AI" button — sticky-dock pattern. See hub original
832
+ for the full mechanism explanation. */}
833
+ {showInternalTrigger && (
834
+ <div
835
+ aria-hidden={isOpen}
836
+ className={`sticky bottom-0 h-0 z-[9990] pointer-events-none ${
837
+ isOpen ? 'opacity-0' : 'opacity-100'
838
+ }`}
839
+ >
840
+ <div className="absolute bottom-4 md:bottom-6 right-4 md:right-6">
841
+ <Button
842
+ onClick={handleOpen}
843
+ leftIcon={<MingoIcon className="h-5 w-5" color="currentColor" />}
844
+ tabIndex={isOpen ? -1 : 0}
845
+ className={`shadow-lg !w-auto pointer-events-auto ${
846
+ isOpen ? '!pointer-events-none' : ''
847
+ }`}
848
+ >
849
+ Ask AI
850
+ </Button>
851
+ </div>
852
+ </div>
853
+ )}
854
+
855
+ <Dialog.Root open={isOpen} onOpenChange={(o) => !o && handleClose()}>
856
+ <Dialog.Portal>
857
+ <Dialog.Overlay className="mingo-chat-overlay fixed inset-0 z-[9997] bg-black/50 md:bg-black/20" />
858
+ {/*
859
+ Panel-level handle for descendants (inline cards via
860
+ `ChatCardNavWrap`, markdown-body links via
861
+ `NavLinkAnchorViaRuntime`) to close the panel after same-tab
862
+ navigation. Same-tab clicks fire `closeChat`; new-tab clicks
863
+ leave the panel open while the new tab loads.
864
+ */}
865
+ <ChatPanelContext.Provider value={chatPanelHandle}>
866
+ <Dialog.Content
867
+ aria-describedby={undefined}
868
+ onAnimationEnd={(e) => {
869
+ if (e.currentTarget === e.target && isOpen) setAnimationRest(true)
870
+ }}
871
+ style={animationRest ? { transform: 'none', animation: 'none' } : undefined}
872
+ className="
873
+ mingo-chat-content
874
+ fixed z-[9998] flex flex-col bg-ods-bg
875
+ focus:outline-none focus-visible:outline-none
876
+ inset-0 h-[100dvh] max-h-[100dvh] shadow-2xl
877
+ md:inset-y-0 md:right-0 md:left-auto md:h-full md:max-h-none md:w-[clamp(480px,50vw,720px)] md:max-w-none md:rounded-l-2xl md:border-l md:border-ods-border
878
+ "
879
+ >
880
+ <VisuallyHidden>
881
+ <Dialog.Title>{sourceLabel} AI Assistant</Dialog.Title>
882
+ </VisuallyHidden>
883
+
884
+ <div className="flex h-full flex-col overflow-hidden">
885
+ <div className="flex-shrink-0 px-5 pt-4">
886
+ <ChatHeader
887
+ userName="Mingo AI"
888
+ userTitle={`${sourceLabel} Assistant`}
889
+ userIcon={<MingoIcon className="h-6 w-6" color="white" />}
890
+ onNewChat={handleNewChat}
891
+ onClose={handleClose}
892
+ showNewChat={hasMessages}
893
+ connectionStatus="connected"
894
+ fullWidth
895
+ className="!rounded-xl"
896
+ />
897
+ </div>
898
+
899
+ <div
900
+ ref={galleryPanelRef}
901
+ className="flex-1 flex flex-col min-h-0 px-5 py-4"
902
+ >
903
+ {hasMessages ? (
904
+ <div className="flex-1 flex flex-col min-h-0">
905
+ <ChatMessageList
906
+ messages={messages}
907
+ isTyping={chatLoading}
908
+ autoScroll={true}
909
+ assistantType="mingo"
910
+ assistantIcon={<MingoIcon className="h-4 w-4" color="white" />}
911
+ renderEntityCard={renderEntityCard}
912
+ NavLinkAnchor={NavLinkAnchorViaRuntime}
913
+ className="flex-1"
914
+ fullWidth
915
+ />
916
+ {lastSources &&
917
+ (lastSources.cited.length > 0 ||
918
+ lastSources.uncited.length > 0) &&
919
+ !chatLoading && (
920
+ <div className="flex-shrink-0 pb-2">
921
+ <SourceChips
922
+ cited={lastSources.cited}
923
+ uncited={lastSources.uncited}
924
+ baseRoute={resolvedBaseRoute}
925
+ chipBasePlatform={chipBasePlatform}
926
+ onClose={handleNavigationClose}
927
+ onDiscuss={discussRef}
928
+ />
929
+ </div>
930
+ )}
931
+ </div>
932
+ ) : (
933
+ <div className="flex-1 flex flex-col items-center pt-8 pb-6 overflow-y-auto -mt-4">
934
+ <div className="text-center max-w-md w-full mb-8">
935
+ <MingoIcon
936
+ className="mx-auto mb-4 h-10 w-10"
937
+ color="var(--ods-accent)"
938
+ />
939
+ <p
940
+ className="text-2xl font-semibold text-ods-text-primary mb-2 tracking-[-0.02em]"
941
+ style={{ fontFamily: 'var(--font-h1-family)' }}
942
+ >
943
+ {userName ? `Hey ${userName}, I'm Mingo` : "Hey, I'm Mingo"}
944
+ </p>
945
+ <p className="text-ods-text-secondary text-sm leading-relaxed">
946
+ {greetingText}
947
+ </p>
948
+ </div>
949
+
950
+ {suggestedQueries && suggestedQueries.length > 0 && (
951
+ <div className="w-full mb-8">
952
+ <p className="text-xs text-ods-text-secondary mb-3 px-1">
953
+ Try asking:
954
+ </p>
955
+ <div className="flex flex-wrap gap-2">
956
+ {suggestedQueries.map((q) => (
957
+ <button
958
+ key={q}
959
+ type="button"
960
+ onClick={() => handleSend(q)}
961
+ className="rounded-full border border-ods-border bg-ods-bg-secondary px-3 py-1.5 text-sm text-ods-text-primary hover:border-ods-accent hover:bg-ods-bg-hover transition-colors"
962
+ >
963
+ {q}
964
+ </button>
965
+ ))}
966
+ </div>
967
+ </div>
968
+ )}
969
+
970
+ {(chipCommands.length > 0 || !commandsLoaded) && (
971
+ <div className="w-full">
972
+ <p className="text-xs text-ods-text-secondary mb-3 px-1">
973
+ I can help you with:
974
+ </p>
975
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
976
+ {!commandsLoaded &&
977
+ chipCommands.length === 0 &&
978
+ Array.from({ length: 4 }).map((_, i) => (
979
+ <Card
980
+ key={`chip-skeleton-${i}`}
981
+ className="flex items-start gap-3 px-4 py-3 bg-ods-card border-ods-border shadow-none animate-pulse"
982
+ >
983
+ <div className="h-5 w-5 mt-0.5 shrink-0 rounded bg-ods-bg" />
984
+ <div className="min-w-0 flex-1 flex flex-col gap-2">
985
+ <div className="h-4 w-1/2 rounded bg-ods-bg" />
986
+ <div className="h-3 w-3/4 rounded bg-ods-bg/60" />
987
+ </div>
988
+ </Card>
989
+ ))}
990
+ {chipCommands.map((cmd) => {
991
+ const Icon = getIconComponent(cmd.iconName)
992
+ const cmdId = cmd.id
993
+ const label = cmd.label ?? `/${cmdId}`
994
+ return (
995
+ <Card
996
+ key={cmdId}
997
+ className="flex flex-col gap-2 px-4 py-3 bg-ods-card border-ods-border shadow-none hover:border-ods-accent/50 transition-colors"
998
+ >
999
+ <div className="flex items-start gap-3">
1000
+ <Icon className="h-5 w-5 mt-0.5 shrink-0 text-ods-text-primary" />
1001
+ <div className="text-sm font-medium text-ods-text-primary truncate flex-1 min-w-0">
1002
+ {label}
1003
+ </div>
1004
+ <span className="font-mono text-[11px] text-ods-text-muted shrink-0 mt-0.5 max-w-[40%] truncate">
1005
+ /{cmdId}
1006
+ </span>
1007
+ </div>
1008
+ {cmd.description && (
1009
+ <p className="text-xs text-ods-text-muted leading-snug line-clamp-2">
1010
+ {cmd.description}
1011
+ </p>
1012
+ )}
1013
+ <div className="flex flex-wrap items-center gap-1.5 -mb-0.5">
1014
+ {cmd.actions.map((action) => (
1015
+ <button
1016
+ key={action.id}
1017
+ type="button"
1018
+ onClick={() =>
1019
+ dispatchSlashCommandAction(
1020
+ action.id,
1021
+ cmdId,
1022
+ chatInputRef,
1023
+ )
1024
+ }
1025
+ aria-label={`${action.label} ${label}`}
1026
+ title={`${action.label} ${label}`}
1027
+ className={CHIP_ACTION_BUTTON_CLASS}
1028
+ >
1029
+ {action.label}
1030
+ </button>
1031
+ ))}
1032
+ </div>
1033
+ </Card>
1034
+ )
1035
+ })}
1036
+ </div>
1037
+ </div>
1038
+ )}
1039
+ </div>
1040
+ )}
1041
+ </div>
1042
+
1043
+ <ChatAttachmentChipStrip
1044
+ attachments={stagedAttachments}
1045
+ onRemove={removeAttachment}
1046
+ disabled={chatLoading}
1047
+ />
1048
+
1049
+ <div
1050
+ className="flex-shrink-0 px-5 pb-4 flex flex-col gap-1"
1051
+ style={{ paddingBottom: 'max(1rem, env(safe-area-inset-bottom))' }}
1052
+ >
1053
+ <ChatFooter className="!p-0" fullWidth>
1054
+ <ChatInput
1055
+ ref={chatInputRef}
1056
+ onSend={handleSend}
1057
+ onStop={stopMessage}
1058
+ sending={chatLoading || hasInflightUploads}
1059
+ placeholder={
1060
+ hasInflightUploads
1061
+ ? 'Waiting for uploads to finish…'
1062
+ : 'Ask a question...'
1063
+ }
1064
+ fullWidth
1065
+ className="px-0"
1066
+ reserveAvatarOffset={false}
1067
+ autoFocus={autoFocusInput}
1068
+ slashCommands={slashCommandsProp}
1069
+ />
1070
+ </ChatFooter>
1071
+
1072
+ <div className="flex items-center gap-2 w-full">
1073
+ <ChatAttachmentAddButton
1074
+ attachmentsEnabled={attachmentsEnabled}
1075
+ attachmentsCount={stagedAttachments.length}
1076
+ onAddFiles={addAttachmentFiles}
1077
+ disabled={chatLoading}
1078
+ />
1079
+ <div className="flex-1 min-w-0">
1080
+ <ModelDisplay
1081
+ provider={currentProvider ?? 'anthropic'}
1082
+ modelName={currentModelLabel ?? 'Claude'}
1083
+ usedTokens={
1084
+ currentInputTokens != null
1085
+ ? (currentInputTokens ?? 0) + (currentOutputTokens ?? 0)
1086
+ : undefined
1087
+ }
1088
+ contextWindow={currentContextWindowMaxTokens ?? undefined}
1089
+ inputTokens={currentInputTokens ?? undefined}
1090
+ outputTokens={currentOutputTokens ?? undefined}
1091
+ hitRatePct={currentCacheHitRatePct ?? undefined}
1092
+ breakdown={currentUsageBreakdown ?? undefined}
1093
+ />
1094
+ </div>
1095
+ </div>
1096
+ </div>
1097
+ </div>
1098
+ {galleryModal}
1099
+ </Dialog.Content>
1100
+ </ChatPanelContext.Provider>
1101
+ </Dialog.Portal>
1102
+ </Dialog.Root>
1103
+ </>
1104
+ )
1105
+ }
1106
+