@flamingo-stack/openframe-frontend-core 0.0.219 → 0.0.220-snapshot.20260602171504
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-EDW2NVRV.js → chunk-4WZOFD46.js} +37 -37
- package/dist/{chunk-EDW2NVRV.js.map → chunk-4WZOFD46.js.map} +1 -1
- package/dist/{chunk-ZGBXHK26.cjs → chunk-5GN7TXHY.cjs} +12 -12
- package/dist/{chunk-ZGBXHK26.cjs.map → chunk-5GN7TXHY.cjs.map} +1 -1
- package/dist/{chunk-F3FO2ZZZ.cjs → chunk-BAKZF4GU.cjs} +7 -7
- package/dist/{chunk-F3FO2ZZZ.cjs.map → chunk-BAKZF4GU.cjs.map} +1 -1
- package/dist/{chunk-MPHDM2VZ.cjs → chunk-BCL24DFU.cjs} +30 -30
- package/dist/{chunk-MPHDM2VZ.cjs.map → chunk-BCL24DFU.cjs.map} +1 -1
- package/dist/{chunk-65CPJ4SX.cjs → chunk-C6ASEPZL.cjs} +30 -30
- package/dist/{chunk-65CPJ4SX.cjs.map → chunk-C6ASEPZL.cjs.map} +1 -1
- package/dist/{chunk-SZXKKEUH.cjs → chunk-E6B4B7GM.cjs} +46 -30
- package/dist/chunk-E6B4B7GM.cjs.map +1 -0
- package/dist/{chunk-SRA2QYK6.js → chunk-HUA4XG4S.js} +4 -4
- package/dist/{chunk-A3PL6ZCF.js → chunk-KXF3WCPH.js} +6397 -5128
- package/dist/chunk-KXF3WCPH.js.map +1 -0
- package/dist/{chunk-SL3RGBPX.cjs → chunk-QNYH3WUU.cjs} +9 -9
- package/dist/{chunk-SL3RGBPX.cjs.map → chunk-QNYH3WUU.cjs.map} +1 -1
- package/dist/{chunk-24Q2WLIU.js → chunk-QYRV6MKX.js} +2 -2
- package/dist/{chunk-XG7DFRJL.js → chunk-RCECOGMI.js} +3 -3
- package/dist/{chunk-7UZLRI7W.cjs → chunk-SEECETJY.cjs} +3301 -2032
- package/dist/chunk-SEECETJY.cjs.map +1 -0
- package/dist/{chunk-ZII7TNVA.js → chunk-T5MEXJD5.js} +3 -3
- package/dist/{chunk-YX3YQNC4.cjs → chunk-W23DRJAA.cjs} +13 -13
- package/dist/{chunk-YX3YQNC4.cjs.map → chunk-W23DRJAA.cjs.map} +1 -1
- package/dist/{chunk-DRPECAXO.js → chunk-WR32ZE63.js} +2 -2
- package/dist/{chunk-Y3MXGCOW.js → chunk-YZDUOUMB.js} +46 -30
- package/dist/chunk-YZDUOUMB.js.map +1 -0
- package/dist/components/chat/chat-archive-page.d.ts +25 -0
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -0
- package/dist/components/chat/chat-composer.d.ts +29 -0
- package/dist/components/chat/chat-composer.d.ts.map +1 -0
- package/dist/components/chat/chat-header-icon-button.d.ts +14 -0
- package/dist/components/chat/chat-header-icon-button.d.ts.map +1 -0
- package/dist/components/chat/chat-panel-header.d.ts +32 -0
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -0
- package/dist/components/chat/embeddable-chat.d.ts +18 -0
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/guide-mode-banner.d.ts +16 -0
- package/dist/components/chat/guide-mode-banner.d.ts.map +1 -0
- package/dist/components/chat/guide-welcome.d.ts +40 -0
- package/dist/components/chat/guide-welcome.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts +58 -0
- package/dist/components/chat/hooks/use-chat-dialog-manager.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +26 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-unified-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +29 -5
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +9 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +28 -4
- package/dist/components/chat/mingo-chat-history.d.ts +37 -0
- package/dist/components/chat/mingo-chat-history.d.ts.map +1 -0
- package/dist/components/chat/mingo-chat-modals.d.ts +50 -0
- package/dist/components/chat/mingo-chat-modals.d.ts.map +1 -0
- package/dist/components/chat/mingo-onboarding-card.d.ts.map +1 -1
- package/dist/components/chat/mingo-welcome.d.ts +78 -0
- package/dist/components/chat/mingo-welcome.d.ts.map +1 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts +6 -0
- package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +6 -6
- package/dist/components/contact/index.js +5 -5
- package/dist/components/features/index.cjs +5 -5
- package/dist/components/features/index.js +4 -4
- package/dist/components/icons-v2-generated/brand-logos/fleet-mdm-logo-grey-icon.d.ts.map +1 -1
- package/dist/components/icons-v2-generated/brand-logos/fleet-mdm-logo-icon.d.ts.map +1 -1
- package/dist/components/icons-v2-generated/index.cjs +2 -2
- package/dist/components/icons-v2-generated/index.js +1 -1
- package/dist/components/index.cjs +128 -104
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +31 -7
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/page-heading.d.ts +7 -6
- package/dist/components/layout/page-heading.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/header-mingo-button.d.ts +4 -2
- package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +5 -5
- package/dist/components/navigation/index.js +4 -4
- package/dist/components/onboarding-guides/index.cjs +28 -28
- package/dist/components/onboarding-guides/index.js +6 -6
- package/dist/components/tickets/index.cjs +88 -88
- package/dist/components/tickets/index.js +7 -7
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +29 -5
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +28 -4
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/more-actions-menu.d.ts +8 -1
- package/dist/components/ui/more-actions-menu.d.ts.map +1 -1
- package/dist/components/ui/portal-container.d.ts +21 -0
- package/dist/components/ui/portal-container.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/index.cjs +29 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +28 -4
- package/package.json +1 -1
- package/src/components/chat/chat-archive-page.tsx +93 -0
- package/src/components/chat/chat-composer.tsx +99 -0
- package/src/components/chat/chat-header-icon-button.tsx +36 -0
- package/src/components/chat/chat-panel-header.tsx +114 -0
- package/src/components/chat/embeddable-chat.tsx +386 -311
- package/src/components/chat/guide-mode-banner.tsx +75 -0
- package/src/components/chat/guide-welcome.tsx +207 -0
- package/src/components/chat/hooks/use-chat-dialog-manager.ts +227 -0
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +85 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +8 -0
- package/src/components/chat/hooks/use-unified-chat.ts +12 -0
- package/src/components/chat/index.ts +9 -0
- package/src/components/chat/mingo-chat-history.tsx +308 -0
- package/src/components/chat/mingo-chat-modals.tsx +223 -0
- package/src/components/chat/mingo-onboarding-card.tsx +5 -8
- package/src/components/chat/mingo-welcome.tsx +396 -0
- package/src/components/chat/types/unified-chat-state.types.ts +8 -0
- package/src/components/icons-v2/brand-logos/fleet-mdm-logo-grey.svg +6 -6
- package/src/components/icons-v2/brand-logos/fleet-mdm-logo.svg +6 -6
- package/src/components/icons-v2-generated/brand-logos/fleet-mdm-logo-grey-icon.tsx +2 -22
- package/src/components/icons-v2-generated/brand-logos/fleet-mdm-logo-icon.tsx +22 -2
- package/src/components/layout/page-heading.tsx +13 -7
- package/src/components/navigation/app-header.tsx +12 -12
- package/src/components/navigation/app-layout-drawer.tsx +25 -15
- package/src/components/navigation/header-mingo-button.tsx +22 -7
- package/src/components/ui/dropdown-menu.tsx +9 -3
- package/src/components/ui/modal-v2.tsx +33 -3
- package/src/components/ui/more-actions-menu.tsx +15 -2
- package/src/components/ui/portal-container.tsx +28 -0
- package/src/components/ui/tooltip.tsx +9 -3
- package/src/stories/AppLayoutSidebar.stories.tsx +184 -0
- package/src/stories/EmbeddableChat.stories.tsx +114 -0
- package/src/stories/GuideWelcome.stories.tsx +102 -0
- package/src/stories/MingoChatModals.stories.tsx +82 -0
- package/src/stories/MingoWelcome.stories.tsx +119 -0
- package/dist/chunk-7UZLRI7W.cjs.map +0 -1
- package/dist/chunk-A3PL6ZCF.js.map +0 -1
- package/dist/chunk-SZXKKEUH.cjs.map +0 -1
- package/dist/chunk-Y3MXGCOW.js.map +0 -1
- /package/dist/{chunk-SRA2QYK6.js.map → chunk-HUA4XG4S.js.map} +0 -0
- /package/dist/{chunk-24Q2WLIU.js.map → chunk-QYRV6MKX.js.map} +0 -0
- /package/dist/{chunk-XG7DFRJL.js.map → chunk-RCECOGMI.js.map} +0 -0
- /package/dist/{chunk-ZII7TNVA.js.map → chunk-T5MEXJD5.js.map} +0 -0
- /package/dist/{chunk-DRPECAXO.js.map → chunk-WR32ZE63.js.map} +0 -0
|
@@ -42,19 +42,23 @@ import { Button } from '../ui/button'
|
|
|
42
42
|
import { Drawer, DrawerContent } from '../ui/drawer'
|
|
43
43
|
import { HoverDropdown, type HoverDropdownItem } from '../ui/hover-dropdown'
|
|
44
44
|
import { MingoIcon } from '../icons'
|
|
45
|
-
import { Chevron01LeftIcon } from '../icons-v2-generated/arrows/chevron-01-left-icon'
|
|
46
|
-
import { XmarkIcon } from '../icons-v2-generated/signs-and-symbols/xmark-icon'
|
|
47
45
|
|
|
48
|
-
import { ChatFooter } from './chat-container'
|
|
49
|
-
import { ChatInput } from './chat-input'
|
|
50
|
-
import { ChatSidebar } from './chat-sidebar'
|
|
51
46
|
import { MingoOnboardingCard } from './mingo-onboarding-card'
|
|
52
47
|
import { MingoOnboardingCardSkeleton } from './mingo-onboarding-card-skeleton'
|
|
48
|
+
import { MingoWelcome, type MingoWelcomeProps } from './mingo-welcome'
|
|
49
|
+
import { MingoChatHistory } from './mingo-chat-history'
|
|
50
|
+
import { GuideWelcome, type GuideWelcomeProps } from './guide-welcome'
|
|
51
|
+
import { GuideModeBanner } from './guide-mode-banner'
|
|
52
|
+
import { PortalContainerContext } from '../ui/portal-container'
|
|
53
|
+
import { ChatPanelHeader } from './chat-panel-header'
|
|
54
|
+
import { ChatArchivePage } from './chat-archive-page'
|
|
55
|
+
import { ChatComposer } from './chat-composer'
|
|
56
|
+
import { useChatDialogManager } from './hooks/use-chat-dialog-manager'
|
|
57
|
+
import { ChatDialogModals } from './mingo-chat-modals'
|
|
53
58
|
import { ChatMessageList } from './chat-message-list'
|
|
54
|
-
import { ModelDisplay } from './model-display'
|
|
55
59
|
import { SourceActionButton } from './source-action-button'
|
|
56
60
|
import { NavLinkAnchorViaRuntime } from './nav-link-anchor-via-runtime'
|
|
57
|
-
import {
|
|
61
|
+
import { ChatAttachmentChipStrip } from './chat-attachment-bar'
|
|
58
62
|
import { renderChatInlineEntityCard } from './entity-cards/dispatch'
|
|
59
63
|
import type { ChatCardDispatchExtras } from './entity-cards/dispatch'
|
|
60
64
|
|
|
@@ -66,7 +70,11 @@ import {
|
|
|
66
70
|
type ChatMode,
|
|
67
71
|
type UseUnifiedChatModes,
|
|
68
72
|
} from './hooks/use-unified-chat'
|
|
69
|
-
import type {
|
|
73
|
+
import type {
|
|
74
|
+
UseNatsChatAdapterConfig,
|
|
75
|
+
FetchDialogsParams,
|
|
76
|
+
FetchDialogsResult,
|
|
77
|
+
} from './hooks/use-nats-chat-adapter'
|
|
70
78
|
import { useChatAttachments } from './hooks/use-chat-attachments'
|
|
71
79
|
import { useChatAttachmentImageGallery } from './hooks/use-chat-attachment-image-gallery'
|
|
72
80
|
import { useChatIdentity } from './hooks/use-chat-identity'
|
|
@@ -183,6 +191,27 @@ export interface EmbeddableChatProps {
|
|
|
183
191
|
* drives).
|
|
184
192
|
*/
|
|
185
193
|
shell?: 'drawer' | 'none'
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Content overrides for the default (Mingo-mode) empty state
|
|
197
|
+
* (`<MingoWelcome>`): greeting `title`/`subtitle`, the capability
|
|
198
|
+
* `featureCards` grid, the `promo` card, and extra `quickActions` chips.
|
|
199
|
+
* Each field falls back to the built-in OpenFrame defaults, so the kit
|
|
200
|
+
* stays platform-agnostic. `userName`, `onStartGuideChat` and
|
|
201
|
+
* `hasExistingChats` are wired internally and are NOT overridable here.
|
|
202
|
+
*/
|
|
203
|
+
mingoWelcome?: Omit<
|
|
204
|
+
MingoWelcomeProps,
|
|
205
|
+
'userName' | 'onStartGuideChat' | 'hasExistingChats'
|
|
206
|
+
>
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Content overrides for the Guide-mode empty state (`<GuideWelcome>`):
|
|
210
|
+
* greeting `title`/`subtitle` and the `quickActions` chips. Each field falls
|
|
211
|
+
* back to the built-in OpenFrame defaults. `onQuickAction` and the
|
|
212
|
+
* slash-command list `children` are wired internally and not overridable.
|
|
213
|
+
*/
|
|
214
|
+
guideWelcome?: Omit<GuideWelcomeProps, 'onQuickAction' | 'children'>
|
|
186
215
|
}
|
|
187
216
|
|
|
188
217
|
// =============================================================================
|
|
@@ -580,6 +609,8 @@ function EmbeddableChatInner({
|
|
|
580
609
|
onActiveModeChange,
|
|
581
610
|
defaultActiveMode,
|
|
582
611
|
shell = 'drawer',
|
|
612
|
+
mingoWelcome,
|
|
613
|
+
guideWelcome,
|
|
583
614
|
}: EmbeddableChatProps) {
|
|
584
615
|
// `shell === 'none'` means the consumer hosts us inside their own panel
|
|
585
616
|
// (e.g. AppLayoutDrawer in openframe-frontend). Several drawer-shell
|
|
@@ -764,10 +795,6 @@ function EmbeddableChatInner({
|
|
|
764
795
|
[controlledActiveMode, onActiveModeChange],
|
|
765
796
|
)
|
|
766
797
|
|
|
767
|
-
// Mode toggle visible only when both slots are populated.
|
|
768
|
-
const showModeToggle =
|
|
769
|
-
effectiveModes.guide !== undefined && effectiveModes.mingo !== undefined
|
|
770
|
-
|
|
771
798
|
const {
|
|
772
799
|
messages: rawMessages,
|
|
773
800
|
isLoading: chatLoading,
|
|
@@ -783,46 +810,17 @@ function EmbeddableChatInner({
|
|
|
783
810
|
currentCacheHitRatePct,
|
|
784
811
|
currentUsageBreakdown,
|
|
785
812
|
displayRef,
|
|
786
|
-
// ─── Dialog management (Mingo-mode
|
|
813
|
+
// ─── Dialog management (Mingo-mode inline history) ───
|
|
787
814
|
dialogs,
|
|
788
815
|
activeDialogId,
|
|
789
816
|
selectDialog,
|
|
790
|
-
|
|
817
|
+
renameDialog,
|
|
818
|
+
archiveDialog,
|
|
791
819
|
isDialogsLoading,
|
|
792
820
|
hasMoreDialogs,
|
|
793
821
|
loadMoreDialogs,
|
|
794
822
|
} = useUnifiedChat({ modes: effectiveModes, activeMode })
|
|
795
823
|
|
|
796
|
-
// Whether the in-panel dialog sidebar should render. Gated on:
|
|
797
|
-
// 1. Mingo mode is active (Guide has localStorage-only history that
|
|
798
|
-
// isn't yet structured as a sidebar list — see
|
|
799
|
-
// [[chat-architecture-and-migration]] for the asymmetry).
|
|
800
|
-
// 2. The host wired `fetchDialogs` — managed-dialog mode, not the
|
|
801
|
-
// bare-transport Tauri flow.
|
|
802
|
-
const showSidebar =
|
|
803
|
-
activeMode === 'mingo' && effectiveModes.mingo?.fetchDialogs !== undefined
|
|
804
|
-
|
|
805
|
-
// Pending startNewDialog promise — used to gate the "Start new chat"
|
|
806
|
-
// button while creation is in flight so a double-click doesn't spawn
|
|
807
|
-
// two backend dialogs.
|
|
808
|
-
const [isStartingNewDialog, setIsStartingNewDialog] = useState<boolean>(false)
|
|
809
|
-
const handleStartNewDialog = useCallback(async () => {
|
|
810
|
-
if (isStartingNewDialog) return
|
|
811
|
-
setIsStartingNewDialog(true)
|
|
812
|
-
try {
|
|
813
|
-
await startNewDialog()
|
|
814
|
-
} finally {
|
|
815
|
-
setIsStartingNewDialog(false)
|
|
816
|
-
}
|
|
817
|
-
}, [isStartingNewDialog, startNewDialog])
|
|
818
|
-
|
|
819
|
-
const handleSelectDialog = useCallback(
|
|
820
|
-
(id: string) => {
|
|
821
|
-
selectDialog(id)
|
|
822
|
-
},
|
|
823
|
-
[selectDialog],
|
|
824
|
-
)
|
|
825
|
-
|
|
826
824
|
// Chat-attachment hooks (v2 attachment feature).
|
|
827
825
|
const {
|
|
828
826
|
attachments: stagedAttachments,
|
|
@@ -918,9 +916,44 @@ function EmbeddableChatInner({
|
|
|
918
916
|
[sendMessage, readyAttachments, viewUrlPrefix, clearAttachments],
|
|
919
917
|
)
|
|
920
918
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
919
|
+
// Dialog-history concerns (archive page, read-only archived conversation,
|
|
920
|
+
// rename/archive/unarchive modals) live in one hook so this component stays
|
|
921
|
+
// a thin orchestrator. Destructured with the names the JSX below uses.
|
|
922
|
+
const {
|
|
923
|
+
fetchArchivedDialogs,
|
|
924
|
+
unarchiveDialog,
|
|
925
|
+
archiveOpen,
|
|
926
|
+
archivedDialogs,
|
|
927
|
+
archivedCursor,
|
|
928
|
+
archivedLoading,
|
|
929
|
+
openArchive,
|
|
930
|
+
closeArchive,
|
|
931
|
+
loadArchivedPage,
|
|
932
|
+
handleArchivedSelect,
|
|
933
|
+
handleSelectDialog,
|
|
934
|
+
isOpeningDialog,
|
|
935
|
+
isViewingArchived,
|
|
936
|
+
handleBack,
|
|
937
|
+
activeDialog,
|
|
938
|
+
renameTarget,
|
|
939
|
+
setRenameTarget,
|
|
940
|
+
archiveTarget,
|
|
941
|
+
setArchiveTarget,
|
|
942
|
+
restoreTarget,
|
|
943
|
+
setRestoreTarget,
|
|
944
|
+
handleConfirmRename,
|
|
945
|
+
handleConfirmArchive,
|
|
946
|
+
handleConfirmRestore,
|
|
947
|
+
} = useChatDialogManager({
|
|
948
|
+
dialogs,
|
|
949
|
+
activeDialogId,
|
|
950
|
+
selectDialog,
|
|
951
|
+
clearMessages,
|
|
952
|
+
renameDialog,
|
|
953
|
+
archiveDialog,
|
|
954
|
+
fetchArchivedDialogs: effectiveModes.mingo?.fetchArchivedDialogs,
|
|
955
|
+
unarchiveDialog: effectiveModes.mingo?.unarchiveDialog,
|
|
956
|
+
})
|
|
924
957
|
|
|
925
958
|
const handleOpen = useCallback(() => setIsOpen(true), [setIsOpen])
|
|
926
959
|
|
|
@@ -950,9 +983,24 @@ function EmbeddableChatInner({
|
|
|
950
983
|
}, [source, discussRef, setIsOpen])
|
|
951
984
|
|
|
952
985
|
const hasMessages = messages.length > 0
|
|
986
|
+
// A conversation is "open" the instant a chat is selected (`isOpeningDialog` /
|
|
987
|
+
// `isViewingArchived` are set synchronously on click) — not only once its
|
|
988
|
+
// history has loaded (`hasMessages`). Driving the surface + content branch
|
|
989
|
+
// off this makes the normal-chat open animate identically to the archived
|
|
990
|
+
// one instead of lagging behind the message fetch.
|
|
991
|
+
const hasConversation = hasMessages || isOpeningDialog || isViewingArchived
|
|
992
|
+
// Keys the content region so each distinct view (open conversation, Mingo
|
|
993
|
+
// welcome, Guide onboarding) remounts on switch → fades in for 200ms, the
|
|
994
|
+
// same feel as the surface flip. Switching dialogs stays "conversation" so
|
|
995
|
+
// streaming messages don't re-trigger the fade.
|
|
996
|
+
const contentViewKey = hasConversation ? 'conversation' : activeMode
|
|
997
|
+
// Guide-mode empty state (no open conversation) — drives the "Mingo Guide"
|
|
998
|
+
// header, the guide banner, and the GuideWelcome content branch.
|
|
999
|
+
const isGuideEmpty = !hasConversation && activeMode === 'guide'
|
|
1000
|
+
// The guide-empty back-chevron returns to Mingo — only offer it when Mingo
|
|
1001
|
+
// mode actually exists to return to (guide is normally entered from Mingo).
|
|
1002
|
+
const guideCanReturnToMingo = isGuideEmpty && !!effectiveModes.mingo
|
|
953
1003
|
const sourceLabel = source === 'flamingo' ? 'Knowledge Base' : 'Data Room'
|
|
954
|
-
const greetingText =
|
|
955
|
-
emptyStateGreeting || `Ask me anything about ${sourceLabel.toLowerCase()}.`
|
|
956
1004
|
|
|
957
1005
|
// Empty-state chip grid — derived directly from the fetched slash commands.
|
|
958
1006
|
const enabledSet = useMemo(
|
|
@@ -998,138 +1046,121 @@ function EmbeddableChatInner({
|
|
|
998
1046
|
}, [lastAssistantMsg, chatLoading])
|
|
999
1047
|
|
|
1000
1048
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1049
|
+
// Host node for in-panel Radix portals (see the body wrapper below).
|
|
1050
|
+
const [portalHost, setPortalHost] = useState<HTMLDivElement | null>(null)
|
|
1051
|
+
|
|
1052
|
+
// Chat body — defined once, then rendered inside whichever shell applies.
|
|
1053
|
+
// Radix overlays (⋯ menus, tooltips) portal into `portalHost` — a node inside
|
|
1054
|
+
// this panel — so they inherit the drawer's stacking context and need only a
|
|
1055
|
+
// small, local z-index instead of escalating to beat the drawer at the
|
|
1056
|
+
// document root. See `PortalContainerContext`.
|
|
1057
|
+
const body = (
|
|
1058
|
+
<PortalContainerContext.Provider value={portalHost}>
|
|
1059
|
+
{/* Panel surface depends on state (Figma):
|
|
1060
|
+
• Mingo empty / returning-user + archive page → grey
|
|
1061
|
+
`ods-card` (#212121),
|
|
1062
|
+
• Guide empty (node 7532:328223) + active conversation → dark
|
|
1063
|
+
`ods-bg` (#161616).
|
|
1064
|
+
The header keeps its own grey `bg-ods-card`; the content and
|
|
1065
|
+
footer have no bg and inherit this root, so they flip together. */}
|
|
1066
|
+
<div
|
|
1067
|
+
className={`flex h-full flex-col overflow-hidden transition-colors duration-200 ${
|
|
1068
|
+
archiveOpen || (!hasConversation && activeMode === 'mingo')
|
|
1069
|
+
? 'bg-ods-card'
|
|
1070
|
+
: 'bg-ods-bg'
|
|
1020
1071
|
}`}
|
|
1021
1072
|
>
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
{/*
|
|
1029
|
-
Conditional shell — overlay (Drawer, MPH default) vs inline
|
|
1030
|
-
(positioned `<aside>` anchored to the nearest positioned
|
|
1031
|
-
ancestor, openframe-frontend's nav-embedded chat). Both share
|
|
1032
|
-
the same `body` JSX so the chat content is defined exactly
|
|
1033
|
-
once; the IIFE keeps both branches type-balanced inside JSX
|
|
1034
|
-
(no opening tag in one ternary branch + closing in another).
|
|
1035
|
-
*/}
|
|
1036
|
-
{(() => {
|
|
1037
|
-
const body = (
|
|
1038
|
-
<>
|
|
1039
|
-
<div className="flex h-full flex-col overflow-hidden">
|
|
1040
|
-
{/* Figma node 7363:205930 — top-navigation. Title intentionally
|
|
1041
|
-
omitted; a chevron-left + "New Chat" back-style affordance
|
|
1042
|
-
appears on the left when a conversation is active
|
|
1043
|
-
(hasMessages) and resets the chat. Close on the right.
|
|
1044
|
-
Left-borders act as 1px cell dividers. */}
|
|
1045
|
-
<div className="flex-shrink-0 flex h-14 w-full overflow-hidden border-b border-ods-border bg-ods-card">
|
|
1046
|
-
<div className="flex flex-1 min-w-0 items-center gap-2 px-4 py-3">
|
|
1047
|
-
{hasMessages ? (
|
|
1048
|
-
<>
|
|
1049
|
-
<button
|
|
1050
|
-
type="button"
|
|
1051
|
-
onClick={handleNewChat}
|
|
1052
|
-
aria-label="Start a new chat"
|
|
1053
|
-
className="inline-flex shrink-0 items-center justify-center size-8 -ml-1 rounded-md 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"
|
|
1054
|
-
>
|
|
1055
|
-
<Chevron01LeftIcon size={20} />
|
|
1056
|
-
</button>
|
|
1057
|
-
<span className="truncate text-h3 text-ods-text-primary">
|
|
1058
|
-
New Chat
|
|
1059
|
-
</span>
|
|
1060
|
-
</>
|
|
1061
|
-
) : (
|
|
1062
|
-
<p className="truncate text-h4 text-ods-text-secondary">
|
|
1063
|
-
Start a conversation
|
|
1064
|
-
</p>
|
|
1065
|
-
)}
|
|
1066
|
-
</div>
|
|
1067
|
-
<button
|
|
1068
|
-
type="button"
|
|
1069
|
-
onClick={handleClose}
|
|
1070
|
-
className="flex size-14 shrink-0 items-center justify-center border-l border-ods-border text-ods-text-primary transition-colors hover:bg-ods-bg-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent"
|
|
1071
|
-
aria-label="Close"
|
|
1073
|
+
{/* Archive-page ↔ chat-panel swap fades in (200ms) to match the
|
|
1074
|
+
surface flip — both branches share the same view-change feel. */}
|
|
1075
|
+
{archiveOpen ? (
|
|
1076
|
+
<div
|
|
1077
|
+
key="archive-view"
|
|
1078
|
+
className="flex flex-1 min-h-0 flex-col animate-in fade-in-0 duration-200"
|
|
1072
1079
|
>
|
|
1073
|
-
<
|
|
1074
|
-
|
|
1075
|
-
|
|
1080
|
+
<ChatArchivePage
|
|
1081
|
+
dialogs={archivedDialogs}
|
|
1082
|
+
onSelectDialog={handleArchivedSelect}
|
|
1083
|
+
onBack={closeArchive}
|
|
1084
|
+
onClose={handleClose}
|
|
1085
|
+
isLoading={archivedLoading}
|
|
1086
|
+
hasMore={archivedCursor != null}
|
|
1087
|
+
onLoadMore={() => {
|
|
1088
|
+
void loadArchivedPage(archivedCursor ?? undefined)
|
|
1089
|
+
}}
|
|
1090
|
+
/>
|
|
1091
|
+
</div>
|
|
1092
|
+
) : (
|
|
1093
|
+
<div
|
|
1094
|
+
key="chat-view"
|
|
1095
|
+
className="flex flex-1 min-h-0 flex-col animate-in fade-in-0 duration-200"
|
|
1096
|
+
>
|
|
1097
|
+
<ChatPanelHeader
|
|
1098
|
+
// Guide-mode empty state shows a back-chevron + "Mingo Guide"
|
|
1099
|
+
// (back returns to the default Mingo welcome); an open
|
|
1100
|
+
// conversation shows back + the dialog title; the Mingo list
|
|
1101
|
+
// keeps the static "Current Chats" title.
|
|
1102
|
+
showBack={hasConversation || guideCanReturnToMingo}
|
|
1103
|
+
title={
|
|
1104
|
+
hasConversation
|
|
1105
|
+
? activeDialog?.title || 'New Chat'
|
|
1106
|
+
: isGuideEmpty
|
|
1107
|
+
? 'Mingo Guide'
|
|
1108
|
+
: 'Current Chats'
|
|
1109
|
+
}
|
|
1110
|
+
backAriaLabel={
|
|
1111
|
+
hasConversation
|
|
1112
|
+
? isViewingArchived
|
|
1113
|
+
? 'Back to archive'
|
|
1114
|
+
: 'Back'
|
|
1115
|
+
: 'Back to Mingo'
|
|
1116
|
+
}
|
|
1117
|
+
isArchivedView={isViewingArchived}
|
|
1118
|
+
onBack={
|
|
1119
|
+
hasConversation
|
|
1120
|
+
? handleBack
|
|
1121
|
+
: () => handleActiveModeChange('mingo')
|
|
1122
|
+
}
|
|
1123
|
+
onClose={handleClose}
|
|
1124
|
+
onRestore={
|
|
1125
|
+
isViewingArchived && unarchiveDialog && activeDialog
|
|
1126
|
+
? () => setRestoreTarget(activeDialog)
|
|
1127
|
+
: undefined
|
|
1128
|
+
}
|
|
1129
|
+
onRename={
|
|
1130
|
+
activeDialogId && activeDialog && effectiveModes.mingo?.renameDialog
|
|
1131
|
+
? () => setRenameTarget(activeDialog)
|
|
1132
|
+
: undefined
|
|
1133
|
+
}
|
|
1134
|
+
onArchive={
|
|
1135
|
+
activeDialogId && activeDialog && effectiveModes.mingo?.archiveDialog
|
|
1136
|
+
? () => setArchiveTarget(activeDialog)
|
|
1137
|
+
: undefined
|
|
1138
|
+
}
|
|
1139
|
+
onOpenArchive={fetchArchivedDialogs ? openArchive : undefined}
|
|
1140
|
+
/>
|
|
1141
|
+
|
|
1142
|
+
{/* Guide-mode indicator banner (Figma node 7532:328222) —
|
|
1143
|
+
full-bleed accent strip below the header whenever Guide mode
|
|
1144
|
+
is active. Fades in to match the panel's view transitions. */}
|
|
1145
|
+
{activeMode === 'guide' && (
|
|
1146
|
+
<GuideModeBanner className="animate-in fade-in-0 duration-200" />
|
|
1147
|
+
)}
|
|
1076
1148
|
|
|
1077
|
-
{/*
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
history in localStorage and currently renders no sidebar. */}
|
|
1149
|
+
{/* Chat-panel row. The dialog history is rendered inline in the
|
|
1150
|
+
Mingo empty state (`<MingoChatHistory>`), so there's no
|
|
1151
|
+
separate left sidebar. */}
|
|
1081
1152
|
<div className="flex flex-1 min-h-0 overflow-hidden">
|
|
1082
|
-
{showSidebar && (
|
|
1083
|
-
<ChatSidebar
|
|
1084
|
-
className="w-72 shrink-0"
|
|
1085
|
-
dialogs={dialogs}
|
|
1086
|
-
activeDialogId={activeDialogId ?? undefined}
|
|
1087
|
-
onDialogSelect={handleSelectDialog}
|
|
1088
|
-
onNewChat={() => { void handleStartNewDialog() }}
|
|
1089
|
-
isLoading={isDialogsLoading && dialogs.length === 0}
|
|
1090
|
-
isCreatingDialog={isStartingNewDialog}
|
|
1091
|
-
hasNextPage={hasMoreDialogs}
|
|
1092
|
-
isFetchingNextPage={isDialogsLoading && dialogs.length > 0}
|
|
1093
|
-
onLoadMore={() => { void loadMoreDialogs() }}
|
|
1094
|
-
/>
|
|
1095
|
-
)}
|
|
1096
1153
|
<div className="flex flex-1 flex-col min-h-0 min-w-0">
|
|
1097
1154
|
|
|
1098
|
-
{showModeToggle ? (
|
|
1099
|
-
<div
|
|
1100
|
-
role="radiogroup"
|
|
1101
|
-
aria-label="Chat mode"
|
|
1102
|
-
className="flex-shrink-0 mx-5 mt-3 inline-flex rounded-lg border border-ods-border bg-ods-bg-secondary p-0.5 self-start"
|
|
1103
|
-
>
|
|
1104
|
-
{(['mingo', 'guide'] as ChatMode[]).map((m) => {
|
|
1105
|
-
const isActive = activeMode === m
|
|
1106
|
-
const label = m === 'mingo' ? 'Mingo' : 'Guide'
|
|
1107
|
-
return (
|
|
1108
|
-
<button
|
|
1109
|
-
key={m}
|
|
1110
|
-
type="button"
|
|
1111
|
-
role="radio"
|
|
1112
|
-
aria-checked={isActive}
|
|
1113
|
-
onClick={() => handleActiveModeChange(m)}
|
|
1114
|
-
className={
|
|
1115
|
-
'px-3 py-1 text-sm rounded-md transition-colors ' +
|
|
1116
|
-
(isActive
|
|
1117
|
-
? 'bg-ods-accent text-ods-text-on-accent'
|
|
1118
|
-
: 'text-ods-text-secondary hover:text-ods-text-primary')
|
|
1119
|
-
}
|
|
1120
|
-
>
|
|
1121
|
-
{label}
|
|
1122
|
-
</button>
|
|
1123
|
-
)
|
|
1124
|
-
})}
|
|
1125
|
-
</div>
|
|
1126
|
-
) : null}
|
|
1127
|
-
|
|
1128
1155
|
<div
|
|
1129
1156
|
ref={galleryPanelRef}
|
|
1130
|
-
className="flex-1 flex flex-col min-h-0
|
|
1157
|
+
className="flex-1 flex flex-col min-h-0 p-[var(--spacing-system-m)]"
|
|
1131
1158
|
>
|
|
1132
|
-
|
|
1159
|
+
<div
|
|
1160
|
+
key={contentViewKey}
|
|
1161
|
+
className="flex-1 flex flex-col min-h-0 animate-in fade-in-0 duration-200"
|
|
1162
|
+
>
|
|
1163
|
+
{hasConversation ? (
|
|
1133
1164
|
<div className="flex-1 flex flex-col min-h-0">
|
|
1134
1165
|
<ChatMessageList
|
|
1135
1166
|
messages={messages}
|
|
@@ -1158,38 +1189,67 @@ function EmbeddableChatInner({
|
|
|
1158
1189
|
</div>
|
|
1159
1190
|
)}
|
|
1160
1191
|
</div>
|
|
1192
|
+
) : activeMode === 'mingo' ? (
|
|
1193
|
+
/* Figma node 7532:222444 — default (Mingo-mode) empty state:
|
|
1194
|
+
centred greeting + capability grid + Guide-chat promo +
|
|
1195
|
+
quick-action chips. Guide mode keeps the slash-command
|
|
1196
|
+
onboarding list below. */
|
|
1197
|
+
<MingoWelcome
|
|
1198
|
+
userName={userName}
|
|
1199
|
+
onStartGuideChat={
|
|
1200
|
+
effectiveModes.guide
|
|
1201
|
+
? () => handleActiveModeChange('guide')
|
|
1202
|
+
: undefined
|
|
1203
|
+
}
|
|
1204
|
+
{...mingoWelcome}
|
|
1205
|
+
// Derived internally (returning-user variation) — placed
|
|
1206
|
+
// after the spread so it can't be overridden by the prop.
|
|
1207
|
+
hasExistingChats={dialogs.length > 0}
|
|
1208
|
+
dialogHistory={
|
|
1209
|
+
dialogs.length > 0 ? (
|
|
1210
|
+
<MingoChatHistory
|
|
1211
|
+
dialogs={dialogs}
|
|
1212
|
+
onSelectDialog={handleSelectDialog}
|
|
1213
|
+
onRequestRename={
|
|
1214
|
+
effectiveModes.mingo?.renameDialog
|
|
1215
|
+
? setRenameTarget
|
|
1216
|
+
: undefined
|
|
1217
|
+
}
|
|
1218
|
+
onRequestArchive={
|
|
1219
|
+
effectiveModes.mingo?.archiveDialog
|
|
1220
|
+
? setArchiveTarget
|
|
1221
|
+
: undefined
|
|
1222
|
+
}
|
|
1223
|
+
hasMore={hasMoreDialogs}
|
|
1224
|
+
isLoadingMore={isDialogsLoading && dialogs.length > 0}
|
|
1225
|
+
onLoadMore={() => {
|
|
1226
|
+
void loadMoreDialogs()
|
|
1227
|
+
}}
|
|
1228
|
+
/>
|
|
1229
|
+
) : undefined
|
|
1230
|
+
}
|
|
1231
|
+
/>
|
|
1161
1232
|
) : (
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
</p>
|
|
1181
|
-
</div>
|
|
1182
|
-
</div>
|
|
1183
|
-
|
|
1184
|
-
{/* Figma node 7363:205938 — single-column slash-command list.
|
|
1185
|
-
The list container has no own bg (inherits the darker
|
|
1186
|
-
`ods-bg` from the drawer panel); each card has the
|
|
1187
|
-
lighter `ods-card` to pop on the dark surface.
|
|
1188
|
-
`flex-1 min-h-0 overflow-y-auto` makes the LIST the
|
|
1189
|
-
scroll target — the rounded-md frame stays visible
|
|
1190
|
-
while inner cards scroll past it. */}
|
|
1233
|
+
/* Figma node 7532:328214 — Guide-mode empty state: greeting
|
|
1234
|
+
+ slash-command onboarding list share one scroll region,
|
|
1235
|
+
with a pinned quick-action chip row above the composer. */
|
|
1236
|
+
<GuideWelcome
|
|
1237
|
+
// Legacy `emptyStateGreeting` still customises the guide
|
|
1238
|
+
// subtitle; an explicit `guideWelcome.subtitle` wins.
|
|
1239
|
+
subtitle={emptyStateGreeting ?? undefined}
|
|
1240
|
+
{...guideWelcome}
|
|
1241
|
+
onQuickAction={(action) => {
|
|
1242
|
+
chatInputRef.current?.setValue(
|
|
1243
|
+
action.prompt ?? action.label,
|
|
1244
|
+
)
|
|
1245
|
+
chatInputRef.current?.focus()
|
|
1246
|
+
}}
|
|
1247
|
+
>
|
|
1248
|
+
{/* Figma node 7363:205938 — single-column slash-command
|
|
1249
|
+
list. No own scroll (GuideWelcome's region scrolls); the
|
|
1250
|
+
rounded-md frame holds the cards on the dark surface. */}
|
|
1191
1251
|
{(chipCommands.length > 0 || !commandsLoaded) && (
|
|
1192
|
-
<div className="
|
|
1252
|
+
<div className="shrink-0 overflow-hidden rounded-md border border-ods-border">
|
|
1193
1253
|
{!commandsLoaded &&
|
|
1194
1254
|
chipCommands.length === 0 &&
|
|
1195
1255
|
SKELETON_ROW_VARIANTS.map((variant, i) => (
|
|
@@ -1227,8 +1287,9 @@ function EmbeddableChatInner({
|
|
|
1227
1287
|
})}
|
|
1228
1288
|
</div>
|
|
1229
1289
|
)}
|
|
1230
|
-
</
|
|
1290
|
+
</GuideWelcome>
|
|
1231
1291
|
)}
|
|
1292
|
+
</div>
|
|
1232
1293
|
</div>
|
|
1233
1294
|
|
|
1234
1295
|
<ChatAttachmentChipStrip
|
|
@@ -1237,122 +1298,136 @@ function EmbeddableChatInner({
|
|
|
1237
1298
|
disabled={chatLoading}
|
|
1238
1299
|
/>
|
|
1239
1300
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
// capability flag. Skipping the render entirely (not
|
|
1272
|
-
// just the icon) collapses the otherwise-invisible 28px
|
|
1273
|
-
// placeholder slot the component leaves for layout.
|
|
1274
|
-
<ChatAttachmentAddButton
|
|
1275
|
-
attachmentsEnabled
|
|
1276
|
-
attachmentsCount={stagedAttachments.length}
|
|
1277
|
-
onAddFiles={addAttachmentFiles}
|
|
1278
|
-
disabled={chatLoading}
|
|
1279
|
-
/>
|
|
1280
|
-
)}
|
|
1281
|
-
<div className="flex-1 min-w-0">
|
|
1282
|
-
<ModelDisplay
|
|
1283
|
-
provider={currentProvider ?? 'anthropic'}
|
|
1284
|
-
modelName={currentModelLabel ?? 'Claude'}
|
|
1285
|
-
usedTokens={
|
|
1286
|
-
currentInputTokens != null
|
|
1287
|
-
? (currentInputTokens ?? 0) + (currentOutputTokens ?? 0)
|
|
1288
|
-
: undefined
|
|
1289
|
-
}
|
|
1290
|
-
contextWindow={currentContextWindowMaxTokens ?? undefined}
|
|
1291
|
-
inputTokens={currentInputTokens ?? undefined}
|
|
1292
|
-
outputTokens={currentOutputTokens ?? undefined}
|
|
1293
|
-
hitRatePct={currentCacheHitRatePct ?? undefined}
|
|
1294
|
-
breakdown={currentUsageBreakdown ?? undefined}
|
|
1295
|
-
/>
|
|
1296
|
-
</div>
|
|
1301
|
+
<ChatComposer
|
|
1302
|
+
archived={isViewingArchived}
|
|
1303
|
+
inputRef={chatInputRef}
|
|
1304
|
+
onSend={handleSend}
|
|
1305
|
+
onStop={stopMessage}
|
|
1306
|
+
sending={chatLoading || hasInflightUploads}
|
|
1307
|
+
placeholder={
|
|
1308
|
+
hasInflightUploads
|
|
1309
|
+
? 'Waiting for uploads to finish…'
|
|
1310
|
+
: 'Ask a question...'
|
|
1311
|
+
}
|
|
1312
|
+
autoFocus={autoFocusInput}
|
|
1313
|
+
slashCommands={slashCommandsProp}
|
|
1314
|
+
showAttachmentButton={attachmentsEnabled && activeMode === 'guide'}
|
|
1315
|
+
attachmentsCount={stagedAttachments.length}
|
|
1316
|
+
onAddFiles={addAttachmentFiles}
|
|
1317
|
+
attachmentsDisabled={chatLoading}
|
|
1318
|
+
model={{
|
|
1319
|
+
provider: currentProvider ?? 'anthropic',
|
|
1320
|
+
modelName: currentModelLabel ?? 'Claude',
|
|
1321
|
+
usedTokens:
|
|
1322
|
+
currentInputTokens != null
|
|
1323
|
+
? (currentInputTokens ?? 0) + (currentOutputTokens ?? 0)
|
|
1324
|
+
: undefined,
|
|
1325
|
+
contextWindow: currentContextWindowMaxTokens ?? undefined,
|
|
1326
|
+
inputTokens: currentInputTokens ?? undefined,
|
|
1327
|
+
outputTokens: currentOutputTokens ?? undefined,
|
|
1328
|
+
hitRatePct: currentCacheHitRatePct ?? undefined,
|
|
1329
|
+
breakdown: currentUsageBreakdown ?? undefined,
|
|
1330
|
+
}}
|
|
1331
|
+
/>
|
|
1297
1332
|
</div>
|
|
1298
1333
|
</div>
|
|
1299
|
-
</div>
|
|
1300
1334
|
</div>
|
|
1335
|
+
)}
|
|
1301
1336
|
</div>
|
|
1302
1337
|
{galleryModal}
|
|
1303
|
-
</>
|
|
1304
|
-
)
|
|
1305
1338
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
{
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1339
|
+
{/* Rename / Archive / Unarchive chat modals (Figma 7592:225962,
|
|
1340
|
+
7592:226181). Triggered from the header ⋯ and the dialog-history
|
|
1341
|
+
rows; rendered inside the panel so they overlay the chat. */}
|
|
1342
|
+
<ChatDialogModals
|
|
1343
|
+
renameTarget={renameTarget}
|
|
1344
|
+
setRenameTarget={setRenameTarget}
|
|
1345
|
+
onConfirmRename={handleConfirmRename}
|
|
1346
|
+
archiveTarget={archiveTarget}
|
|
1347
|
+
setArchiveTarget={setArchiveTarget}
|
|
1348
|
+
onConfirmArchive={handleConfirmArchive}
|
|
1349
|
+
restoreTarget={restoreTarget}
|
|
1350
|
+
setRestoreTarget={setRestoreTarget}
|
|
1351
|
+
onConfirmRestore={handleConfirmRestore}
|
|
1352
|
+
/>
|
|
1317
1353
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1354
|
+
{/* Portal target for in-panel Radix overlays — `display: contents`
|
|
1355
|
+
so it adds no box; content is positioned `fixed` by Radix. */}
|
|
1356
|
+
<div ref={setPortalHost} style={{ display: 'contents' }} />
|
|
1357
|
+
</PortalContainerContext.Provider>
|
|
1358
|
+
)
|
|
1359
|
+
|
|
1360
|
+
// Conditional shell: inline (shell-less host, e.g. AppLayoutDrawer, which
|
|
1361
|
+
// provides its own chrome/focus-trap/size) vs the body-level Radix Drawer
|
|
1362
|
+
// (backdrop, iOS scroll-lock, drag-to-resize). Both render the same `body`.
|
|
1363
|
+
const panel = shellLess ? (
|
|
1364
|
+
<ChatPanelContext.Provider value={chatPanelHandle}>
|
|
1365
|
+
{/* Shell-less hosts (AppLayoutDrawer) own the Radix Dialog but not its
|
|
1366
|
+
accessible name — Radix warns when a Dialog.Content has no Title. The
|
|
1367
|
+
panel supplies its own visually-hidden title here, mirroring the
|
|
1368
|
+
drawer-shell branch below, so it's accessible regardless of host. */}
|
|
1369
|
+
<VisuallyHidden>
|
|
1370
|
+
<DialogPrimitive.Title>{sourceLabel} AI Assistant</DialogPrimitive.Title>
|
|
1371
|
+
</VisuallyHidden>
|
|
1372
|
+
{body}
|
|
1373
|
+
</ChatPanelContext.Provider>
|
|
1374
|
+
) : (
|
|
1375
|
+
<Drawer open={isOpen} onOpenChange={(o: boolean) => !o && handleClose()}>
|
|
1376
|
+
<ChatPanelContext.Provider value={chatPanelHandle}>
|
|
1377
|
+
{/* Panel-level handle for descendants (inline cards, markdown-body
|
|
1378
|
+
links) to close the panel after same-tab navigation. */}
|
|
1379
|
+
<DrawerContent
|
|
1380
|
+
side="right"
|
|
1381
|
+
flush
|
|
1382
|
+
resizable
|
|
1383
|
+
minSize={480}
|
|
1384
|
+
maxSize={1600}
|
|
1385
|
+
defaultSize={750}
|
|
1386
|
+
storageKey="mingo-chat-width"
|
|
1387
|
+
resizeAriaLabel="Resize chat panel"
|
|
1388
|
+
overlayClassName="mingo-chat-overlay md:bg-black/20"
|
|
1389
|
+
aria-describedby={undefined}
|
|
1390
|
+
className="
|
|
1391
|
+
mingo-chat-content !bg-ods-bg shadow-2xl
|
|
1392
|
+
focus:outline-none focus-visible:outline-none
|
|
1393
|
+
w-screen md:w-auto
|
|
1394
|
+
"
|
|
1395
|
+
>
|
|
1396
|
+
<VisuallyHidden>
|
|
1397
|
+
<DialogPrimitive.Title>{sourceLabel} AI Assistant</DialogPrimitive.Title>
|
|
1398
|
+
</VisuallyHidden>
|
|
1399
|
+
{body}
|
|
1400
|
+
</DrawerContent>
|
|
1401
|
+
</ChatPanelContext.Provider>
|
|
1402
|
+
</Drawer>
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1405
|
+
return (
|
|
1406
|
+
<>
|
|
1407
|
+
{/* Floating "Ask AI" button — sticky-dock pattern. Suppressed in
|
|
1408
|
+
shell-less mode: the host controls open/close. */}
|
|
1409
|
+
{showInternalTrigger && !shellLess && (
|
|
1410
|
+
<div
|
|
1411
|
+
aria-hidden={isOpen}
|
|
1412
|
+
className={`sticky bottom-0 h-0 z-[9990] pointer-events-none ${
|
|
1413
|
+
isOpen ? 'opacity-0' : 'opacity-100'
|
|
1414
|
+
}`}
|
|
1415
|
+
>
|
|
1416
|
+
<div className="absolute bottom-4 md:bottom-6 right-4 md:right-6">
|
|
1417
|
+
<Button
|
|
1418
|
+
onClick={handleOpen}
|
|
1419
|
+
leftIcon={<MingoIcon className="h-5 w-5" color="currentColor" />}
|
|
1420
|
+
tabIndex={isOpen ? -1 : 0}
|
|
1421
|
+
className={`shadow-lg !w-auto pointer-events-auto ${
|
|
1422
|
+
isOpen ? '!pointer-events-none' : ''
|
|
1423
|
+
}`}
|
|
1424
|
+
>
|
|
1425
|
+
Ask AI
|
|
1426
|
+
</Button>
|
|
1427
|
+
</div>
|
|
1428
|
+
</div>
|
|
1429
|
+
)}
|
|
1430
|
+
{panel}
|
|
1356
1431
|
</>
|
|
1357
1432
|
)
|
|
1358
1433
|
}
|