@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
@@ -61,6 +61,7 @@ import { Drawer, DrawerContent } from '../ui/drawer';
61
61
  import { HoverDropdown, type HoverDropdownItem } from '../ui/hover-dropdown';
62
62
  import { CollisionBoundaryContext, PortalContainerContext } from '../ui/portal-container';
63
63
  import { SquareAvatar } from '../ui/square-avatar';
64
+ import { ChatAppearanceContext } from './chat-appearance-context';
64
65
  import { ChatArchivePage } from './chat-archive-page';
65
66
  import { ChatAttachmentChipStrip } from './chat-attachment-bar';
66
67
  import { ChatComposer, type ChatComposerLock } from './chat-composer';
@@ -85,7 +86,10 @@ import { useEmptyStateConfig } from './hooks/use-empty-state-config';
85
86
  import { fetchSlashCommands, useSlashCommandRegistry, type SlashCommandSummary } from './hooks/use-slash-commands';
86
87
  import type { ChatSource, UseSseChatAdapterOptions } from './hooks/use-sse-chat-adapter';
87
88
  import { useUnifiedChat, type ChatMode, type UseUnifiedChatModes } from './hooks/use-unified-chat';
89
+ import { MingoArchiveList } from './mingo-archive-list';
90
+ import { MingoChatHeader } from './mingo-chat-header';
88
91
  import { ChatDialogModals } from './mingo-chat-modals';
92
+ import { MingoChatRail, type MingoDialogStatus } from './mingo-chat-rail';
89
93
  import { MingoHistoryRail } from './mingo-history-rail';
90
94
  import { MingoOnboardingCard } from './mingo-onboarding-card';
91
95
  import { MingoOnboardingCardSkeleton } from './mingo-onboarding-card-skeleton';
@@ -93,7 +97,8 @@ import { MingoWelcome, type MingoWelcomeProps } from './mingo-welcome';
93
97
  import { NavLinkAnchorViaRuntime } from './nav-link-anchor-via-runtime';
94
98
  import { accentFromIdentityIcon, getAgentAccent } from './quick-action-chip';
95
99
  import { SourceActionButton } from './source-action-button';
96
- import type { ChatInputRef, SlashCommandActionId } from './types/component.types';
100
+ import { CHAT_APPEARANCE, type ChatAppearance } from './types/chat.types';
101
+ import type { ChatInputRef, DialogItem, SlashCommandActionId } from './types/component.types';
97
102
  import type { ChatContextItem, ChatContextPickerConfig } from './types/context-item.types';
98
103
  import type { MessageSegment, Message } from './types/message.types';
99
104
  import type {
@@ -123,16 +128,18 @@ import { resolveSourceRowCTA, sourceRowCtxFromRuntime } from './utils/source-row
123
128
  * self-contained — the host wires nothing.
124
129
  */
125
130
  const HISTORY_RAIL_WIDTH = 320;
131
+ /** The v2 chat list (`MingoChatRail`, Figma `chat-sidebar`): the narrowest the v2 chat draws. */
132
+ export const MINGO_V2_RAIL_WIDTH = 296;
126
133
  const CHAT_BLOCK_MIN_WIDTH = 400;
127
134
  const SPLIT_MIN_WIDTH = HISTORY_RAIL_WIDTH + CHAT_BLOCK_MIN_WIDTH;
128
135
 
129
- // Desktop drawer opens at this fraction of the viewport width (clamped to the
130
- // Drawer's own min/max). The user can still resize (persisted per DRAWER_WIDTH_KEY).
136
+ // Desktop drawer is this fraction of the viewport width (clamped to the
137
+ // Drawer's own min/max) until the user resizes it. Only the size they choose is
138
+ // stored (see DRAWER_WIDTH_KEY); the default tracks the window, so changing
139
+ // this policy needs no new key.
131
140
  const DRAWER_DEFAULT_WIDTH_RATIO = 0.5;
132
- // Bump the suffix whenever the default policy changes so previously-persisted
133
- // widths (e.g. the old fixed 750px / earlier 30% default) reset on next open.
134
141
  const DRAWER_WIDTH_KEY = 'mingo-chat-width-v4';
135
- const DRAWER_DEFAULT_WIDTH_PX = 750; // SSR fallback before the viewport is known
142
+ const DRAWER_DEFAULT_WIDTH_PX = 750; // before the viewport is known (server render)
136
143
  /**
137
144
  * A panel that owns a dialog list opens WIDE ENOUGH TO SPLIT, so the "Current
138
145
  * Chats" rail sits beside the conversation instead of replacing it.
@@ -144,12 +151,14 @@ const DRAWER_DEFAULT_WIDTH_PX = 750; // SSR fallback before the viewport is know
144
151
  * viewport, so a genuinely small screen keeps the stacked view.
145
152
  */
146
153
  const SPLIT_DEFAULT_WIDTH_ALLOWANCE = 16;
147
- function drawerDefaultWidth(withHistoryRail: boolean): number {
148
- if (typeof window === 'undefined') return DRAWER_DEFAULT_WIDTH_PX;
149
- const ratioWidth = Math.round(window.innerWidth * DRAWER_DEFAULT_WIDTH_RATIO);
154
+ function drawerDefaultWidth(viewportWidth: number, withHistoryRail: boolean): number {
155
+ if (viewportWidth <= 0) return DRAWER_DEFAULT_WIDTH_PX;
156
+ const ratioWidth = Math.round(viewportWidth * DRAWER_DEFAULT_WIDTH_RATIO);
150
157
  if (!withHistoryRail) return ratioWidth;
151
158
  return Math.max(ratioWidth, SPLIT_MIN_WIDTH + SPLIT_DEFAULT_WIDTH_ALLOWANCE);
152
159
  }
160
+ const drawerDefaultWidthWithRail = (viewportWidth: number) => drawerDefaultWidth(viewportWidth, true);
161
+ const drawerDefaultWidthWithoutRail = (viewportWidth: number) => drawerDefaultWidth(viewportWidth, false);
153
162
 
154
163
  // =============================================================================
155
164
  // Types
@@ -322,9 +331,42 @@ export interface EmbeddableChatProps {
322
331
  * suppressed — those are Drawer-shell concerns. The consumer is
323
332
  * responsible for mount/unmount and for opening/closing via the
324
333
  * `open` / `onOpenChange` props (which the in-body close button still
325
- * drives).
334
+ * drives). The host must provide the Radix Dialog it sits in.
335
+ * - `'inline'`: like `'none'`, for a host that is NOT a dialog (a panel
336
+ * docked into the page layout): no Radix Dialog is expected, so the
337
+ * panel names itself through the host's region rather than a
338
+ * `Dialog.Title`.
326
339
  */
327
- shell?: 'drawer' | 'none';
340
+ shell?: 'drawer' | 'none' | 'inline';
341
+
342
+ /**
343
+ * Whether the panel offers a close button. `false` for a chat that is part
344
+ * of the layout rather than an overlay (docked beside the page): every
345
+ * header leaves its close control out. Default `true`.
346
+ */
347
+ closable?: boolean;
348
+
349
+ /**
350
+ * `'v2'`: the Mingo v2 layout (Figma openframe - mingo) — the chat list as a
351
+ * full-height `MingoChatRail` beside the chat, the `MingoChatHeader` over the
352
+ * chat only, Mingo's cyan-tinted surface. Default `'classic'`.
353
+ */
354
+ appearance?: ChatAppearance;
355
+
356
+ /**
357
+ * v2: the →| control, shown while the list and the chat both fit. The host
358
+ * steps its panel back one size (see `collapseTo`).
359
+ */
360
+ onCollapse?: () => void;
361
+ /**
362
+ * v2: what `onCollapse` leaves room for. `list` (default): the list alone, so
363
+ * an open chat is closed first. `column`: a narrower panel that still shows
364
+ * the open chat, which stays open.
365
+ */
366
+ collapseTo?: 'list' | 'column';
367
+
368
+ /** v2: the status glyph at the end of a chat's row (working / unread / approval). */
369
+ dialogStatusOf?: (dialog: DialogItem) => MingoDialogStatus | undefined;
328
370
 
329
371
  /**
330
372
  * Display name of the signed-in user, shown as the sub-line under the chat
@@ -527,22 +569,29 @@ function useRailPresence(open: boolean): { mounted: boolean; active: boolean } {
527
569
  function RailSlot({
528
570
  active,
529
571
  innerClassName,
572
+ className,
573
+ narrow = false,
530
574
  children,
531
575
  }: {
532
576
  active: boolean;
533
577
  innerClassName?: string;
578
+ className?: string;
579
+ /** The v2 rail's 296px instead of 320px. */
580
+ narrow?: boolean;
534
581
  children: React.ReactNode;
535
582
  }) {
583
+ const width = narrow ? 'w-[296px]' : 'w-80';
536
584
  return (
537
585
  <div
538
586
  className={cn(
539
587
  // Border on the OUTER so the rail/chat divider tracks the animating
540
588
  // boundary (and fades out with the panel) instead of vanishing early.
541
589
  'shrink-0 overflow-hidden border-r border-ods-border transition-[width,opacity] ease-out',
542
- active ? 'duration-[240ms] w-80 opacity-100' : 'duration-[300ms] w-0 opacity-0',
590
+ active ? cn('duration-[240ms] opacity-100', width) : 'duration-[300ms] w-0 opacity-0',
591
+ className,
543
592
  )}
544
593
  >
545
- <div className={cn('h-full w-80', innerClassName)}>{children}</div>
594
+ <div className={cn('h-full', width, innerClassName)}>{children}</div>
546
595
  </div>
547
596
  );
548
597
  }
