@flamingo-stack/openframe-frontend-core 0.0.684 → 0.0.685-2452.7452.1
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-T6KWNJDW.js → chunk-22RSJ57Z.js} +470 -465
- package/dist/chunk-22RSJ57Z.js.map +1 -0
- package/dist/{chunk-K2HZG4RM.cjs → chunk-2OD2PPNO.cjs} +32 -32
- package/dist/{chunk-K2HZG4RM.cjs.map → chunk-2OD2PPNO.cjs.map} +1 -1
- package/dist/{chunk-QFJHEDZV.js → chunk-3NY5ILEE.js} +3 -3
- package/dist/{chunk-QMUALBRX.js → chunk-4A5PYSPH.js} +6 -5
- package/dist/{chunk-QMUALBRX.js.map → chunk-4A5PYSPH.js.map} +1 -1
- package/dist/{chunk-CHFHYEZR.js → chunk-5QXN54QK.js} +3 -3
- package/dist/{chunk-6T4RTIW6.cjs → chunk-7CTJLBZM.cjs} +5 -5
- package/dist/{chunk-6T4RTIW6.cjs.map → chunk-7CTJLBZM.cjs.map} +1 -1
- package/dist/{chunk-ZYHN5ABN.js → chunk-7GKYMBQ4.js} +4 -4
- package/dist/{chunk-GJ7UAN7G.js → chunk-AELY4TAA.js} +3 -3
- package/dist/{chunk-SHNSCUIR.cjs → chunk-AKFA7ZDD.cjs} +28 -28
- package/dist/{chunk-SHNSCUIR.cjs.map → chunk-AKFA7ZDD.cjs.map} +1 -1
- package/dist/{chunk-PUZQENGY.js → chunk-BYG2NLD7.js} +3 -3
- package/dist/{chunk-VW4JDXAT.js → chunk-EXZPVTUM.js} +2086 -1644
- package/dist/chunk-EXZPVTUM.js.map +1 -0
- package/dist/{chunk-XQXIFRFQ.js → chunk-FTPDLV7B.js} +4 -4
- package/dist/{chunk-WCZJQIDW.js → chunk-G6G6R3HQ.js} +3 -3
- package/dist/{chunk-4IRROD7M.cjs → chunk-H5JOWXGI.cjs} +30 -30
- package/dist/{chunk-4IRROD7M.cjs.map → chunk-H5JOWXGI.cjs.map} +1 -1
- package/dist/{chunk-TUG7IT6H.js → chunk-HLEXYTOE.js} +2 -2
- package/dist/{chunk-UHLN2S7J.cjs → chunk-HTIOL44C.cjs} +1247 -805
- package/dist/chunk-HTIOL44C.cjs.map +1 -0
- package/dist/{chunk-777QY5YZ.cjs → chunk-ILMGYP66.cjs} +3 -3
- package/dist/{chunk-777QY5YZ.cjs.map → chunk-ILMGYP66.cjs.map} +1 -1
- package/dist/{chunk-7GSLZBGB.cjs → chunk-JEEYLBPQ.cjs} +10 -10
- package/dist/{chunk-7GSLZBGB.cjs.map → chunk-JEEYLBPQ.cjs.map} +1 -1
- package/dist/{chunk-KSZBUUTY.js → chunk-KLWCURLZ.js} +6 -6
- package/dist/{chunk-7HROJ2CB.cjs → chunk-KVOA22BW.cjs} +81 -81
- package/dist/{chunk-7HROJ2CB.cjs.map → chunk-KVOA22BW.cjs.map} +1 -1
- package/dist/{chunk-FVEQYZDH.js → chunk-L5OLMZGC.js} +7 -7
- package/dist/{chunk-64MGLDCX.cjs → chunk-LSY7OGTC.cjs} +384 -383
- package/dist/chunk-LSY7OGTC.cjs.map +1 -0
- package/dist/{chunk-J5CGC6ES.cjs → chunk-MGYWANYK.cjs} +12 -12
- package/dist/{chunk-J5CGC6ES.cjs.map → chunk-MGYWANYK.cjs.map} +1 -1
- package/dist/{chunk-E3G22CS7.cjs → chunk-OFYXFNVF.cjs} +33 -33
- package/dist/{chunk-E3G22CS7.cjs.map → chunk-OFYXFNVF.cjs.map} +1 -1
- package/dist/{chunk-DJ5UHDWB.cjs → chunk-OIAI5J35.cjs} +59 -59
- package/dist/{chunk-DJ5UHDWB.cjs.map → chunk-OIAI5J35.cjs.map} +1 -1
- package/dist/{chunk-DQOQ3KTP.cjs → chunk-QNWQQ4RT.cjs} +78 -73
- package/dist/chunk-QNWQQ4RT.cjs.map +1 -0
- package/dist/{chunk-5GMTMALA.js → chunk-ROOCATH7.js} +6 -6
- package/dist/{chunk-7MCK6KMW.js → chunk-RXFHPJ5O.js} +5 -5
- package/dist/{chunk-P7TNZJBZ.cjs → chunk-SO5NWQMG.cjs} +6 -6
- package/dist/{chunk-P7TNZJBZ.cjs.map → chunk-SO5NWQMG.cjs.map} +1 -1
- package/dist/{chunk-B5JLPHHE.js → chunk-THLCPUEG.js} +2 -2
- package/dist/{chunk-WA4O5ILG.js → chunk-TKRGKDNG.js} +5 -5
- package/dist/{chunk-KCEACU3F.js → chunk-URJD7BME.js} +3 -3
- package/dist/{chunk-OKXB2A2F.cjs → chunk-UU7NOXIK.cjs} +7 -7
- package/dist/{chunk-OKXB2A2F.cjs.map → chunk-UU7NOXIK.cjs.map} +1 -1
- package/dist/{chunk-CZUO5KVD.cjs → chunk-XWP5E453.cjs} +20 -20
- package/dist/{chunk-CZUO5KVD.cjs.map → chunk-XWP5E453.cjs.map} +1 -1
- package/dist/{chunk-ERTG6C5E.cjs → chunk-YTVZJNJ7.cjs} +22 -22
- package/dist/{chunk-ERTG6C5E.cjs.map → chunk-YTVZJNJ7.cjs.map} +1 -1
- package/dist/{chunk-VNNEAPXL.cjs → chunk-YVARUYL5.cjs} +15 -15
- package/dist/{chunk-VNNEAPXL.cjs.map → chunk-YVARUYL5.cjs.map} +1 -1
- 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-appearance-context.d.ts +9 -0
- package/dist/components/chat/chat-appearance-context.d.ts.map +1 -0
- 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/chat-nav-sidebar.d.ts +50 -0
- package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -0
- package/dist/components/chat/chat-top-navigation.d.ts +29 -0
- package/dist/components/chat/chat-top-navigation.d.ts.map +1 -0
- package/dist/components/chat/error-message-display.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +21 -3
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +6 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +20 -2
- package/dist/components/chat/msp-organization-card.d.ts +7 -1
- package/dist/components/chat/msp-organization-card.d.ts.map +1 -1
- package/dist/components/chat/onboarding-carousel.d.ts +29 -0
- package/dist/components/chat/onboarding-carousel.d.ts.map +1 -0
- package/dist/components/chat/system-event-message.d.ts +14 -0
- package/dist/components/chat/system-event-message.d.ts.map +1 -0
- package/dist/components/chat/types/chat.types.d.ts +13 -0
- package/dist/components/chat/types/chat.types.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +20 -1
- 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 +73 -55
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +33 -15
- 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 +5 -1
- package/dist/components/ui/avatar-stack.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +22 -4
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +21 -3
- package/dist/components/ui/reopen-ticket-request-modal.d.ts +4 -1
- package/dist/components/ui/reopen-ticket-request-modal.d.ts.map +1 -1
- package/dist/index.cjs +27 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/__tests__/chat-appearance.test.tsx +79 -0
- package/src/components/chat/__tests__/onboarding-carousel.test.tsx +56 -0
- package/src/components/chat/ai-assistant-info.tsx +7 -1
- package/src/components/chat/approval-request-message.tsx +6 -1
- package/src/components/chat/chat-appearance-context.tsx +15 -0
- package/src/components/chat/chat-input.tsx +26 -1
- package/src/components/chat/chat-message-enhanced.tsx +180 -142
- package/src/components/chat/chat-message-list.tsx +169 -164
- package/src/components/chat/chat-nav-sidebar.tsx +163 -0
- package/src/components/chat/chat-top-navigation.tsx +109 -0
- package/src/components/chat/error-message-display.tsx +21 -3
- package/src/components/chat/index.ts +6 -0
- package/src/components/chat/msp-organization-card.tsx +35 -0
- package/src/components/chat/onboarding-carousel.tsx +162 -0
- package/src/components/chat/system-event-message.tsx +54 -0
- package/src/components/chat/types/chat.types.ts +15 -0
- package/src/components/chat/types/component.types.ts +20 -0
- package/src/components/ui/__tests__/avatar-stack.test.tsx +25 -0
- package/src/components/ui/avatar-stack.tsx +15 -5
- package/src/components/ui/reopen-ticket-request-modal.tsx +6 -2
- package/src/stories/FaeLayoutV2.stories.tsx +150 -0
- package/src/stories/FaeOnboardingV2.stories.tsx +68 -0
- package/src/stories/FaeThreadV2.stories.tsx +192 -0
- package/src/stories/fae-brand-decorator.tsx +28 -0
- package/src/styles/ods-colors.css +10 -0
- package/src/styles/ods-effects.css +13 -0
- package/dist/chunk-64MGLDCX.cjs.map +0 -1
- package/dist/chunk-DQOQ3KTP.cjs.map +0 -1
- package/dist/chunk-T6KWNJDW.js.map +0 -1
- package/dist/chunk-UHLN2S7J.cjs.map +0 -1
- package/dist/chunk-VW4JDXAT.js.map +0 -1
- /package/dist/{chunk-QFJHEDZV.js.map → chunk-3NY5ILEE.js.map} +0 -0
- /package/dist/{chunk-CHFHYEZR.js.map → chunk-5QXN54QK.js.map} +0 -0
- /package/dist/{chunk-ZYHN5ABN.js.map → chunk-7GKYMBQ4.js.map} +0 -0
- /package/dist/{chunk-GJ7UAN7G.js.map → chunk-AELY4TAA.js.map} +0 -0
- /package/dist/{chunk-PUZQENGY.js.map → chunk-BYG2NLD7.js.map} +0 -0
- /package/dist/{chunk-XQXIFRFQ.js.map → chunk-FTPDLV7B.js.map} +0 -0
- /package/dist/{chunk-WCZJQIDW.js.map → chunk-G6G6R3HQ.js.map} +0 -0
- /package/dist/{chunk-TUG7IT6H.js.map → chunk-HLEXYTOE.js.map} +0 -0
- /package/dist/{chunk-KSZBUUTY.js.map → chunk-KLWCURLZ.js.map} +0 -0
- /package/dist/{chunk-FVEQYZDH.js.map → chunk-L5OLMZGC.js.map} +0 -0
- /package/dist/{chunk-5GMTMALA.js.map → chunk-ROOCATH7.js.map} +0 -0
- /package/dist/{chunk-7MCK6KMW.js.map → chunk-RXFHPJ5O.js.map} +0 -0
- /package/dist/{chunk-B5JLPHHE.js.map → chunk-THLCPUEG.js.map} +0 -0
- /package/dist/{chunk-WA4O5ILG.js.map → chunk-TKRGKDNG.js.map} +0 -0
- /package/dist/{chunk-KCEACU3F.js.map → chunk-URJD7BME.js.map} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { ChatInput } from '../components/chat/chat-input';
|
|
5
|
+
import { ChatListEmptyState } from '../components/chat/chat-list-empty-state';
|
|
6
|
+
import { ChatMessageList } from '../components/chat/chat-message-list';
|
|
7
|
+
import { ChatListItem, ChatNavSidebar } from '../components/chat/chat-nav-sidebar';
|
|
8
|
+
import { ChatTopNavigation } from '../components/chat/chat-top-navigation';
|
|
9
|
+
import { MspOrganizationCard } from '../components/chat/msp-organization-card';
|
|
10
|
+
import type { Message } from '../components/chat/types/message.types';
|
|
11
|
+
import { OpenFrameLogo } from '../components/icons';
|
|
12
|
+
import {
|
|
13
|
+
ChatPlusIcon,
|
|
14
|
+
ChatQuestionIcon,
|
|
15
|
+
ChatsIcon,
|
|
16
|
+
CheckCircleIcon,
|
|
17
|
+
ClockHistoryIcon,
|
|
18
|
+
DotsLoaderIcon,
|
|
19
|
+
HourglassClockIcon,
|
|
20
|
+
MessageIndicatorIcon,
|
|
21
|
+
UserCheckIcon,
|
|
22
|
+
} from '../components/icons-v2-generated';
|
|
23
|
+
import { Button } from '../components/ui/button';
|
|
24
|
+
import { withFaeBrand } from './fae-brand-decorator';
|
|
25
|
+
|
|
26
|
+
const at = (minute: number) => new Date(2026, 8, 29, 14, minute);
|
|
27
|
+
|
|
28
|
+
const THREAD: Message[] = [
|
|
29
|
+
{
|
|
30
|
+
id: 'u1',
|
|
31
|
+
role: 'user',
|
|
32
|
+
name: 'John Smith',
|
|
33
|
+
content: 'My laptop has been running really slowly lately',
|
|
34
|
+
timestamp: at(47),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'a1',
|
|
38
|
+
role: 'assistant',
|
|
39
|
+
name: 'Fae',
|
|
40
|
+
assistantType: 'fae',
|
|
41
|
+
content: "I'll run a quick diagnostic to identify what's causing the performance issues.",
|
|
42
|
+
timestamp: at(47),
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const CHATS = [
|
|
47
|
+
{ id: '1', title: 'Slow Laptop', mark: <DotsLoaderIcon className="text-ods-accent" /> },
|
|
48
|
+
{ id: '2', title: 'Forgot my password' },
|
|
49
|
+
{ id: '3', title: 'Teams mic not working', mark: <HourglassClockIcon className="text-ods-accent" /> },
|
|
50
|
+
{ id: '4', title: 'Email not syncing on phone', mark: <MessageIndicatorIcon className="text-ods-accent" /> },
|
|
51
|
+
{ id: '5', title: 'Blue screen on startup', resolved: true },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
function Layout({ withChats, withThread }: { withChats: boolean; withThread: boolean }) {
|
|
55
|
+
const [sidebarOpen, setSidebarOpen] = useState(true);
|
|
56
|
+
return (
|
|
57
|
+
<div data-surface="brand-tinted" className="flex h-[800px] w-[1024px] border-t border-ods-border bg-ods-bg">
|
|
58
|
+
{sidebarOpen && (
|
|
59
|
+
<ChatNavSidebar
|
|
60
|
+
logo={
|
|
61
|
+
<span className="flex items-center gap-[var(--spacing-system-xxs)] text-ods-text-primary">
|
|
62
|
+
<OpenFrameLogo
|
|
63
|
+
className="size-6"
|
|
64
|
+
lowerPathColor="var(--ods-open-yellow-base)"
|
|
65
|
+
upperPathColor="var(--color-text-primary)"
|
|
66
|
+
/>
|
|
67
|
+
<span className="text-wordmark">OpenFrame</span>
|
|
68
|
+
</span>
|
|
69
|
+
}
|
|
70
|
+
items={[
|
|
71
|
+
{ id: 'new', label: 'Start New Chat', icon: <ChatPlusIcon />, active: !withThread },
|
|
72
|
+
{ id: 'archive', label: 'Chat Archive', icon: <ClockHistoryIcon /> },
|
|
73
|
+
{ id: 'guide', label: 'What to ask Fae?', icon: <ChatQuestionIcon /> },
|
|
74
|
+
]}
|
|
75
|
+
sectionLabel="Your Current Chats"
|
|
76
|
+
footer={<MspOrganizationCard variant="footer" name="TechFlow Solutions" onOpenWebsite={() => undefined} />}
|
|
77
|
+
>
|
|
78
|
+
{withChats ? (
|
|
79
|
+
CHATS.map((chat, i) => (
|
|
80
|
+
<ChatListItem
|
|
81
|
+
key={chat.id}
|
|
82
|
+
title={chat.title}
|
|
83
|
+
subtitle="Ticket Number: 1932"
|
|
84
|
+
active={withThread && i === 0}
|
|
85
|
+
leading={chat.resolved ? <CheckCircleIcon className="text-ods-success" /> : undefined}
|
|
86
|
+
trailing={chat.mark}
|
|
87
|
+
/>
|
|
88
|
+
))
|
|
89
|
+
) : (
|
|
90
|
+
<ChatListEmptyState
|
|
91
|
+
icon={<ChatsIcon size={24} />}
|
|
92
|
+
title="No Current Chats"
|
|
93
|
+
description="Your conversations will show here"
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
</ChatNavSidebar>
|
|
97
|
+
)}
|
|
98
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
99
|
+
<ChatTopNavigation
|
|
100
|
+
name="Fae"
|
|
101
|
+
subtitle="Your AI Assistant"
|
|
102
|
+
sidebarOpen={sidebarOpen}
|
|
103
|
+
onToggleSidebar={() => setSidebarOpen(open => !open)}
|
|
104
|
+
onOpenSettings={() => undefined}
|
|
105
|
+
actions={
|
|
106
|
+
<Button variant="outline" size="small" leftIcon={<UserCheckIcon />}>
|
|
107
|
+
Request a Technician
|
|
108
|
+
</Button>
|
|
109
|
+
}
|
|
110
|
+
/>
|
|
111
|
+
<main className="ods-glow-accent-corner flex min-h-0 flex-1 flex-col gap-[var(--spacing-system-m)] px-[var(--spacing-system-m)] pb-[var(--spacing-system-m)] pt-[var(--spacing-system-mf)]">
|
|
112
|
+
{withThread ? (
|
|
113
|
+
<ChatMessageList
|
|
114
|
+
messages={THREAD}
|
|
115
|
+
assistantType="fae"
|
|
116
|
+
approvalVariant="client"
|
|
117
|
+
appearance="v2"
|
|
118
|
+
fullWidth
|
|
119
|
+
contentClassName="px-0"
|
|
120
|
+
autoScroll={false}
|
|
121
|
+
/>
|
|
122
|
+
) : (
|
|
123
|
+
<div className="flex min-h-0 flex-1 flex-col items-center justify-center gap-[var(--spacing-system-l)]">
|
|
124
|
+
<div className="flex flex-col gap-[var(--spacing-system-xsf)] text-center">
|
|
125
|
+
<h1 className="text-ods-text-primary text-h2">Hi, I'm your IT assistant</h1>
|
|
126
|
+
<p className="text-ods-text-secondary text-h4">
|
|
127
|
+
Having trouble with your computer, email, Wi-Fi, or software? Just describe it.
|
|
128
|
+
</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
<ChatInput fullWidth appearance="v2" placeholder="Enter your request here..." />
|
|
133
|
+
</main>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const meta: Meta<typeof Layout> = {
|
|
140
|
+
decorators: [withFaeBrand],
|
|
141
|
+
title: 'Chat/Fae Layout v2',
|
|
142
|
+
component: Layout,
|
|
143
|
+
parameters: { layout: 'fullscreen' },
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export default meta;
|
|
147
|
+
type Story = StoryObj<typeof Layout>;
|
|
148
|
+
|
|
149
|
+
export const StartEmpty: Story = { args: { withChats: false, withThread: false } };
|
|
150
|
+
export const ChatOpen: Story = { args: { withChats: true, withThread: true } };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
|
|
3
|
+
import { OnboardingCarousel, type OnboardingCarouselStep } from '../components/chat/onboarding-carousel';
|
|
4
|
+
import { PoweredByFlamingo } from '../components/features/auth/auth-branding';
|
|
5
|
+
import { OpenFrameLogo } from '../components/icons';
|
|
6
|
+
import { ClockCheckIcon, MessagesIcon, UserCheckIcon } from '../components/icons-v2-generated';
|
|
7
|
+
import { SquareAvatar } from '../components/ui/square-avatar';
|
|
8
|
+
import { withFaeBrand } from './fae-brand-decorator';
|
|
9
|
+
|
|
10
|
+
const STEPS: OnboardingCarouselStep[] = [
|
|
11
|
+
{
|
|
12
|
+
id: 'meet',
|
|
13
|
+
icon: <SquareAvatar variant="round" sizePx={28} fallback="Fae" />,
|
|
14
|
+
title: 'Meet Your AI IT Assistant',
|
|
15
|
+
description: 'Fixes what it can right away,\nand hands off the rest to your technicians.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'type',
|
|
19
|
+
icon: <MessagesIcon size={28} />,
|
|
20
|
+
title: "Type what's going on.",
|
|
21
|
+
description:
|
|
22
|
+
"Your assistant troubleshoots first.\nAnything it can't solve goes to your technician with full context.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'human',
|
|
26
|
+
icon: <UserCheckIcon size={28} />,
|
|
27
|
+
title: 'Need a human?',
|
|
28
|
+
description: 'Skip the chat and create a ticket.\nIt goes straight to your technicians.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'anytime',
|
|
32
|
+
icon: <ClockCheckIcon size={28} />,
|
|
33
|
+
title: 'Ask anything, anytime.',
|
|
34
|
+
description: 'No hold music, no queue.\nJust immediate help.',
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
function Screen() {
|
|
39
|
+
return (
|
|
40
|
+
<div className="flex h-[800px] w-[1024px] flex-col items-center gap-[calc(2*var(--spacing-system-xlf))] bg-ods-bg px-[calc(2*var(--spacing-system-xlf))] py-[var(--spacing-system-xl)]">
|
|
41
|
+
<span className="flex items-center gap-[var(--spacing-system-xxs)] text-ods-text-primary">
|
|
42
|
+
<OpenFrameLogo
|
|
43
|
+
className="size-6"
|
|
44
|
+
lowerPathColor="var(--ods-open-yellow-base)"
|
|
45
|
+
upperPathColor="var(--color-text-primary)"
|
|
46
|
+
/>
|
|
47
|
+
<span className="text-wordmark">OpenFrame</span>
|
|
48
|
+
</span>
|
|
49
|
+
{/* The side cards run to the window edge, past the screen padding. */}
|
|
50
|
+
<div className="-mx-[calc(2*var(--spacing-system-xlf))] flex min-h-0 flex-1 self-stretch">
|
|
51
|
+
<OnboardingCarousel steps={STEPS} onComplete={() => undefined} />
|
|
52
|
+
</div>
|
|
53
|
+
<PoweredByFlamingo />
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const meta: Meta<typeof Screen> = {
|
|
59
|
+
decorators: [withFaeBrand],
|
|
60
|
+
title: 'Chat/Fae Onboarding v2',
|
|
61
|
+
component: Screen,
|
|
62
|
+
parameters: { layout: 'fullscreen' },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default meta;
|
|
66
|
+
type Story = StoryObj<typeof Screen>;
|
|
67
|
+
|
|
68
|
+
export const Introduction: Story = {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
|
|
3
|
+
import { ChatInput } from '../components/chat/chat-input';
|
|
4
|
+
import { ChatMessageList } from '../components/chat/chat-message-list';
|
|
5
|
+
import type { ChatAppearance } from '../components/chat/types/chat.types';
|
|
6
|
+
import type { Message } from '../components/chat/types/message.types';
|
|
7
|
+
import { withFaeBrand } from './fae-brand-decorator';
|
|
8
|
+
|
|
9
|
+
const at = (minute: number) => new Date(2026, 8, 29, 14, minute);
|
|
10
|
+
|
|
11
|
+
// The "Admin joins" frame of the fae chat v2 escalation flow: a user request,
|
|
12
|
+
// an escalation offer the user approved, the hand-off and join receipts, then
|
|
13
|
+
// the technician's reply.
|
|
14
|
+
const ESCALATION_THREAD: Message[] = [
|
|
15
|
+
{
|
|
16
|
+
id: 'u1',
|
|
17
|
+
role: 'user',
|
|
18
|
+
name: 'John Smith',
|
|
19
|
+
content: 'My laptop has been running really slowly lately',
|
|
20
|
+
timestamp: at(47),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'a1',
|
|
24
|
+
role: 'assistant',
|
|
25
|
+
name: 'Fae',
|
|
26
|
+
assistantType: 'fae',
|
|
27
|
+
content: 'Sorry, I can’t help with this problem',
|
|
28
|
+
timestamp: at(47),
|
|
29
|
+
},
|
|
30
|
+
{ id: 'u2', role: 'user', name: 'John Smith', content: 'call the admin', timestamp: at(47) },
|
|
31
|
+
{
|
|
32
|
+
id: 'a2',
|
|
33
|
+
role: 'assistant',
|
|
34
|
+
name: 'Fae',
|
|
35
|
+
assistantType: 'fae',
|
|
36
|
+
timestamp: at(47),
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'escalation_offer',
|
|
40
|
+
status: 'approved',
|
|
41
|
+
resolvedByName: 'User',
|
|
42
|
+
data: {
|
|
43
|
+
offerId: 'offer-1',
|
|
44
|
+
text: 'Having issues with the AI assistant? This ticket can be handed off to a technician. Fae will no longer respond in this conversation. A technician will review the ticket and reply when available.',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{ type: 'ticket_escalated', data: { ticketId: 't-1', reason: 'MANUAL' } },
|
|
48
|
+
{
|
|
49
|
+
type: 'error',
|
|
50
|
+
title: 'AI response error',
|
|
51
|
+
details: 'Something went wrong. Please try again.',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 's1',
|
|
57
|
+
role: 'user',
|
|
58
|
+
authorType: 'system',
|
|
59
|
+
name: 'Michael Johnson joined the chat',
|
|
60
|
+
content: '',
|
|
61
|
+
timestamp: at(48),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 't1',
|
|
65
|
+
role: 'assistant',
|
|
66
|
+
name: 'Michael Johnson',
|
|
67
|
+
authorType: 'admin',
|
|
68
|
+
content: 'I’ll do it faster',
|
|
69
|
+
timestamp: at(48),
|
|
70
|
+
},
|
|
71
|
+
{ id: 'u3', role: 'user', name: 'John Smith', content: 'Cool!', timestamp: at(48) },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const APPROVAL_THREAD: Message[] = [
|
|
75
|
+
{
|
|
76
|
+
id: 'u1',
|
|
77
|
+
role: 'user',
|
|
78
|
+
name: 'John Smith',
|
|
79
|
+
content: 'My laptop has been running really slowly lately',
|
|
80
|
+
timestamp: at(47),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'a1',
|
|
84
|
+
role: 'assistant',
|
|
85
|
+
name: 'Fae',
|
|
86
|
+
assistantType: 'fae',
|
|
87
|
+
content:
|
|
88
|
+
'I found that your C: drive is 89% full. I can clean up temporary files to free some space - may I proceed with clearing C:/temp?',
|
|
89
|
+
timestamp: at(47),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'a2',
|
|
93
|
+
role: 'assistant',
|
|
94
|
+
name: 'Fae',
|
|
95
|
+
assistantType: 'fae',
|
|
96
|
+
timestamp: at(47),
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'approval_request',
|
|
100
|
+
status: 'pending',
|
|
101
|
+
data: {
|
|
102
|
+
requestId: 'req-1',
|
|
103
|
+
command: 'Remove-Item C:\\Temp\\* -Recurse',
|
|
104
|
+
explanation: 'Clears temporary files in C:\\Temp to free disk space.',
|
|
105
|
+
},
|
|
106
|
+
onApprove: () => undefined,
|
|
107
|
+
onReject: () => undefined,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
function Thread({
|
|
114
|
+
messages,
|
|
115
|
+
appearance,
|
|
116
|
+
handedOff,
|
|
117
|
+
}: {
|
|
118
|
+
messages: Message[];
|
|
119
|
+
appearance: ChatAppearance;
|
|
120
|
+
handedOff?: boolean;
|
|
121
|
+
}) {
|
|
122
|
+
return (
|
|
123
|
+
<div className="flex h-[720px] w-[728px] flex-col bg-ods-bg">
|
|
124
|
+
<ChatMessageList
|
|
125
|
+
messages={messages}
|
|
126
|
+
assistantType="fae"
|
|
127
|
+
approvalVariant="client"
|
|
128
|
+
appearance={appearance}
|
|
129
|
+
fullWidth
|
|
130
|
+
autoScroll={false}
|
|
131
|
+
/>
|
|
132
|
+
<div className="px-[var(--spacing-system-m)] pb-[var(--spacing-system-m)]">
|
|
133
|
+
<ChatInput
|
|
134
|
+
fullWidth
|
|
135
|
+
placeholder="Enter your request here..."
|
|
136
|
+
awaitingResponse={handedOff}
|
|
137
|
+
appearance={appearance}
|
|
138
|
+
awaitingTeam={[
|
|
139
|
+
{ key: '1', name: 'Roman Smith' },
|
|
140
|
+
{ key: '2', name: 'Ada Lin' },
|
|
141
|
+
{ key: '3', name: 'Michael Johnson' },
|
|
142
|
+
]}
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const meta: Meta<typeof Thread> = {
|
|
150
|
+
decorators: [withFaeBrand],
|
|
151
|
+
title: 'Chat/Fae Thread v2',
|
|
152
|
+
component: Thread,
|
|
153
|
+
parameters: { layout: 'centered' },
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export default meta;
|
|
157
|
+
type Story = StoryObj<typeof Thread>;
|
|
158
|
+
|
|
159
|
+
export const EscalationV2: Story = { args: { messages: ESCALATION_THREAD, appearance: 'v2', handedOff: true } };
|
|
160
|
+
export const EscalationClassic: Story = {
|
|
161
|
+
args: { messages: ESCALATION_THREAD, appearance: 'classic', handedOff: true },
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const approvalCard = (id: string, status: 'approved' | 'rejected' | 'cancelled', resolvedByName: string) => ({
|
|
165
|
+
type: 'approval_request' as const,
|
|
166
|
+
status,
|
|
167
|
+
resolvedByName,
|
|
168
|
+
data: {
|
|
169
|
+
requestId: id,
|
|
170
|
+
command: 'Remove-Item C:\\Temp\\* -Recurse',
|
|
171
|
+
explanation: 'Clears temporary files in C:\\Temp.',
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const APPROVAL_STATES_THREAD: Message[] = [
|
|
176
|
+
{
|
|
177
|
+
id: 'a1',
|
|
178
|
+
role: 'assistant',
|
|
179
|
+
name: 'Fae',
|
|
180
|
+
assistantType: 'fae',
|
|
181
|
+
timestamp: at(47),
|
|
182
|
+
content: [
|
|
183
|
+
approvalCard('r1', 'approved', 'Michael Johnson'),
|
|
184
|
+
approvalCard('r2', 'rejected', 'Michael Johnson'),
|
|
185
|
+
approvalCard('r3', 'cancelled', 'Michael Johnson'),
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
|
|
190
|
+
export const ApprovalV2: Story = { args: { messages: APPROVAL_THREAD, appearance: 'v2' } };
|
|
191
|
+
|
|
192
|
+
export const ApprovalStatesV2: Story = { args: { messages: APPROVAL_STATES_THREAD, appearance: 'v2' } };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Decorator } from '@storybook/nextjs-vite';
|
|
2
|
+
import { useLayoutEffect, type ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders a story in the Fae client's default brand. The chat app sets
|
|
6
|
+
* `data-app-type="flamingo"` on <html> before first paint, which points the
|
|
7
|
+
* accent tokens at flamingo pink; without it Storybook falls back to the
|
|
8
|
+
* OpenFrame yellow and a Fae screen shows two brand colours at once. Restores
|
|
9
|
+
* the previous value so the next story is unaffected.
|
|
10
|
+
*/
|
|
11
|
+
function FaeBrand({ children }: { children: ReactNode }) {
|
|
12
|
+
useLayoutEffect(() => {
|
|
13
|
+
const root = document.documentElement;
|
|
14
|
+
const previous = root.getAttribute('data-app-type');
|
|
15
|
+
root.setAttribute('data-app-type', 'flamingo');
|
|
16
|
+
return () => {
|
|
17
|
+
if (previous === null) root.removeAttribute('data-app-type');
|
|
18
|
+
else root.setAttribute('data-app-type', previous);
|
|
19
|
+
};
|
|
20
|
+
}, []);
|
|
21
|
+
return <>{children}</>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const withFaeBrand: Decorator = Story => (
|
|
25
|
+
<FaeBrand>
|
|
26
|
+
<Story />
|
|
27
|
+
</FaeBrand>
|
|
28
|
+
);
|
|
@@ -407,6 +407,16 @@
|
|
|
407
407
|
--color-focus-ring: #00ff00;
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
/* ===================================================================== */
|
|
411
|
+
/* BRAND-TINTED SURFACE — the page background takes 2% of the accent. */
|
|
412
|
+
/* Figma's fae-background (#1a1719) and mingo-background (#171b1a) are */
|
|
413
|
+
/* exactly 2% of their brand over the base background. Resolved on the */
|
|
414
|
+
/* element, so it follows the theme and a runtime (tenant) accent. */
|
|
415
|
+
/* ===================================================================== */
|
|
416
|
+
[data-surface='brand-tinted'] {
|
|
417
|
+
--color-bg: color-mix(in srgb, var(--color-accent-primary) 2%, var(--ods-system-greys-background));
|
|
418
|
+
}
|
|
419
|
+
|
|
410
420
|
/* ===================================================================== */
|
|
411
421
|
/* PLATFORM OVERRIDES — brand/accent ONLY. */
|
|
412
422
|
/* Reference `--ods-*` so they follow the active theme; never touch */
|
|
@@ -51,3 +51,16 @@
|
|
|
51
51
|
.ods-shadow-glyph {
|
|
52
52
|
filter: var(--ods-shadow-glyph);
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
/* The brand glow in the bottom-right corner of a chat surface (Fae chat v2):
|
|
56
|
+
the accent at 4% fading out over the whole surface (radial fill centred on
|
|
57
|
+
the corner, radii 100% × 103% as in the design). Written
|
|
58
|
+
on the class, not as a :root variable, so the accent resolves on the element
|
|
59
|
+
and follows a platform or runtime (tenant) accent set below the root. */
|
|
60
|
+
.ods-glow-accent-corner {
|
|
61
|
+
background-image: radial-gradient(
|
|
62
|
+
100% 103% at 100% 100%,
|
|
63
|
+
color-mix(in srgb, var(--color-accent-primary) 4%, transparent) 0%,
|
|
64
|
+
color-mix(in srgb, var(--color-accent-primary) 0%, transparent) 100%
|
|
65
|
+
);
|
|
66
|
+
}
|