@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,114 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Click-to-expand handler for chat-attachment images in the chat thread.
5
+ *
6
+ * Returns `{ panelRef, modal }`:
7
+ * - `panelRef` — attach to the chat content wrapper. The hook
8
+ * installs a delegated `click` listener on this ref.
9
+ * - `modal` — render as a sibling of the chat panel (so it lives
10
+ * OUTSIDE any overflow-hidden message bubbles). Returns `null`
11
+ * while closed.
12
+ *
13
+ * Behavior on click:
14
+ * 1. Filter for `<img>` whose `src` starts with the runtime's
15
+ * `attachmentViewUrlPrefix` (so non-chat images don't intercept).
16
+ * 2. Walk up to the nearest `[data-message-role]` ancestor — the
17
+ * boundary the chat-shell wraps each message bubble with.
18
+ * 3. Collect ALL chat-attachment `<img>`s inside that boundary in
19
+ * DOM order. Compute the clicked index for `initialIndex`.
20
+ * 4. Open `ImageGalleryModal` (UI-Kit, also used by
21
+ * `roadmap-card.tsx` for screenshot expansion).
22
+ *
23
+ * The hook deliberately owns the click DOM-walk + modal state so
24
+ * the host component consumes a single `{panelRef, modal}` API.
25
+ */
26
+
27
+ import { useCallback, useEffect, useRef, useState } from 'react'
28
+ import { ImageGalleryModal } from '../../ui/image-gallery-modal'
29
+ import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context'
30
+
31
+ interface GalleryState {
32
+ isOpen: boolean
33
+ images: string[]
34
+ initialIndex: number
35
+ }
36
+
37
+ const CLOSED: GalleryState = { isOpen: false, images: [], initialIndex: 0 }
38
+
39
+ export function useChatAttachmentImageGallery() {
40
+ const runtime = useRequiredChatRuntime()
41
+ const viewUrlPrefix = runtime.endpoints.attachmentViewUrlPrefix
42
+ const panelRef = useRef<HTMLDivElement | null>(null)
43
+ const [state, setState] = useState<GalleryState>(CLOSED)
44
+
45
+ const handleClose = useCallback(() => setState(CLOSED), [])
46
+
47
+ useEffect(() => {
48
+ const root = panelRef.current
49
+ if (!root) return
50
+
51
+ const onClick = (ev: Event) => {
52
+ const target = ev.target as HTMLElement | null
53
+ if (!target) return
54
+ // Match an <img> whose `src` starts with the proxy URL prefix.
55
+ const img = target.closest('img') as HTMLImageElement | null
56
+ if (!img || !img.src.startsWith(absolutize(viewUrlPrefix))) return
57
+
58
+ // Walk up to the message-bubble boundary.
59
+ const bubble = img.closest('[data-message-role]')
60
+ if (!bubble) {
61
+ // Fallback: open the modal with just the clicked image.
62
+ ev.preventDefault()
63
+ setState({ isOpen: true, images: [img.src], initialIndex: 0 })
64
+ return
65
+ }
66
+
67
+ // Collect all chat-attachment images inside this bubble in DOM
68
+ // order. The `closest` lookup matched img.src.startsWith already,
69
+ // but we re-check below because `querySelectorAll` returns ALL
70
+ // <img>s in the bubble (avatar images etc.).
71
+ const allImgs = Array.from(bubble.querySelectorAll('img')) as HTMLImageElement[]
72
+ const matching = allImgs.filter((el) => el.src.startsWith(absolutize(viewUrlPrefix)))
73
+ if (matching.length === 0) return
74
+
75
+ const idx = Math.max(0, matching.indexOf(img))
76
+ ev.preventDefault()
77
+ setState({
78
+ isOpen: true,
79
+ images: matching.map((el) => el.src),
80
+ initialIndex: idx,
81
+ })
82
+ }
83
+
84
+ root.addEventListener('click', onClick)
85
+ return () => root.removeEventListener('click', onClick)
86
+ }, [viewUrlPrefix])
87
+
88
+ // `ImageGalleryModal` doesn't accept `null` for `images` — render
89
+ // it only when open. Closing returns `null` so the modal unmounts
90
+ // and React garbage-collects any blob URLs it might hold.
91
+ const modal = state.isOpen ? (
92
+ <ImageGalleryModal
93
+ images={state.images}
94
+ isOpen={state.isOpen}
95
+ onClose={handleClose}
96
+ initialIndex={state.initialIndex}
97
+ />
98
+ ) : null
99
+
100
+ return { panelRef, modal }
101
+ }
102
+
103
+ /** Absolutize a prefix for comparison against `<img>.src`. Browsers
104
+ * resolve `<img src="/api/storage/view/...">` to an absolute URL when
105
+ * reading `.src` — we need the same shape on the prefix to match.
106
+ *
107
+ * Embedded apps that supply an absolute prefix already work as-is
108
+ * (the prefix matches `.src` directly). For relative prefixes (host
109
+ * mode), prefix with `window.location.origin`. */
110
+ function absolutize(prefix: string): string {
111
+ if (prefix.startsWith('http://') || prefix.startsWith('https://')) return prefix
112
+ if (typeof window === 'undefined') return prefix
113
+ return `${window.location.origin}${prefix.startsWith('/') ? '' : '/'}${prefix}`
114
+ }
@@ -0,0 +1,429 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Chat-attachment upload state machine.
5
+ *
6
+ * Multi-file state (precedent: hub `components/blog/image-upload-manager.tsx`).
7
+ * Magic-byte sniff via `file-type` BEFORE upload (rejects extension
8
+ * spoofs like `evil.exe` renamed `screenshot.png`). Per-attachment
9
+ * `AbortController` for clean remove-mid-upload.
10
+ *
11
+ * Auth propagation:
12
+ * - `chatAuthedFetch` carries bearer-act-as headers + Supabase
13
+ * session cookies — used both for minting the upload URL and as
14
+ * the auth path before falling through to the signed PUT.
15
+ * - The byte-PUT to the signed URL uses an inline `uploadWithProgress`
16
+ * XHR helper (ported from the hub `unified-upload-service`) so the
17
+ * lib-side chat hook is self-contained.
18
+ *
19
+ * URL endpoint: `runtime.endpoints.attachmentUploadUrl` (hub default
20
+ * `/api/storage/generate-upload-url`). Embedded apps override.
21
+ *
22
+ * Dedup: files keyed by `(name, size, lastModified)` — second pick of
23
+ * the same file is silently ignored.
24
+ */
25
+
26
+ import { useCallback, useMemo, useRef, useState } from 'react'
27
+ import { fileTypeFromBlob } from 'file-type'
28
+ import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context'
29
+ import { chatAuthedFetch } from '../utils/chat-authed-fetch'
30
+ import {
31
+ CHAT_ATTACHMENT_MIME_TYPES,
32
+ CHAT_ATTACHMENT_CONCURRENT_UPLOADS_PER_USER,
33
+ } from '../chat-attachment-bar'
34
+ import type { ChatAttachment } from '../utils/chat-attachment-markdown'
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Inlined hub-side constants (single source of truth — `lib/config/
38
+ // chat-attachment-config.ts` on the hub). Hard-coded here so the lib
39
+ // hook is self-contained; if hub-side bumps the cap, update both.
40
+ // ---------------------------------------------------------------------------
41
+
42
+ const CHAT_ATTACHMENTS_BUCKET = 'chat-attachments'
43
+ const CHAT_ATTACHMENTS_FOLDER = 'chat'
44
+ const CHAT_ATTACHMENT_MAX_SIZE_BYTES = 25 * 1024 * 1024
45
+
46
+ /** Cache-Control header for content-addressed uploads — Storage paths
47
+ * embed `{timestamp}-{random}-{filename}` so a given URL is immutable. */
48
+ const STORAGE_CACHE_CONTROL_IMMUTABLE = 'public, max-age=31536000, immutable'
49
+
50
+ type Status = 'sniffing' | 'uploading' | 'ready' | 'error'
51
+
52
+ /** Internal staged-attachment shape (more fields than the wire
53
+ * `ChatAttachment` — carries state-machine metadata). */
54
+ export interface StagedAttachment {
55
+ /** Stable client-side id; survives across re-renders. */
56
+ id: string
57
+ file: File
58
+ status: Status
59
+ /** Set when `status === 'ready'`. Server-issued. */
60
+ storagePath?: string
61
+ /** Set when `status === 'ready'`. Server-issued HMAC view token. */
62
+ viewToken?: string
63
+ /** 0-100 during 'uploading'. */
64
+ progress: number
65
+ /** Set when `status === 'error'`. */
66
+ errorMessage?: string
67
+ }
68
+
69
+ /** Response shape from `/api/storage/generate-upload-url`. */
70
+ interface UploadUrlResponse {
71
+ uploadUrl: string
72
+ filePath: string
73
+ token: string
74
+ bucket: string
75
+ publicUrl: string | null
76
+ viewToken: string | null
77
+ }
78
+
79
+ interface UseChatAttachmentsApi {
80
+ attachments: StagedAttachment[]
81
+ /** All staged files in 'ready' state, projected to the wire
82
+ * `ChatAttachment` shape — pass to `sendMessage`'s `pendingAttachments`. */
83
+ readyAttachments: ChatAttachment[]
84
+ /** True iff any attachment is mid-upload — Send button reads this
85
+ * to disable itself. */
86
+ hasInflightUploads: boolean
87
+ addFiles: (files: FileList | File[]) => void
88
+ removeAttachment: (id: string) => void
89
+ clear: () => void
90
+ }
91
+
92
+ /**
93
+ * Inline `uploadWithProgress` — XHR PUT to a Supabase signed-upload URL
94
+ * with progress + abort support. Ported verbatim from the hub
95
+ * `unified-upload-service.ts`. Maps the XHR 0-100 to the caller's 10-95
96
+ * range (leaving 0-10 for the mint + 95-100 for state transitions).
97
+ *
98
+ * AbortSignal wiring: `signal?.addEventListener('abort', () => xhr.abort())`
99
+ * — without this, calling `removeAttachment`/`clear()` aborts the
100
+ * controller but the XHR runs to completion and bytes still land in
101
+ * Supabase.
102
+ */
103
+ function uploadWithProgress(
104
+ url: string,
105
+ file: File,
106
+ token: string,
107
+ onProgress?: (progress: number) => void,
108
+ signal?: AbortSignal,
109
+ ): Promise<void> {
110
+ return new Promise((resolve, reject) => {
111
+ if (signal?.aborted) {
112
+ reject(new Error('Upload cancelled'))
113
+ return
114
+ }
115
+ const xhr = new XMLHttpRequest()
116
+ let isCompleted = false
117
+
118
+ const cleanup = () => {
119
+ if (isCompleted) return
120
+ isCompleted = true
121
+ xhr.upload.removeEventListener('progress', handleProgress)
122
+ xhr.removeEventListener('load', handleLoad)
123
+ xhr.removeEventListener('error', handleError)
124
+ xhr.removeEventListener('abort', handleAbort)
125
+ xhr.removeEventListener('timeout', handleTimeout)
126
+ signal?.removeEventListener('abort', handleSignalAbort)
127
+ if (xhr.readyState !== XMLHttpRequest.DONE) xhr.abort()
128
+ }
129
+
130
+ const handleProgress = (event: ProgressEvent) => {
131
+ if (event.lengthComputable && onProgress) {
132
+ // Map 0-100 of network progress to 10-95.
133
+ const pct = 10 + (event.loaded / event.total) * 85
134
+ onProgress(Math.round(pct))
135
+ }
136
+ }
137
+
138
+ const handleLoad = () => {
139
+ cleanup()
140
+ if (xhr.status >= 200 && xhr.status < 300) {
141
+ resolve()
142
+ } else {
143
+ reject(
144
+ new Error(
145
+ `Upload failed with status ${xhr.status}: ${xhr.responseText || xhr.statusText}`,
146
+ ),
147
+ )
148
+ }
149
+ }
150
+
151
+ const handleError = () => {
152
+ cleanup()
153
+ reject(new Error('Network error during upload'))
154
+ }
155
+ const handleAbort = () => {
156
+ cleanup()
157
+ reject(new Error('Upload cancelled'))
158
+ }
159
+ const handleTimeout = () => {
160
+ cleanup()
161
+ reject(new Error('Upload timed out'))
162
+ }
163
+ const handleSignalAbort = () => {
164
+ xhr.abort()
165
+ }
166
+ signal?.addEventListener('abort', handleSignalAbort, { once: true })
167
+
168
+ xhr.upload.addEventListener('progress', handleProgress)
169
+ xhr.addEventListener('load', handleLoad)
170
+ xhr.addEventListener('error', handleError)
171
+ xhr.addEventListener('abort', handleAbort)
172
+ xhr.addEventListener('timeout', handleTimeout)
173
+
174
+ xhr.open('PUT', url)
175
+ xhr.setRequestHeader('Authorization', `Bearer ${token}`)
176
+ // `x-upsert: true` — signed upload tokens can target the same path
177
+ // more than once on retry; without this header, Storage returns 409
178
+ // on the second attempt instead of overwriting.
179
+ xhr.setRequestHeader('x-upsert', 'true')
180
+ xhr.setRequestHeader('Content-Type', file.type)
181
+ xhr.setRequestHeader('cache-control', STORAGE_CACHE_CONTROL_IMMUTABLE)
182
+ xhr.timeout = 30 * 60 * 1000
183
+ xhr.send(file)
184
+ })
185
+ }
186
+
187
+ export function useChatAttachments(): UseChatAttachmentsApi {
188
+ const runtime = useRequiredChatRuntime()
189
+ const uploadUrlEndpoint = runtime.endpoints.attachmentUploadUrl
190
+
191
+ const [attachments, setAttachments] = useState<StagedAttachment[]>([])
192
+ // AbortControllers indexed by attachment id. Survives re-renders.
193
+ const controllersRef = useRef<Map<string, AbortController>>(new Map())
194
+
195
+ const updateOne = useCallback((id: string, patch: Partial<StagedAttachment>) => {
196
+ setAttachments((prev) => prev.map((a) => (a.id === id ? { ...a, ...patch } : a)))
197
+ }, [])
198
+
199
+ const removeAttachment = useCallback((id: string) => {
200
+ // Abort any in-flight work for this attachment.
201
+ const ctrl = controllersRef.current.get(id)
202
+ if (ctrl) {
203
+ ctrl.abort()
204
+ controllersRef.current.delete(id)
205
+ }
206
+ setAttachments((prev) => prev.filter((a) => a.id !== id))
207
+ }, [])
208
+
209
+ const clear = useCallback(() => {
210
+ for (const ctrl of controllersRef.current.values()) ctrl.abort()
211
+ controllersRef.current.clear()
212
+ setAttachments([])
213
+ }, [])
214
+
215
+ const uploadOne = useCallback(
216
+ async (att: StagedAttachment) => {
217
+ const ctrl = controllersRef.current.get(att.id)
218
+ try {
219
+ // 1. Magic-byte sniff. The `file` ARG is a Blob; `fileTypeFromBlob`
220
+ // reads the first few KB. Rejects extension spoofs.
221
+ let sniffed: { mime: string } | undefined
222
+ try {
223
+ sniffed = await fileTypeFromBlob(att.file)
224
+ } catch (err) {
225
+ if (ctrl?.signal.aborted) return
226
+ throw new Error(
227
+ `Could not read file content: ${err instanceof Error ? err.message : String(err)}`,
228
+ )
229
+ }
230
+ if (ctrl?.signal.aborted) return
231
+ if (!sniffed) {
232
+ throw new Error('Unrecognized file format')
233
+ }
234
+ const actualMime = sniffed.mime
235
+ if (!(CHAT_ATTACHMENT_MIME_TYPES as readonly string[]).includes(actualMime)) {
236
+ throw new Error(
237
+ `File type "${actualMime}" is not allowed. ` +
238
+ `Allowed: ${CHAT_ATTACHMENT_MIME_TYPES.join(', ')}`,
239
+ )
240
+ }
241
+
242
+ // 2. Move to 'uploading' and POST the URL-mint request with
243
+ // bearer-act-as headers (if present).
244
+ updateOne(att.id, { status: 'uploading', progress: 5 })
245
+
246
+ const urlResp = await chatAuthedFetch(uploadUrlEndpoint, {
247
+ method: 'POST',
248
+ headers: { 'Content-Type': 'application/json' },
249
+ signal: ctrl?.signal,
250
+ body: JSON.stringify({
251
+ fileName: att.file.name,
252
+ // Pass the SNIFFED MIME (not `file.type`, which can be a
253
+ // user-supplied lie) so the server's MIME pre-validation
254
+ // matches reality.
255
+ fileType: actualMime,
256
+ bucket: CHAT_ATTACHMENTS_BUCKET,
257
+ folder: CHAT_ATTACHMENTS_FOLDER,
258
+ }),
259
+ })
260
+
261
+ if (!urlResp.ok) {
262
+ const errBody = await urlResp.json().catch(() => ({}))
263
+ throw new Error(errBody.error || `Failed to mint upload URL: ${urlResp.statusText}`)
264
+ }
265
+
266
+ const mintData = (await urlResp.json()) as UploadUrlResponse
267
+ if (!mintData.viewToken) {
268
+ throw new Error('Server did not return a viewToken for the chat-attachments bucket')
269
+ }
270
+
271
+ // 3. PUT the bytes via inline `uploadWithProgress`.
272
+ await uploadWithProgress(
273
+ mintData.uploadUrl,
274
+ att.file,
275
+ mintData.token,
276
+ (pct) => {
277
+ updateOne(att.id, { progress: pct })
278
+ },
279
+ ctrl?.signal,
280
+ )
281
+
282
+ if (ctrl?.signal.aborted) return
283
+
284
+ // 4. Mark ready.
285
+ updateOne(att.id, {
286
+ status: 'ready',
287
+ progress: 100,
288
+ storagePath: mintData.filePath,
289
+ viewToken: mintData.viewToken,
290
+ })
291
+ } catch (err) {
292
+ if (ctrl?.signal.aborted) return
293
+ const message = err instanceof Error ? err.message : String(err)
294
+ updateOne(att.id, { status: 'error', progress: 0, errorMessage: message })
295
+ } finally {
296
+ controllersRef.current.delete(att.id)
297
+ }
298
+ },
299
+ [uploadUrlEndpoint, updateOne],
300
+ )
301
+
302
+ // `attachmentsRef` mirrors the staged set for SYNCHRONOUS reads
303
+ // during `addFiles`. Two assignment sites keep it in lockstep:
304
+ // (a) `attachmentsRef.current = attachments` after every render —
305
+ // picks up state mutations from `updateOne` / `removeAttachment`
306
+ // / `clear`.
307
+ // (b) `attachmentsRef.current = [...attachmentsRef.current,
308
+ // ...additions]` INSIDE `addFiles` BEFORE `setAttachments`
309
+ // commits — guarantees a SECOND `addFiles` call in the same
310
+ // tick sees the first call's additions for dedup.
311
+ const attachmentsRef = useRef<StagedAttachment[]>([])
312
+ attachmentsRef.current = attachments
313
+
314
+ const addFiles = useCallback(
315
+ (files: FileList | File[]) => {
316
+ const arr = Array.from(files)
317
+ if (arr.length === 0) return
318
+
319
+ // Compute additions OUTSIDE the setState updater — the updater
320
+ // must be a pure function. React 18 StrictMode invokes updaters
321
+ // TWICE in dev; concurrent transitions can do the same in prod.
322
+ // If side effects (UUID gen, controllerRef.set, uploadOne) lived
323
+ // inside, each render double-fired the upload.
324
+ const prev = attachmentsRef.current
325
+ const seen = new Set(
326
+ prev.map((a) => `${a.file.name}|${a.file.size}|${a.file.lastModified}`),
327
+ )
328
+ const additions: StagedAttachment[] = []
329
+ // `error`-status chips don't count toward the cap — they're
330
+ // visual diagnostics, not active uploads. Without this filter
331
+ // a single oversized-file mistake would block the user from
332
+ // adding further files until they manually `×` the error chip.
333
+ const remainingSlots = Math.max(
334
+ 0,
335
+ CHAT_ATTACHMENT_CONCURRENT_UPLOADS_PER_USER -
336
+ prev.filter((a) => a.status !== 'error').length,
337
+ )
338
+ for (const file of arr) {
339
+ if (additions.length >= remainingSlots) break
340
+ const key = `${file.name}|${file.size}|${file.lastModified}`
341
+ if (seen.has(key)) continue
342
+
343
+ // Size pre-check — server-side bucket policy is the
344
+ // authoritative gate, but rejecting here saves the user a
345
+ // round-trip on obviously-too-big files.
346
+ if (file.size > CHAT_ATTACHMENT_MAX_SIZE_BYTES) {
347
+ additions.push({
348
+ id: crypto.randomUUID(),
349
+ file,
350
+ status: 'error',
351
+ progress: 0,
352
+ errorMessage: `File too large (${(file.size / 1024 / 1024).toFixed(1)} MB; max 25 MB)`,
353
+ })
354
+ seen.add(key)
355
+ continue
356
+ }
357
+
358
+ additions.push({
359
+ id: crypto.randomUUID(),
360
+ file,
361
+ status: 'sniffing',
362
+ progress: 0,
363
+ })
364
+ seen.add(key)
365
+ }
366
+
367
+ if (additions.length === 0) return
368
+
369
+ // SYNCHRONOUSLY update the ref BEFORE the React commit. Without
370
+ // this, a second `addFiles` call in the SAME tick (paste-then-drop
371
+ // browser event sequence; touch-then-mouse on iOS hybrid; double
372
+ // user clicks on the chip-strip's hidden file input) would read
373
+ // the same stale `attachmentsRef.current` and bypass dedup.
374
+ attachmentsRef.current = [...prev, ...additions]
375
+
376
+ // Register AbortControllers + kick off uploads BEFORE the state
377
+ // commit. The XHR doesn't depend on React state; `uploadOne`'s
378
+ // `updateOne` calls are queued behind this `setAttachments`,
379
+ // so by the time the first progress callback fires React has
380
+ // already rendered the new chips.
381
+ for (const a of additions) {
382
+ if (a.status === 'sniffing') {
383
+ const ctrl = new AbortController()
384
+ controllersRef.current.set(a.id, ctrl)
385
+ void uploadOne(a)
386
+ }
387
+ }
388
+
389
+ // Pure updater — no side effects, idempotent under StrictMode
390
+ // double-invocation.
391
+ setAttachments((p) => [...p, ...additions])
392
+ },
393
+ [uploadOne],
394
+ )
395
+
396
+ // Memoized so `handleSend`'s `useCallback([readyAttachments, ...])`
397
+ // doesn't re-create the handler (and re-render `ChatInput`) on every
398
+ // unrelated re-render. Stable reference while the underlying
399
+ // attachments array is unchanged.
400
+ const readyAttachments: ChatAttachment[] = useMemo(
401
+ () =>
402
+ attachments
403
+ .filter(
404
+ (a): a is StagedAttachment & { storagePath: string; viewToken: string } =>
405
+ a.status === 'ready' && !!a.storagePath && !!a.viewToken,
406
+ )
407
+ .map((a) => ({
408
+ storagePath: a.storagePath,
409
+ viewToken: a.viewToken,
410
+ contentType: a.file.type,
411
+ fileName: a.file.name,
412
+ size: a.file.size,
413
+ })),
414
+ [attachments],
415
+ )
416
+
417
+ const hasInflightUploads = attachments.some(
418
+ (a) => a.status === 'sniffing' || a.status === 'uploading',
419
+ )
420
+
421
+ return {
422
+ attachments,
423
+ readyAttachments,
424
+ hasInflightUploads,
425
+ addFiles,
426
+ removeAttachment,
427
+ clear,
428
+ }
429
+ }
@@ -0,0 +1,102 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * useChatCardItem — fetches the full mapped item for an inline chat
5
+ * card via the SAME list-API endpoint the public pages use.
6
+ *
7
+ * Single source of truth: no parallel `buildImageUrl` / `buildMetadata`
8
+ * synthesis. The chat renders the same shape, with the same joins
9
+ * (author / categories / platforms / hosts / etc.), as `/blog`,
10
+ * `/case-studies`, `/podcasts`, `/roadmap`, etc.
11
+ *
12
+ * Batching: every compact card with the same `(type, id)` shares one
13
+ * TanStack-Query entry; multiple refs of the same type in the same
14
+ * chat message produce ONE network request (TanStack dedups by
15
+ * queryKey). 5-minute staleTime matches `RelatedContentSection`.
16
+ *
17
+ * URL builder is read from `runtime.endpoints.buildListUrl` so the
18
+ * embedded app can supply a per-type URL builder against the reverse
19
+ * proxy; the hub wires the registry-driven builder directly.
20
+ */
21
+
22
+ import { useQuery } from '@tanstack/react-query'
23
+ import { useRequiredChatRuntime } from '../../../contexts/chat-runtime-context'
24
+
25
+ export interface UseChatCardItemResult<T = unknown> {
26
+ item: T | undefined
27
+ isLoading: boolean
28
+ isError: boolean
29
+ }
30
+
31
+ /** Extract the items array from each endpoint's response shape (some
32
+ * return `{ items }`, others `{ posts }`, etc.). Single normalization
33
+ * point — callers always read `Item[]`. */
34
+ function extractItems(data: unknown): unknown[] {
35
+ if (!data || typeof data !== 'object') return []
36
+ const obj = data as Record<string, unknown>
37
+ if (Array.isArray(obj.items)) return obj.items
38
+ if (Array.isArray(obj.posts)) return obj.posts
39
+ if (Array.isArray(obj.campaigns)) return obj.campaigns
40
+ if (Array.isArray(obj.completed) || Array.isArray(obj.inProgress)) {
41
+ // Delivery endpoint splits into completed/inProgress arrays — flatten.
42
+ const completed = Array.isArray(obj.completed) ? obj.completed : []
43
+ const inProgress = Array.isArray(obj.inProgress) ? obj.inProgress : []
44
+ return [...completed, ...inProgress]
45
+ }
46
+ if (Array.isArray(obj.data)) return obj.data
47
+ if (Array.isArray(data)) return data
48
+ return []
49
+ }
50
+
51
+ /** Extract a stable id from a fetched item — different shapes use
52
+ * `id` vs `external_id`. Used by the chat loader to match the fetched
53
+ * array element back to the marker's id. */
54
+ function extractItemId(type: string, item: unknown): string | null {
55
+ if (!item || typeof item !== 'object') return null
56
+ const obj = item as Record<string, unknown>
57
+ if (type === 'roadmap_item' || type === 'delivery_item' || type === 'internal_task') {
58
+ const ext = obj.external_id
59
+ if (typeof ext === 'string') return ext
60
+ }
61
+ // RoadmapItem-shaped responses use `id` to carry the external_id; the
62
+ // mapper renames external_id→id at the API boundary. Treat both id and
63
+ // external_id as primary-key candidates for these clickup-backed types.
64
+ const id = obj.id
65
+ if (typeof id === 'string') return id
66
+ if (typeof id === 'number') return String(id)
67
+ return null
68
+ }
69
+
70
+ export function useChatCardItem<T = unknown>(
71
+ type: string,
72
+ id: string,
73
+ ): UseChatCardItemResult<T> {
74
+ // Read the list-URL builder from the chat runtime — hub uses the
75
+ // rag-table-config registry directly; embedded apps supply a per-type
76
+ // URL builder against the reverse proxy.
77
+ const runtime = useRequiredChatRuntime()
78
+ const url = runtime.endpoints.buildListUrl(type, [id])
79
+ const query = useQuery({
80
+ queryKey: ['chat-card-item', type, id],
81
+ queryFn: async (): Promise<T | null> => {
82
+ if (!url) return null
83
+ const res = await fetch(url)
84
+ if (!res.ok) return null
85
+ const data = await res.json()
86
+ const items = extractItems(data)
87
+ const match = items.find((it) => extractItemId(type, it) === id)
88
+ return (match ?? null) as T | null
89
+ },
90
+ enabled: !!url && id.length > 0,
91
+ // Don't refetch on window focus inside the chat — the chat panel
92
+ // is opened and closed frequently, and re-fetching every time the
93
+ // user toggles back to the tab is wasteful (the row's id-keyed
94
+ // payload is effectively immutable).
95
+ refetchOnWindowFocus: false,
96
+ })
97
+ return {
98
+ item: (query.data ?? undefined) as T | undefined,
99
+ isLoading: query.isLoading,
100
+ isError: query.isError,
101
+ }
102
+ }