@@ -971,6 +1020,11 @@ function EmbeddableChatInner({
971
1020
  aiAgentConfigUrl: aiAgentConfigUrlProp,
972
1021
  defaultActiveMode,
973
1022
  shell = 'drawer',
1023
+ closable = true,
1024
+ appearance = CHAT_APPEARANCE.CLASSIC,
1025
+ onCollapse,
1026
+ collapseTo = 'list',
1027
+ dialogStatusOf,
974
1028
  userDisplayName,
975
1029
  userAvatarUrl,
976
1030
  mingoWelcome,
@@ -983,11 +1037,12 @@ function EmbeddableChatInner({
983
1037
  contextMemory,
984
1038
  handleRef,
985
1039
  }: EmbeddableChatProps & { handleRef?: React.Ref<EmbeddableChatHandle> }) {
986
- // `shell === 'none'` means the consumer hosts us inside their own panel
1040
+ // `shell` 'none' / 'inline' means the consumer hosts us inside their own panel
987
1041
  // (e.g. AppLayoutDrawer in openframe-frontend). Several drawer-shell
988
1042
  // concerns are unconditional in this codebase — gate them off here so
989
1043
  // we don't double-up with the host's behaviour.
990
- const shellLess = shell === 'none';
1044
+ const shellLess = shell !== 'drawer';
1045
+ const isV2 = appearance === CHAT_APPEARANCE.V2;
991
1046
  const runtime = useRequiredChatRuntime();
992
1047
  // Optional on embedders (platform-agnostic); '' is a harmless sentinel for the
993
1048
  // `ask-ai:open-with-ref` event filter below. Deliberately NOT the hub's
@@ -1524,6 +1579,8 @@ function EmbeddableChatInner({
1524
1579
  const resolvedBaseRoute = baseRoute || (source === 'flamingo' ? '/knowledge-base' : '/data-room');
1525
1580
 
1526
1581
  const handleClose = useCallback(() => setIsOpen(false), [setIsOpen]);
1582
+ // What the header close controls call; absent on a panel that cannot close.
1583
+ const closeControl = closable ? handleClose : undefined;
1527
1584
 
1528
1585
  const handleNavigationClose = useCallback(() => {
1529
1586
  navigatingAwayRef.current = true;
@@ -1940,16 +1997,17 @@ function EmbeddableChatInner({
1940
1997
  // Only the split threshold is state, not the width: the observer fires on
1941
1998
  // every pixel of a drawer drag, and a width in state re-rendered this whole
1942
1999
  // component each frame for a value that changes at one width.
2000
+ const splitMinWidth = (isV2 ? MINGO_V2_RAIL_WIDTH : HISTORY_RAIL_WIDTH) + CHAT_BLOCK_MIN_WIDTH;
1943
2001
  const [canSplit, setCanSplit] = useState(false);
1944
2002
  useEffect(() => {
1945
2003
  if (!panelBoundary || typeof ResizeObserver === 'undefined') return undefined;
1946
2004
  const ro = new ResizeObserver(entries => {
1947
2005
  const w = entries[0]?.contentRect?.width;
1948
- if (typeof w === 'number') setCanSplit(w >= SPLIT_MIN_WIDTH);
2006
+ if (typeof w === 'number') setCanSplit(w >= splitMinWidth);
1949
2007
  });
1950
2008
  ro.observe(panelBoundary);
1951
2009
  return () => ro.disconnect();
1952
- }, [panelBoundary]);
2010
+ }, [panelBoundary, splitMinWidth]);
1953
2011
 
1954
2012
  // ONE node for two consumers: the width measurement above and the
1955
2013
  // collision-boundary context. State, not a ref, so both re-run when the node
@@ -1961,10 +2019,13 @@ function EmbeddableChatInner({
1961
2019
  // width 0 before flipping. Seeding in the effect instead is what the
1962
2020
  // pre-refactor code did, but `react-hooks/set-state-in-effect` rejects it in
1963
2021
  // a reactive (non-mount-only) effect.
1964
- const setPanelNode = useCallback((node: HTMLDivElement | null) => {
1965
- setPanelBoundary(node);
1966
- if (node) setCanSplit(node.clientWidth >= SPLIT_MIN_WIDTH);
1967
- }, []);
2022
+ const setPanelNode = useCallback(
2023
+ (node: HTMLDivElement | null) => {
2024
+ setPanelBoundary(node);
2025
+ if (node) setCanSplit(node.clientWidth >= splitMinWidth);
2026
+ },
2027
+ [splitMinWidth],
2028
+ );
1968
2029
 
1969
2030
  // Rail collapse toggle (Figma ⟶| control), persisted so it survives the
1970
2031
  // drawer remounting on close.
@@ -2203,12 +2264,12 @@ function EmbeddableChatInner({
2203
2264
  avatar: headerAvatar,
2204
2265
  backAriaLabel: 'Back to chats',
2205
2266
  onBack: () => setComposeOpen(false),
2206
- onClose: handleClose,
2267
+ onClose: closeControl,
2207
2268
  }
2208
2269
  : {
2209
2270
  showBack: false,
2210
2271
  title: 'Current Chats',
2211
- onClose: handleClose,
2272
+ onClose: closeControl,
2212
2273
  onOpenArchive: headerOnOpenArchive,
2213
2274
  onToggleSearch: mingoCaps.onSearchChange ? () => setRailSearchOpen(o => !o) : undefined,
2214
2275
  searchActive: railSearchOpen,
@@ -2223,7 +2284,7 @@ function EmbeddableChatInner({
2223
2284
  backAriaLabel: headerBackAriaLabel,
2224
2285
  isArchivedView: isViewingArchived,
2225
2286
  onBack: headerOnBack,
2226
- onClose: handleClose,
2287
+ onClose: closeControl,
2227
2288
  onRestore: headerOnRestore,
2228
2289
  onRename: headerOnRename,
2229
2290
  onArchive: headerOnArchive,
@@ -2232,15 +2293,87 @@ function EmbeddableChatInner({
2232
2293
  onOpenArchive: headerOnOpenArchive,
2233
2294
  };
2234
2295
 
2296
+ // v2 list + header (see `appearance`). The list stays on screen while the
2297
+ // archive opens beside it, so picking a chat from it leaves the archive.
2298
+ const leaveArchiveThen =
2299
+ (action: () => void): (() => void) =>
2300
+ () => {
2301
+ if (archiveOpen) closeArchive();
2302
+ action();
2303
+ };
2304
+ const mingoRailProps = {
2305
+ dialogs,
2306
+ activeDialogId: activeDialogId ?? undefined,
2307
+ // Beside a new chat (wide only: narrow, the list is its own screen).
2308
+ draftTitle: wideMingo && !hasConversation && !archiveOpen ? freshConversationTitle : undefined,
2309
+ onSelectDialog: (id: string) => leaveArchiveThen(() => handleSelectDialog(id))(),
2310
+ onNewChat: leaveArchiveThen(handleNewChat),
2311
+ onOpenArchive: headerOnOpenArchive && (archiveOpen ? closeArchive : headerOnOpenArchive),
2312
+ archiveActive: archiveOpen,
2313
+ // The suggestions a new chat opens on.
2314
+ onWhatToAsk: leaveArchiveThen(handleNewChat),
2315
+ statusOf: dialogStatusOf,
2316
+ onRequestRename: mingoCaps.canRename ? setRenameTarget : undefined,
2317
+ onRequestArchive: mingoCaps.canArchive ? setArchiveTarget : undefined,
2318
+ onRequestCopyLink: mingoCaps.onCopyLink,
2319
+ onRequestCompact: mingoCaps.compactDialog,
2320
+ scope: dialogScope,
2321
+ onScopeChange: setDialogScope,
2322
+ searchQuery: mingoCaps.searchQuery,
2323
+ onSearchChange: mingoCaps.onSearchChange,
2324
+ hasMore: hasMoreDialogs,
2325
+ isLoadingMore: isDialogsLoading && dialogs.length > 0,
2326
+ onLoadMore: () => {
2327
+ void loadMoreDialogs();
2328
+ },
2329
+ isLoadingHistory: dialogsInitialLoading,
2330
+ loadError: dialogsLoadError,
2331
+ onRetry: reloadDialogs,
2332
+ };
2333
+ // Over the chat column only; none on the narrow list (it is its own screen)
2334
+ // or over the archive (it brings its own back bar).
2335
+ const v2Header =
2336
+ isV2 && !stackedListView ? (
2337
+ <div className="col-start-2 row-start-1 min-w-0">
2338
+ <MingoChatHeader
2339
+ title={headerShowBack || isGuideEmpty ? headerTitle : freshConversationTitle}
2340
+ subtitle={headerPersonName}
2341
+ avatar={headerAvatar}
2342
+ // Wide: shows / hides the list beside the chat. Narrow: back to the
2343
+ // list, the only one of the two that fits.
2344
+ onToggleList={wideMingo ? toggleRailCollapsed : headerShowBack ? headerOnBack : () => setComposeOpen(false)}
2345
+ listOpen={splitActive}
2346
+ menuItems={headerShowBack && !isViewingArchived ? splitHeaderMenuItems : []}
2347
+ onRestore={isViewingArchived ? headerOnRestore : undefined}
2348
+ // Only while the list and the chat both fit: narrow, the toggle is
2349
+ // already the way back to the list.
2350
+ onCollapse={
2351
+ wideMingo && onCollapse
2352
+ ? () => {
2353
+ // The minimum is the list alone: a conversation left open
2354
+ // would take the narrow panel instead.
2355
+ if (collapseTo === 'list' && hasConversation) headerOnBack();
2356
+ onCollapse();
2357
+ }
2358
+ : undefined
2359
+ }
2360
+ onClose={closeControl}
2361
+ />
2362
+ </div>
2363
+ ) : null;
2364
+
2235
2365
  // Chat body — defined once, then rendered inside whichever shell applies.
2236
2366
  // Radix overlays (⋯ menus, tooltips) portal into `portalHost` — a node inside
2237
2367
  // this panel — so they inherit the drawer's stacking context and need only a
2238
2368
  // small, local z-index instead of escalating to beat the drawer at the
2239
2369
  // document root. See `PortalContainerContext`.
2240
2370
  const body = (
2241
- <PortalContainerContext.Provider value={portalHost}>
2242
- <CollisionBoundaryContext.Provider value={panelBoundary}>
2243
- {/* Panel surface depends on state (Figma):
2371
+ // The whole panel reads the appearance (the composer's footer too), not
2372
+ // only the thread `ChatMessageList` provides it to.
2373
+ <ChatAppearanceContext.Provider value={appearance}>
2374
+ <PortalContainerContext.Provider value={portalHost}>
2375
+ <CollisionBoundaryContext.Provider value={panelBoundary}>
2376
+ {/* Panel surface depends on state (Figma):
2244
2377
  • Narrow "Current Chats" LIST + FULL-PANEL archive page → grey
2245
2378
  `ods-card` (#212121) — matching the grey rail those lists live
2246
2379
  in when the panel is wide,
@@ -2253,209 +2386,286 @@ function EmbeddableChatInner({
2253
2386
  — in the split layout it lives in the rail (which owns its own
2254
2387
  grey), so the chat block on the right must keep the conversation's
2255
2388
  dark surface instead of following the archive. */}
2256
- <div
2257
- ref={setPanelNode}
2258
- className={`flex h-full flex-col overflow-hidden transition-colors duration-200 ${
2259
- (archiveOpen && !splitActive) || stackedListView ? 'bg-ods-card' : 'bg-ods-bg'
2260
- } ${previewMode ? 'pointer-events-none select-none' : ''}`}
2261
- >
2262
- {/* Archive-page ↔ chat-panel swap fades in (200ms) to match the
2389
+ <div
2390
+ ref={setPanelNode}
2391
+ // v2: one surface for the list and the chat, Mingo's cyan-tinted bg.
2392
+ data-surface={isV2 ? 'mingo' : undefined}
2393
+ className={`flex h-full flex-col overflow-hidden transition-colors duration-200 ${
2394
+ !isV2 && ((archiveOpen && !splitActive) || stackedListView) ? 'bg-ods-card' : 'bg-ods-bg'
2395
+ } ${previewMode ? 'pointer-events-none select-none' : ''}`}
2396
+ >
2397
+ {/* Archive-page ↔ chat-panel swap fades in (200ms) to match the
2263
2398
  surface flip. The full-panel archive is used only when NOT in
2264
2399
  the wide split layout — there the archive opens inside the left
2265
2400
  rail instead (see the rail column below). */}
2266
- {archiveOpen && !splitActive ? (
2267
- <div key="archive-view" className="flex min-h-0 flex-1 flex-col duration-200 animate-in fade-in-0">
2268
- <ChatArchivePage
2269
- dialogs={archivedDialogs}
2270
- onSelectDialog={handleArchivedSelect}
2271
- onBack={closeArchive}
2272
- onClose={handleClose}
2273
- isLoading={archivedLoading}
2274
- isFetching={archivedPending}
2275
- hasMore={archivedCursor != null}
2276
- onLoadMore={() => {
2277
- void loadArchivedPage(archivedCursor ?? undefined);
2278
- }}
2279
- />
2280
- </div>
2281
- ) : (
2282
- <div key="chat-view" className="flex min-h-0 flex-1 flex-col duration-200 animate-in fade-in-0">
2283
- {showWideHeader ? (
2284
- // Wide Mingo header (Figma 113:60931 / 113:63630). Split: a
2285
- // two-cell bar — the "Current Chats" rail header (search /
2286
- // archive / collapse) over the 320px rail + the chat-block header
2287
- // (title / ⋯ / close) over the fill column. Collapsed
2288
- // (`wideCollapsed`): the rail cell is gone, leaving the corner
2289
- // expand toggle + the chat-block header (the fill column shows
2290
- // the chat / new-chat welcome). No back chevron — chat-list
2291
- // navigation is the rail toggle. Shown whenever the panel is wide
2292
- // enough to split (width ≥ `SPLIT_MIN_WIDTH`), NOT gated on
2293
- // the viewport `md` breakpoint — otherwise a 720–799px panel on a
2294
- // sub-`md` viewport would split the body yet drop to the mobile
2295
- // single-bar header (no rail search / collapse controls).
2296
- <div className="flex h-14 w-full flex-shrink-0 overflow-hidden border-b border-ods-border bg-ods-card">
2297
- {railPresence.mounted && (
2298
- <RailSlot active={railPresence.active} innerClassName="flex overflow-hidden">
2299
- {archiveOpen ? (
2300
- // Archive open in the wide layout — the rail header
2301
- // turns into a "Chat Archive" back bar (the list itself
2302
- // renders in the rail below); the chat block is untouched.
2303
- // Back is a full-height leading cell, matching the other
2304
- // header cells (not a small inline chevron).
2305
- <>
2306
- <ChatHeaderIconButton divider="right" onClick={closeArchive} aria-label="Back to chats">
2307
- <Chevron02LeftIcon size={24} />
2308
- </ChatHeaderIconButton>
2309
- <div className="flex min-w-0 flex-1 items-center px-[var(--spacing-system-mf)]">
2310
- <span className="min-w-0 flex-1 truncate text-ods-text-primary text-h3">Chat Archive</span>
2311
- </div>
2312
- </>
2313
- ) : (
2314
- // "Current Chats" rail header — title fills, then the
2315
- // search + archive controls. When search is open the
2316
- // inline field takes over the title area in place
2317
- // (Figma 116:51217) and the magnifier toggle is hidden.
2318
- <>
2319
- {railSearchOpen && mingoCaps.onSearchChange ? (
2320
- <ChatHeaderSearchField
2321
- initialValue={mingoCaps.searchQuery}
2322
- onSearchChange={mingoCaps.onSearchChange}
2323
- onCollapse={() => setRailSearchOpen(false)}
2324
- />
2325
- ) : (
2326
- <>
2327
- <div className="flex min-w-0 flex-1 items-center px-[var(--spacing-system-mf)]">
2328
- <span className="min-w-0 flex-1 truncate text-ods-text-primary text-h3">
2329
- Current Chats
2330
- </span>
2331
- </div>
2332
- {mingoCaps.onSearchChange && (
2333
- <ChatHeaderIconButton
2334
- onClick={() => setRailSearchOpen(o => !o)}
2335
- aria-label="Search chats"
2336
- aria-pressed={railSearchOpen}
2337
- >
2338
- <SearchIcon size={24} />
2339
- </ChatHeaderIconButton>
2340
- )}
2341
- </>
2342
- )}
2343
- {headerOnOpenArchive && (
2344
- <ChatHeaderIconButton onClick={headerOnOpenArchive} aria-label="Chat archive">
2345
- <ClockHistoryIcon size={24} />
2401
+ {archiveOpen && !splitActive ? (
2402
+ <div key="archive-view" className="flex min-h-0 flex-1 flex-col duration-200 animate-in fade-in-0">
2403
+ <ChatArchivePage
2404
+ dialogs={archivedDialogs}
2405
+ onSelectDialog={handleArchivedSelect}
2406
+ onBack={closeArchive}
2407
+ onClose={closeControl}
2408
+ isLoading={archivedLoading}
2409
+ isFetching={archivedPending}
2410
+ hasMore={archivedCursor != null}
2411
+ onLoadMore={() => {
2412
+ void loadArchivedPage(archivedCursor ?? undefined);
2413
+ }}
2414
+ />
2415
+ </div>
2416
+ ) : (
2417
+ <div
2418
+ key="chat-view"
2419
+ className={cn(
2420
+ 'min-h-0 flex-1 duration-200 animate-in fade-in-0',
2421
+ // v2: the list runs the full height beside a column of header,
2422
+ // banner and chat; each child below takes its cell explicitly.
2423
+ isV2 ? 'grid grid-cols-[auto_minmax(0,1fr)] grid-rows-[auto_auto_minmax(0,1fr)]' : 'flex flex-col',
2424
+ )}
2425
+ >
2426
+ {isV2 && railPresence.mounted && (
2427
+ <RailSlot
2428
+ active={railPresence.active}
2429
+ narrow
2430
+ className="col-start-1 row-span-3 row-start-1"
2431
+ innerClassName="flex min-h-0 flex-col"
2432
+ >
2433
+ <MingoChatRail {...mingoRailProps} className="w-full border-r-0" />
2434
+ </RailSlot>
2435
+ )}
2436
+ {isV2 ? (
2437
+ v2Header
2438
+ ) : showWideHeader ? (
2439
+ // Wide Mingo header (Figma 113:60931 / 113:63630). Split: a
2440
+ // two-cell bar — the "Current Chats" rail header (search /
2441
+ // archive / collapse) over the 320px rail + the chat-block header
2442
+ // (title / ⋯ / close) over the fill column. Collapsed
2443
+ // (`wideCollapsed`): the rail cell is gone, leaving the corner
2444
+ // expand toggle + the chat-block header (the fill column shows
2445
+ // the chat / new-chat welcome). No back chevron — chat-list
2446
+ // navigation is the rail toggle. Shown whenever the panel is wide
2447
+ // enough to split (width ≥ `SPLIT_MIN_WIDTH`), NOT gated on
2448
+ // the viewport `md` breakpoint — otherwise a 720–799px panel on a
2449
+ // sub-`md` viewport would split the body yet drop to the mobile
2450
+ // single-bar header (no rail search / collapse controls).
2451
+ <div className="flex h-14 w-full flex-shrink-0 overflow-hidden border-b border-ods-border bg-ods-card">
2452
+ {railPresence.mounted && (
2453
+ <RailSlot active={railPresence.active} innerClassName="flex overflow-hidden">
2454
+ {archiveOpen ? (
2455
+ // Archive open in the wide layout — the rail header
2456
+ // turns into a "Chat Archive" back bar (the list itself
2457
+ // renders in the rail below); the chat block is untouched.
2458
+ // Back is a full-height leading cell, matching the other
2459
+ // header cells (not a small inline chevron).
2460
+ <>
2461
+ <ChatHeaderIconButton divider="right" onClick={closeArchive} aria-label="Back to chats">
2462
+ <Chevron02LeftIcon size={24} />
2346
2463
  </ChatHeaderIconButton>
2347
- )}
2348
- </>
2349
- )}
2350
- </RailSlot>
2351
- )}
2464
+ <div className="flex min-w-0 flex-1 items-center px-[var(--spacing-system-mf)]">
2465
+ <span className="min-w-0 flex-1 truncate text-ods-text-primary text-h3">
2466
+ Chat Archive
2467
+ </span>
2468
+ </div>
2469
+ </>
2470
+ ) : (
2471
+ // "Current Chats" rail header — title fills, then the
2472
+ // search + archive controls. When search is open the
2473
+ // inline field takes over the title area in place
2474
+ // (Figma 116:51217) and the magnifier toggle is hidden.
2475
+ <>
2476
+ {railSearchOpen && mingoCaps.onSearchChange ? (
2477
+ <ChatHeaderSearchField
2478
+ initialValue={mingoCaps.searchQuery}
2479
+ onSearchChange={mingoCaps.onSearchChange}
2480
+ onCollapse={() => setRailSearchOpen(false)}
2481
+ />
2482
+ ) : (
2483
+ <>
2484
+ <div className="flex min-w-0 flex-1 items-center px-[var(--spacing-system-mf)]">
2485
+ <span className="min-w-0 flex-1 truncate text-ods-text-primary text-h3">
2486
+ Current Chats
2487
+ </span>
2488
+ </div>
2489
+ {mingoCaps.onSearchChange && (
2490
+ <ChatHeaderIconButton
2491
+ onClick={() => setRailSearchOpen(o => !o)}
2492
+ aria-label="Search chats"
2493
+ aria-pressed={railSearchOpen}
2494
+ >
2495
+ <SearchIcon size={24} />
2496
+ </ChatHeaderIconButton>
2497
+ )}
2498
+ </>
2499
+ )}
2500
+ {headerOnOpenArchive && (
2501
+ <ChatHeaderIconButton onClick={headerOnOpenArchive} aria-label="Chat archive">
2502
+ <ClockHistoryIcon size={24} />
2503
+ </ChatHeaderIconButton>
2504
+ )}
2505
+ </>
2506
+ )}
2507
+ </RailSlot>
2508
+ )}
2352
2509
 
2353
- <div className="flex min-w-0 flex-1">
2354
- {/* Rail collapse / expand toggle — lives on the chat side,
2510
+ <div className="flex min-w-0 flex-1">
2511
+ {/* Rail collapse / expand toggle — lives on the chat side,
2355
2512
  at its left edge (Figma 259:90465). Right arrow collapses
2356
2513
  the list; left arrow brings it back. */}
2357
- <button
2358
- type="button"
2359
- onClick={toggleRailCollapsed}
2360
- aria-label={splitActive ? 'Collapse chat list' : 'Show chat list'}
2361
- aria-pressed={!splitActive}
2362
- className="flex size-14 shrink-0 items-center justify-center border-r border-ods-border text-ods-text-secondary transition-colors hover:bg-ods-bg-hover hover:text-ods-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
2363
- >
2364
- {splitActive ? <Arrow02RightIcon size={24} /> : <Arrow02LeftIcon size={24} />}
2365
- </button>
2366
- {/* No back cell in the wide layout — the left rail (and
2514
+ <button
2515
+ type="button"
2516
+ onClick={toggleRailCollapsed}
2517
+ aria-label={splitActive ? 'Collapse chat list' : 'Show chat list'}
2518
+ aria-pressed={!splitActive}
2519
+ className="flex size-14 shrink-0 items-center justify-center border-r border-ods-border text-ods-text-secondary transition-colors hover:bg-ods-bg-hover hover:text-ods-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
2520
+ >
2521
+ {splitActive ? <Arrow02RightIcon size={24} /> : <Arrow02LeftIcon size={24} />}
2522
+ </button>
2523
+ {/* No back cell in the wide layout — the left rail (and
2367
2524
  its collapse/expand toggle) already provides chat-list
2368
2525
  navigation, so a back chevron would be redundant. */}
2369
- <div className="flex min-w-0 flex-1 items-center gap-[var(--spacing-system-m)] px-[var(--spacing-system-mf)] py-[var(--spacing-system-sf)]">
2370
- <div className="flex min-w-0 flex-col">
2371
- <p className="truncate leading-tight text-ods-text-primary text-h3">
2372
- {headerShowBack || isGuideEmpty ? headerTitle : freshConversationTitle}
2373
- </p>
2374
- {headerPersonName && (
2375
- <p className="truncate leading-tight text-ods-text-secondary text-h6">{headerPersonName}</p>
2376
- )}
2526
+ <div className="flex min-w-0 flex-1 items-center gap-[var(--spacing-system-m)] px-[var(--spacing-system-mf)] py-[var(--spacing-system-sf)]">
2527
+ <div className="flex min-w-0 flex-col">
2528
+ <p className="truncate leading-tight text-ods-text-primary text-h3">
2529
+ {headerShowBack || isGuideEmpty ? headerTitle : freshConversationTitle}
2530
+ </p>
2531
+ {headerPersonName && (
2532
+ <p className="truncate leading-tight text-ods-text-secondary text-h6">{headerPersonName}</p>
2533
+ )}
2534
+ </div>
2535
+ {headerAvatar ? (
2536
+ // Owner avatar at the title cell's right edge (Figma 113:63273).
2537
+ <SquareAvatar
2538
+ variant="round"
2539
+ sizePx={32}
2540
+ className="ml-auto"
2541
+ src={headerAvatar.avatarUrl || undefined}
2542
+ alt={headerAvatar.name || undefined}
2543
+ fallback={headerAvatar.name || undefined}
2544
+ initialsClassName="text-[11px] text-ods-text-secondary"
2545
+ title={headerAvatar.name || undefined}
2546
+ />
2547
+ ) : null}
2377
2548
  </div>
2378
- {headerAvatar ? (
2379
- // Owner avatar at the title cell's right edge (Figma 113:63273).
2380
- <SquareAvatar
2381
- variant="round"
2382
- sizePx={32}
2383
- className="ml-auto"
2384
- src={headerAvatar.avatarUrl || undefined}
2385
- alt={headerAvatar.name || undefined}
2386
- fallback={headerAvatar.name || undefined}
2387
- initialsClassName="text-[11px] text-ods-text-secondary"
2388
- title={headerAvatar.name || undefined}
2549
+
2550
+ {isViewingArchived && headerOnRestore && (
2551
+ <ChatHeaderIconButton onClick={headerOnRestore} aria-label="Unarchive chat">
2552
+ <Refresh01LeftIcon size={24} />
2553
+ </ChatHeaderIconButton>
2554
+ )}
2555
+ {headerShowBack && !isViewingArchived && splitHeaderMenuItems.length > 0 && (
2556
+ <ActionsMenuDropdown
2557
+ triggerAriaLabel="Chat actions"
2558
+ onCloseAutoFocus={e => e.preventDefault()}
2559
+ groups={[{ items: splitHeaderMenuItems }]}
2560
+ customTrigger={
2561
+ <ChatHeaderIconButton aria-label="Chat actions">
2562
+ <Ellipsis01Icon size={24} />
2563
+ </ChatHeaderIconButton>
2564
+ }
2389
2565
  />
2390
- ) : null}
2566
+ )}
2567
+ {closeControl && (
2568
+ <ChatHeaderIconButton onClick={closeControl} aria-label="Close">
2569
+ <XmarkIcon size={24} />
2570
+ </ChatHeaderIconButton>
2571
+ )}
2391
2572
  </div>
2392
-
2393
- {isViewingArchived && headerOnRestore && (
2394
- <ChatHeaderIconButton onClick={headerOnRestore} aria-label="Unarchive chat">
2395
- <Refresh01LeftIcon size={24} />
2396
- </ChatHeaderIconButton>
2397
- )}
2398
- {headerShowBack && !isViewingArchived && splitHeaderMenuItems.length > 0 && (
2399
- <ActionsMenuDropdown
2400
- triggerAriaLabel="Chat actions"
2401
- onCloseAutoFocus={e => e.preventDefault()}
2402
- groups={[{ items: splitHeaderMenuItems }]}
2403
- customTrigger={
2404
- <ChatHeaderIconButton aria-label="Chat actions">
2405
- <Ellipsis01Icon size={24} />
2406
- </ChatHeaderIconButton>
2407
- }
2408
- />
2409
- )}
2410
- <ChatHeaderIconButton onClick={handleClose} aria-label="Close">
2411
- <XmarkIcon size={24} />
2412
- </ChatHeaderIconButton>
2413
2573
  </div>
2414
- </div>
2415
- ) : (
2416
- // Narrow single-column header — list / compose / conversation /
2417
- // guide, resolved into `narrowHeaderProps` above. (Wide layouts,
2418
- // including rail-collapsed, use the wide header above.) Embedded
2419
- // previews (hero demo tabs) force the compact bar via `compact`.
2420
- <ChatPanelHeader {...narrowHeaderProps} compact={previewMode} />
2421
- )}
2422
-
2423
- {/* Guide-mode indicator banner (Figma node 7532:328222) —
2574
+ ) : (
2575
+ // Narrow single-column header — list / compose / conversation /
2576
+ // guide, resolved into `narrowHeaderProps` above. (Wide layouts,
2577
+ // including rail-collapsed, use the wide header above.) Embedded
2578
+ // previews (hero demo tabs) force the compact bar via `compact`.
2579
+ <ChatPanelHeader {...narrowHeaderProps} compact={previewMode} />
2580
+ )}
2581
+
2582
+ {/* Guide-mode indicator banner (Figma node 7532:328222) —
2424
2583
  full-bleed accent strip below the header. Shown only when
2425
2584
  Guide mode is active AND the default (Mingo) mode also exists:
2426
2585
  the banner contrasts the temporary Guide session against the
2427
2586
  default chat, so it's meaningless in a guide-only setup. */}
2428
- {activeMode === 'guide' && hasMingoMode && (
2429
- <GuideModeBanner className="duration-200 animate-in fade-in-0" />
2430
- )}
2587
+ {activeMode === 'guide' && hasMingoMode && (
2588
+ <GuideModeBanner
2589
+ className={cn('duration-200 animate-in fade-in-0', isV2 && 'col-start-2 row-start-2')}
2590
+ />
2591
+ )}
2431
2592
 
2432
- {/* Chat-panel row. In the wide Mingo layout (`splitActive`) the
2593
+ {/* Chat-panel row. In the wide Mingo layout (`splitActive`) the
2433
2594
  dialog history is hoisted into a fixed 320px "Current Chats"
2434
2595
  rail on the left; the stacked layout keeps it inline in the
2435
2596
  Mingo empty state (`<MingoChatHistory>` via `<MingoWelcome>`). */}
2436
- <div className="flex min-h-0 flex-1 overflow-hidden">
2437
- {railPresence.mounted && (
2438
- <RailSlot active={railPresence.active} innerClassName="flex flex-col min-h-0 bg-ods-card">
2439
- {archiveOpen ? (
2440
- // Archive list inside the rail (header lives in the split
2441
- // header's left cell above). Right chat block stays put.
2442
- <ChatArchivePage
2443
- embedded
2597
+ <div className={cn('flex min-h-0 flex-1 overflow-hidden', isV2 && 'col-start-2 row-start-3 min-w-0')}>
2598
+ {!isV2 && railPresence.mounted && (
2599
+ <RailSlot active={railPresence.active} innerClassName="flex flex-col min-h-0 bg-ods-card">
2600
+ {archiveOpen ? (
2601
+ // Archive list inside the rail (header lives in the split
2602
+ // header's left cell above). Right chat block stays put.
2603
+ <ChatArchivePage
2604
+ embedded
2605
+ dialogs={archivedDialogs}
2606
+ onSelectDialog={handleArchivedSelect}
2607
+ isLoading={archivedLoading}
2608
+ isFetching={archivedPending}
2609
+ hasMore={archivedCursor != null}
2610
+ onLoadMore={() => {
2611
+ void loadArchivedPage(archivedCursor ?? undefined);
2612
+ }}
2613
+ />
2614
+ ) : (
2615
+ <MingoHistoryRail
2616
+ dialogs={dialogs}
2617
+ activeDialogId={activeDialogId ?? undefined}
2618
+ onSelectDialog={handleSelectDialog}
2619
+ onNewChat={handleNewChat}
2620
+ onRequestRename={mingoCaps.canRename ? setRenameTarget : undefined}
2621
+ onRequestArchive={mingoCaps.canArchive ? setArchiveTarget : undefined}
2622
+ onRequestCopyLink={mingoCaps.onCopyLink}
2623
+ onRequestCompact={mingoCaps.compactDialog}
2624
+ scope={dialogScope}
2625
+ onScopeChange={setDialogScope}
2626
+ searchQuery={mingoCaps.searchQuery}
2627
+ hasMore={hasMoreDialogs}
2628
+ isLoadingMore={isDialogsLoading && dialogs.length > 0}
2629
+ onLoadMore={() => {
2630
+ void loadMoreDialogs();
2631
+ }}
2632
+ isLoadingHistory={dialogsInitialLoading}
2633
+ loadError={dialogsLoadError}
2634
+ onRetry={reloadDialogs}
2635
+ />
2636
+ )}
2637
+ </RailSlot>
2638
+ )}
2639
+ <div className={cn('flex min-h-0 min-w-0 flex-1 flex-col', isV2 && 'ods-glow-accent-corner')}>
2640
+ {isV2 && archiveOpen ? (
2641
+ // v2: the archive opens in the chat column under the chat's
2642
+ // header; the list stays, and "Chat Archive" there toggles back.
2643
+ <MingoArchiveList
2444
2644
  dialogs={archivedDialogs}
2445
2645
  onSelectDialog={handleArchivedSelect}
2446
2646
  isLoading={archivedLoading}
2447
- isFetching={archivedPending}
2448
2647
  hasMore={archivedCursor != null}
2648
+ isLoadingMore={archivedPending}
2449
2649
  onLoadMore={() => {
2450
2650
  void loadArchivedPage(archivedCursor ?? undefined);
2451
2651
  }}
2452
2652
  />
2453
- ) : (
2653
+ ) : stackedListView && isV2 ? (
2654
+ <MingoChatRail
2655
+ {...mingoRailProps}
2656
+ onNewChat={() => setComposeOpen(true)}
2657
+ className="w-full border-r-0 duration-200 animate-in fade-in-0"
2658
+ />
2659
+ ) : stackedListView ? (
2660
+ // Narrow "Current Chats" list (Figma 341:36190) — the default
2661
+ // single-column view. No composer here; "Start New Chat" opens
2662
+ // the compose view.
2454
2663
  <MingoHistoryRail
2664
+ className="duration-200 animate-in fade-in-0"
2455
2665
  dialogs={dialogs}
2456
2666
  activeDialogId={activeDialogId ?? undefined}
2457
2667
  onSelectDialog={handleSelectDialog}
2458
- onNewChat={handleNewChat}
2668
+ onNewChat={() => setComposeOpen(true)}
2459
2669
  onRequestRename={mingoCaps.canRename ? setRenameTarget : undefined}
2460
2670
  onRequestArchive={mingoCaps.canArchive ? setArchiveTarget : undefined}
2461
2671
  onRequestCopyLink={mingoCaps.onCopyLink}
@@ -2472,317 +2682,297 @@ function EmbeddableChatInner({
2472
2682
  loadError={dialogsLoadError}
2473
2683
  onRetry={reloadDialogs}
2474
2684
  />
2475
- )}
2476
- </RailSlot>
2477
- )}
2478
- <div className="flex min-h-0 min-w-0 flex-1 flex-col">
2479
- {stackedListView ? (
2480
- // Narrow "Current Chats" list (Figma 341:36190) — the default
2481
- // single-column view. No composer here; "Start New Chat" opens
2482
- // the compose view.
2483
- <MingoHistoryRail
2484
- className="duration-200 animate-in fade-in-0"
2485
- dialogs={dialogs}
2486
- activeDialogId={activeDialogId ?? undefined}
2487
- onSelectDialog={handleSelectDialog}
2488
- onNewChat={() => setComposeOpen(true)}
2489
- onRequestRename={mingoCaps.canRename ? setRenameTarget : undefined}
2490
- onRequestArchive={mingoCaps.canArchive ? setArchiveTarget : undefined}
2491
- onRequestCopyLink={mingoCaps.onCopyLink}
2492
- onRequestCompact={mingoCaps.compactDialog}
2493
- scope={dialogScope}
2494
- onScopeChange={setDialogScope}
2495
- searchQuery={mingoCaps.searchQuery}
2496
- hasMore={hasMoreDialogs}
2497
- isLoadingMore={isDialogsLoading && dialogs.length > 0}
2498
- onLoadMore={() => {
2499
- void loadMoreDialogs();
2500
- }}
2501
- isLoadingHistory={dialogsInitialLoading}
2502
- loadError={dialogsLoadError}
2503
- onRetry={reloadDialogs}
2504
- />
2505
- ) : (
2506
- <>
2507
- <div ref={galleryPanelRef} className="flex min-h-0 flex-1 flex-col p-[var(--spacing-system-m)]">
2508
- <div
2509
- key={contentViewKey}
2510
- className="flex min-h-0 flex-1 flex-col duration-200 animate-in fade-in-0"
2511
- >
2512
- {hasConversation ? (
2513
- <div className="flex min-h-0 flex-1 flex-col">
2514
- {messagesInitialLoading ? (
2515
- // Match the real list's `fullWidth` layout so swapping
2516
- // skeleton → bubbles doesn't shift the column. The panel
2517
- // wrapper above already pads (`p-[var(--spacing-system-m)]`),
2518
- // so the skeleton sits flush (no inner px/pb).
2519
- //
2520
- // In `previewMode` the chat lives in a fixed-height host box
2521
- // (e.g. the Mingo hero demo). The default bottom-anchored
2522
- // skeleton overflows a short panel and grows its OWN
2523
- // scrollbar — ugly. `fill` top-anchors + clips overflow so
2524
- // it reads as "the whole panel is loading" and never scrolls.
2525
- <ChatMessageListSkeleton fullWidth fill={previewMode} className="flex-1" />
2526
- ) : (
2527
- <ChatMessageList
2528
- messages={messages}
2529
- isTyping={chatLoading}
2530
- // The stage the turn reports while the user waits
2531
- // ("Searching 28 sources"); the generic phrase otherwise.
2532
- typingMessage={streamingProgress ? chatProgressLabel(streamingProgress) : undefined}
2533
- // Sticky footer for approvals the host lifted out of the
2534
- // thread — see the prop's docblock.
2535
- pendingApprovals={pendingApprovals}
2536
- // Real drawer: the library's smart follow. Passive in-page
2537
- // demo (previewMode): deterministic hard pin instead — a
2538
- // scripted assistant-only stream from a cold mount never
2539
- // satisfies the library's "at bottom" gate, so the reply
2540
- // landed below the fold. `pinBottom` snaps to bottom on
2541
- // every frame; identical mechanism to the Fae demo box, so
2542
- // both surfaces behave 1:1.
2543
- autoScroll={!previewMode}
2544
- pinBottom={previewMode}
2545
- // Passive in-page demos (previewMode) let the surrounding
2546
- // page scroll over the thread; the real drawer keeps
2547
- // containment (deck slide-scroll fix).
2548
- overscrollContain={!previewMode}
2549
- assistantType="mingo"
2550
- assistantIcon={mingoAssistantIcon}
2551
- renderEntityCard={renderEntityCard}
2552
- resolveContextIcon={resolveContextIcon}
2553
- renderContextItem={renderContextItem}
2554
- renderMention={renderMention}
2555
- renderAfterMessage={renderMessageSources}
2556
- // Gated on `chatLoading` for the same reason the composer
2557
- // is: no second send while a turn is in flight. Passive
2558
- // demo hosts (previewMode) stay read-only.
2559
- onAskSelect={chatLoading || previewMode ? undefined : handleAskSelect}
2560
- NavLinkAnchor={NavLinkAnchorViaRuntime}
2561
- // Real Mingo drawer: hide the message-list scrollbar
2562
- // (scroll stays functional). Scoped here via `className`
2563
- // instead of `ChatMessageList` itself, so other list
2564
- // consumers (host chat, tickets) keep their thin bar.
2685
+ ) : (
2686
+ <>
2687
+ <div ref={galleryPanelRef} className="flex min-h-0 flex-1 flex-col p-[var(--spacing-system-m)]">
2688
+ <div
2689
+ key={contentViewKey}
2690
+ className="flex min-h-0 flex-1 flex-col duration-200 animate-in fade-in-0"
2691
+ >
2692
+ {hasConversation ? (
2693
+ <div className="flex min-h-0 flex-1 flex-col">
2694
+ {messagesInitialLoading ? (
2695
+ // Match the real list's `fullWidth` layout so swapping
2696
+ // skeleton → bubbles doesn't shift the column. The panel
2697
+ // wrapper above already pads (`p-[var(--spacing-system-m)]`),
2698
+ // so the skeleton sits flush (no inner px/pb).
2565
2699
  //
2566
- // `previewMode` (the passive in-page demo) instead:
2567
- // • KEEP the default thin scrollbar — the "elevator" —
2568
- // so the Mingo demo's scroll controls match the Fae
2569
- // demo box (which renders the default ChatMessageList
2570
- // scrollbar) 1:1.
2571
- // • Re-enable pointer events on the scroller: previewMode
2572
- // puts `pointer-events-none` on the whole panel (line
2573
- // ~1981) so the demo doesn't trap clicks, but that also
2574
- // killed wheel/touch scroll on the thread (the Mingo
2575
- // embed couldn't scroll while the Fae box could). Only
2576
- // the scroller re-enables; composer + chrome stay inert.
2577
- className={
2578
- previewMode
2579
- ? 'pointer-events-auto flex-1'
2580
- : 'flex-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'
2581
- }
2582
- // No inner `px`/`pb`: the panel wrapper already pads with
2583
- // `p-[var(--spacing-system-m)]`. The default content class
2584
- // adds `px-[var(--spacing-system-m)]` + `pb-…xs`, which
2585
- // double-inset the thread; `pb-0` overrides via twMerge.
2586
- contentClassName="max-w-none pb-0"
2587
- fullWidth
2588
- hasNextPage={hasMoreMessages}
2589
- isFetchingNextPage={isMessagesLoading}
2590
- onLoadMore={loadMoreMessages}
2591
- />
2592
- )}
2593
- </div>
2594
- ) : activeMode === 'mingo' ? (
2595
- /* Figma node 7532:222444 — default (Mingo-mode) empty state:
2700
+ // In `previewMode` the chat lives in a fixed-height host box
2701
+ // (e.g. the Mingo hero demo). The default bottom-anchored
2702
+ // skeleton overflows a short panel and grows its OWN
2703
+ // scrollbar — ugly. `fill` top-anchors + clips overflow so
2704
+ // it reads as "the whole panel is loading" and never scrolls.
2705
+ <ChatMessageListSkeleton fullWidth fill={previewMode} className="flex-1" />
2706
+ ) : (
2707
+ <ChatMessageList
2708
+ messages={messages}
2709
+ // v2 thread: names without the colon, 24px
2710
+ // faces, 16px between messages (shared with fae v2).
2711
+ appearance={appearance}
2712
+ isTyping={chatLoading}
2713
+ // The stage the turn reports while the user waits
2714
+ // ("Searching 28 sources"); the generic phrase otherwise.
2715
+ typingMessage={streamingProgress ? chatProgressLabel(streamingProgress) : undefined}
2716
+ // Sticky footer for approvals the host lifted out of the
2717
+ // thread — see the prop's docblock.
2718
+ pendingApprovals={pendingApprovals}
2719
+ // Real drawer: the library's smart follow. Passive in-page
2720
+ // demo (previewMode): deterministic hard pin instead — a
2721
+ // scripted assistant-only stream from a cold mount never
2722
+ // satisfies the library's "at bottom" gate, so the reply
2723
+ // landed below the fold. `pinBottom` snaps to bottom on
2724
+ // every frame; identical mechanism to the Fae demo box, so
2725
+ // both surfaces behave 1:1.
2726
+ autoScroll={!previewMode}
2727
+ pinBottom={previewMode}
2728
+ // Passive in-page demos (previewMode) let the surrounding
2729
+ // page scroll over the thread; the real drawer keeps
2730
+ // containment (deck slide-scroll fix).
2731
+ overscrollContain={!previewMode}
2732
+ assistantType="mingo"
2733
+ assistantIcon={mingoAssistantIcon}
2734
+ renderEntityCard={renderEntityCard}
2735
+ resolveContextIcon={resolveContextIcon}
2736
+ renderContextItem={renderContextItem}
2737
+ renderMention={renderMention}
2738
+ renderAfterMessage={renderMessageSources}
2739
+ // Gated on `chatLoading` for the same reason the composer
2740
+ // is: no second send while a turn is in flight. Passive
2741
+ // demo hosts (previewMode) stay read-only.
2742
+ onAskSelect={chatLoading || previewMode ? undefined : handleAskSelect}
2743
+ NavLinkAnchor={NavLinkAnchorViaRuntime}
2744
+ // Real Mingo drawer: hide the message-list scrollbar
2745
+ // (scroll stays functional). Scoped here via `className`
2746
+ // instead of `ChatMessageList` itself, so other list
2747
+ // consumers (host chat, tickets) keep their thin bar.
2748
+ //
2749
+ // `previewMode` (the passive in-page demo) instead:
2750
+ // • KEEP the default thin scrollbar — the "elevator" —
2751
+ // so the Mingo demo's scroll controls match the Fae
2752
+ // demo box (which renders the default ChatMessageList
2753
+ // scrollbar) 1:1.
2754
+ // • Re-enable pointer events on the scroller: previewMode
2755
+ // puts `pointer-events-none` on the whole panel (line
2756
+ // ~1981) so the demo doesn't trap clicks, but that also
2757
+ // killed wheel/touch scroll on the thread (the Mingo
2758
+ // embed couldn't scroll while the Fae box could). Only
2759
+ // the scroller re-enables; composer + chrome stay inert.
2760
+ className={
2761
+ previewMode
2762
+ ? 'pointer-events-auto flex-1'
2763
+ : 'flex-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'
2764
+ }
2765
+ // No inner `px`/`pb`: the panel wrapper already pads with
2766
+ // `p-[var(--spacing-system-m)]`. The default content class
2767
+ // adds `px-[var(--spacing-system-m)]` + `pb-…xs`, which
2768
+ // double-inset the thread; `pb-0` overrides via twMerge.
2769
+ contentClassName="max-w-none pb-0"
2770
+ fullWidth
2771
+ hasNextPage={hasMoreMessages}
2772
+ isFetchingNextPage={isMessagesLoading}
2773
+ onLoadMore={loadMoreMessages}
2774
+ />
2775
+ )}
2776
+ </div>
2777
+ ) : activeMode === 'mingo' ? (
2778
+ /* Figma node 7532:222444 — default (Mingo-mode) empty state:
2596
2779
  centred greeting + capability grid + Guide-chat promo +
2597
2780
  quick-action chips. Guide mode keeps the slash-command
2598
2781
  onboarding list below. */
2599
- <MingoWelcome
2600
- onStartGuideChat={
2601
- effectiveModes.guide ? () => handleActiveModeChange('guide') : undefined
2602
- }
2603
- {...mingoWelcome}
2604
- // History now lives in its own list view (the wide rail or
2605
- // the narrow "Current Chats" list), so the welcome is always
2606
- // the new-user compose surface: greeting + grid + promo +
2607
- // primary Guide chip.
2608
- hasExistingChats={false}
2609
- // Hover/focus PREVIEWS the action's full prompt as ghost text
2610
- // in the empty composer (see `quickActionPreview` /
2611
- // `ChatInput.previewText`). Wired after the `{...mingoWelcome}`
2612
- // spread so the host can't override it.
2613
- onQuickActionHover={action => setQuickActionPreview(action.prompt ?? action.label)}
2614
- onQuickActionHoverEnd={() => setQuickActionPreview(null)}
2615
- // A built-in agent caps the quick-action wall at 2 rows;
2616
- // this is the Mingo surface, so fall back to 'mingo'.
2617
- agentSlug={activeAgentSlug ?? 'mingo'}
2618
- />
2619
- ) : (
2620
- /* Figma node 7532:328214 — Guide-mode empty state: greeting
2782
+ <MingoWelcome
2783
+ onStartGuideChat={
2784
+ effectiveModes.guide ? () => handleActiveModeChange('guide') : undefined
2785
+ }
2786
+ {...mingoWelcome}
2787
+ // History now lives in its own list view (the wide rail or
2788
+ // the narrow "Current Chats" list), so the welcome is always
2789
+ // the new-user compose surface: greeting + grid + promo +
2790
+ // primary Guide chip.
2791
+ hasExistingChats={false}
2792
+ // Hover/focus PREVIEWS the action's full prompt as ghost text
2793
+ // in the empty composer (see `quickActionPreview` /
2794
+ // `ChatInput.previewText`). Wired after the `{...mingoWelcome}`
2795
+ // spread so the host can't override it.
2796
+ onQuickActionHover={action => setQuickActionPreview(action.prompt ?? action.label)}
2797
+ onQuickActionHoverEnd={() => setQuickActionPreview(null)}
2798
+ // A built-in agent caps the quick-action wall at 2 rows;
2799
+ // this is the Mingo surface, so fall back to 'mingo'.
2800
+ agentSlug={activeAgentSlug ?? 'mingo'}
2801
+ />
2802
+ ) : (
2803
+ /* Figma node 7532:328214 — Guide-mode empty state: greeting
2621
2804
  + slash-command onboarding list share one scroll region,
2622
2805
  with a pinned quick-action chip row above the composer. */
2623
- <GuideWelcome
2624
- // Admin/host greeting customises the guide subtitle; an
2625
- // explicit `guideWelcome.subtitle` still wins (spread below).
2626
- subtitle={effectiveGreeting ?? undefined}
2627
- // While the admin greeting is still being fetched, render a
2628
- // subtitle skeleton instead of flashing empty → text.
2629
- subtitleLoading={emptyStateLoading}
2630
- {...guideWelcome}
2631
- // Agent identity (name → title, icon → empty-state glyph)
2632
- // wins over the host `guideWelcome` spread + built-in
2633
- // defaults; falls back to the host value when no agent is
2634
- // active. Placed after the spread so resolution is
2635
- // deterministic.
2636
- title={effectiveAssistantName ?? guideWelcome?.title}
2637
- icon={effectiveAssistantIcon ?? guideWelcome?.icon}
2638
- // Admin "try-asking chips" → Guide quick-action chips.
2639
- // Precedence: when an AGENT is active and its fetched chips
2640
- // arrived, the agent's chips win — the host array is the
2641
- // platform default, not the agent's. (`agentConfigUrl` is
2642
- // the same derived value that drives the config fetch, so
2643
- // this can never disagree with what was fetched; while the
2644
- // fetch is in-flight the fetched list is [] and host chips
2645
- // show.) Outside agent mode the host-provided
2646
- // `guideWelcome.quickActions` wins as before.
2647
- quickActions={
2648
- agentConfigUrl && guideSuggestedActions.length > 0
2649
- ? guideSuggestedActions
2650
- : (guideWelcome?.quickActions ?? guideSuggestedActions)
2651
- }
2652
- // Quick-action chips SEND the prompt immediately on click.
2653
- onQuickAction={action => {
2654
- setQuickActionPreview(null);
2655
- handleSend(action.prompt ?? action.label);
2656
- }}
2657
- // Hover/focus PREVIEWS the action's full prompt as ghost text
2658
- // in the empty composer (the chip label is short; this reveals
2659
- // what will be sent). Declarative + non-destructive — see
2660
- // `quickActionPreview` / `ChatInput.previewText`.
2661
- onQuickActionHover={action => setQuickActionPreview(action.prompt ?? action.label)}
2662
- onQuickActionHoverEnd={() => setQuickActionPreview(null)}
2663
- // In agent mode a built-in agent (fae/mingo) caps the wall at
2664
- // 2 rows; host/guide mode (no active agent) keeps the default.
2665
- agentSlug={activeAgentSlug}
2666
- >
2667
- {/* Figma node 7363:205938 — single-column slash-command
2806
+ <GuideWelcome
2807
+ // Admin/host greeting customises the guide subtitle; an
2808
+ // explicit `guideWelcome.subtitle` still wins (spread below).
2809
+ subtitle={effectiveGreeting ?? undefined}
2810
+ // While the admin greeting is still being fetched, render a
2811
+ // subtitle skeleton instead of flashing empty → text.
2812
+ subtitleLoading={emptyStateLoading}
2813
+ {...guideWelcome}
2814
+ // Agent identity (name → title, icon → empty-state glyph)
2815
+ // wins over the host `guideWelcome` spread + built-in
2816
+ // defaults; falls back to the host value when no agent is
2817
+ // active. Placed after the spread so resolution is
2818
+ // deterministic.
2819
+ title={effectiveAssistantName ?? guideWelcome?.title}
2820
+ icon={effectiveAssistantIcon ?? guideWelcome?.icon}
2821
+ // Admin "try-asking chips" → Guide quick-action chips.
2822
+ // Precedence: when an AGENT is active and its fetched chips
2823
+ // arrived, the agent's chips win — the host array is the
2824
+ // platform default, not the agent's. (`agentConfigUrl` is
2825
+ // the same derived value that drives the config fetch, so
2826
+ // this can never disagree with what was fetched; while the
2827
+ // fetch is in-flight the fetched list is [] and host chips
2828
+ // show.) Outside agent mode the host-provided
2829
+ // `guideWelcome.quickActions` wins as before.
2830
+ quickActions={
2831
+ agentConfigUrl && guideSuggestedActions.length > 0
2832
+ ? guideSuggestedActions
2833
+ : (guideWelcome?.quickActions ?? guideSuggestedActions)
2834
+ }
2835
+ // Quick-action chips SEND the prompt immediately on click.
2836
+ onQuickAction={action => {
2837
+ setQuickActionPreview(null);
2838
+ handleSend(action.prompt ?? action.label);
2839
+ }}
2840
+ // Hover/focus PREVIEWS the action's full prompt as ghost text
2841
+ // in the empty composer (the chip label is short; this reveals
2842
+ // what will be sent). Declarative + non-destructive — see
2843
+ // `quickActionPreview` / `ChatInput.previewText`.
2844
+ onQuickActionHover={action => setQuickActionPreview(action.prompt ?? action.label)}
2845
+ onQuickActionHoverEnd={() => setQuickActionPreview(null)}
2846
+ // In agent mode a built-in agent (fae/mingo) caps the wall at
2847
+ // 2 rows; host/guide mode (no active agent) keeps the default.
2848
+ agentSlug={activeAgentSlug}
2849
+ >
2850
+ {/* Figma node 7363:205938 — single-column slash-command
2668
2851
  list. No own scroll (GuideWelcome's region scrolls); the
2669
2852
  rounded-md frame holds the cards on the dark surface. */}
2670
- {(chipCommands.length > 0 || !commandsLoaded) && (
2671
- <div className="shrink-0 overflow-hidden rounded-md border border-ods-border">
2672
- {!commandsLoaded &&
2673
- chipCommands.length === 0 &&
2674
- SKELETON_ROW_VARIANTS.map((variant, i) => (
2675
- <MingoOnboardingCardSkeleton
2676
- key={`chip-skeleton-${i}`}
2677
- titleWidth={variant.titleWidth}
2678
- slashWidth={variant.slashWidth}
2679
- descriptionLines={variant.descriptionLines}
2680
- />
2681
- ))}
2682
- {chipCommands.map(cmd => {
2683
- const Icon = resolveIcon(cmd.iconName);
2684
- const cmdId = cmd.id;
2685
- const label = cmd.label ?? `/${cmdId}`;
2686
- const cardActions = cmd.actions.map(action => ({
2687
- id: action.id,
2688
- label: action.label,
2689
- onClick: () => dispatchSlashCommandAction(action.id, cmdId, chatInputRef),
2690
- }));
2691
- return (
2692
- <MingoOnboardingCard
2693
- key={cmdId}
2694
- icon={<Icon size={16} />}
2695
- title={label}
2696
- slashCommand={`/${cmdId}`}
2697
- description={cmd.description}
2698
- actions={cardActions}
2699
- />
2700
- );
2701
- })}
2702
- </div>
2703
- )}
2704
- </GuideWelcome>
2705
- )}
2853
+ {(chipCommands.length > 0 || !commandsLoaded) && (
2854
+ <div className="shrink-0 overflow-hidden rounded-md border border-ods-border">
2855
+ {!commandsLoaded &&
2856
+ chipCommands.length === 0 &&
2857
+ SKELETON_ROW_VARIANTS.map((variant, i) => (
2858
+ <MingoOnboardingCardSkeleton
2859
+ key={`chip-skeleton-${i}`}
2860
+ titleWidth={variant.titleWidth}
2861
+ slashWidth={variant.slashWidth}
2862
+ descriptionLines={variant.descriptionLines}
2863
+ />
2864
+ ))}
2865
+ {chipCommands.map(cmd => {
2866
+ const Icon = resolveIcon(cmd.iconName);
2867
+ const cmdId = cmd.id;
2868
+ const label = cmd.label ?? `/${cmdId}`;
2869
+ const cardActions = cmd.actions.map(action => ({
2870
+ id: action.id,
2871
+ label: action.label,
2872
+ onClick: () => dispatchSlashCommandAction(action.id, cmdId, chatInputRef),
2873
+ }));
2874
+ return (
2875
+ <MingoOnboardingCard
2876
+ key={cmdId}
2877
+ icon={<Icon size={16} />}
2878
+ title={label}
2879
+ slashCommand={`/${cmdId}`}
2880
+ description={cmd.description}
2881
+ actions={cardActions}
2882
+ />
2883
+ );
2884
+ })}
2885
+ </div>
2886
+ )}
2887
+ </GuideWelcome>
2888
+ )}
2889
+ </div>
2706
2890
  </div>
2707
- </div>
2708
2891
 
2709
- <ChatAttachmentChipStrip
2710
- attachments={stagedAttachments}
2711
- onRemove={removeAttachment}
2712
- disabled={chatLoading}
2713
- />
2892
+ <ChatAttachmentChipStrip
2893
+ attachments={stagedAttachments}
2894
+ onRemove={removeAttachment}
2895
+ disabled={chatLoading}
2896
+ />
2714
2897
 
2715
- <ChatComposer
2716
- key={composerKey}
2717
- archived={isViewingArchived}
2718
- lock={composerLock}
2719
- inputRef={chatInputRef}
2720
- onSend={handleSend}
2721
- onStop={stopMessage}
2722
- sending={chatLoading || hasInflightUploads}
2723
- placeholder={hasInflightUploads ? 'Waiting for uploads to finish…' : 'Ask a question...'}
2724
- autoFocus={previewMode ? false : autoFocusInput}
2725
- slashCommands={slashCommandsProp}
2726
- previewText={quickActionPreview ?? undefined}
2727
- showAttachmentButton={attachmentsEnabled && activeMode === 'guide'}
2728
- attachmentsCount={stagedAttachments.length}
2729
- onAddFiles={addAttachmentFiles}
2730
- attachmentsDisabled={chatLoading}
2731
- contextPicker={contextPickerForMode}
2732
- selectedContextItems={contextItems}
2733
- onToggleContextItem={toggleContextItem}
2734
- onRemoveContextItem={removeContextItem}
2735
- contextMemoryItems={contextMemory?.items}
2736
- onRemoveContextMemoryItem={contextMemory?.onRemove}
2737
- contextPickerOpen={contextPickerOpen}
2738
- onOpenContextPicker={openContextPicker}
2739
- onCloseContextPicker={closeContextPicker}
2740
- mentionQuery={mentionQuery}
2741
- onMentionQueryChange={handleMentionQueryChange}
2742
- onValueChange={handleContextValueChange}
2743
- model={{
2744
- provider: currentProvider ?? 'anthropic',
2745
- modelName: currentModelLabel ?? 'Claude',
2746
- usedTokens:
2747
- currentInputTokens != null
2748
- ? (currentInputTokens ?? 0) + (currentOutputTokens ?? 0)
2749
- : undefined,
2750
- contextWindow: currentContextWindowMaxTokens ?? undefined,
2751
- inputTokens: currentInputTokens ?? undefined,
2752
- outputTokens: currentOutputTokens ?? undefined,
2753
- hitRatePct: currentCacheHitRatePct ?? undefined,
2754
- breakdown: currentUsageBreakdown ?? undefined,
2755
- }}
2756
- />
2757
- </>
2758
- )}
2898
+ <ChatComposer
2899
+ key={composerKey}
2900
+ archived={isViewingArchived}
2901
+ lock={composerLock}
2902
+ inputRef={chatInputRef}
2903
+ onSend={handleSend}
2904
+ onStop={stopMessage}
2905
+ sending={chatLoading || hasInflightUploads}
2906
+ placeholder={
2907
+ hasInflightUploads
2908
+ ? 'Waiting for uploads to finish…'
2909
+ : isV2
2910
+ ? 'Enter your Request...'
2911
+ : 'Ask a question...'
2912
+ }
2913
+ autoFocus={previewMode ? false : autoFocusInput}
2914
+ slashCommands={slashCommandsProp}
2915
+ previewText={quickActionPreview ?? undefined}
2916
+ showAttachmentButton={attachmentsEnabled && activeMode === 'guide'}
2917
+ attachmentsCount={stagedAttachments.length}
2918
+ onAddFiles={addAttachmentFiles}
2919
+ attachmentsDisabled={chatLoading}
2920
+ contextPicker={contextPickerForMode}
2921
+ selectedContextItems={contextItems}
2922
+ onToggleContextItem={toggleContextItem}
2923
+ onRemoveContextItem={removeContextItem}
2924
+ contextMemoryItems={contextMemory?.items}
2925
+ onRemoveContextMemoryItem={contextMemory?.onRemove}
2926
+ contextPickerOpen={contextPickerOpen}
2927
+ onOpenContextPicker={openContextPicker}
2928
+ onCloseContextPicker={closeContextPicker}
2929
+ mentionQuery={mentionQuery}
2930
+ onMentionQueryChange={handleMentionQueryChange}
2931
+ onValueChange={handleContextValueChange}
2932
+ model={{
2933
+ provider: currentProvider ?? 'anthropic',
2934
+ modelName: currentModelLabel ?? 'Claude',
2935
+ usedTokens:
2936
+ currentInputTokens != null
2937
+ ? (currentInputTokens ?? 0) + (currentOutputTokens ?? 0)
2938
+ : undefined,
2939
+ contextWindow: currentContextWindowMaxTokens ?? undefined,
2940
+ inputTokens: currentInputTokens ?? undefined,
2941
+ outputTokens: currentOutputTokens ?? undefined,
2942
+ hitRatePct: currentCacheHitRatePct ?? undefined,
2943
+ breakdown: currentUsageBreakdown ?? undefined,
2944
+ }}
2945
+ />
2946
+ </>
2947
+ )}
2948
+ </div>
2759
2949
  </div>
2760
2950
  </div>
2761
- </div>
2762
- )}
2763
- </div>
2764
- {galleryModal}
2951
+ )}
2952
+ </div>
2953
+ {galleryModal}
2765
2954
 
2766
- {/* Rename / Archive / Unarchive chat modals (Figma 7592:225962,
2955
+ {/* Rename / Archive / Unarchive chat modals (Figma 7592:225962,
2767
2956
  7592:226181). Triggered from the header ⋯ and the dialog-history
2768
2957
  rows; rendered inside the panel so they overlay the chat. */}
2769
- <ChatDialogModals
2770
- renameTarget={renameTarget}
2771
- setRenameTarget={setRenameTarget}
2772
- onConfirmRename={handleConfirmRename}
2773
- archiveTarget={archiveTarget}
2774
- setArchiveTarget={setArchiveTarget}
2775
- onConfirmArchive={handleConfirmArchive}
2776
- restoreTarget={restoreTarget}
2777
- setRestoreTarget={setRestoreTarget}
2778
- onConfirmRestore={handleConfirmRestore}
2779
- />
2958
+ <ChatDialogModals
2959
+ renameTarget={renameTarget}
2960
+ setRenameTarget={setRenameTarget}
2961
+ onConfirmRename={handleConfirmRename}
2962
+ archiveTarget={archiveTarget}
2963
+ setArchiveTarget={setArchiveTarget}
2964
+ onConfirmArchive={handleConfirmArchive}
2965
+ restoreTarget={restoreTarget}
2966
+ setRestoreTarget={setRestoreTarget}
2967
+ onConfirmRestore={handleConfirmRestore}
2968
+ />
2780
2969
 
2781
- {/* Portal target for in-panel Radix overlays — `display: contents`
2970
+ {/* Portal target for in-panel Radix overlays — `display: contents`
2782
2971
  so it adds no box; content is positioned `fixed` by Radix. */}
2783
- <div ref={setPortalHost} style={{ display: 'contents' }} />
2784
- </CollisionBoundaryContext.Provider>
2785
- </PortalContainerContext.Provider>
2972
+ <div ref={setPortalHost} style={{ display: 'contents' }} />
2973
+ </CollisionBoundaryContext.Provider>
2974
+ </PortalContainerContext.Provider>
2975
+ </ChatAppearanceContext.Provider>
2786
2976
  );
2787
2977
 
2788
2978
  // Conditional shell: inline (shell-less host, e.g. AppLayoutDrawer, which
@@ -2794,9 +2984,11 @@ function EmbeddableChatInner({
2794
2984
  accessible name — Radix warns when a Dialog.Content has no Title. The
2795
2985
  panel supplies its own visually-hidden title here, mirroring the
2796
2986
  drawer-shell branch below, so it's accessible regardless of host. */}
2797
- <VisuallyHidden>
2798
- <DialogPrimitive.Title>{sourceLabel} AI Assistant</DialogPrimitive.Title>
2799
- </VisuallyHidden>
2987
+ {shell === 'none' && (
2988
+ <VisuallyHidden>
2989
+ <DialogPrimitive.Title>{sourceLabel} AI Assistant</DialogPrimitive.Title>
2990
+ </VisuallyHidden>
2991
+ )}
2800
2992
  {body}
2801
2993
  </ChatPanelContext.Provider>
2802
2994
  ) : (
@@ -2810,7 +3002,7 @@ function EmbeddableChatInner({
2810
3002
  resizable
2811
3003
  minSize={480}
2812
3004
  maxSize={1600}
2813
- defaultSize={drawerDefaultWidth(historyListMode)}
3005
+ defaultSize={historyListMode ? drawerDefaultWidthWithRail : drawerDefaultWidthWithoutRail}
2814
3006
  storageKey={DRAWER_WIDTH_KEY}
2815
3007
  resizeAriaLabel="Resize chat panel"
2816
3008
  overlayClassName="mingo-chat-overlay"