@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.
- package/dist/{chunk-CYYTD5ZK.cjs → chunk-262YQFOS.cjs} +47 -47
- package/dist/{chunk-SAA7B3DA.js → chunk-2G546OND.js} +500 -67
- package/dist/{chunk-6JPQDYYY.js → chunk-2JDVL7UV.js} +2614 -2001
- package/dist/{chunk-ASN65P3F.cjs → chunk-4DQMTBMK.cjs} +15 -15
- package/dist/{chunk-CZ34HXDW.js → chunk-4KGXLVCF.js} +686 -725
- package/dist/{chunk-TCZCMD5Q.js → chunk-5HBHO5TB.js} +1 -1
- package/dist/{chunk-446KS5BG.js → chunk-5QJDM6UV.js} +5 -5
- package/dist/{chunk-UJJWTJ6S.js → chunk-6WQIUQUC.js} +30 -16
- package/dist/{chunk-YR6JPJF4.js → chunk-6XIF2Z7I.js} +4 -4
- package/dist/{chunk-YGRHITPY.cjs → chunk-7JIL3WOH.cjs} +24 -24
- package/dist/{chunk-RS7HWAYX.js → chunk-7LFIKJX2.js} +3 -3
- package/dist/{chunk-4OGAXAEG.cjs → chunk-ABU3TGXA.cjs} +3 -3
- package/dist/{chunk-3WSFIV5G.cjs → chunk-AMYY3PNL.cjs} +69 -55
- package/dist/{chunk-BRXOGCH3.cjs → chunk-BIZNSF3U.cjs} +3 -3
- package/dist/{chunk-2A4XES7R.cjs → chunk-BM5MVGH3.cjs} +3 -3
- package/dist/{chunk-IIMICN7L.cjs → chunk-C3BWIU7R.cjs} +4 -44
- package/dist/{chunk-LZNJH23A.cjs → chunk-C7XGRLT5.cjs} +12 -12
- package/dist/{chunk-6YCXA7CN.js → chunk-CH5232HV.js} +20 -19
- package/dist/{chunk-UIBSSVON.cjs → chunk-D7BTSVMS.cjs} +2 -2
- package/dist/{chunk-BO5ZHDA4.js → chunk-ERCV6HHJ.js} +7 -7
- package/dist/{chunk-5V6O73EH.js → chunk-FLKJYMVB.js} +3 -3
- package/dist/{chunk-Z5HDCZAS.js → chunk-FXENTYE5.js} +15 -15
- package/dist/{chunk-HIXGAY2L.cjs → chunk-HFJW24JJ.cjs} +121 -121
- package/dist/{chunk-HPJ4XS2L.js → chunk-HJAYVKWQ.js} +24 -24
- package/dist/{chunk-PLK6GHN7.js → chunk-I7DFUQMN.js} +10 -10
- package/dist/{chunk-KJ5LWATV.js → chunk-IHU5ANUY.js} +7 -7
- package/dist/{chunk-AEQANLBW.cjs → chunk-IZGDG5X6.cjs} +505 -72
- package/dist/{chunk-2V4Z44G6.js → chunk-JEYRXT5L.js} +17 -17
- package/dist/{chunk-B3LZJ6J3.cjs → chunk-L3IC42IF.cjs} +32 -32
- package/dist/{chunk-RTGPIHVO.cjs → chunk-LDPEQGKT.cjs} +2243 -1630
- package/dist/{chunk-FD4DBTFO.cjs → chunk-LTSTC3N5.cjs} +1253 -1292
- package/dist/{chunk-OGLCD3UL.js → chunk-MFFCUVWG.js} +6 -6
- package/dist/{chunk-3L7KCOGO.cjs → chunk-MGGGBHYD.cjs} +30 -30
- package/dist/{chunk-QFW3RUZS.cjs → chunk-MI3JUJ75.cjs} +33 -33
- package/dist/{chunk-EMCQGFXD.cjs → chunk-MMOODW57.cjs} +28 -27
- package/dist/{chunk-Q76ZTHNV.js → chunk-MP2C3LJ4.js} +1 -1
- package/dist/{chunk-E3DH543A.cjs → chunk-N3TO7JV3.cjs} +98 -98
- package/dist/{chunk-2T6QWNXU.js → chunk-NDESEPW5.js} +99 -11
- package/dist/{chunk-ETFFEA3J.js → chunk-NRLJUBCS.js} +7 -7
- package/dist/{chunk-HUOLB7ZN.cjs → chunk-OHFS7VOU.cjs} +9 -9
- package/dist/{chunk-MWUBBBWZ.js → chunk-QVSFBSWV.js} +10 -10
- package/dist/{chunk-MDBHZBW7.cjs → chunk-RKV5B7EU.cjs} +92 -4
- package/dist/{chunk-3G76LRGL.cjs → chunk-SYXQLETR.cjs} +29 -29
- package/dist/{chunk-FHM7GD5B.cjs → chunk-UK62YTH6.cjs} +28 -14
- package/dist/{chunk-HUMWQMCU.js → chunk-UT4YHQZW.js} +3 -3
- package/dist/{chunk-NGJCWEYX.cjs → chunk-VHCACDKP.cjs} +46 -46
- package/dist/{chunk-5FFNNJM7.js → chunk-VR2ZLFCP.js} +1 -1
- package/dist/{chunk-IZKOQVXO.js → chunk-VTASHWER.js} +14 -14
- package/dist/{chunk-64O4L7WN.js → chunk-WUJNYGO2.js} +11 -51
- package/dist/{chunk-JOY6HVPV.js → chunk-XATTBO5X.js} +25 -11
- package/dist/{chunk-L7KZA7ZD.cjs → chunk-XIFOBQRQ.cjs} +6 -6
- package/dist/{chunk-CXDJUKC6.cjs → chunk-XJKZ4IKT.cjs} +4 -4
- package/dist/{chunk-XCIFHW4E.cjs → chunk-XOYH2YQE.cjs} +2 -2
- package/dist/{chunk-HYFLTFFB.js → chunk-ZQFZSFQI.js} +2 -2
- package/dist/components/case-studies/index.cjs +24 -24
- package/dist/components/case-studies/index.js +10 -10
- package/dist/components/chat/chat-archive-page.d.ts +3 -3
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
- package/dist/components/chat/chat-nav-sidebar.d.ts +4 -0
- package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
- package/dist/components/chat/chat-panel-header.d.ts +3 -2
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +36 -2
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +14 -6
- package/dist/components/chat/index.d.ts +4 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +13 -5
- package/dist/components/chat/mingo-archive-list.d.ts +17 -0
- package/dist/components/chat/mingo-archive-list.d.ts.map +1 -0
- package/dist/components/chat/mingo-chat-header.d.ts +31 -0
- package/dist/components/chat/mingo-chat-header.d.ts.map +1 -0
- package/dist/components/chat/mingo-chat-rail.d.ts +54 -0
- package/dist/components/chat/mingo-chat-rail.d.ts.map +1 -0
- package/dist/components/chat/model-display.d.ts.map +1 -1
- package/dist/components/contact/contact-form.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +11 -11
- package/dist/components/contact/index.js +10 -10
- package/dist/components/content-loading-container.d.ts +1 -1
- package/dist/components/content-loading-container.d.ts.map +1 -1
- package/dist/components/docs/index.cjs +14 -14
- package/dist/components/docs/index.js +13 -13
- package/dist/components/docs/skeletons.d.ts.map +1 -1
- package/dist/components/embeds/index.cjs +11 -11
- package/dist/components/embeds/index.js +10 -10
- package/dist/components/faq/index.cjs +12 -12
- package/dist/components/faq/index.js +11 -11
- package/dist/components/features/figma-prototype-viewer.d.ts.map +1 -1
- package/dist/components/features/index.cjs +10 -10
- package/dist/components/features/index.js +9 -9
- package/dist/components/features/media-gallery-manager.d.ts.map +1 -1
- package/dist/components/features/parallax-image-showcase.d.ts.map +1 -1
- package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
- package/dist/components/features/section-selector.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +119 -119
- package/dist/components/help-center-pages/index.js +39 -39
- package/dist/components/help-center-pages/trust-center-sections.d.ts +2 -2
- package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
- package/dist/components/index.cjs +94 -86
- package/dist/components/index.js +32 -24
- package/dist/components/layout/article-detail-layout.d.ts +1 -1
- package/dist/components/layout/article-detail-layout.d.ts.map +1 -1
- package/dist/components/layout/page-container.d.ts.map +1 -1
- package/dist/components/layout/title-block.d.ts +2 -2
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/loading/page-layout-skeleton.d.ts.map +1 -1
- package/dist/components/loading/unified-skeleton.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/booking-form.d.ts +4 -4
- package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/context-panel.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +127 -127
- package/dist/components/meeting-scheduler/index.d.ts +2 -2
- package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.js +50 -50
- package/dist/components/navigation/app-header.d.ts +4 -0
- package/dist/components/navigation/app-header.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-side-panel.d.ts +142 -0
- package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -0
- package/dist/components/navigation/app-layout.d.ts +11 -1
- package/dist/components/navigation/app-layout.d.ts.map +1 -1
- package/dist/components/navigation/header-mingo-button.d.ts +4 -1
- package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +13 -13
- package/dist/components/navigation/index.d.ts +1 -0
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +12 -12
- package/dist/components/navigation/navigation-sidebar.d.ts +8 -1
- package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
- package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +14 -14
- package/dist/components/onboarding-guides/index.js +13 -13
- package/dist/components/persistent-filter-controls.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +7 -7
- package/dist/components/related-content/index.js +6 -6
- package/dist/components/shared/detail-page-skeleton.d.ts.map +1 -1
- package/dist/components/tickets/help-center-create-form.d.ts +4 -4
- package/dist/components/tickets/index.cjs +15 -15
- package/dist/components/tickets/index.js +14 -14
- package/dist/components/ui/announcement-bar-view.d.ts.map +1 -1
- package/dist/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/components/ui/checkbox-block.d.ts.map +1 -1
- package/dist/components/ui/dashboard-info-card.d.ts +1 -1
- package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-body.d.ts +2 -2
- package/dist/components/ui/data-table/data-table-skeleton.d.ts +2 -2
- package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
- package/dist/components/ui/data-table/utils.d.ts +2 -2
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/drawer.d.ts +7 -3
- package/dist/components/ui/drawer.d.ts.map +1 -1
- package/dist/components/ui/entity-image.d.ts +2 -2
- package/dist/components/ui/file-manager/index.cjs +36 -36
- package/dist/components/ui/file-manager/index.js +3 -3
- package/dist/components/ui/filter-modal.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +18 -10
- package/dist/components/ui/index.js +17 -9
- package/dist/components/ui/info-card-row.d.ts.map +1 -1
- package/dist/components/ui/menubar.d.ts +1 -1
- package/dist/components/ui/menubar.d.ts.map +1 -1
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/modal.d.ts.map +1 -1
- package/dist/components/ui/no-data/no-data-actions.d.ts.map +1 -1
- package/dist/components/ui/no-data/no-data-styles.d.ts +1 -1
- package/dist/components/ui/no-data/no-data-styles.d.ts.map +1 -1
- package/dist/components/ui/page-actions.d.ts.map +1 -1
- package/dist/components/ui/panel-resize-handle.d.ts +25 -0
- package/dist/components/ui/panel-resize-handle.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/table/table-skeleton.d.ts +1 -1
- package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
- package/dist/hooks/index.cjs +23 -9
- package/dist/hooks/index.js +24 -10
- package/dist/hooks/ui/index.d.ts +1 -0
- package/dist/hooks/ui/index.d.ts.map +1 -1
- package/dist/hooks/ui/use-content-breakpoint.d.ts +32 -0
- package/dist/hooks/ui/use-content-breakpoint.d.ts.map +1 -0
- package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts +2 -2
- package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts.map +1 -1
- package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
- package/dist/hooks/ui/use-resizable-panel-size.d.ts +46 -0
- package/dist/hooks/ui/use-resizable-panel-size.d.ts.map +1 -0
- package/dist/index.cjs +39 -17
- package/dist/index.js +41 -19
- package/dist/styles/layout-steps.d.ts +30 -0
- package/dist/styles/layout-steps.d.ts.map +1 -0
- package/dist/tailwind.config.cjs +36 -11
- package/dist/tailwind.config.js +36 -11
- package/eslint-config/README.md +9 -0
- package/eslint-config/base.js +17 -1
- package/eslint-config/layout-variants.js +57 -0
- package/package.json +3 -1
- package/src/ODS_TOKEN_RULES.md +40 -0
- package/src/components/chat/chat-archive-page.tsx +9 -7
- package/src/components/chat/chat-nav-sidebar.tsx +10 -4
- package/src/components/chat/chat-panel-header.tsx +8 -5
- package/src/components/chat/embeddable-chat.tsx +686 -494
- package/src/components/chat/index.ts +4 -0
- package/src/components/chat/mingo-archive-list.tsx +108 -0
- package/src/components/chat/mingo-chat-header.tsx +104 -0
- package/src/components/chat/mingo-chat-rail.tsx +361 -0
- package/src/components/chat/model-display.tsx +8 -1
- package/src/components/contact/contact-form.tsx +11 -9
- package/src/components/content-loading-container.tsx +4 -2
- package/src/components/docs/doc-viewer.tsx +7 -7
- package/src/components/docs/skeletons.tsx +7 -4
- package/src/components/dynamic-skeleton.tsx +1 -1
- package/src/components/embeds/claude-embed.tsx +1 -1
- package/src/components/embeds/embed-viewer-frame.tsx +4 -4
- package/src/components/embeds/figma-embed.tsx +2 -2
- package/src/components/embeds/google-sheets-viewer.tsx +1 -1
- package/src/components/embeds/pdf-viewer.tsx +3 -3
- package/src/components/empty-state.tsx +6 -6
- package/src/components/faq/faq-section.tsx +2 -2
- package/src/components/faq-accordion.tsx +1 -1
- package/src/components/features/board/board-column-header.tsx +1 -1
- package/src/components/features/command-box.tsx +3 -3
- package/src/components/features/entity-video-section.tsx +2 -2
- package/src/components/features/figma-prototype-viewer.tsx +4 -1
- package/src/components/features/media-gallery-manager.tsx +3 -1
- package/src/components/features/parallax-image-showcase.tsx +9 -6
- package/src/components/features/policy-configuration-panel.tsx +9 -7
- package/src/components/features/provider-button.tsx +1 -1
- package/src/components/features/section-selector.tsx +11 -6
- package/src/components/features/select-button.tsx +4 -4
- package/src/components/features/seo-editor-preview.tsx +2 -2
- package/src/components/features/video-ratio-tabs.tsx +3 -3
- package/src/components/features/waitlist-form.tsx +1 -1
- package/src/components/help-center-pages/trust-center-page.tsx +1 -1
- package/src/components/help-center-pages/trust-center-sections.tsx +14 -14
- package/src/components/icon-display.tsx +1 -1
- package/src/components/layout/article-detail-layout.tsx +4 -2
- package/src/components/layout/list-page-layout.tsx +2 -2
- package/src/components/layout/page-container.tsx +26 -14
- package/src/components/layout/page-heading.tsx +1 -1
- package/src/components/layout/page-layout.tsx +1 -1
- package/src/components/layout/title-block.tsx +13 -13
- package/src/components/loading/card-skeleton.tsx +11 -11
- package/src/components/loading/category-vendor-selector-skeleton.tsx +1 -1
- package/src/components/loading/content-skeleton.tsx +6 -6
- package/src/components/loading/device-card-skeleton.tsx +1 -1
- package/src/components/loading/margin-report-skeleton.tsx +7 -7
- package/src/components/loading/msp-profile-form-skeleton.tsx +3 -3
- package/src/components/loading/organization-card-skeleton.tsx +2 -2
- package/src/components/loading/page-layout-skeleton.tsx +80 -68
- package/src/components/loading/unified-skeleton.tsx +16 -6
- package/src/components/media-carousel.tsx +3 -3
- package/src/components/meeting-scheduler/booking-form.tsx +9 -8
- package/src/components/meeting-scheduler/context-panel.tsx +8 -6
- package/src/components/meeting-scheduler/directory.tsx +4 -4
- package/src/components/meeting-scheduler/index.tsx +15 -15
- package/src/components/meeting-scheduler/slot-picker.tsx +12 -12
- package/src/components/navigation/app-header.tsx +6 -0
- package/src/components/navigation/app-layout-drawer.tsx +14 -237
- package/src/components/navigation/app-layout-side-panel.tsx +594 -0
- package/src/components/navigation/app-layout.tsx +69 -4
- package/src/components/navigation/header-mingo-button.tsx +13 -3
- package/src/components/navigation/index.ts +5 -0
- package/src/components/navigation/navigation-sidebar.tsx +17 -3
- package/src/components/navigation/sliding-sidebar.tsx +2 -1
- package/src/components/pagination.tsx +3 -3
- package/src/components/persistent-filter-controls.tsx +6 -2
- package/src/components/platform/ScriptArguments.tsx +1 -1
- package/src/components/platform/ScriptInfoSection.tsx +4 -4
- package/src/components/shared/article-author-byline.tsx +1 -1
- package/src/components/shared/delivery/delivery-lists.tsx +2 -2
- package/src/components/shared/delivery/delivery-row.tsx +7 -7
- package/src/components/shared/delivery/delivery-table.tsx +3 -3
- package/src/components/shared/detail-page-skeleton.tsx +11 -5
- package/src/components/shared/dev-section/dev-card-row.tsx +6 -6
- package/src/components/shared/dev-section/dev-section-view.tsx +1 -1
- package/src/components/shared/product-release/product-release-card-skeleton.tsx +8 -8
- package/src/components/shared/product-release/product-release-card.tsx +9 -9
- package/src/components/shared/product-release/product-releases-view.tsx +1 -1
- package/src/components/shared/product-release/release-detail-page.tsx +5 -5
- package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +1 -1
- package/src/components/shared/roadmap/roadmap-grid.tsx +3 -3
- package/src/components/tickets/help-center-card.tsx +1 -1
- package/src/components/tickets/help-center-create-form.tsx +12 -12
- package/src/components/ui/announcement-bar-view.tsx +2 -10
- package/src/components/ui/benefit-card.tsx +18 -18
- package/src/components/ui/brand-association-grid.tsx +4 -4
- package/src/components/ui/breadcrumb.tsx +4 -1
- package/src/components/ui/card.tsx +1 -1
- package/src/components/ui/checkbox-block.tsx +9 -7
- package/src/components/ui/content-loader.tsx +3 -3
- package/src/components/ui/cursor-pagination.tsx +3 -3
- package/src/components/ui/dashboard-info-card.tsx +13 -9
- package/src/components/ui/data-attribution.tsx +2 -2
- package/src/components/ui/data-table/data-table-body.tsx +2 -2
- package/src/components/ui/data-table/data-table-header.tsx +15 -15
- package/src/components/ui/data-table/data-table-skeleton.tsx +6 -6
- package/src/components/ui/data-table/utils.ts +5 -5
- package/src/components/ui/dialog.tsx +4 -1
- package/src/components/ui/drawer.tsx +43 -189
- package/src/components/ui/duration-input.tsx +1 -1
- package/src/components/ui/entity-image.tsx +3 -3
- package/src/components/ui/feature-card.tsx +10 -10
- package/src/components/ui/file-manager/file-manager-action-bar.tsx +1 -1
- package/src/components/ui/file-manager/file-manager.tsx +1 -1
- package/src/components/ui/filter-modal.tsx +8 -1
- package/src/components/ui/highlight-card.tsx +7 -7
- package/src/components/ui/info-card-row.tsx +10 -6
- package/src/components/ui/modal-v2.tsx +7 -2
- package/src/components/ui/modal.tsx +3 -2
- package/src/components/ui/no-data/no-data-actions.tsx +10 -4
- package/src/components/ui/no-data/no-data-styles.ts +1 -1
- package/src/components/ui/no-data/no-data.tsx +1 -1
- package/src/components/ui/organization-card.tsx +1 -1
- package/src/components/ui/page-actions.tsx +34 -5
- package/src/components/ui/panel-resize-handle.tsx +184 -0
- package/src/components/ui/progress-bar.tsx +2 -2
- package/src/components/ui/query-report-table/query-report-table.tsx +1 -1
- package/src/components/ui/search-input.tsx +2 -2
- package/src/components/ui/service-card.tsx +2 -2
- package/src/components/ui/sheet.tsx +2 -1
- package/src/components/ui/skeleton.tsx +3 -3
- package/src/components/ui/stacked-rows-panel.tsx +4 -4
- package/src/components/ui/tab-navigation.tsx +2 -2
- package/src/components/ui/tab-selector.tsx +2 -2
- package/src/components/ui/table/table-header.tsx +3 -3
- package/src/components/ui/table/table-skeleton.tsx +3 -3
- package/src/components/ui/table/utils.ts +5 -5
- package/src/components/ui/tag-search-input.tsx +3 -3
- package/src/components/ui/tags-manager.tsx +5 -5
- package/src/components/ui/test-run.tsx +2 -2
- package/src/components/ui/ticket-info-section.tsx +1 -1
- package/src/components/ui/ticket-notes-section.tsx +1 -1
- package/src/components/ui/ticket-status-config-row.tsx +15 -15
- package/src/hooks/ui/index.ts +1 -0
- package/src/hooks/ui/use-content-breakpoint.ts +58 -0
- package/src/hooks/ui/use-media-query.ts +3 -2
- package/src/hooks/ui/use-resizable-panel-size.ts +114 -0
- package/src/styles/index.css +2 -0
- package/src/styles/layout-steps.ts +14 -0
- package/src/styles/ods-colors.css +15 -0
- package/src/styles/ods-content-area-tokens.css +27 -0
- package/src/styles/ods-content-area.css +31 -0
- package/src/styles/ods-effects.css +4 -2
- package/src/styles/ods-responsive-tokens.css +14 -4
- 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
|
|
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
|
|
130
|
-
// Drawer's own min/max)
|
|
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; //
|
|
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 (
|
|
149
|
-
const ratioWidth = Math.round(
|
|
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]
|
|
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
|
|
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
|
|
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
|
|
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 >=
|
|
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(
|
|
1965
|
-
|
|
1966
|
-
|
|
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:
|
|
2267
|
+
onClose: closeControl,
|
|
2207
2268
|
}
|
|
2208
2269
|
: {
|
|
2209
2270
|
showBack: false,
|
|
2210
2271
|
title: 'Current Chats',
|
|
2211
|
-
onClose:
|
|
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:
|
|
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
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
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
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
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
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
<
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
2354
|
-
|
|
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
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
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
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
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
|
-
|
|
2379
|
-
|
|
2380
|
-
<
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
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
|
-
)
|
|
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
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
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
|
-
|
|
2429
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
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={
|
|
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
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
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`
|
|
2567
|
-
//
|
|
2568
|
-
//
|
|
2569
|
-
//
|
|
2570
|
-
//
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
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
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
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
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
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
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
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
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2892
|
+
<ChatAttachmentChipStrip
|
|
2893
|
+
attachments={stagedAttachments}
|
|
2894
|
+
onRemove={removeAttachment}
|
|
2895
|
+
disabled={chatLoading}
|
|
2896
|
+
/>
|
|
2714
2897
|
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
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
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
{galleryModal}
|
|
2951
|
+
)}
|
|
2952
|
+
</div>
|
|
2953
|
+
{galleryModal}
|
|
2765
2954
|
|
|
2766
|
-
|
|
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
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
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
|
-
|
|
2970
|
+
{/* Portal target for in-panel Radix overlays — `display: contents`
|
|
2782
2971
|
so it adds no box; content is positioned `fixed` by Radix. */}
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
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
|
-
|
|
2798
|
-
<
|
|
2799
|
-
|
|
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={
|
|
3005
|
+
defaultSize={historyListMode ? drawerDefaultWidthWithRail : drawerDefaultWidthWithoutRail}
|
|
2814
3006
|
storageKey={DRAWER_WIDTH_KEY}
|
|
2815
3007
|
resizeAriaLabel="Resize chat panel"
|
|
2816
3008
|
overlayClassName="mingo-chat-overlay"
|