@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
@@ -67,6 +67,20 @@ export function formatBytes(bytes: number, decimals = 2): string {
67
67
  return Number.parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i]
68
68
  }
69
69
 
70
+ /**
71
+ * Compact bytes formatter using the single-letter unit `'B'` (not
72
+ * `'Bytes'`). Used by upload-progress UIs where horizontal space is
73
+ * tight and the longer "Bytes" string wraps. Tops out at `TB`.
74
+ * @example formatBytesShort(0) → "0 B"; formatBytesShort(1536) → "1.5 KB"
75
+ */
76
+ export function formatBytesShort(bytes: number): string {
77
+ if (bytes === 0) return '0 B'
78
+ const k = 1024
79
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
80
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
81
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`
82
+ }
83
+
70
84
  /**
71
85
  * Format large numbers to abbreviated form (K, M, B) with no decimal points
72
86
  * @param num - The number to format
@@ -101,7 +115,6 @@ export function formatLargeNumber(num: number): string {
101
115
  /**
102
116
  * Abbreviate large numbers for compact display.
103
117
  * 1 200 → 1.2K , 15 000 → 15K , 2 000 000 → 2M
104
- * Mirrors helper previously embedded in profile components.
105
118
  * @param n Number to format
106
119
  */
107
120
  export function formatAbbreviatedNumber(n: number): string {
@@ -119,3 +132,514 @@ export function formatAbbreviatedNumber(n: number): string {
119
132
  }
120
133
  return n.toLocaleString();
121
134
  }
135
+
136
+ /**
137
+ * Two-letter uppercase initials from the FIRST + LAST word of a name.
138
+ * Used by avatar-style fallbacks (SquareAvatar, EntityImage) where
139
+ * "John Michael Doe" → "JD" reads better than "JM".
140
+ *
141
+ * Returns `''` for empty / whitespace-only input. Single-word names
142
+ * return a single uppercase letter. Pure — same input always produces
143
+ * the same output, no locale or timezone surface.
144
+ */
145
+ export function getFirstLastInitials(name?: string | null): string {
146
+ if (!name) return ''
147
+ const words = name.trim().split(/\s+/)
148
+ if (words.length === 0 || !words[0]) return ''
149
+ if (words.length === 1) return words[0].charAt(0).toUpperCase()
150
+ return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase()
151
+ }
152
+
153
+ /**
154
+ * Two-letter uppercase initials from the FIRST + SECOND word of a name.
155
+ * Used as SquareAvatar / EntityImage / EntityAuthorCard fallback across
156
+ * admin and public pages in both the lib and the hub. Handles empty
157
+ * strings, all-whitespace input, and single-word names cleanly — always
158
+ * returns at least one character so the fallback slot is never empty.
159
+ *
160
+ * Single source of truth: every "first-letter of each word, uppercase,
161
+ * max 2 chars" computation across hub + lib MUST come through here.
162
+ */
163
+ export function nameInitials(
164
+ name: string | null | undefined,
165
+ fallback: string = 'E',
166
+ ): string {
167
+ const source = typeof name === 'string' ? name.trim() : ''
168
+ const words = source.length > 0 ? source.split(/\s+/) : []
169
+ const letters = words
170
+ .map((w) => w[0])
171
+ .filter(Boolean)
172
+ .slice(0, 2)
173
+ .join('')
174
+ return (letters || fallback).toUpperCase()
175
+ }
176
+
177
+ /**
178
+ * Format seconds to MM:SS or HH:MM:SS format
179
+ * Used for media durations (podcasts, videos)
180
+ * Returns: "MM:SS" or "HH:MM:SS" if hours > 0
181
+ */
182
+ export function formatDurationMMSS(seconds: number | null | undefined): string {
183
+ if (!seconds) return '';
184
+ const hours = Math.floor(seconds / 3600);
185
+ const minutes = Math.floor((seconds % 3600) / 60);
186
+ const secs = Math.floor(seconds % 60);
187
+
188
+ if (hours > 0) {
189
+ return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
190
+ }
191
+ return `${minutes}:${secs.toString().padStart(2, '0')}`;
192
+ }
193
+
194
+ /**
195
+ * Format seconds to compact human-readable duration
196
+ * Used for displaying duration in cards and headers
197
+ * Returns: "Xh Xm" or "X min"
198
+ */
199
+ export function formatDurationCompact(seconds: number | null | undefined): string {
200
+ if (!seconds) return '';
201
+ const hours = Math.floor(seconds / 3600);
202
+ const minutes = Math.floor((seconds % 3600) / 60);
203
+ if (hours > 0) {
204
+ return `${hours}h ${minutes}m`;
205
+ }
206
+ return `${minutes} min`;
207
+ }
208
+
209
+ /**
210
+ * Format time with optional timezone
211
+ * Used for webinar and event times
212
+ * Returns: "10:30 AM EST" or "10:30 AM"
213
+ */
214
+ export function formatTimeWithTimezone(
215
+ date: Date | string | null | undefined,
216
+ timezone?: string | null
217
+ ): string {
218
+ if (!date) return '';
219
+
220
+ const dateObj = typeof date === 'string' ? new Date(date) : date;
221
+ const timeStr = dateObj.toLocaleTimeString('en-US', {
222
+ hour: 'numeric',
223
+ minute: '2-digit',
224
+ hour12: true,
225
+ });
226
+
227
+ return timezone ? `${timeStr} ${timezone}` : timeStr;
228
+ }
229
+
230
+ /**
231
+ * Calculate and format duration between two timestamps
232
+ * Used for webinar durations
233
+ * Returns: "1h 30m" or "45m"
234
+ */
235
+ export function formatDurationFromRange(
236
+ startAt: string | Date | null | undefined,
237
+ endAt: string | Date | null | undefined
238
+ ): string {
239
+ if (!startAt || !endAt) return '';
240
+
241
+ const start = typeof startAt === 'string' ? new Date(startAt) : startAt;
242
+ const end = typeof endAt === 'string' ? new Date(endAt) : endAt;
243
+ const durationMs = end.getTime() - start.getTime();
244
+ const minutes = Math.round(durationMs / 60000);
245
+
246
+ if (minutes >= 60) {
247
+ const hours = Math.floor(minutes / 60);
248
+ const mins = minutes % 60;
249
+ return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
250
+ }
251
+ return `${minutes}m`;
252
+ }
253
+
254
+ /**
255
+ * UTC-anchored date formatter — distinct from the local-rendering
256
+ * `formatDate` above. RAG mappers + audit-style consumers want UTC anchor
257
+ * so date-only strings (`'2024-08-15'`) don't drift across timezones, AND
258
+ * a `'N/A'` (or caller-supplied) fallback for missing values so the LLM
259
+ * never sees a blank.
260
+ *
261
+ * Accepts:
262
+ * - ISO date or timestamp string (`'2024-08-15'` or `'2024-08-15T10:30:00Z'`)
263
+ * - Numeric epoch milliseconds
264
+ * - Numeric string (treated as epoch ms — Slack and GitHub timestamps
265
+ * sometimes arrive that way)
266
+ * - `null` / `undefined` / empty / unparseable → returns `options.fallback`
267
+ *
268
+ * Date-only strings (no `'T'`) get `T00:00:00Z` appended so they anchor to
269
+ * UTC midnight rather than midnight in the runtime's local timezone.
270
+ */
271
+ export interface FormatDateUTCOptions {
272
+ /** Returned for null/undefined/empty/unparseable input. Defaults to 'N/A'. */
273
+ fallback?: string
274
+ /** 'UTC' (default) or 'local' — switch off the UTC anchor when audit
275
+ * stability matters less than local relevance (chat-card timestamps). */
276
+ timezone?: 'UTC' | 'local'
277
+ }
278
+
279
+ export function formatDateUTC(
280
+ value: string | number | null | undefined,
281
+ options: FormatDateUTCOptions = {},
282
+ ): string {
283
+ const { fallback = 'N/A', timezone = 'UTC' } = options
284
+
285
+ if (value === null || value === undefined || value === '') return fallback
286
+
287
+ let ms: number
288
+ if (typeof value === 'number') {
289
+ ms = value
290
+ } else {
291
+ // String input — first try to interpret as a numeric epoch (Slack/GitHub
292
+ // sometimes arrive that way). Number('') is 0, but we already ruled out
293
+ // empty strings above.
294
+ const n = Number(value)
295
+ if (Number.isFinite(n) && n > 0 && /^-?\d+(\.\d+)?$/.test(value.trim())) {
296
+ ms = n
297
+ } else {
298
+ // ISO string — for date-only forms, anchor to UTC midnight to avoid
299
+ // timezone-offset drift (preserves the original RAG-mapper contract).
300
+ ms = Date.parse(value.includes('T') ? value : value + 'T00:00:00Z')
301
+ }
302
+ }
303
+
304
+ if (!Number.isFinite(ms)) return fallback
305
+
306
+ return new Date(ms).toLocaleDateString('en-US', {
307
+ year: 'numeric',
308
+ month: 'short',
309
+ day: 'numeric',
310
+ timeZone: timezone === 'local' ? undefined : 'UTC',
311
+ })
312
+ }
313
+
314
+ /**
315
+ * Format a date string as `MM/DD/YYYY` for legal-document display
316
+ * (privacy policy, terms of service). Locale-stable: always en-US.
317
+ */
318
+ export function formatLegalDate(dateInput: string): string {
319
+ return new Intl.DateTimeFormat('en-US', {
320
+ year: 'numeric',
321
+ month: '2-digit',
322
+ day: '2-digit',
323
+ }).format(new Date(dateInput))
324
+ }
325
+
326
+ /**
327
+ * Format a currency value as `$1,234`. Returns `'N/A'` for null/undefined.
328
+ * USD-rounded (no cents). Used on KPI cards + investor pages.
329
+ */
330
+ export function formatCurrency(value: number | null | undefined): string {
331
+ if (value == null) return 'N/A'
332
+ return new Intl.NumberFormat('en-US', {
333
+ style: 'currency',
334
+ currency: 'USD',
335
+ maximumFractionDigits: 0,
336
+ }).format(value)
337
+ }
338
+
339
+ /**
340
+ * Format a percent value as `12.50%`. Returns `'N/A'` for null/undefined.
341
+ */
342
+ export function formatPercent(value: number | null | undefined): string {
343
+ if (value == null) return 'N/A'
344
+ return `${value.toFixed(2)}%`
345
+ }
346
+
347
+ /**
348
+ * Whole-dollar price (no cents) — `$1,234`. Configurable currency code.
349
+ */
350
+ export function formatWholeDollars(price: number, currency = 'USD'): string {
351
+ return new Intl.NumberFormat('en-US', {
352
+ style: 'currency',
353
+ currency,
354
+ maximumFractionDigits: 0,
355
+ }).format(price)
356
+ }
357
+
358
+ // =============================================================================
359
+ // Metric Formatting (for KPI cards / investor updates)
360
+ // =============================================================================
361
+
362
+ export type MetricFormat = 'number' | 'currency' | 'percentage' | 'months'
363
+
364
+ /**
365
+ * Polarity determines whether an increase is good or bad.
366
+ * - 'positive': higher is better (revenue, users, MRR) → up = green, down = red
367
+ * - 'negative': higher is worse (burn rate, churn, CAC) → up = red, down = green
368
+ * - 'neutral': no judgment (headcount, runway) → always gray
369
+ */
370
+ export type TrendPolarity = 'positive' | 'negative' | 'neutral'
371
+
372
+ /**
373
+ * Format a metric value with compact notation ($1.2M, 150K, 12 months).
374
+ */
375
+ export function formatCompactMetric(
376
+ value: number,
377
+ format: MetricFormat = 'number',
378
+ options?: { prefix?: string; suffix?: string },
379
+ ): string {
380
+ if (value === 0 || value === null || value === undefined) {
381
+ if (format === 'currency') return `${options?.prefix || '$'}0`
382
+ if (format === 'percentage') return '0%'
383
+ if (format === 'months') return `0 ${options?.suffix || 'months'}`
384
+ return '0'
385
+ }
386
+
387
+ const absValue = Math.abs(value)
388
+ const sign = value < 0 ? '-' : ''
389
+
390
+ if (format === 'currency') {
391
+ const prefix = options?.prefix || '$'
392
+ const compact = (val: number, divisor: number, suffix: string) => {
393
+ const divided = val / divisor
394
+ const formatted = divided % 1 === 0 ? divided.toFixed(0) : divided.toFixed(1)
395
+ return `${sign}${prefix}${formatted}${suffix}`
396
+ }
397
+ if (absValue >= 1_000_000_000) return compact(absValue, 1_000_000_000, 'B')
398
+ if (absValue >= 1_000_000) return compact(absValue, 1_000_000, 'M')
399
+ if (absValue >= 1_000) return `${sign}${prefix}${(absValue / 1_000).toFixed(0)}K`
400
+ return `${sign}${prefix}${absValue.toLocaleString()}`
401
+ }
402
+
403
+ if (format === 'percentage') {
404
+ return `${sign}${absValue}%`
405
+ }
406
+
407
+ if (format === 'months') {
408
+ const rounded = Math.round(value * 10) / 10
409
+ return `${rounded} ${options?.suffix || 'months'}`
410
+ }
411
+
412
+ if (absValue >= 1_000_000) return `${sign}${(absValue / 1_000_000).toFixed(1)}M`
413
+ if (absValue >= 1_000) return `${sign}${(absValue / 1_000).toFixed(1)}K`
414
+ return value.toLocaleString()
415
+ }
416
+
417
+ /**
418
+ * Get ODS trend colors based on direction AND polarity. Single source
419
+ * of truth for trend coloring across hub + lib.
420
+ */
421
+ export function getTrendColors(
422
+ direction: 'up' | 'down' | 'neutral',
423
+ polarity: TrendPolarity = 'positive',
424
+ ): { textClass: string; badgeClass: string } {
425
+ if (direction === 'neutral' || polarity === 'neutral') {
426
+ return {
427
+ textClass: 'text-ods-text-secondary',
428
+ badgeClass: 'bg-ods-border text-ods-text-secondary',
429
+ }
430
+ }
431
+
432
+ const isPositiveOutcome =
433
+ (direction === 'up' && polarity === 'positive') ||
434
+ (direction === 'down' && polarity === 'negative')
435
+
436
+ if (isPositiveOutcome) {
437
+ return {
438
+ textClass: 'text-ods-success',
439
+ badgeClass: 'bg-ods-success-secondary text-ods-success',
440
+ }
441
+ }
442
+
443
+ return {
444
+ textClass: 'text-ods-error',
445
+ badgeClass: 'bg-ods-error-secondary text-ods-error',
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Format a date range as `Apr 20, 2026 — Jul 20, 2026`. Used on review
451
+ * cycle list/detail rows and summary headers.
452
+ *
453
+ * Accepts either full ISO timestamps or bare `YYYY-MM-DD` dates. Bare
454
+ * dates are interpreted in the viewer's LOCAL timezone — otherwise
455
+ * `"2026-04-20"` renders as `"Apr 19"` west of UTC. DB values for cycle
456
+ * period are stored as plain dates, so local-tz parsing is correct.
457
+ */
458
+ export function formatDateRange(
459
+ start: string | null | undefined,
460
+ end: string | null | undefined,
461
+ ): string {
462
+ if (!start || !end) return ''
463
+ const fmt = (s: string): string => {
464
+ const bareMatch = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s)
465
+ const d = bareMatch
466
+ ? new Date(Number(bareMatch[1]), Number(bareMatch[2]) - 1, Number(bareMatch[3]))
467
+ : new Date(s)
468
+ if (Number.isNaN(d.getTime())) return s
469
+ return d.toLocaleDateString(undefined, {
470
+ month: 'short',
471
+ day: 'numeric',
472
+ year: 'numeric',
473
+ })
474
+ }
475
+ return `${fmt(start)} — ${fmt(end)}`
476
+ }
477
+
478
+ /**
479
+ * Format an ISO date string as `"Jan 5, 2025 at 10:30 AM"`. Used by
480
+ * admin podcast/webinar cards where the display needs both the short
481
+ * date AND wall-clock time in a single readable phrase.
482
+ *
483
+ * Uses `new Date(...).toLocale*` (NOT the TZ-safe split) because the
484
+ * source columns store full timestamps + the wall-clock half MUST
485
+ * render in the viewer's local timezone (a podcast scheduled "10:30 AM
486
+ * EST" should display "10:30 AM" for the Eastern admin, "7:30 AM" for
487
+ * the Pacific admin — viewer-local is the right semantics here, unlike
488
+ * date-only fields).
489
+ */
490
+ export function formatDateTimeAt(dateString: string): string {
491
+ const date = new Date(dateString)
492
+ const dateStr = date.toLocaleDateString('en-US', {
493
+ month: 'short',
494
+ day: 'numeric',
495
+ year: 'numeric',
496
+ })
497
+ const timeStr = date.toLocaleTimeString('en-US', {
498
+ hour: 'numeric',
499
+ minute: '2-digit',
500
+ hour12: true,
501
+ })
502
+ return `${dateStr} at ${timeStr}`
503
+ }
504
+
505
+ /**
506
+ * Format a duration measured in MILLISECONDS as a compact human-readable
507
+ * string. Returns `"0ms"` for null/NaN/negative, then `Xms` → `X.Xs` →
508
+ * `X.Xm` as the input grows. Used by job-runs / orchestrator dashboards
509
+ * where elapsed milliseconds is the natural unit.
510
+ *
511
+ * Distinct from `formatDuration(seconds)` (verbose `"X hours Y minutes"`)
512
+ * and `formatDurationMMSS(seconds)` (media timecode `MM:SS`).
513
+ */
514
+ export function formatDurationFromMs(ms: number | null | undefined): string {
515
+ if (!ms || isNaN(ms) || ms < 0) return '0ms'
516
+ if (ms < 1000) return `${ms}ms`
517
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
518
+ return `${(ms / 60000).toFixed(1)}m`
519
+ }
520
+
521
+ /**
522
+ * Format seconds to verbose human-readable duration: `"X seconds"`,
523
+ * `"X minutes"`, `"X hours Y minutes"`. Use this for human-readable
524
+ * spans; for media timecodes use `formatDurationMMSS`; for compact
525
+ * media labels use `formatDurationCompact`; for elapsed milliseconds
526
+ * use `formatDurationFromMs`.
527
+ */
528
+ export function formatDuration(seconds: number): string {
529
+ if (seconds < 60) return `${seconds} seconds`
530
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes`
531
+
532
+ const hours = Math.floor(seconds / 3600)
533
+ const minutes = Math.floor((seconds % 3600) / 60)
534
+
535
+ if (minutes === 0) return `${hours} hour${hours > 1 ? 's' : ''}`
536
+ return `${hours} hour${hours > 1 ? 's' : ''} ${minutes} minute${minutes > 1 ? 's' : ''}`
537
+ }
538
+
539
+ // =============================================================================
540
+ // Text formatting helpers (proper-case, HTML strip, bio cleanup)
541
+ // =============================================================================
542
+
543
+ /**
544
+ * Format underscore-separated text into proper case.
545
+ * `"self_hosted"` → `"Self Hosted"`
546
+ * `"open_source"` → `"Open Source"`
547
+ */
548
+ export function formatUnderscoreText(text: string): string {
549
+ return text
550
+ .split('_')
551
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
552
+ .join(' ')
553
+ }
554
+
555
+ /**
556
+ * Strip HTML tags and decode common HTML entities from a string.
557
+ * Useful for cleaning API responses that contain HTML content.
558
+ *
559
+ * @example
560
+ * stripHtml('<p>Hello <strong>World</strong></p>') // "Hello World"
561
+ * stripHtml('Test &amp; Example') // "Test & Example"
562
+ */
563
+ export function stripHtml(html: string): string {
564
+ // Iterative tag-strip — a single pass is bypass-able by inputs like
565
+ // `<scr<script>ipt>`: stripping the inner `<script>` leaves the outer
566
+ // `<script>` reassembled. Loop until the string is stable so multi-
567
+ // character bypasses cannot survive (CodeQL
568
+ // `js/incomplete-multi-character-sanitization`). `<[^<>]*>` rejects
569
+ // `<` inside the tag body so each pass is itself ReDoS-safe (no
570
+ // backtracking on `<<<<<...<>` inputs).
571
+ let noTags = html
572
+ let prev: string
573
+ do {
574
+ prev = noTags
575
+ noTags = noTags.replace(/<[^<>]*>/g, '')
576
+ } while (noTags !== prev)
577
+
578
+ // Decode entities. `&amp;` MUST come LAST so we don't double-decode
579
+ // sequences like `&amp;lt;` (which should render as the LITERAL text
580
+ // `&lt;`, not as `<`). All other named/numeric entities decode first;
581
+ // only after those have been replaced do we collapse `&amp;` → `&`.
582
+ return noTags
583
+ .replace(/&nbsp;/g, ' ')
584
+ .replace(/&lt;/g, '<')
585
+ .replace(/&gt;/g, '>')
586
+ .replace(/&quot;/g, '"')
587
+ .replace(/&#39;/g, "'")
588
+ .replace(/&#x27;/g, "'")
589
+ .replace(/&apos;/g, "'")
590
+ .replace(/&amp;/g, '&')
591
+ .replace(/\s+/g, ' ')
592
+ .trim()
593
+ }
594
+
595
+ /**
596
+ * Display label for the openmsp `vendors.classification` enum.
597
+ * Falls back to proper-cased underscore split for values not in the
598
+ * curated mapping.
599
+ */
600
+ export function formatClassification(classification: string): string {
601
+ const customMappings: Record<string, string> = {
602
+ openframe_selected: 'OpenFrame Selected',
603
+ }
604
+ return customMappings[classification] || formatUnderscoreText(classification)
605
+ }
606
+
607
+ /**
608
+ * Display label for the openmsp `vendors.pricing_model` enum.
609
+ * Falls back to proper-cased underscore split for values not in the
610
+ * curated mapping.
611
+ */
612
+ export function formatPricingModel(pricingModel: string): string {
613
+ const customMappings: Record<string, string> = {
614
+ one_time: 'One-time Purchase',
615
+ self_hosted: 'Self-hosted',
616
+ }
617
+ return customMappings[pricingModel] || formatUnderscoreText(pricingModel)
618
+ }
619
+
620
+ /**
621
+ * Format a bio/about text from the profiles table for display.
622
+ * Handles HTML content (e.g. `<p>` tags from rich text editors),
623
+ * plain text passthrough, and null/undefined values.
624
+ */
625
+ export function formatBioText(
626
+ aboutHtml: string | null | undefined,
627
+ fallback: string = '',
628
+ ): string {
629
+ if (!aboutHtml || !aboutHtml.trim()) return fallback
630
+
631
+ if (aboutHtml.includes('<p')) {
632
+ // `<p[^<>]*>` rejects `<` inside the tag so the automaton can't
633
+ // backtrack on `<<<<<...<p>` inputs (ReDoS class fix vs. `<p[^>]*>`).
634
+ const paragraphs = aboutHtml
635
+ .split(/<p[^<>]*>/)
636
+ .slice(1)
637
+ .map((part) => part.split('</p>')[0])
638
+ .map((text) => stripHtml(text).trim())
639
+ .filter((text) => text.length > 0)
640
+
641
+ if (paragraphs.length > 0) return paragraphs.join(' ')
642
+ }
643
+
644
+ return stripHtml(aboutHtml).trim() || fallback
645
+ }
@@ -1,9 +1,129 @@
1
- export function getProxiedImageUrl(url: string): string {
2
- // Simple pass-through for now - can be enhanced later
3
- return url
1
+ /**
2
+ * Pure image-proxy URL builder + helpers.
3
+ *
4
+ * Lib-side replacement for the hub's `lib/utils/image-proxy.ts`. The
5
+ * hub used to hardcode `/api/image-proxy` + an `openmsp.ai` skip-domain;
6
+ * this version takes BOTH as parameters so embedded apps (and other
7
+ * platforms that host the lib) can wire their own proxy prefix + skip
8
+ * list at the runtime layer.
9
+ *
10
+ * Pure function — no side effects, no env reads. Callers thread the
11
+ * proxy config through (typically from `ChatRuntime.endpoints.imageProxyUrlPrefix`).
12
+ */
13
+
14
+ export type GetProxiedImageUrlOptions = {
15
+ /**
16
+ * URL prefix for the image proxy (`<prefix>?url=<encoded>`). When unset,
17
+ * `getProxiedImageUrl` returns the original URL unchanged — relative
18
+ * URLs always pass through. Hub default: `/api/image-proxy`.
19
+ */
20
+ proxyPrefix?: string;
21
+ /**
22
+ * Domains that should bypass the proxy (e.g. own-CDN hosts whose
23
+ * `Content-Type` is reliable and that already serve CORS-permitting
24
+ * headers). Matched as `imageUrl.includes(domain)` so subdomains
25
+ * inherit. Default: `[]`.
26
+ */
27
+ skipDomains?: string[];
28
+ /**
29
+ * Return the original `https://` URL so the browser loads it directly
30
+ * (no proxy). Use when upstream `Content-Type` breaks the proxy (common
31
+ * with SVG on some CDNs) or you want the origin to see the client
32
+ * request. HTTP stays proxied (mixed content / legacy).
33
+ */
34
+ directHttps?: boolean;
35
+ };
36
+
37
+ /**
38
+ * Resolve an external image URL through (or around) the image proxy.
39
+ *
40
+ * Resolution order:
41
+ * 1. `imageUrl` already contains `proxyPrefix` → return unchanged
42
+ * (self-skip — prevents double-wrap).
43
+ * 2. `directHttps` set AND `imageUrl` starts with `https://` → return
44
+ * unchanged.
45
+ * 3. `imageUrl` doesn't start with `http://` or `https://` → return
46
+ * unchanged (relative URLs pass through).
47
+ * 4. `proxyPrefix` unset → return unchanged.
48
+ * 5. `skipDomains` matches the URL's host → return unchanged.
49
+ * 6. Else return `<proxyPrefix>?url=<encoded>`.
50
+ */
51
+ export function getProxiedImageUrl(
52
+ imageUrl: string | null,
53
+ options?: GetProxiedImageUrlOptions,
54
+ ): string | null {
55
+ if (!imageUrl) return null;
56
+
57
+ const proxyPrefix = options?.proxyPrefix;
58
+ const skipDomains = options?.skipDomains ?? [];
59
+ const directHttps = options?.directHttps ?? false;
60
+
61
+ // (1) Self-skip — already proxied. Check this BEFORE the http/https
62
+ // gate so an absolute proxy URL passed in (e.g. `https://hub.example/api/image-proxy?url=…`)
63
+ // is treated as already-proxied even though it starts with https://.
64
+ if (proxyPrefix && imageUrl.includes(proxyPrefix)) {
65
+ return imageUrl;
66
+ }
67
+
68
+ // (3) Relative URLs / data: / blob: — return as-is.
69
+ if (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://')) {
70
+ return imageUrl;
71
+ }
72
+
73
+ // (2) Direct-https opt-out — only applies to https (http stays proxied).
74
+ if (directHttps && imageUrl.startsWith('https://')) {
75
+ return imageUrl;
76
+ }
77
+
78
+ // (4) No proxy configured — return as-is.
79
+ if (!proxyPrefix) {
80
+ return imageUrl;
81
+ }
82
+
83
+ // (5) Skip-list match.
84
+ for (const domain of skipDomains) {
85
+ if (imageUrl.includes(domain)) {
86
+ return imageUrl;
87
+ }
88
+ }
89
+
90
+ // (6) Proxy.
91
+ return `${proxyPrefix}?url=${encodeURIComponent(imageUrl)}`;
92
+ }
93
+
94
+ /**
95
+ * Heuristic: URL path looks like an SVG. Useful with `{ directHttps: true }`
96
+ * when only SVGs misbehave through the proxy; raster images can stay
97
+ * proxied if you prefer.
98
+ */
99
+ export function urlPathLooksLikeSvg(imageUrl: string): boolean {
100
+ try {
101
+ return /\.svg$/i.test(new URL(imageUrl).pathname);
102
+ } catch {
103
+ return /\.svg(\?|#|$)/i.test(imageUrl);
104
+ }
4
105
  }
5
106
 
6
- export function generateImageSizes(url: string): string {
7
- // Return responsive image sizes
8
- return `(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw`
9
- }
107
+ /**
108
+ * Check if an image URL needs to be proxied. `proxyPrefix` is the same
109
+ * value passed to `getProxiedImageUrl` used to short-circuit the
110
+ * self-skip case.
111
+ */
112
+ export function shouldProxyImage(
113
+ imageUrl: string | null,
114
+ proxyPrefix?: string,
115
+ ): boolean {
116
+ if (!imageUrl) return false;
117
+ if (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://')) return false;
118
+ if (proxyPrefix && imageUrl.includes(proxyPrefix)) return false;
119
+ return true;
120
+ }
121
+
122
+ /**
123
+ * Generate a responsive `sizes` attribute for `<img>` / `<Image>` tags.
124
+ * Pure utility — kept here for backwards-compatibility with the previous
125
+ * stub.
126
+ */
127
+ export function generateImageSizes(_url: string): string {
128
+ return `(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw`;
129
+ }