@flamingo-stack/openframe-frontend-core 0.0.702 → 0.0.703-snapshot.20261006080901

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 (341) hide show
  1. package/dist/{chunk-CYYTD5ZK.cjs → chunk-262YQFOS.cjs} +47 -47
  2. package/dist/{chunk-SAA7B3DA.js → chunk-2G546OND.js} +500 -67
  3. package/dist/{chunk-6JPQDYYY.js → chunk-2JDVL7UV.js} +2614 -2001
  4. package/dist/{chunk-ASN65P3F.cjs → chunk-4DQMTBMK.cjs} +15 -15
  5. package/dist/{chunk-CZ34HXDW.js → chunk-4KGXLVCF.js} +686 -725
  6. package/dist/{chunk-TCZCMD5Q.js → chunk-5HBHO5TB.js} +1 -1
  7. package/dist/{chunk-446KS5BG.js → chunk-5QJDM6UV.js} +5 -5
  8. package/dist/{chunk-UJJWTJ6S.js → chunk-6WQIUQUC.js} +30 -16
  9. package/dist/{chunk-YR6JPJF4.js → chunk-6XIF2Z7I.js} +4 -4
  10. package/dist/{chunk-YGRHITPY.cjs → chunk-7JIL3WOH.cjs} +24 -24
  11. package/dist/{chunk-RS7HWAYX.js → chunk-7LFIKJX2.js} +3 -3
  12. package/dist/{chunk-4OGAXAEG.cjs → chunk-ABU3TGXA.cjs} +3 -3
  13. package/dist/{chunk-3WSFIV5G.cjs → chunk-AMYY3PNL.cjs} +69 -55
  14. package/dist/{chunk-BRXOGCH3.cjs → chunk-BIZNSF3U.cjs} +3 -3
  15. package/dist/{chunk-2A4XES7R.cjs → chunk-BM5MVGH3.cjs} +3 -3
  16. package/dist/{chunk-IIMICN7L.cjs → chunk-C3BWIU7R.cjs} +4 -44
  17. package/dist/{chunk-LZNJH23A.cjs → chunk-C7XGRLT5.cjs} +12 -12
  18. package/dist/{chunk-6YCXA7CN.js → chunk-CH5232HV.js} +20 -19
  19. package/dist/{chunk-UIBSSVON.cjs → chunk-D7BTSVMS.cjs} +2 -2
  20. package/dist/{chunk-BO5ZHDA4.js → chunk-ERCV6HHJ.js} +7 -7
  21. package/dist/{chunk-5V6O73EH.js → chunk-FLKJYMVB.js} +3 -3
  22. package/dist/{chunk-Z5HDCZAS.js → chunk-FXENTYE5.js} +15 -15
  23. package/dist/{chunk-HIXGAY2L.cjs → chunk-HFJW24JJ.cjs} +121 -121
  24. package/dist/{chunk-HPJ4XS2L.js → chunk-HJAYVKWQ.js} +24 -24
  25. package/dist/{chunk-PLK6GHN7.js → chunk-I7DFUQMN.js} +10 -10
  26. package/dist/{chunk-KJ5LWATV.js → chunk-IHU5ANUY.js} +7 -7
  27. package/dist/{chunk-AEQANLBW.cjs → chunk-IZGDG5X6.cjs} +505 -72
  28. package/dist/{chunk-2V4Z44G6.js → chunk-JEYRXT5L.js} +17 -17
  29. package/dist/{chunk-B3LZJ6J3.cjs → chunk-L3IC42IF.cjs} +32 -32
  30. package/dist/{chunk-RTGPIHVO.cjs → chunk-LDPEQGKT.cjs} +2243 -1630
  31. package/dist/{chunk-FD4DBTFO.cjs → chunk-LTSTC3N5.cjs} +1253 -1292
  32. package/dist/{chunk-OGLCD3UL.js → chunk-MFFCUVWG.js} +6 -6
  33. package/dist/{chunk-3L7KCOGO.cjs → chunk-MGGGBHYD.cjs} +30 -30
  34. package/dist/{chunk-QFW3RUZS.cjs → chunk-MI3JUJ75.cjs} +33 -33
  35. package/dist/{chunk-EMCQGFXD.cjs → chunk-MMOODW57.cjs} +28 -27
  36. package/dist/{chunk-Q76ZTHNV.js → chunk-MP2C3LJ4.js} +1 -1
  37. package/dist/{chunk-E3DH543A.cjs → chunk-N3TO7JV3.cjs} +98 -98
  38. package/dist/{chunk-2T6QWNXU.js → chunk-NDESEPW5.js} +99 -11
  39. package/dist/{chunk-ETFFEA3J.js → chunk-NRLJUBCS.js} +7 -7
  40. package/dist/{chunk-HUOLB7ZN.cjs → chunk-OHFS7VOU.cjs} +9 -9
  41. package/dist/{chunk-MWUBBBWZ.js → chunk-QVSFBSWV.js} +10 -10
  42. package/dist/{chunk-MDBHZBW7.cjs → chunk-RKV5B7EU.cjs} +92 -4
  43. package/dist/{chunk-3G76LRGL.cjs → chunk-SYXQLETR.cjs} +29 -29
  44. package/dist/{chunk-FHM7GD5B.cjs → chunk-UK62YTH6.cjs} +28 -14
  45. package/dist/{chunk-HUMWQMCU.js → chunk-UT4YHQZW.js} +3 -3
  46. package/dist/{chunk-NGJCWEYX.cjs → chunk-VHCACDKP.cjs} +46 -46
  47. package/dist/{chunk-5FFNNJM7.js → chunk-VR2ZLFCP.js} +1 -1
  48. package/dist/{chunk-IZKOQVXO.js → chunk-VTASHWER.js} +14 -14
  49. package/dist/{chunk-64O4L7WN.js → chunk-WUJNYGO2.js} +11 -51
  50. package/dist/{chunk-JOY6HVPV.js → chunk-XATTBO5X.js} +25 -11
  51. package/dist/{chunk-L7KZA7ZD.cjs → chunk-XIFOBQRQ.cjs} +6 -6
  52. package/dist/{chunk-CXDJUKC6.cjs → chunk-XJKZ4IKT.cjs} +4 -4
  53. package/dist/{chunk-XCIFHW4E.cjs → chunk-XOYH2YQE.cjs} +2 -2
  54. package/dist/{chunk-HYFLTFFB.js → chunk-ZQFZSFQI.js} +2 -2
  55. package/dist/components/case-studies/index.cjs +24 -24
  56. package/dist/components/case-studies/index.js +10 -10
  57. package/dist/components/chat/chat-archive-page.d.ts +3 -3
  58. package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
  59. package/dist/components/chat/chat-nav-sidebar.d.ts +4 -0
  60. package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
  61. package/dist/components/chat/chat-panel-header.d.ts +3 -2
  62. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  63. package/dist/components/chat/embeddable-chat.d.ts +36 -2
  64. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  65. package/dist/components/chat/index.cjs +14 -6
  66. package/dist/components/chat/index.d.ts +4 -1
  67. package/dist/components/chat/index.d.ts.map +1 -1
  68. package/dist/components/chat/index.js +13 -5
  69. package/dist/components/chat/mingo-archive-list.d.ts +17 -0
  70. package/dist/components/chat/mingo-archive-list.d.ts.map +1 -0
  71. package/dist/components/chat/mingo-chat-header.d.ts +31 -0
  72. package/dist/components/chat/mingo-chat-header.d.ts.map +1 -0
  73. package/dist/components/chat/mingo-chat-rail.d.ts +54 -0
  74. package/dist/components/chat/mingo-chat-rail.d.ts.map +1 -0
  75. package/dist/components/chat/model-display.d.ts.map +1 -1
  76. package/dist/components/contact/contact-form.d.ts.map +1 -1
  77. package/dist/components/contact/index.cjs +11 -11
  78. package/dist/components/contact/index.js +10 -10
  79. package/dist/components/content-loading-container.d.ts +1 -1
  80. package/dist/components/content-loading-container.d.ts.map +1 -1
  81. package/dist/components/docs/index.cjs +14 -14
  82. package/dist/components/docs/index.js +13 -13
  83. package/dist/components/docs/skeletons.d.ts.map +1 -1
  84. package/dist/components/embeds/index.cjs +11 -11
  85. package/dist/components/embeds/index.js +10 -10
  86. package/dist/components/faq/index.cjs +12 -12
  87. package/dist/components/faq/index.js +11 -11
  88. package/dist/components/features/figma-prototype-viewer.d.ts.map +1 -1
  89. package/dist/components/features/index.cjs +10 -10
  90. package/dist/components/features/index.js +9 -9
  91. package/dist/components/features/media-gallery-manager.d.ts.map +1 -1
  92. package/dist/components/features/parallax-image-showcase.d.ts.map +1 -1
  93. package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
  94. package/dist/components/features/section-selector.d.ts.map +1 -1
  95. package/dist/components/help-center-pages/index.cjs +119 -119
  96. package/dist/components/help-center-pages/index.js +39 -39
  97. package/dist/components/help-center-pages/trust-center-sections.d.ts +2 -2
  98. package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
  99. package/dist/components/index.cjs +94 -86
  100. package/dist/components/index.js +32 -24
  101. package/dist/components/layout/article-detail-layout.d.ts +1 -1
  102. package/dist/components/layout/article-detail-layout.d.ts.map +1 -1
  103. package/dist/components/layout/page-container.d.ts.map +1 -1
  104. package/dist/components/layout/title-block.d.ts +2 -2
  105. package/dist/components/layout/title-block.d.ts.map +1 -1
  106. package/dist/components/loading/page-layout-skeleton.d.ts.map +1 -1
  107. package/dist/components/loading/unified-skeleton.d.ts.map +1 -1
  108. package/dist/components/meeting-scheduler/booking-form.d.ts +4 -4
  109. package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
  110. package/dist/components/meeting-scheduler/context-panel.d.ts.map +1 -1
  111. package/dist/components/meeting-scheduler/index.cjs +127 -127
  112. package/dist/components/meeting-scheduler/index.d.ts +2 -2
  113. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  114. package/dist/components/meeting-scheduler/index.js +50 -50
  115. package/dist/components/navigation/app-header.d.ts +4 -0
  116. package/dist/components/navigation/app-header.d.ts.map +1 -1
  117. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
  118. package/dist/components/navigation/app-layout-side-panel.d.ts +142 -0
  119. package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -0
  120. package/dist/components/navigation/app-layout.d.ts +11 -1
  121. package/dist/components/navigation/app-layout.d.ts.map +1 -1
  122. package/dist/components/navigation/header-mingo-button.d.ts +4 -1
  123. package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
  124. package/dist/components/navigation/index.cjs +13 -13
  125. package/dist/components/navigation/index.d.ts +1 -0
  126. package/dist/components/navigation/index.d.ts.map +1 -1
  127. package/dist/components/navigation/index.js +12 -12
  128. package/dist/components/navigation/navigation-sidebar.d.ts +8 -1
  129. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  130. package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
  131. package/dist/components/onboarding-guides/index.cjs +14 -14
  132. package/dist/components/onboarding-guides/index.js +13 -13
  133. package/dist/components/persistent-filter-controls.d.ts.map +1 -1
  134. package/dist/components/related-content/index.cjs +7 -7
  135. package/dist/components/related-content/index.js +6 -6
  136. package/dist/components/shared/detail-page-skeleton.d.ts.map +1 -1
  137. package/dist/components/tickets/help-center-create-form.d.ts +4 -4
  138. package/dist/components/tickets/index.cjs +15 -15
  139. package/dist/components/tickets/index.js +14 -14
  140. package/dist/components/ui/announcement-bar-view.d.ts.map +1 -1
  141. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  142. package/dist/components/ui/checkbox-block.d.ts.map +1 -1
  143. package/dist/components/ui/dashboard-info-card.d.ts +1 -1
  144. package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
  145. package/dist/components/ui/data-table/data-table-body.d.ts +2 -2
  146. package/dist/components/ui/data-table/data-table-skeleton.d.ts +2 -2
  147. package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
  148. package/dist/components/ui/data-table/utils.d.ts +2 -2
  149. package/dist/components/ui/dialog.d.ts.map +1 -1
  150. package/dist/components/ui/drawer.d.ts +7 -3
  151. package/dist/components/ui/drawer.d.ts.map +1 -1
  152. package/dist/components/ui/entity-image.d.ts +2 -2
  153. package/dist/components/ui/file-manager/index.cjs +36 -36
  154. package/dist/components/ui/file-manager/index.js +3 -3
  155. package/dist/components/ui/filter-modal.d.ts.map +1 -1
  156. package/dist/components/ui/index.cjs +18 -10
  157. package/dist/components/ui/index.js +17 -9
  158. package/dist/components/ui/info-card-row.d.ts.map +1 -1
  159. package/dist/components/ui/menubar.d.ts +1 -1
  160. package/dist/components/ui/menubar.d.ts.map +1 -1
  161. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  162. package/dist/components/ui/modal.d.ts.map +1 -1
  163. package/dist/components/ui/no-data/no-data-actions.d.ts.map +1 -1
  164. package/dist/components/ui/no-data/no-data-styles.d.ts +1 -1
  165. package/dist/components/ui/no-data/no-data-styles.d.ts.map +1 -1
  166. package/dist/components/ui/page-actions.d.ts.map +1 -1
  167. package/dist/components/ui/panel-resize-handle.d.ts +25 -0
  168. package/dist/components/ui/panel-resize-handle.d.ts.map +1 -0
  169. package/dist/components/ui/sheet.d.ts.map +1 -1
  170. package/dist/components/ui/table/table-skeleton.d.ts +1 -1
  171. package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
  172. package/dist/hooks/index.cjs +23 -9
  173. package/dist/hooks/index.js +24 -10
  174. package/dist/hooks/ui/index.d.ts +1 -0
  175. package/dist/hooks/ui/index.d.ts.map +1 -1
  176. package/dist/hooks/ui/use-content-breakpoint.d.ts +32 -0
  177. package/dist/hooks/ui/use-content-breakpoint.d.ts.map +1 -0
  178. package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts +2 -2
  179. package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts.map +1 -1
  180. package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
  181. package/dist/hooks/ui/use-resizable-panel-size.d.ts +46 -0
  182. package/dist/hooks/ui/use-resizable-panel-size.d.ts.map +1 -0
  183. package/dist/index.cjs +39 -17
  184. package/dist/index.js +41 -19
  185. package/dist/styles/layout-steps.d.ts +30 -0
  186. package/dist/styles/layout-steps.d.ts.map +1 -0
  187. package/dist/tailwind.config.cjs +36 -11
  188. package/dist/tailwind.config.js +36 -11
  189. package/eslint-config/README.md +9 -0
  190. package/eslint-config/base.js +17 -1
  191. package/eslint-config/layout-variants.js +57 -0
  192. package/package.json +3 -1
  193. package/src/ODS_TOKEN_RULES.md +40 -0
  194. package/src/components/chat/chat-archive-page.tsx +9 -7
  195. package/src/components/chat/chat-nav-sidebar.tsx +10 -4
  196. package/src/components/chat/chat-panel-header.tsx +8 -5
  197. package/src/components/chat/embeddable-chat.tsx +686 -494
  198. package/src/components/chat/index.ts +4 -0
  199. package/src/components/chat/mingo-archive-list.tsx +108 -0
  200. package/src/components/chat/mingo-chat-header.tsx +104 -0
  201. package/src/components/chat/mingo-chat-rail.tsx +361 -0
  202. package/src/components/chat/model-display.tsx +8 -1
  203. package/src/components/contact/contact-form.tsx +11 -9
  204. package/src/components/content-loading-container.tsx +4 -2
  205. package/src/components/docs/doc-viewer.tsx +7 -7
  206. package/src/components/docs/skeletons.tsx +7 -4
  207. package/src/components/dynamic-skeleton.tsx +1 -1
  208. package/src/components/embeds/claude-embed.tsx +1 -1
  209. package/src/components/embeds/embed-viewer-frame.tsx +4 -4
  210. package/src/components/embeds/figma-embed.tsx +2 -2
  211. package/src/components/embeds/google-sheets-viewer.tsx +1 -1
  212. package/src/components/embeds/pdf-viewer.tsx +3 -3
  213. package/src/components/empty-state.tsx +6 -6
  214. package/src/components/faq/faq-section.tsx +2 -2
  215. package/src/components/faq-accordion.tsx +1 -1
  216. package/src/components/features/board/board-column-header.tsx +1 -1
  217. package/src/components/features/command-box.tsx +3 -3
  218. package/src/components/features/entity-video-section.tsx +2 -2
  219. package/src/components/features/figma-prototype-viewer.tsx +4 -1
  220. package/src/components/features/media-gallery-manager.tsx +3 -1
  221. package/src/components/features/parallax-image-showcase.tsx +9 -6
  222. package/src/components/features/policy-configuration-panel.tsx +9 -7
  223. package/src/components/features/provider-button.tsx +1 -1
  224. package/src/components/features/section-selector.tsx +11 -6
  225. package/src/components/features/select-button.tsx +4 -4
  226. package/src/components/features/seo-editor-preview.tsx +2 -2
  227. package/src/components/features/video-ratio-tabs.tsx +3 -3
  228. package/src/components/features/waitlist-form.tsx +1 -1
  229. package/src/components/help-center-pages/trust-center-page.tsx +1 -1
  230. package/src/components/help-center-pages/trust-center-sections.tsx +14 -14
  231. package/src/components/icon-display.tsx +1 -1
  232. package/src/components/layout/article-detail-layout.tsx +4 -2
  233. package/src/components/layout/list-page-layout.tsx +2 -2
  234. package/src/components/layout/page-container.tsx +26 -14
  235. package/src/components/layout/page-heading.tsx +1 -1
  236. package/src/components/layout/page-layout.tsx +1 -1
  237. package/src/components/layout/title-block.tsx +13 -13
  238. package/src/components/loading/card-skeleton.tsx +11 -11
  239. package/src/components/loading/category-vendor-selector-skeleton.tsx +1 -1
  240. package/src/components/loading/content-skeleton.tsx +6 -6
  241. package/src/components/loading/device-card-skeleton.tsx +1 -1
  242. package/src/components/loading/margin-report-skeleton.tsx +7 -7
  243. package/src/components/loading/msp-profile-form-skeleton.tsx +3 -3
  244. package/src/components/loading/organization-card-skeleton.tsx +2 -2
  245. package/src/components/loading/page-layout-skeleton.tsx +80 -68
  246. package/src/components/loading/unified-skeleton.tsx +16 -6
  247. package/src/components/media-carousel.tsx +3 -3
  248. package/src/components/meeting-scheduler/booking-form.tsx +9 -8
  249. package/src/components/meeting-scheduler/context-panel.tsx +8 -6
  250. package/src/components/meeting-scheduler/directory.tsx +4 -4
  251. package/src/components/meeting-scheduler/index.tsx +15 -15
  252. package/src/components/meeting-scheduler/slot-picker.tsx +12 -12
  253. package/src/components/navigation/app-header.tsx +6 -0
  254. package/src/components/navigation/app-layout-drawer.tsx +14 -237
  255. package/src/components/navigation/app-layout-side-panel.tsx +594 -0
  256. package/src/components/navigation/app-layout.tsx +69 -4
  257. package/src/components/navigation/header-mingo-button.tsx +13 -3
  258. package/src/components/navigation/index.ts +5 -0
  259. package/src/components/navigation/navigation-sidebar.tsx +17 -3
  260. package/src/components/navigation/sliding-sidebar.tsx +2 -1
  261. package/src/components/pagination.tsx +3 -3
  262. package/src/components/persistent-filter-controls.tsx +6 -2
  263. package/src/components/platform/ScriptArguments.tsx +1 -1
  264. package/src/components/platform/ScriptInfoSection.tsx +4 -4
  265. package/src/components/shared/article-author-byline.tsx +1 -1
  266. package/src/components/shared/delivery/delivery-lists.tsx +2 -2
  267. package/src/components/shared/delivery/delivery-row.tsx +7 -7
  268. package/src/components/shared/delivery/delivery-table.tsx +3 -3
  269. package/src/components/shared/detail-page-skeleton.tsx +11 -5
  270. package/src/components/shared/dev-section/dev-card-row.tsx +6 -6
  271. package/src/components/shared/dev-section/dev-section-view.tsx +1 -1
  272. package/src/components/shared/product-release/product-release-card-skeleton.tsx +8 -8
  273. package/src/components/shared/product-release/product-release-card.tsx +9 -9
  274. package/src/components/shared/product-release/product-releases-view.tsx +1 -1
  275. package/src/components/shared/product-release/release-detail-page.tsx +5 -5
  276. package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +1 -1
  277. package/src/components/shared/roadmap/roadmap-grid.tsx +3 -3
  278. package/src/components/tickets/help-center-card.tsx +1 -1
  279. package/src/components/tickets/help-center-create-form.tsx +12 -12
  280. package/src/components/ui/announcement-bar-view.tsx +2 -10
  281. package/src/components/ui/benefit-card.tsx +18 -18
  282. package/src/components/ui/brand-association-grid.tsx +4 -4
  283. package/src/components/ui/breadcrumb.tsx +4 -1
  284. package/src/components/ui/card.tsx +1 -1
  285. package/src/components/ui/checkbox-block.tsx +9 -7
  286. package/src/components/ui/content-loader.tsx +3 -3
  287. package/src/components/ui/cursor-pagination.tsx +3 -3
  288. package/src/components/ui/dashboard-info-card.tsx +13 -9
  289. package/src/components/ui/data-attribution.tsx +2 -2
  290. package/src/components/ui/data-table/data-table-body.tsx +2 -2
  291. package/src/components/ui/data-table/data-table-header.tsx +15 -15
  292. package/src/components/ui/data-table/data-table-skeleton.tsx +6 -6
  293. package/src/components/ui/data-table/utils.ts +5 -5
  294. package/src/components/ui/dialog.tsx +4 -1
  295. package/src/components/ui/drawer.tsx +43 -189
  296. package/src/components/ui/duration-input.tsx +1 -1
  297. package/src/components/ui/entity-image.tsx +3 -3
  298. package/src/components/ui/feature-card.tsx +10 -10
  299. package/src/components/ui/file-manager/file-manager-action-bar.tsx +1 -1
  300. package/src/components/ui/file-manager/file-manager.tsx +1 -1
  301. package/src/components/ui/filter-modal.tsx +8 -1
  302. package/src/components/ui/highlight-card.tsx +7 -7
  303. package/src/components/ui/info-card-row.tsx +10 -6
  304. package/src/components/ui/modal-v2.tsx +7 -2
  305. package/src/components/ui/modal.tsx +3 -2
  306. package/src/components/ui/no-data/no-data-actions.tsx +10 -4
  307. package/src/components/ui/no-data/no-data-styles.ts +1 -1
  308. package/src/components/ui/no-data/no-data.tsx +1 -1
  309. package/src/components/ui/organization-card.tsx +1 -1
  310. package/src/components/ui/page-actions.tsx +34 -5
  311. package/src/components/ui/panel-resize-handle.tsx +184 -0
  312. package/src/components/ui/progress-bar.tsx +2 -2
  313. package/src/components/ui/query-report-table/query-report-table.tsx +1 -1
  314. package/src/components/ui/search-input.tsx +2 -2
  315. package/src/components/ui/service-card.tsx +2 -2
  316. package/src/components/ui/sheet.tsx +2 -1
  317. package/src/components/ui/skeleton.tsx +3 -3
  318. package/src/components/ui/stacked-rows-panel.tsx +4 -4
  319. package/src/components/ui/tab-navigation.tsx +2 -2
  320. package/src/components/ui/tab-selector.tsx +2 -2
  321. package/src/components/ui/table/table-header.tsx +3 -3
  322. package/src/components/ui/table/table-skeleton.tsx +3 -3
  323. package/src/components/ui/table/utils.ts +5 -5
  324. package/src/components/ui/tag-search-input.tsx +3 -3
  325. package/src/components/ui/tags-manager.tsx +5 -5
  326. package/src/components/ui/test-run.tsx +2 -2
  327. package/src/components/ui/ticket-info-section.tsx +1 -1
  328. package/src/components/ui/ticket-notes-section.tsx +1 -1
  329. package/src/components/ui/ticket-status-config-row.tsx +15 -15
  330. package/src/hooks/ui/index.ts +1 -0
  331. package/src/hooks/ui/use-content-breakpoint.ts +58 -0
  332. package/src/hooks/ui/use-media-query.ts +3 -2
  333. package/src/hooks/ui/use-resizable-panel-size.ts +114 -0
  334. package/src/styles/index.css +2 -0
  335. package/src/styles/layout-steps.ts +14 -0
  336. package/src/styles/ods-colors.css +15 -0
  337. package/src/styles/ods-content-area-tokens.css +27 -0
  338. package/src/styles/ods-content-area.css +31 -0
  339. package/src/styles/ods-effects.css +4 -2
  340. package/src/styles/ods-responsive-tokens.css +14 -4
  341. package/tailwind.config.ts +42 -7
