@flamingo-stack/openframe-frontend-core 0.0.685-snapshot.20260929164409 → 0.0.685
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-DL67S7H4.cjs → chunk-5LJZPJHX.cjs} +59 -59
- package/dist/{chunk-DL67S7H4.cjs.map → chunk-5LJZPJHX.cjs.map} +1 -1
- package/dist/{chunk-IZ3WKG62.js → chunk-65KTXRBY.js} +6 -6
- package/dist/{chunk-IBKMIJ7Y.js → chunk-7XF63THA.js} +6 -6
- package/dist/{chunk-SI77HF4U.cjs → chunk-A3CSQB4A.cjs} +805 -1247
- package/dist/chunk-A3CSQB4A.cjs.map +1 -0
- package/dist/{chunk-5MUR43QD.cjs → chunk-B25CF3PO.cjs} +30 -30
- package/dist/{chunk-5MUR43QD.cjs.map → chunk-B25CF3PO.cjs.map} +1 -1
- package/dist/{chunk-32KBPEF7.js → chunk-B32YLOYQ.js} +4 -4
- package/dist/{chunk-OBLED7JY.cjs → chunk-B5N3AHPG.cjs} +81 -81
- package/dist/{chunk-OBLED7JY.cjs.map → chunk-B5N3AHPG.cjs.map} +1 -1
- package/dist/{chunk-QSRUYHTW.js → chunk-BZ2XOEHW.js} +3 -3
- package/dist/{chunk-XATA4OVO.cjs → chunk-C3B36OYZ.cjs} +3 -3
- package/dist/{chunk-XATA4OVO.cjs.map → chunk-C3B36OYZ.cjs.map} +1 -1
- package/dist/{chunk-V26TKJ4Z.cjs → chunk-CMQXPD5C.cjs} +28 -28
- package/dist/{chunk-V26TKJ4Z.cjs.map → chunk-CMQXPD5C.cjs.map} +1 -1
- package/dist/{chunk-QNWQQ4RT.cjs → chunk-DQOQ3KTP.cjs} +73 -78
- package/dist/chunk-DQOQ3KTP.cjs.map +1 -0
- package/dist/{chunk-5HVGOIAE.cjs → chunk-DWXWEGGO.cjs} +22 -22
- package/dist/{chunk-5HVGOIAE.cjs.map → chunk-DWXWEGGO.cjs.map} +1 -1
- package/dist/{chunk-6CMH53MP.cjs → chunk-EFBMNVSH.cjs} +15 -15
- package/dist/{chunk-6CMH53MP.cjs.map → chunk-EFBMNVSH.cjs.map} +1 -1
- package/dist/{chunk-DDFYKF3B.js → chunk-GHLGXFNU.js} +3 -3
- package/dist/{chunk-VDV7K5TD.js → chunk-GXV6UEVY.js} +3 -3
- package/dist/{chunk-AEAFQTVR.cjs → chunk-HMXUOLZR.cjs} +20 -20
- package/dist/{chunk-AEAFQTVR.cjs.map → chunk-HMXUOLZR.cjs.map} +1 -1
- package/dist/{chunk-2OD2PPNO.cjs → chunk-K2HZG4RM.cjs} +32 -32
- package/dist/{chunk-2OD2PPNO.cjs.map → chunk-K2HZG4RM.cjs.map} +1 -1
- package/dist/{chunk-JNDHXL2D.cjs → chunk-M4HYIXP6.cjs} +383 -384
- package/dist/chunk-M4HYIXP6.cjs.map +1 -0
- package/dist/{chunk-KTWJKCUB.js → chunk-N4Z6X5BV.js} +5 -5
- package/dist/{chunk-T2RHI676.js → chunk-O7LOZW4R.js} +5 -5
- package/dist/{chunk-ZHYYGIAI.cjs → chunk-P2ASEPA2.cjs} +6 -6
- package/dist/{chunk-ZHYYGIAI.cjs.map → chunk-P2ASEPA2.cjs.map} +1 -1
- package/dist/{chunk-CHRDLXXX.js → chunk-PSQKD6ZN.js} +3 -3
- package/dist/{chunk-GSJR33UP.cjs → chunk-QHLJSB2J.cjs} +12 -12
- package/dist/{chunk-GSJR33UP.cjs.map → chunk-QHLJSB2J.cjs.map} +1 -1
- package/dist/{chunk-CFL3EW4V.cjs → chunk-T4DUTP5C.cjs} +5 -5
- package/dist/{chunk-CFL3EW4V.cjs.map → chunk-T4DUTP5C.cjs.map} +1 -1
- package/dist/{chunk-22RSJ57Z.js → chunk-T6KWNJDW.js} +465 -470
- package/dist/chunk-T6KWNJDW.js.map +1 -0
- package/dist/{chunk-2FGFB2HY.cjs → chunk-TJI5V5BR.cjs} +7 -7
- package/dist/{chunk-2FGFB2HY.cjs.map → chunk-TJI5V5BR.cjs.map} +1 -1
- package/dist/{chunk-HLEXYTOE.js → chunk-TUG7IT6H.js} +2 -2
- package/dist/{chunk-DWCVA6XT.cjs → chunk-U3IXG7TR.cjs} +33 -33
- package/dist/{chunk-DWCVA6XT.cjs.map → chunk-U3IXG7TR.cjs.map} +1 -1
- package/dist/{chunk-WJ5XN4AL.js → chunk-UT333EOT.js} +4 -4
- package/dist/{chunk-OTV4W3YD.js → chunk-VDPCK437.js} +5 -6
- package/dist/{chunk-OTV4W3YD.js.map → chunk-VDPCK437.js.map} +1 -1
- package/dist/{chunk-5GK7TU2Q.js → chunk-WJK2EETW.js} +2 -2
- package/dist/{chunk-YPXXXX6R.js → chunk-XGX6JSEU.js} +1644 -2086
- package/dist/chunk-XGX6JSEU.js.map +1 -0
- package/dist/{chunk-XODE4YTG.js → chunk-XW7MUIW4.js} +3 -3
- package/dist/{chunk-N74MQ4HQ.js → chunk-Y7ZYOQNB.js} +3 -3
- package/dist/{chunk-2NUB5EMG.cjs → chunk-YCTVLN47.cjs} +10 -10
- package/dist/{chunk-2NUB5EMG.cjs.map → chunk-YCTVLN47.cjs.map} +1 -1
- package/dist/{chunk-TILC52ZF.js → chunk-ZF4Z36G4.js} +7 -7
- package/dist/components/case-studies/index.cjs +16 -16
- package/dist/components/case-studies/index.js +4 -4
- package/dist/components/chat/ai-assistant-info.d.ts.map +1 -1
- package/dist/components/chat/approval-request-message.d.ts.map +1 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/error-message-display.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +3 -21
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +0 -6
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +2 -20
- package/dist/components/chat/msp-organization-card.d.ts +1 -7
- package/dist/components/chat/msp-organization-card.d.ts.map +1 -1
- package/dist/components/chat/types/chat.types.d.ts +0 -13
- package/dist/components/chat/types/chat.types.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +1 -20
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +5 -5
- package/dist/components/contact/index.js +4 -4
- package/dist/components/docs/index.cjs +8 -8
- package/dist/components/docs/index.js +7 -7
- package/dist/components/embeds/index.cjs +5 -5
- package/dist/components/embeds/index.js +4 -4
- package/dist/components/faq/index.cjs +5 -5
- package/dist/components/faq/index.js +4 -4
- package/dist/components/features/index.cjs +4 -4
- package/dist/components/features/index.js +3 -3
- package/dist/components/help-center-pages/index.cjs +91 -91
- package/dist/components/help-center-pages/index.js +15 -15
- package/dist/components/index.cjs +55 -73
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +15 -33
- package/dist/components/index.js.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +41 -41
- package/dist/components/meeting-scheduler/index.js +3 -3
- package/dist/components/navigation/index.cjs +7 -7
- package/dist/components/navigation/index.js +6 -6
- package/dist/components/onboarding-guides/index.cjs +7 -7
- package/dist/components/onboarding-guides/index.js +6 -6
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +8 -8
- package/dist/components/tickets/index.js +7 -7
- package/dist/components/ui/avatar-stack.d.ts +1 -5
- package/dist/components/ui/avatar-stack.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +4 -22
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +3 -21
- package/dist/components/ui/reopen-ticket-request-modal.d.ts +1 -4
- package/dist/components/ui/reopen-ticket-request-modal.d.ts.map +1 -1
- package/dist/index.cjs +9 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/ai-assistant-info.tsx +1 -7
- package/src/components/chat/approval-request-message.tsx +1 -6
- package/src/components/chat/chat-input.tsx +1 -26
- package/src/components/chat/chat-message-enhanced.tsx +142 -180
- package/src/components/chat/chat-message-list.tsx +164 -169
- package/src/components/chat/error-message-display.tsx +3 -21
- package/src/components/chat/index.ts +0 -6
- package/src/components/chat/msp-organization-card.tsx +0 -35
- package/src/components/chat/types/chat.types.ts +0 -15
- package/src/components/chat/types/component.types.ts +0 -20
- package/src/components/ui/avatar-stack.tsx +5 -15
- package/src/components/ui/reopen-ticket-request-modal.tsx +2 -6
- package/src/styles/ods-colors.css +0 -10
- package/src/styles/ods-effects.css +0 -13
- package/dist/chunk-22RSJ57Z.js.map +0 -1
- package/dist/chunk-JNDHXL2D.cjs.map +0 -1
- package/dist/chunk-QNWQQ4RT.cjs.map +0 -1
- package/dist/chunk-SI77HF4U.cjs.map +0 -1
- package/dist/chunk-YPXXXX6R.js.map +0 -1
- package/dist/components/chat/chat-appearance-context.d.ts +0 -9
- package/dist/components/chat/chat-appearance-context.d.ts.map +0 -1
- package/dist/components/chat/chat-nav-sidebar.d.ts +0 -50
- package/dist/components/chat/chat-nav-sidebar.d.ts.map +0 -1
- package/dist/components/chat/chat-top-navigation.d.ts +0 -29
- package/dist/components/chat/chat-top-navigation.d.ts.map +0 -1
- package/dist/components/chat/onboarding-carousel.d.ts +0 -29
- package/dist/components/chat/onboarding-carousel.d.ts.map +0 -1
- package/dist/components/chat/system-event-message.d.ts +0 -14
- package/dist/components/chat/system-event-message.d.ts.map +0 -1
- package/src/components/chat/__tests__/chat-appearance.test.tsx +0 -79
- package/src/components/chat/__tests__/onboarding-carousel.test.tsx +0 -56
- package/src/components/chat/chat-appearance-context.tsx +0 -15
- package/src/components/chat/chat-nav-sidebar.tsx +0 -163
- package/src/components/chat/chat-top-navigation.tsx +0 -109
- package/src/components/chat/onboarding-carousel.tsx +0 -162
- package/src/components/chat/system-event-message.tsx +0 -54
- package/src/components/ui/__tests__/avatar-stack.test.tsx +0 -25
- package/src/stories/FaeLayoutV2.stories.tsx +0 -150
- package/src/stories/FaeOnboardingV2.stories.tsx +0 -68
- package/src/stories/FaeThreadV2.stories.tsx +0 -192
- package/src/stories/fae-brand-decorator.tsx +0 -28
- /package/dist/{chunk-IZ3WKG62.js.map → chunk-65KTXRBY.js.map} +0 -0
- /package/dist/{chunk-IBKMIJ7Y.js.map → chunk-7XF63THA.js.map} +0 -0
- /package/dist/{chunk-32KBPEF7.js.map → chunk-B32YLOYQ.js.map} +0 -0
- /package/dist/{chunk-QSRUYHTW.js.map → chunk-BZ2XOEHW.js.map} +0 -0
- /package/dist/{chunk-DDFYKF3B.js.map → chunk-GHLGXFNU.js.map} +0 -0
- /package/dist/{chunk-VDV7K5TD.js.map → chunk-GXV6UEVY.js.map} +0 -0
- /package/dist/{chunk-KTWJKCUB.js.map → chunk-N4Z6X5BV.js.map} +0 -0
- /package/dist/{chunk-T2RHI676.js.map → chunk-O7LOZW4R.js.map} +0 -0
- /package/dist/{chunk-CHRDLXXX.js.map → chunk-PSQKD6ZN.js.map} +0 -0
- /package/dist/{chunk-HLEXYTOE.js.map → chunk-TUG7IT6H.js.map} +0 -0
- /package/dist/{chunk-WJ5XN4AL.js.map → chunk-UT333EOT.js.map} +0 -0
- /package/dist/{chunk-5GK7TU2Q.js.map → chunk-WJK2EETW.js.map} +0 -0
- /package/dist/{chunk-XODE4YTG.js.map → chunk-XW7MUIW4.js.map} +0 -0
- /package/dist/{chunk-N74MQ4HQ.js.map → chunk-Y7ZYOQNB.js.map} +0 -0
- /package/dist/{chunk-TILC52ZF.js.map → chunk-ZF4Z36G4.js.map} +0 -0
|
@@ -14,12 +14,10 @@ import { useStickToBottom } from 'use-stick-to-bottom';
|
|
|
14
14
|
import { cn } from '../../utils/cn';
|
|
15
15
|
import { DotsLoaderIcon, Arrow02DownIcon } from '../icons-v2-generated';
|
|
16
16
|
import { OverlayOpenRegistryProvider } from '../ui/overlay-open-registry';
|
|
17
|
-
import { ChatAppearanceContext } from './chat-appearance-context';
|
|
18
17
|
import { ChatMessageEnhanced } from './chat-message-enhanced';
|
|
19
18
|
import { ChatMessageListSkeleton } from './chat-message-skeleton';
|
|
20
19
|
import { CyclingPhrase } from './cycling-phrase';
|
|
21
20
|
import type { ChatMessageListProps } from './types';
|
|
22
|
-
import { CHAT_APPEARANCE } from './types/chat.types';
|
|
23
21
|
import { SCROLL_ANCHOR } from './types/message.types';
|
|
24
22
|
import type { MessageContent } from './types/message.types';
|
|
25
23
|
|
|
@@ -130,7 +128,6 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
130
128
|
contentClassName,
|
|
131
129
|
assistantType,
|
|
132
130
|
approvalVariant,
|
|
133
|
-
appearance = CHAT_APPEARANCE.CLASSIC,
|
|
134
131
|
assistantIcon,
|
|
135
132
|
pendingApprovals,
|
|
136
133
|
hasNextPage,
|
|
@@ -918,119 +915,118 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
918
915
|
const showStreamingLoader = isTyping && !isPausedOnApproval && !(pendingApprovals && pendingApprovals.length > 0);
|
|
919
916
|
|
|
920
917
|
return (
|
|
921
|
-
<
|
|
922
|
-
<
|
|
923
|
-
|
|
924
|
-
{/* Positioning box for the jump-to-bottom button: it wraps ONLY the
|
|
918
|
+
<OverlayOpenRegistryProvider onOpenChange={handleOverlayOpenChange}>
|
|
919
|
+
<div className="flex min-h-0 flex-1 flex-col">
|
|
920
|
+
{/* Positioning box for the jump-to-bottom button: it wraps ONLY the
|
|
925
921
|
scroller, so `absolute bottom-…` really is the scroller's lower
|
|
926
922
|
edge. The outer flex column also holds the streaming loader and the
|
|
927
923
|
approvals bar, and anchoring against THAT box put the button over
|
|
928
924
|
the loader for the whole of every stream — precisely when it is
|
|
929
925
|
most likely to be visible. */}
|
|
930
|
-
|
|
926
|
+
<div className="relative flex min-h-0 flex-1 flex-col">
|
|
927
|
+
<div
|
|
928
|
+
ref={setScrollRef}
|
|
929
|
+
className={cn(
|
|
930
|
+
'flex h-full w-full flex-1 flex-col overflow-y-auto overflow-x-hidden',
|
|
931
|
+
// `overscroll-contain` (default ON, opt-out via `overscrollContain={false}`):
|
|
932
|
+
// reaching the top/bottom of the thread must NOT chain the wheel/
|
|
933
|
+
// touch scroll to the page behind the chat — e.g. the company-hub
|
|
934
|
+
// deck (native body scroll + sticky slide panels) would otherwise
|
|
935
|
+
// advance slides while you scroll the chat (fixed in #1501; kept as
|
|
936
|
+
// the prop default here). Passive in-page DEMO chats disable it so
|
|
937
|
+
// the surrounding page keeps scrolling normally when the pointer is
|
|
938
|
+
// over the (non-interactive) thread.
|
|
939
|
+
overscrollContain && 'overscroll-contain',
|
|
940
|
+
'scrollbar-thin scrollbar-track-transparent scrollbar-thumb-ods-border/30 hover:scrollbar-thumb-ods-text-secondary/30',
|
|
941
|
+
className,
|
|
942
|
+
)}
|
|
943
|
+
{...props}
|
|
944
|
+
>
|
|
931
945
|
<div
|
|
932
|
-
ref={
|
|
946
|
+
ref={setContentRef}
|
|
933
947
|
className={cn(
|
|
934
|
-
|
|
935
|
-
//
|
|
936
|
-
//
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
// the surrounding page keeps scrolling normally when the pointer is
|
|
942
|
-
// over the (non-interactive) thread.
|
|
943
|
-
overscrollContain && 'overscroll-contain',
|
|
944
|
-
'scrollbar-thin scrollbar-track-transparent scrollbar-thumb-ods-border/30 hover:scrollbar-thumb-ods-text-secondary/30',
|
|
945
|
-
className,
|
|
948
|
+
// `fullWidth=true` drops the centered-narrow column for
|
|
949
|
+
// side-panel hosts (e.g. multi-platform-hub Mingo). Same
|
|
950
|
+
// semantics as ChatHeader / ChatInput / ChatFooter.
|
|
951
|
+
fullWidth
|
|
952
|
+
? 'flex w-full min-w-0 flex-col pb-[var(--spacing-system-xs)]'
|
|
953
|
+
: 'mx-auto flex w-full min-w-0 max-w-ods-content-narrow flex-col pb-[var(--spacing-system-xs)]',
|
|
954
|
+
contentClassName ?? 'px-[var(--spacing-system-m)]',
|
|
946
955
|
)}
|
|
947
|
-
{
|
|
956
|
+
style={{ minHeight: '100%' }}
|
|
948
957
|
>
|
|
949
|
-
<div
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
message.role === 'assistant'
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
/>
|
|
1018
|
-
);
|
|
1019
|
-
// Only wrap when there IS owned content: the bare row keeps
|
|
1020
|
-
// its own key and its place as a direct flex child, so a
|
|
1021
|
-
// thread without any stays byte-identical to before.
|
|
1022
|
-
if (!ownedContent) return row;
|
|
1023
|
-
return (
|
|
1024
|
-
<div key={message.id} className="grid w-full min-w-0 grid-cols-1">
|
|
1025
|
-
{row}
|
|
1026
|
-
{ownedContent}
|
|
1027
|
-
</div>
|
|
1028
|
-
);
|
|
1029
|
-
})}
|
|
1030
|
-
</div>
|
|
958
|
+
{hasNextPage && <div ref={sentinelRef} className="h-px" />}
|
|
959
|
+
<div className="flex-1" />
|
|
960
|
+
{messages.map((message, index) => {
|
|
961
|
+
// Hidden messages (synthetic continuation prompts the host
|
|
962
|
+
// injects after an approval card) are part of the API
|
|
963
|
+
// conversation history but never render. Skipping here
|
|
964
|
+
// keeps the visible thread coherent — see
|
|
965
|
+
// `Message.hidden` doc-comment in message.types.ts.
|
|
966
|
+
if (message.hidden) return null;
|
|
967
|
+
// ONE OWNER for the pending turn. Both send paths mint an empty
|
|
968
|
+
// assistant placeholder (the accumulation target — it MUST stay
|
|
969
|
+
// in state) and flip the phase to 'thinking'. Rendering it here
|
|
970
|
+
// too would put an author label with no body in the transcript
|
|
971
|
+
// WHILE the footer loader below already represents the same
|
|
972
|
+
// pending turn — two owners, and an `aria-live` log that
|
|
973
|
+
// announces a bare "Mingo". The footer loader wins (it is
|
|
974
|
+
// already `role="status" aria-live="polite"`, sits outside the
|
|
975
|
+
// scroller so it can't jitter the thread, and cycles the
|
|
976
|
+
// progress phrase), so the placeholder is not rendered AT ALL
|
|
977
|
+
// until it carries something visible — removed from the live
|
|
978
|
+
// region, not merely hidden. Mirrors `endSseTurn`, which prunes
|
|
979
|
+
// an empty trailing assistant AFTER a turn; this closes the same
|
|
980
|
+
// window BEFORE the first token. Any non-text segment (tool
|
|
981
|
+
// execution, approval card, thinking) counts as visible.
|
|
982
|
+
const isEmptyPendingTurn =
|
|
983
|
+
index === messages.length - 1 &&
|
|
984
|
+
isTyping &&
|
|
985
|
+
message.role === 'assistant' &&
|
|
986
|
+
!hasNonEmptyContent(message.content) &&
|
|
987
|
+
(!Array.isArray(message.content) || message.content.every(s => s.type === 'text'));
|
|
988
|
+
if (isEmptyPendingTurn) return null;
|
|
989
|
+
const ownedContent = renderAfterMessage?.(message, index);
|
|
990
|
+
const row = (
|
|
991
|
+
<ChatMessageEnhanced
|
|
992
|
+
key={message.id}
|
|
993
|
+
ref={getRegisterMessageEl(message.id)}
|
|
994
|
+
role={message.role}
|
|
995
|
+
name={message.name}
|
|
996
|
+
content={message.content}
|
|
997
|
+
timestamp={message.timestamp}
|
|
998
|
+
isTyping={index === messages.length - 1 && isTyping && message.role === 'assistant'}
|
|
999
|
+
avatar={showAvatars ? message.avatar : null}
|
|
1000
|
+
showAvatar={showAvatars}
|
|
1001
|
+
assistantType={message.assistantType || assistantType}
|
|
1002
|
+
approvalVariant={approvalVariant}
|
|
1003
|
+
authorType={message.authorType}
|
|
1004
|
+
assistantIcon={message.role !== 'user' ? assistantIcon : undefined}
|
|
1005
|
+
contextItems={message.contextItems}
|
|
1006
|
+
resolveContextIcon={resolveContextIcon}
|
|
1007
|
+
renderContextItem={renderContextItem}
|
|
1008
|
+
renderMention={renderMention}
|
|
1009
|
+
renderEntityCard={renderEntityCard}
|
|
1010
|
+
refs={message.refs}
|
|
1011
|
+
onAskSelect={index > lastUserMessageIndex ? onAskSelect : undefined}
|
|
1012
|
+
NavLinkAnchor={NavLinkAnchor}
|
|
1013
|
+
/>
|
|
1014
|
+
);
|
|
1015
|
+
// Only wrap when there IS owned content: the bare row keeps
|
|
1016
|
+
// its own key and its place as a direct flex child, so a
|
|
1017
|
+
// thread without any stays byte-identical to before.
|
|
1018
|
+
if (!ownedContent) return row;
|
|
1019
|
+
return (
|
|
1020
|
+
<div key={message.id} className="grid w-full min-w-0 grid-cols-1">
|
|
1021
|
+
{row}
|
|
1022
|
+
{ownedContent}
|
|
1023
|
+
</div>
|
|
1024
|
+
);
|
|
1025
|
+
})}
|
|
1031
1026
|
</div>
|
|
1027
|
+
</div>
|
|
1032
1028
|
|
|
1033
|
-
|
|
1029
|
+
{/* Jump to bottom. The manual escape hatch from auto-scroll that
|
|
1034
1030
|
every 2026 chat surface ships (AI Elements calls it
|
|
1035
1031
|
`ConversationScrollButton`) and the WCAG 2.2.2 counterpart to
|
|
1036
1032
|
moving content: the reader can always get back to the live
|
|
@@ -1041,80 +1037,79 @@ const ChatMessageList = forwardRef<HTMLDivElement, ChatMessageListProps>(
|
|
|
1041
1037
|
height either way — a sibling would shrink the scroller and
|
|
1042
1038
|
move the very bottom it points at. Hidden while at the bottom
|
|
1043
1039
|
and in passive demo hosts. */}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
className={cn(
|
|
1055
|
-
'absolute bottom-[var(--spacing-system-s)] left-1/2 z-10 -translate-x-1/2',
|
|
1056
|
-
'flex size-9 items-center justify-center rounded-full',
|
|
1057
|
-
'border border-ods-border bg-ods-card text-ods-text-primary shadow-lg',
|
|
1058
|
-
'transition-colors hover:bg-ods-bg-hover',
|
|
1059
|
-
)}
|
|
1060
|
-
>
|
|
1061
|
-
<Arrow02DownIcon size={16} />
|
|
1062
|
-
</button>
|
|
1063
|
-
)}
|
|
1064
|
-
</div>
|
|
1065
|
-
|
|
1066
|
-
{/* Footer-pinned streaming loader — outside the scroller so it
|
|
1067
|
-
doesn't jitter as the streaming message grows. Color is set
|
|
1068
|
-
via inline style (CSS var) rather than a Tailwind class so it
|
|
1069
|
-
is JIT-independent when this lib is consumed via yalc. */}
|
|
1070
|
-
{showStreamingLoader && (
|
|
1071
|
-
<div
|
|
1040
|
+
{autoScroll && !atBottom && messages.length > 0 && (
|
|
1041
|
+
<button
|
|
1042
|
+
type="button"
|
|
1043
|
+
onClick={() => {
|
|
1044
|
+
// Clicking IS the intent to return to the live end, so it
|
|
1045
|
+
// re-arms the follow lock a scroll-up had released.
|
|
1046
|
+
followBottomRef.current = true;
|
|
1047
|
+
void scrollToBottom({ animation: 'smooth' });
|
|
1048
|
+
}}
|
|
1049
|
+
aria-label="Scroll to latest message"
|
|
1072
1050
|
className={cn(
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1051
|
+
'absolute bottom-[var(--spacing-system-s)] left-1/2 z-10 -translate-x-1/2',
|
|
1052
|
+
'flex size-9 items-center justify-center rounded-full',
|
|
1053
|
+
'border border-ods-border bg-ods-card text-ods-text-primary shadow-lg',
|
|
1054
|
+
'transition-colors hover:bg-ods-bg-hover',
|
|
1077
1055
|
)}
|
|
1078
|
-
style={{ color: 'var(--color-text-muted)' }}
|
|
1079
|
-
role="status"
|
|
1080
|
-
aria-live="polite"
|
|
1081
1056
|
>
|
|
1082
|
-
<
|
|
1083
|
-
|
|
1084
|
-
</div>
|
|
1057
|
+
<Arrow02DownIcon size={16} />
|
|
1058
|
+
</button>
|
|
1085
1059
|
)}
|
|
1060
|
+
</div>
|
|
1086
1061
|
|
|
1087
|
-
|
|
1062
|
+
{/* Footer-pinned streaming loader — outside the scroller so it
|
|
1063
|
+
doesn't jitter as the streaming message grows. Color is set
|
|
1064
|
+
via inline style (CSS var) rather than a Tailwind class so it
|
|
1065
|
+
is JIT-independent when this lib is consumed via yalc. */}
|
|
1066
|
+
{showStreamingLoader && (
|
|
1067
|
+
<div
|
|
1068
|
+
className={cn(
|
|
1069
|
+
fullWidth
|
|
1070
|
+
? 'flex w-full items-center gap-[var(--spacing-system-xxs)] py-[var(--spacing-system-xs)]'
|
|
1071
|
+
: 'mx-auto flex w-full max-w-ods-content-narrow items-center gap-[var(--spacing-system-xxs)] py-[var(--spacing-system-xs)]',
|
|
1072
|
+
contentClassName ?? 'px-[var(--spacing-system-m)]',
|
|
1073
|
+
)}
|
|
1074
|
+
style={{ color: 'var(--color-text-muted)' }}
|
|
1075
|
+
role="status"
|
|
1076
|
+
aria-live="polite"
|
|
1077
|
+
>
|
|
1078
|
+
<DotsLoaderIcon className="h-6 w-6" />
|
|
1079
|
+
<CyclingPhrase words={STREAMING_WORDS} className="text-h6" />
|
|
1080
|
+
</div>
|
|
1081
|
+
)}
|
|
1082
|
+
|
|
1083
|
+
{/* Sticky Pending Approvals — outside the scroller; same
|
|
1088
1084
|
structure as the v1 baseline. The library's RO watches
|
|
1089
1085
|
`contentRef` (INSIDE the scroller), so height changes to
|
|
1090
1086
|
this sibling don't trigger the library directly. If a
|
|
1091
1087
|
future UX requires snapping to bottom when approvals
|
|
1092
1088
|
appear, that hookup goes here. */}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
</ChatAppearanceContext.Provider>
|
|
1089
|
+
{pendingApprovals && pendingApprovals.length > 0 && (
|
|
1090
|
+
<div
|
|
1091
|
+
className={cn(
|
|
1092
|
+
'border-t border-ods-border bg-ods-bg/95 backdrop-blur-sm',
|
|
1093
|
+
fullWidth ? 'w-full' : 'mx-auto w-full max-w-ods-content-narrow',
|
|
1094
|
+
contentClassName ?? 'px-[var(--spacing-system-m)]',
|
|
1095
|
+
)}
|
|
1096
|
+
>
|
|
1097
|
+
<ChatMessageEnhanced
|
|
1098
|
+
role="assistant"
|
|
1099
|
+
name={assistantType === 'mingo' ? 'Mingo' : 'Fae'}
|
|
1100
|
+
content={pendingApprovals}
|
|
1101
|
+
timestamp={new Date()}
|
|
1102
|
+
showAvatar={showAvatars}
|
|
1103
|
+
assistantType={assistantType}
|
|
1104
|
+
approvalVariant={approvalVariant}
|
|
1105
|
+
assistantIcon={assistantIcon}
|
|
1106
|
+
NavLinkAnchor={NavLinkAnchor}
|
|
1107
|
+
className="py-3"
|
|
1108
|
+
/>
|
|
1109
|
+
</div>
|
|
1110
|
+
)}
|
|
1111
|
+
</div>
|
|
1112
|
+
</OverlayOpenRegistryProvider>
|
|
1118
1113
|
);
|
|
1119
1114
|
},
|
|
1120
1115
|
);
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
import { forwardRef, useState } from 'react';
|
|
4
4
|
import { cn } from '../../utils/cn';
|
|
5
5
|
import { AlertCircleIcon } from '../icons-v2-generated';
|
|
6
|
-
import { useChatAppearance } from './chat-appearance-context';
|
|
7
6
|
import { ExpandChevron } from './expand-chevron';
|
|
8
7
|
import { useCollapsible } from './hooks/use-collapsible';
|
|
9
8
|
import type { ErrorMessageDisplayProps } from './types';
|
|
10
|
-
import { CHAT_APPEARANCE } from './types/chat.types';
|
|
11
9
|
|
|
12
10
|
const iconTint = {
|
|
13
11
|
error: 'text-ods-error',
|
|
@@ -20,20 +18,11 @@ const ErrorMessageDisplay = forwardRef<HTMLDivElement, ErrorMessageDisplayProps>
|
|
|
20
18
|
const [expanded, setExpanded] = useState(false);
|
|
21
19
|
const { innerRef, containerStyle } = useCollapsible({ expanded });
|
|
22
20
|
const hasDetails = Boolean(details);
|
|
23
|
-
// v2 (fae chat `chat-info-block`): outlined on the page surface, the title
|
|
24
|
-
// always reads as a heading, and the row spacing comes from the thread.
|
|
25
|
-
const isV2 = useChatAppearance() === CHAT_APPEARANCE.V2;
|
|
26
21
|
|
|
27
22
|
return (
|
|
28
23
|
<div
|
|
29
24
|
ref={ref}
|
|
30
|
-
className={cn(
|
|
31
|
-
'rounded-md',
|
|
32
|
-
isV2
|
|
33
|
-
? 'border border-ods-border bg-ods-bg p-[var(--spacing-system-xs)]'
|
|
34
|
-
: 'mb-[var(--spacing-system-xsf)] bg-ods-card p-[var(--spacing-system-xsf)]',
|
|
35
|
-
className,
|
|
36
|
-
)}
|
|
25
|
+
className={cn('mb-[var(--spacing-system-xsf)] rounded-md bg-ods-card p-[var(--spacing-system-xsf)]', className)}
|
|
37
26
|
{...props}
|
|
38
27
|
>
|
|
39
28
|
<button
|
|
@@ -51,8 +40,7 @@ const ErrorMessageDisplay = forwardRef<HTMLDivElement, ErrorMessageDisplayProps>
|
|
|
51
40
|
<span
|
|
52
41
|
className={cn(
|
|
53
42
|
'min-w-0 flex-1 text-h5',
|
|
54
|
-
expanded
|
|
55
|
-
!expanded && 'truncate',
|
|
43
|
+
expanded ? 'text-ods-text-primary' : 'truncate text-ods-text-secondary',
|
|
56
44
|
)}
|
|
57
45
|
>
|
|
58
46
|
{title}
|
|
@@ -62,13 +50,7 @@ const ErrorMessageDisplay = forwardRef<HTMLDivElement, ErrorMessageDisplayProps>
|
|
|
62
50
|
|
|
63
51
|
{hasDetails && (
|
|
64
52
|
<div style={containerStyle}>
|
|
65
|
-
<div
|
|
66
|
-
ref={innerRef}
|
|
67
|
-
className={cn(
|
|
68
|
-
'px-[var(--spacing-system-lf)]',
|
|
69
|
-
isV2 ? 'pt-[var(--spacing-system-xs)]' : 'pt-[var(--spacing-system-xsf)]',
|
|
70
|
-
)}
|
|
71
|
-
>
|
|
53
|
+
<div ref={innerRef} className="px-[var(--spacing-system-lf)] pt-[var(--spacing-system-xsf)]">
|
|
72
54
|
<p className="text-ods-text-primary text-h6">{details}</p>
|
|
73
55
|
</div>
|
|
74
56
|
</div>
|
|
@@ -6,9 +6,7 @@ export * from './approval-batch-message';
|
|
|
6
6
|
export * from './escalation-offer-message';
|
|
7
7
|
export * from './ticket-escalated-message';
|
|
8
8
|
export * from './ticket-event-message';
|
|
9
|
-
export * from './system-event-message';
|
|
10
9
|
export * from './ai-assistant-info';
|
|
11
|
-
export * from './chat-appearance-context';
|
|
12
10
|
export * from './context-compaction-display';
|
|
13
11
|
export * from './expand-chevron';
|
|
14
12
|
export * from './thinking-display';
|
|
@@ -49,10 +47,6 @@ export * from './context-items-list';
|
|
|
49
47
|
export * from './chat-archive-page';
|
|
50
48
|
export * from './model-display';
|
|
51
49
|
export * from './chat-sidebar';
|
|
52
|
-
export * from './chat-nav-sidebar';
|
|
53
|
-
export * from './chat-list-empty-state';
|
|
54
|
-
export * from './chat-top-navigation';
|
|
55
|
-
export * from './onboarding-carousel';
|
|
56
50
|
export type { ChatRef } from './chat-ref.types';
|
|
57
51
|
export { remarkCardLinks } from './remark-card-links';
|
|
58
52
|
export { remarkMentionChips } from './remark-mention-chips';
|
|
@@ -22,12 +22,6 @@ export interface MspOrganizationCardProps {
|
|
|
22
22
|
href?: string;
|
|
23
23
|
/** Click handler for the trailing action (used when `href` is not set). */
|
|
24
24
|
onOpenWebsite?: () => void;
|
|
25
|
-
/**
|
|
26
|
-
* `card` (default): the welcome-screen card, title with the website beneath.
|
|
27
|
-
* `footer`: the compact sidebar footer of the fae chat v2 layout, a caption
|
|
28
|
-
* "Your IT is managed by" over the name, no website line, no card chrome.
|
|
29
|
-
*/
|
|
30
|
-
variant?: 'card' | 'footer';
|
|
31
25
|
/** Appended to the root element. */
|
|
32
26
|
className?: string;
|
|
33
27
|
}
|
|
@@ -50,39 +44,10 @@ export function MspOrganizationCard({
|
|
|
50
44
|
title,
|
|
51
45
|
href,
|
|
52
46
|
onOpenWebsite,
|
|
53
|
-
variant = 'card',
|
|
54
47
|
className,
|
|
55
48
|
}: MspOrganizationCardProps) {
|
|
56
49
|
const actionLabel = website ? `Open ${website}` : 'Open organization website';
|
|
57
50
|
|
|
58
|
-
if (variant === 'footer') {
|
|
59
|
-
return (
|
|
60
|
-
<div
|
|
61
|
-
className={cn(
|
|
62
|
-
'flex items-center gap-[var(--spacing-system-s)] px-[var(--spacing-system-m)] py-[var(--spacing-system-s)]',
|
|
63
|
-
className,
|
|
64
|
-
)}
|
|
65
|
-
>
|
|
66
|
-
<SquareAvatar src={logoUrl} alt={name} fallback={name} size="sm" variant="square" />
|
|
67
|
-
<div className="flex min-w-0 flex-1 flex-col">
|
|
68
|
-
<span className="truncate text-ods-text-secondary text-h6">Your IT is managed by</span>
|
|
69
|
-
<span className="truncate text-ods-text-primary text-h4">{title ?? name}</span>
|
|
70
|
-
</div>
|
|
71
|
-
{href ? (
|
|
72
|
-
<Button asChild variant="outline" size="icon-sm" aria-label={actionLabel}>
|
|
73
|
-
<a href={href} target="_blank" rel="noreferrer noopener">
|
|
74
|
-
<ExternalLinkIcon />
|
|
75
|
-
</a>
|
|
76
|
-
</Button>
|
|
77
|
-
) : onOpenWebsite ? (
|
|
78
|
-
<Button variant="outline" size="icon-sm" aria-label={actionLabel} onClick={onOpenWebsite}>
|
|
79
|
-
<ExternalLinkIcon />
|
|
80
|
-
</Button>
|
|
81
|
-
) : null}
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
51
|
return (
|
|
87
52
|
<div
|
|
88
53
|
className={cn(
|
|
@@ -81,21 +81,6 @@ export const APPROVAL_BLOCK_VARIANT = {
|
|
|
81
81
|
|
|
82
82
|
export type ApprovalBlockVariant = (typeof APPROVAL_BLOCK_VARIANT)[keyof typeof APPROVAL_BLOCK_VARIANT];
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
* Visual generation of a chat thread. `v2` is the fae chat v2 design: author
|
|
86
|
-
* names without the trailing colon, a 24px assistant avatar, 16px between
|
|
87
|
-
* messages, and in-thread info/error blocks on the page surface. `classic`
|
|
88
|
-
* (default) is everything that predates it. The Fae client opts in first;
|
|
89
|
-
* Mingo and the admin views stay classic until their own redesign lands, then
|
|
90
|
-
* `classic` goes away.
|
|
91
|
-
*/
|
|
92
|
-
export const CHAT_APPEARANCE = {
|
|
93
|
-
CLASSIC: 'classic',
|
|
94
|
-
V2: 'v2',
|
|
95
|
-
} as const;
|
|
96
|
-
|
|
97
|
-
export type ChatAppearance = (typeof CHAT_APPEARANCE)[keyof typeof CHAT_APPEARANCE];
|
|
98
|
-
|
|
99
84
|
// ========== Connection Status Definitions ==========
|
|
100
85
|
|
|
101
86
|
export const CONNECTION_STATUS = {
|
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { ComponentType, HTMLAttributes, ReactNode, TextareaHTMLAttributes } from 'react';
|
|
6
|
-
import type { AvatarStackPerson } from '../../ui/avatar-stack';
|
|
7
6
|
import type { ChatRef } from '../chat-ref.types';
|
|
8
7
|
import type { MspOrganizationCardProps } from '../msp-organization-card';
|
|
9
8
|
import type {
|
|
10
9
|
ApprovalBlockVariant,
|
|
11
10
|
AssistantType,
|
|
12
11
|
AuthorType,
|
|
13
|
-
ChatAppearance,
|
|
14
12
|
ChatApprovalStatus,
|
|
15
13
|
ConnectionStatus,
|
|
16
14
|
} from './chat.types';
|
|
@@ -143,9 +141,6 @@ export interface ChatMessageEnhancedProps extends Omit<HTMLAttributes<HTMLDivEle
|
|
|
143
141
|
* (Fae desktop app) title-only card. Forwarded to
|
|
144
142
|
* ApprovalRequestMessage / ApprovalBatchMessage. */
|
|
145
143
|
approvalVariant?: ApprovalBlockVariant;
|
|
146
|
-
/** Visual generation of the row. Omit to inherit the thread's
|
|
147
|
-
* (`ChatMessageList.appearance`); `classic` outside a list. */
|
|
148
|
-
appearance?: ChatAppearance;
|
|
149
144
|
assistantIcon?: ReactNode;
|
|
150
145
|
avatar?: string | null;
|
|
151
146
|
timestamp?: Date;
|
|
@@ -293,9 +288,6 @@ export interface ChatMessageListProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
293
288
|
* `'client'` ONLY on true end-client surfaces — admin views of a Fae
|
|
294
289
|
* dialog (tickets dialog client tab) keep the default. */
|
|
295
290
|
approvalVariant?: ApprovalBlockVariant;
|
|
296
|
-
/** Visual generation of the whole thread, blocks included. Default
|
|
297
|
-
* `classic`; the Fae client passes `v2`. See `ChatAppearance`. */
|
|
298
|
-
appearance?: ChatAppearance;
|
|
299
291
|
assistantIcon?: ReactNode;
|
|
300
292
|
pendingApprovals?: MessageSegment[];
|
|
301
293
|
onApprove?: ApprovalResolutionHandler;
|
|
@@ -450,19 +442,7 @@ export interface ChatInputProps extends Omit<TextareaHTMLAttributes<HTMLTextArea
|
|
|
450
442
|
onSend?: (message: string) => void | boolean | Promise<boolean | void>;
|
|
451
443
|
onStop?: () => void | Promise<void>;
|
|
452
444
|
sending?: boolean;
|
|
453
|
-
/** Replaces the editor with the hand-off bar: the conversation is with the
|
|
454
|
-
* technicians now, and the client waits for one of them to reply. */
|
|
455
445
|
awaitingResponse?: boolean;
|
|
456
|
-
/** Faces shown in the v2 hand-off bar (the tenant's technicians). Omit or
|
|
457
|
-
* pass an empty list for a text-only bar. */
|
|
458
|
-
awaitingTeam?: AvatarStackPerson[];
|
|
459
|
-
/** Size of the whole team when `awaitingTeam` is only a sample of it: the
|
|
460
|
-
* bar shows those faces and "+N" for the rest. */
|
|
461
|
-
awaitingTeamCount?: number;
|
|
462
|
-
/** `v2`: the fae chat v2 hand-off bar ("Handed off to your technical
|
|
463
|
-
* support team" + faces). `classic` (default): "Waiting for Technician
|
|
464
|
-
* Response". Only the awaiting state differs. */
|
|
465
|
-
appearance?: ChatAppearance;
|
|
466
446
|
/** Same `fullWidth` semantics as `ChatHeaderProps.fullWidth` — drops
|
|
467
447
|
* the default `max-w-ods-content-narrow` so the input fills the
|
|
468
448
|
* parent. */
|