@@ -37,6 +37,9 @@ export * from './msp-organization-card';
37
37
  export * from './msp-organization-card-skeleton';
38
38
  export * from './guide-mode-banner';
39
39
  export * from './mingo-chat-history';
40
+ export * from './mingo-archive-list';
41
+ export * from './mingo-chat-header';
42
+ export * from './mingo-chat-rail';
40
43
  export * from './mingo-history-rail';
41
44
  export * from './mingo-chat-modals';
42
45
  export * from './chat-header-icon-button';
@@ -95,5 +98,6 @@ export {
95
98
  EmbeddableChat,
96
99
  type EmbeddableChatHandle,
97
100
  type EmbeddableChatProps,
101
+ MINGO_V2_RAIL_WIDTH,
98
102
  } from './embeddable-chat';
99
103
  export * from './proxy-credentials-panel';
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef } from 'react';
4
+ import { formatRelativeTime } from '../../utils/date-utils';
5
+ import { BoxArchiveIcon } from '../icons-v2-generated';
6
+ import { SquareAvatar } from '../ui/square-avatar';
7
+ import { ChatListEmptyState } from './chat-list-empty-state';
8
+ import { ChatListItem } from './chat-nav-sidebar';
9
+ import type { DialogItem } from './types/component.types';
10
+
11
+ export interface MingoArchiveListProps {
12
+ dialogs: ReadonlyArray<DialogItem>;
13
+ onSelectDialog?: (id: string) => void;
14
+ /** The first page is loading. */
15
+ isLoading?: boolean;
16
+ hasMore?: boolean;
17
+ isLoadingMore?: boolean;
18
+ onLoadMore?: () => void;
19
+ }
20
+
21
+ /**
22
+ * The Mingo v2 chat archive (Figma "chat archive"): it opens in the chat
23
+ * column with the chat list kept beside it, one row per archived chat with
24
+ * how long ago it was active and its owner.
25
+ */
26
+ export function MingoArchiveList({
27
+ dialogs,
28
+ onSelectDialog,
29
+ isLoading = false,
30
+ hasMore = false,
31
+ isLoadingMore = false,
32
+ onLoadMore,
33
+ }: MingoArchiveListProps) {
34
+ const sentinelRef = useRef<HTMLDivElement>(null);
35
+ const onLoadMoreRef = useRef(onLoadMore);
36
+ const isLoadingMoreRef = useRef(isLoadingMore);
37
+ useEffect(() => {
38
+ onLoadMoreRef.current = onLoadMore;
39
+ isLoadingMoreRef.current = isLoadingMore;
40
+ });
41
+ const dialogCount = dialogs.length;
42
+ useEffect(() => {
43
+ const sentinel = sentinelRef.current;
44
+ if (!sentinel || !hasMore || typeof IntersectionObserver === 'undefined') return undefined;
45
+ const observer = new IntersectionObserver(
46
+ ([entry]) => {
47
+ if (entry?.isIntersecting && !isLoadingMoreRef.current) onLoadMoreRef.current?.();
48
+ },
49
+ { rootMargin: '120px', threshold: 0.1 },
50
+ );
51
+ observer.observe(sentinel);
52
+ return () => observer.disconnect();
53
+ }, [hasMore, dialogCount, isLoadingMore]);
54
+
55
+ return (
56
+ <section
57
+ aria-label="Archived chats"
58
+ className="flex min-h-0 flex-1 flex-col gap-[var(--spacing-system-xxs)] px-[var(--spacing-system-xs)] pt-[var(--spacing-system-mf)]"
59
+ >
60
+ <p className="shrink-0 px-[var(--spacing-system-xs)] pt-[var(--spacing-system-xs)] text-ods-text-secondary text-h5">
61
+ Archived Chats
62
+ </p>
63
+ <div className="flex min-h-0 flex-1 flex-col gap-[var(--spacing-system-xxs)] overflow-y-auto overscroll-contain pb-[var(--spacing-system-mf)]">
64
+ {dialogs.length > 0 ? (
65
+ <>
66
+ {dialogs.map(dialog => (
67
+ <ChatListItem
68
+ key={dialog.id}
69
+ title={dialog.title || 'Untitled Chat'}
70
+ onClick={() => onSelectDialog?.(dialog.id)}
71
+ trailing={
72
+ <span className="flex items-center gap-[var(--spacing-system-xs)]">
73
+ {dialog.timestamp && (
74
+ <span className="whitespace-nowrap text-ods-text-secondary text-h6">
75
+ {formatRelativeTime(dialog.timestamp)}
76
+ </span>
77
+ )}
78
+ {dialog.owner && (dialog.owner.name || dialog.owner.avatarUrl) ? (
79
+ <SquareAvatar
80
+ variant="round"
81
+ sizePx={24}
82
+ src={dialog.owner.avatarUrl || undefined}
83
+ alt={dialog.owner.name || undefined}
84
+ fallback={dialog.owner.name || undefined}
85
+ title={dialog.owner.name || undefined}
86
+ />
87
+ ) : null}
88
+ </span>
89
+ }
90
+ />
91
+ ))}
92
+ {hasMore && <div ref={sentinelRef} className="h-px shrink-0" aria-hidden />}
93
+ </>
94
+ ) : isLoading ? (
95
+ Array.from({ length: 6 }, (_, i) => (
96
+ <div key={i} className="h-10 shrink-0 animate-pulse rounded-md bg-ods-skeleton" aria-hidden />
97
+ ))
98
+ ) : (
99
+ <ChatListEmptyState
100
+ icon={<BoxArchiveIcon size={24} />}
101
+ title="No Archived Chats"
102
+ description="Archived Mingo sessions will show here"
103
+ />
104
+ )}
105
+ </div>
106
+ </section>
107
+ );
108
+ }
@@ -0,0 +1,104 @@
1
+ 'use client';
2
+
3
+ import { Arrow02RightIcon, Ellipsis01Icon, Refresh01LeftIcon, SidebarIcon, XmarkIcon } from '../icons-v2-generated';
4
+ import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu';
5
+ import { SquareAvatar } from '../ui/square-avatar';
6
+ import { ChatHeaderIconButton } from './chat-header-icon-button';
7
+
8
+ export interface MingoChatHeaderProps {
9
+ title: string;
10
+ /** Second line: the chat's owner. */
11
+ subtitle?: string | null;
12
+ /** The owner's avatar at the title cell's end. */
13
+ avatar?: { name?: string | null; avatarUrl?: string | null } | null;
14
+ /** Shows the chat list toggle at the start. */
15
+ onToggleList?: () => void;
16
+ /** The list is on screen beside the chat; drives the toggle's state. */
17
+ listOpen?: boolean;
18
+ /** The chat's ⋯ menu. Empty or omitted leaves it out. */
19
+ menuItems?: ActionsMenuItem[];
20
+ /** An archived chat: offers "Unarchive". */
21
+ onRestore?: () => void;
22
+ /** Collapse the chat back to the list alone (the panel's minimum). */
23
+ onCollapse?: () => void;
24
+ /** Close the panel; only a panel that can close passes it. */
25
+ onClose?: () => void;
26
+ }
27
+
28
+ /**
29
+ * Top bar of the Mingo v2 chat (Figma `chat-top-navigation`): the chat list
30
+ * toggle, the title over its owner with the owner's avatar, then the chat's ⋯
31
+ * menu and the collapse arrow, split by vertical dividers. It sits over the
32
+ * chat column only; the list beside it has no bar.
33
+ */
34
+ export function MingoChatHeader({
35
+ title,
36
+ subtitle,
37
+ avatar,
38
+ onToggleList,
39
+ listOpen = false,
40
+ menuItems = [],
41
+ onRestore,
42
+ onCollapse,
43
+ onClose,
44
+ }: MingoChatHeaderProps) {
45
+ return (
46
+ <div className="flex h-14 w-full shrink-0 overflow-hidden border-b border-ods-border">
47
+ {onToggleList && (
48
+ <ChatHeaderIconButton
49
+ divider="right"
50
+ onClick={onToggleList}
51
+ aria-label={listOpen ? 'Hide chat list' : 'Show chat list'}
52
+ aria-pressed={listOpen}
53
+ >
54
+ <SidebarIcon size={24} />
55
+ </ChatHeaderIconButton>
56
+ )}
57
+
58
+ <div className="flex min-w-0 flex-1 items-center gap-[var(--spacing-system-m)] px-[var(--spacing-system-m)] py-[var(--spacing-system-sf)]">
59
+ <div className="flex min-w-0 flex-1 flex-col">
60
+ <p className="truncate text-ods-text-primary text-h3">{title}</p>
61
+ {subtitle && <p className="truncate text-ods-text-secondary text-h6">{subtitle}</p>}
62
+ </div>
63
+ {avatar && (avatar.name || avatar.avatarUrl) ? (
64
+ <SquareAvatar
65
+ variant="round"
66
+ sizePx={32}
67
+ src={avatar.avatarUrl || undefined}
68
+ alt={avatar.name || undefined}
69
+ fallback={avatar.name || undefined}
70
+ title={avatar.name || undefined}
71
+ />
72
+ ) : null}
73
+ </div>
74
+
75
+ {onRestore && (
76
+ <ChatHeaderIconButton onClick={onRestore} aria-label="Unarchive chat">
77
+ <Refresh01LeftIcon size={24} />
78
+ </ChatHeaderIconButton>
79
+ )}
80
+ {menuItems.length > 0 && (
81
+ <ActionsMenuDropdown
82
+ triggerAriaLabel="Chat actions"
83
+ onCloseAutoFocus={e => e.preventDefault()}
84
+ groups={[{ items: menuItems }]}
85
+ customTrigger={
86
+ <ChatHeaderIconButton aria-label="Chat actions">
87
+ <Ellipsis01Icon size={24} />
88
+ </ChatHeaderIconButton>
89
+ }
90
+ />
91
+ )}
92
+ {onCollapse && (
93
+ <ChatHeaderIconButton onClick={onCollapse} aria-label="Collapse chat">
94
+ <Arrow02RightIcon size={24} />
95
+ </ChatHeaderIconButton>
96
+ )}
97
+ {onClose && (
98
+ <ChatHeaderIconButton onClick={onClose} aria-label="Close">
99
+ <XmarkIcon size={24} />
100
+ </ChatHeaderIconButton>
101
+ )}
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,361 @@
1
+ 'use client';
2
+
3
+ import { type ReactNode, useEffect, useRef, useState } from 'react';
4
+ import { cn } from '../../utils/cn';
5
+ import {
6
+ AlertCircleIcon,
7
+ ChatPlusIcon,
8
+ ChatQuestionIcon,
9
+ ChatsIcon,
10
+ ClockHistoryIcon,
11
+ DotsLoaderIcon,
12
+ Ellipsis01Icon,
13
+ MessageIndicatorIcon,
14
+ Refresh01RightIcon,
15
+ SearchIcon,
16
+ ShieldCheckIcon,
17
+ UserIcon,
18
+ UsersIcon,
19
+ } from '../icons-v2-generated';
20
+ import { MingoIcon } from '../icons/mingo-icon';
21
+ import { ActionsMenuDropdown, type ActionsMenuItem } from '../ui/actions-menu';
22
+ import { Button } from '../ui/button';
23
+ import { chatDialogMenuItems } from './chat-dialog-menu-items';
24
+ import { ChatHeaderSearchField } from './chat-header-search-field';
25
+ import { ChatListEmptyState } from './chat-list-empty-state';
26
+ import { ChatListItem, ChatNavSidebar, type ChatNavSidebarItem } from './chat-nav-sidebar';
27
+ import type { MingoHistoryScope } from './mingo-history-rail';
28
+ import type { DialogItem } from './types/component.types';
29
+
30
+ /**
31
+ * What a chat is doing, shown at the end of its row:
32
+ * - `working`: Mingo is answering in it
33
+ * - `unread`: a reply the user has not seen
34
+ * - `approval`: Mingo waits for an approval there
35
+ */
36
+ export type MingoDialogStatus = 'working' | 'unread' | 'approval';
37
+
38
+ export interface MingoChatRailProps {
39
+ /** Dialogs to list, newest first. */
40
+ dialogs: ReadonlyArray<DialogItem>;
41
+ activeDialogId?: string;
42
+ onSelectDialog?: (id: string) => void;
43
+ /** A chat being started and not saved yet: a selected row at the top
44
+ * (e.g. "New Chat"), so the list says what the chat beside it is. */
45
+ draftTitle?: string;
46
+ onNewChat?: () => void;
47
+ /** Open the archive. `archiveActive` marks it as the view on screen. */
48
+ onOpenArchive?: () => void;
49
+ archiveActive?: boolean;
50
+ /** "What to ask Mingo?": the suggestions a new chat starts from. */
51
+ onWhatToAsk?: () => void;
52
+ /** A row's status glyph. Unread replies are shown without it. */
53
+ statusOf?: (dialog: DialogItem) => MingoDialogStatus | undefined;
54
+ onRequestRename?: (dialog: DialogItem) => void;
55
+ onRequestArchive?: (dialog: DialogItem) => void;
56
+ onRequestCopyLink?: (dialog: DialogItem) => void;
57
+ onRequestCompact?: (dialog: DialogItem) => void;
58
+ /** "Show My Chats" / "Show All Chats" in the list's ⋯ menu; both or neither. */
59
+ scope?: MingoHistoryScope;
60
+ onScopeChange?: (scope: MingoHistoryScope) => void;
61
+ /** Server-side search. With a handler the ⋯ menu offers "Search for Chat". */
62
+ searchQuery?: string;
63
+ onSearchChange?: (query: string) => void;
64
+ hasMore?: boolean;
65
+ isLoadingMore?: boolean;
66
+ onLoadMore?: () => void;
67
+ /** The first page is still loading. */
68
+ isLoadingHistory?: boolean;
69
+ /** The list failed to load with nothing cached. */
70
+ loadError?: boolean;
71
+ onRetry?: () => void;
72
+ className?: string;
73
+ }
74
+
75
+ const STATUS_GLYPHS: Record<MingoDialogStatus, { icon: ReactNode; label: string }> = {
76
+ working: { icon: <DotsLoaderIcon className="text-ods-text-secondary" />, label: 'Mingo is working' },
77
+ unread: { icon: <MessageIndicatorIcon className="text-ods-accent" />, label: 'New reply' },
78
+ approval: { icon: <ShieldCheckIcon className="text-ods-accent" />, label: 'Waiting for approval' },
79
+ };
80
+
81
+ function statusFor(dialog: DialogItem, statusOf: MingoChatRailProps['statusOf']): MingoDialogStatus | undefined {
82
+ return statusOf?.(dialog) ?? ((dialog.unreadMessagesCount ?? 0) > 0 ? 'unread' : undefined);
83
+ }
84
+
85
+ /**
86
+ * The Mingo chat list of the v2 layout (Figma `chat-sidebar`, type
87
+ * mingo-my-chats): the Mingo mark, the three actions, a "Your Current Chats"
88
+ * list with a ⋯ menu that scopes or searches it, and one row per chat with its
89
+ * status at the end. Row actions (rename, archive, ...) sit behind a ⋯ that
90
+ * replaces the status on hover.
91
+ */
92
+ export function MingoChatRail({
93
+ dialogs,
94
+ activeDialogId,
95
+ onSelectDialog,
96
+ draftTitle,
97
+ onNewChat,
98
+ onOpenArchive,
99
+ archiveActive = false,
100
+ onWhatToAsk,
101
+ statusOf,
102
+ onRequestRename,
103
+ onRequestArchive,
104
+ onRequestCopyLink,
105
+ onRequestCompact,
106
+ scope,
107
+ onScopeChange,
108
+ searchQuery,
109
+ onSearchChange,
110
+ hasMore = false,
111
+ isLoadingMore = false,
112
+ onLoadMore,
113
+ isLoadingHistory = false,
114
+ loadError = false,
115
+ onRetry,
116
+ className,
117
+ }: MingoChatRailProps) {
118
+ const [searchOpen, setSearchOpen] = useState(!!searchQuery?.trim());
119
+ const hasSearch = !!searchQuery?.trim();
120
+
121
+ const items: ChatNavSidebarItem[] = [];
122
+ if (onNewChat) items.push({ id: 'new', label: 'Start New Chat', icon: <ChatPlusIcon />, onClick: onNewChat });
123
+ if (onOpenArchive) {
124
+ items.push({
125
+ id: 'archive',
126
+ label: 'Chat Archive',
127
+ icon: <ClockHistoryIcon />,
128
+ active: archiveActive,
129
+ onClick: onOpenArchive,
130
+ });
131
+ }
132
+ if (onWhatToAsk)
133
+ items.push({ id: 'ask', label: 'What to ask Mingo?', icon: <ChatQuestionIcon />, onClick: onWhatToAsk });
134
+
135
+ const listMenu: ActionsMenuItem[] = [];
136
+ if (scope && onScopeChange) {
137
+ listMenu.push(
138
+ {
139
+ id: 'my',
140
+ label: 'Show My Chats',
141
+ icon: <UserIcon className="h-full w-full text-ods-text-secondary" />,
142
+ type: 'checkbox',
143
+ checked: scope === 'my',
144
+ onClick: () => onScopeChange('my'),
145
+ },
146
+ {
147
+ id: 'all',
148
+ label: 'Show All Chats',
149
+ icon: <UsersIcon className="h-full w-full text-ods-text-secondary" />,
150
+ type: 'checkbox',
151
+ checked: scope === 'all',
152
+ onClick: () => onScopeChange('all'),
153
+ },
154
+ );
155
+ }
156
+ if (onSearchChange) {
157
+ if (listMenu.length > 0) listMenu.push({ id: 'divider', label: '', type: 'separator' });
158
+ listMenu.push({
159
+ id: 'search',
160
+ label: 'Search for Chat',
161
+ icon: <SearchIcon className="h-full w-full text-ods-text-secondary" />,
162
+ onClick: () => setSearchOpen(true),
163
+ });
164
+ }
165
+
166
+ // Infinite scroll: the next page when the sentinel at the list's end shows.
167
+ // The observer clips against the scrolling list, so the viewport root works.
168
+ const sentinelRef = useRef<HTMLDivElement>(null);
169
+ const onLoadMoreRef = useRef(onLoadMore);
170
+ const isLoadingMoreRef = useRef(isLoadingMore);
171
+ useEffect(() => {
172
+ onLoadMoreRef.current = onLoadMore;
173
+ isLoadingMoreRef.current = isLoadingMore;
174
+ });
175
+ const dialogCount = dialogs.length;
176
+ useEffect(() => {
177
+ const sentinel = sentinelRef.current;
178
+ if (!sentinel || !hasMore || typeof IntersectionObserver === 'undefined') return undefined;
179
+ const observer = new IntersectionObserver(
180
+ ([entry]) => {
181
+ if (entry?.isIntersecting && !isLoadingMoreRef.current) onLoadMoreRef.current?.();
182
+ },
183
+ { rootMargin: '120px', threshold: 0.1 },
184
+ );
185
+ observer.observe(sentinel);
186
+ return () => observer.disconnect();
187
+ // Re-armed per page: a sentinel still in view after a short page keeps loading.
188
+ }, [hasMore, dialogCount, isLoadingMore]);
189
+
190
+ let list: ReactNode;
191
+ if (loadError) {
192
+ list = (
193
+ <div className="flex flex-col items-center gap-[var(--spacing-system-m)] px-[var(--spacing-system-xs)] py-[var(--spacing-system-l)] text-center">
194
+ <AlertCircleIcon className="size-8 shrink-0 text-ods-text-secondary" />
195
+ <div className="flex flex-col gap-[var(--spacing-system-xxs)]">
196
+ <p className="text-ods-text-primary text-h4">Couldn’t load your chats</p>
197
+ <p className="text-ods-text-secondary text-h6">Something went wrong reaching the server. Try again.</p>
198
+ </div>
199
+ {onRetry && (
200
+ <Button variant="outline" size="small" leftIcon={<Refresh01RightIcon />} onClick={onRetry}>
201
+ Try again
202
+ </Button>
203
+ )}
204
+ </div>
205
+ );
206
+ } else if (isLoadingHistory) {
207
+ list = Array.from({ length: 6 }, (_, i) => (
208
+ <div key={i} className="h-10 shrink-0 animate-pulse rounded-md bg-ods-skeleton" aria-hidden />
209
+ ));
210
+ } else if (dialogs.length === 0 && !draftTitle) {
211
+ list = hasSearch ? (
212
+ <p className="px-[var(--spacing-system-xs)] py-[var(--spacing-system-s)] text-ods-text-secondary text-h6">
213
+ No chats found
214
+ </p>
215
+ ) : (
216
+ <ChatListEmptyState
217
+ icon={<ChatsIcon size={24} />}
218
+ title="No Current Chats"
219
+ description="Previous Mingo sessions will show here"
220
+ />
221
+ );
222
+ } else {
223
+ list = (
224
+ <>
225
+ {draftTitle && <ChatListItem active title={draftTitle} tabIndex={-1} className="cursor-default" />}
226
+ {dialogs.map(dialog => (
227
+ <MingoChatRailRow
228
+ key={dialog.id}
229
+ dialog={dialog}
230
+ active={dialog.id === activeDialogId}
231
+ status={statusFor(dialog, statusOf)}
232
+ onSelect={onSelectDialog}
233
+ menuItems={chatDialogMenuItems({
234
+ onCopyLink: onRequestCopyLink && (() => onRequestCopyLink(dialog)),
235
+ onRename: onRequestRename && (() => onRequestRename(dialog)),
236
+ onCompact: onRequestCompact && (() => onRequestCompact(dialog)),
237
+ onArchive: onRequestArchive && (() => onRequestArchive(dialog)),
238
+ })}
239
+ />
240
+ ))}
241
+ {hasMore && <div ref={sentinelRef} className="h-px shrink-0" aria-hidden />}
242
+ </>
243
+ );
244
+ }
245
+
246
+ return (
247
+ <ChatNavSidebar
248
+ className={className}
249
+ // Mingo's cyan for the selection and the status glyphs, whatever the
250
+ // tenant accent the rest of the panel carries.
251
+ data-accent="mingo"
252
+ aria-label="Mingo chats"
253
+ logo={
254
+ <MingoIcon
255
+ color="currentColor"
256
+ eyesColor="var(--ods-flamingo-cyan-base)"
257
+ cornerColor="var(--ods-flamingo-cyan-base)"
258
+ className="size-6 text-ods-text-primary"
259
+ aria-hidden
260
+ />
261
+ }
262
+ items={items}
263
+ sectionLabel="Your Current Chats"
264
+ sectionActions={
265
+ listMenu.length > 0 ? (
266
+ <ActionsMenuDropdown
267
+ triggerAriaLabel="Chat list options"
268
+ groups={[{ items: listMenu }]}
269
+ onCloseAutoFocus={e => e.preventDefault()}
270
+ customTrigger={
271
+ <Button
272
+ variant="transparent"
273
+ size="icon"
274
+ aria-label="Chat list options"
275
+ className="size-6 p-0 text-ods-text-secondary hover:text-ods-text-primary md:size-6"
276
+ >
277
+ <Ellipsis01Icon size={24} />
278
+ </Button>
279
+ }
280
+ />
281
+ ) : undefined
282
+ }
283
+ listHeader={
284
+ searchOpen && onSearchChange ? (
285
+ <ChatHeaderSearchField
286
+ initialValue={searchQuery}
287
+ onSearchChange={onSearchChange}
288
+ onCollapse={() => setSearchOpen(false)}
289
+ className="h-10 rounded-md border border-ods-border bg-ods-card px-[var(--spacing-system-xs)]"
290
+ />
291
+ ) : undefined
292
+ }
293
+ >
294
+ {list}
295
+ </ChatNavSidebar>
296
+ );
297
+ }
298
+
299
+ interface MingoChatRailRowProps {
300
+ dialog: DialogItem;
301
+ active: boolean;
302
+ status?: MingoDialogStatus;
303
+ onSelect?: (id: string) => void;
304
+ menuItems: ActionsMenuItem[];
305
+ }
306
+
307
+ function MingoChatRailRow({ dialog, active, status, onSelect, menuItems }: MingoChatRailRowProps) {
308
+ const [menuOpen, setMenuOpen] = useState(false);
309
+ const hasMenu = menuItems.length > 0;
310
+ const glyph = status ? STATUS_GLYPHS[status] : undefined;
311
+ const title = dialog.title || 'Untitled Chat';
312
+
313
+ return (
314
+ <div className="group/row relative shrink-0">
315
+ <ChatListItem
316
+ active={active}
317
+ title={title}
318
+ onClick={() => onSelect?.(dialog.id)}
319
+ className={cn(hasMenu && 'pr-10')}
320
+ trailing={
321
+ glyph ? (
322
+ <span
323
+ role="img"
324
+ aria-label={glyph.label}
325
+ className={cn('flex transition-opacity', hasMenu && 'group-hover/row:opacity-0', menuOpen && 'opacity-0')}
326
+ >
327
+ {glyph.icon}
328
+ </span>
329
+ ) : undefined
330
+ }
331
+ />
332
+ {hasMenu && (
333
+ // Revealed on hover or its own focus, and kept while its menu is open.
334
+ <span
335
+ className={cn(
336
+ 'absolute inset-y-0 right-[var(--spacing-system-xs)] flex items-center transition-opacity',
337
+ menuOpen ? 'opacity-100' : 'opacity-0 focus-within:opacity-100 group-hover/row:opacity-100',
338
+ )}
339
+ >
340
+ <ActionsMenuDropdown
341
+ triggerAriaLabel={`Actions for ${title}`}
342
+ groups={[{ items: menuItems }]}
343
+ open={menuOpen}
344
+ onOpenChange={setMenuOpen}
345
+ onCloseAutoFocus={e => e.preventDefault()}
346
+ customTrigger={
347
+ <Button
348
+ variant="transparent"
349
+ size="icon"
350
+ aria-label={`Actions for ${title}`}
351
+ className="size-6 p-0 text-ods-text-secondary hover:text-ods-text-primary md:size-6"
352
+ >
353
+ <Ellipsis01Icon size={24} />
354
+ </Button>
355
+ }
356
+ />
357
+ </span>
358
+ )}
359
+ </div>
360
+ );
361
+ }
@@ -5,7 +5,9 @@ import { cn } from '../../utils/cn';
5
5
  import { HoverCard, HoverCardContent, HoverCardTrigger } from '../hover-card';
6
6
  import { AnthropicLogoGreyIcon, GeminiLogoGreyIcon, OpenaiLogoGreyIcon } from '../icons-v2-generated';
7
7
  import { Skeleton } from '../ui/skeleton';
8
+ import { useChatAppearance } from './chat-appearance-context';
8
9
  import type { ModelDisplayProps, ModelUsageBreakdown } from './types';
10
+ import { CHAT_APPEARANCE } from './types/chat.types';
9
11
 
10
12
  const getProviderIcon = (provider?: string) => {
11
13
  if (!provider) return null;
@@ -66,6 +68,9 @@ const ModelDisplay = React.forwardRef<HTMLDivElement, ModelDisplayProps>(
66
68
  ) => {
67
69
  const icon = getProviderIcon(provider);
68
70
  const name = displayName || modelName;
71
+ // v2 spells the counts out ("372 / 50,000 tokens used"); classic keeps them short.
72
+ const isV2 = useChatAppearance() === CHAT_APPEARANCE.V2;
73
+ const formatCount = (count: number) => (isV2 ? count.toLocaleString('en-US') : formatTokenCount(count));
69
74
 
70
75
  // Inline pill: provider icon + model name + "X / Y tokens used".
71
76
  // The "tokens used" tail renders as soon as `contextWindow` is known
@@ -79,7 +84,9 @@ const ModelDisplay = React.forwardRef<HTMLDivElement, ModelDisplayProps>(
79
84
  <span className="font-body">{name}</span>
80
85
  {contextWindow != null && (
81
86
  <span className="ml-auto opacity-70 text-h6">
82
- {usedTokens != null ? formatTokenCount(usedTokens) : '—'}/{formatTokenCount(contextWindow)} tokens used
87
+ {usedTokens != null ? formatCount(usedTokens) : '—'}
88
+ {isV2 ? ' / ' : '/'}
89
+ {formatCount(contextWindow)} tokens used
83
90
  </span>
84
91
  )}
85
92
  </div>
@@ -262,11 +262,11 @@ export function ContactForm({
262
262
 
263
263
  return (
264
264
  <div
265
- className={`flex h-full flex-col ${!noBorder ? 'rounded-2xl border border-ods-border md:rounded-3xl' : ''} ${!noPadding ? 'p-6 md:p-8 lg:p-10' : ''}`}
265
+ className={`flex h-full flex-col ${!noBorder ? 'rounded-2xl border border-ods-border content-md:rounded-3xl' : ''} ${!noPadding ? 'p-6 content-md:p-8 content-lg:p-10' : ''}`}
266
266
  >
267
267
  {(title || subtitle) && (
268
- <div className="mb-6 md:mb-8">
269
- {title && <h2 className={`${SECTION_HEADING_CLASS} mb-3 md:mb-4`}>{accentSentenceMarks(title)}</h2>}
268
+ <div className="mb-6 content-md:mb-8">
269
+ {title && <h2 className={`${SECTION_HEADING_CLASS} mb-3 content-md:mb-4`}>{accentSentenceMarks(title)}</h2>}
270
270
  {subtitle && <p className="text-ods-text-primary text-h4">{subtitle}</p>}
271
271
  </div>
272
272
  )}
@@ -283,7 +283,7 @@ export function ContactForm({
283
283
  Object.fromEntries(Object.entries(validationErrors).map(([k, v]) => [k, v?.message ?? v])),
284
284
  );
285
285
  })}
286
- className="flex flex-grow flex-col space-y-4 md:space-y-6"
286
+ className="flex flex-grow flex-col space-y-4 content-md:space-y-6"
287
287
  {...HUBSPOT_DO_NOT_COLLECT_FORM_PROPS}
288
288
  >
289
289
  {/* Hidden inputs for fields that are required by `ContactSchema`
@@ -308,7 +308,7 @@ export function ContactForm({
308
308
  {extraTopField}
309
309
 
310
310
  {showNameEmailRow && (
311
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6">
311
+ <div className="grid grid-cols-1 gap-4 content-md:grid-cols-2 content-md:gap-6">
312
312
  {showName && (
313
313
  <div className="flex flex-col">
314
314
  <Label htmlFor="name">
@@ -359,7 +359,7 @@ export function ContactForm({
359
359
  )}
360
360
 
361
361
  {(showCompanySize || showReferralSource) && (
362
- <div className="grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6">
362
+ <div className="grid grid-cols-1 gap-4 content-md:grid-cols-2 content-md:gap-6">
363
363
  {showCompanySize && (
364
364
  <div className="flex flex-col">
365
365
  <Label htmlFor="companySize">Company Size</Label>
@@ -511,14 +511,16 @@ export function ContactForm({
511
511
  </div>
512
512
  )}
513
513
 
514
- <div className="mt-auto flex w-full flex-col items-center justify-end gap-4 pt-2 md:flex-row md:gap-6">
515
- {footerText && <p className="text-center text-ods-text-secondary text-h6 md:text-left">{footerText}</p>}
514
+ <div className="mt-auto flex w-full flex-col items-center justify-end gap-4 pt-2 content-md:flex-row content-md:gap-6">
515
+ {footerText && (
516
+ <p className="text-center text-ods-text-secondary text-h6 content-md:text-left">{footerText}</p>
517
+ )}
516
518
  <Button
517
519
  type="submit"
518
520
  loading={isSubmitting}
519
521
  disabled={isSubmitting || isSuccess || (attachmentsEnabled && attachments.hasInflightUploads)}
520
522
  variant={buttonVariant}
521
- className={`w-full md:w-auto ${buttonClassName}`}
523
+ className={`w-full content-md:w-auto ${buttonClassName}`}
522
524
  >
523
525
  {isSuccess ? submitSuccessLabel : submitLabel}
524
526
  </Button>