blixify-ui-web-mcp 0.1.2 → 0.1.18
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/lib/components/action/button/README.md +12 -11
- package/lib/components/action/button/button.stories.tsx +4 -10
- package/lib/components/action/buttonGroup/buttonGroup.stories.tsx +4 -10
- package/lib/components/action/iconButton/iconButton.stories.tsx +4 -10
- package/lib/components/action/radioList/radioList.stories.tsx +4 -10
- package/lib/components/action/slider/slider.stories.tsx +4 -10
- package/lib/components/action/textButton/textButton.stories.tsx +4 -10
- package/lib/components/action/toggle/toggle.stories.tsx +4 -10
- package/lib/components/advanced/collapse/collapse.stories.tsx +4 -10
- package/lib/components/advanced/infoWindow/infoWindow.stories.tsx +4 -10
- package/lib/components/advanced/popUp/README.md +21 -7
- package/lib/components/advanced/popUp/popUp.stories.tsx +4 -10
- package/lib/components/auth/bareSignIn/bareSignIn.stories.tsx +30 -10
- package/lib/components/auth/bareSignUp/bareSignUp.stories.tsx +4 -10
- package/lib/components/auth/signIn/signIn.stories.tsx +4 -10
- package/lib/components/auth/signUp/signUp.stories.tsx +4 -10
- package/lib/components/chart/areaChart/areaChart.stories.tsx +7 -13
- package/lib/components/chart/barChart/barChart.stories.tsx +7 -13
- package/lib/components/chart/boxPlotChart/boxPlotChart.stories.tsx +8 -14
- package/lib/components/chart/bubbleChart/bubbleChart.stories.tsx +8 -14
- package/lib/components/chart/card/card.stories.tsx +4 -10
- package/lib/components/chart/ganttChart/README.md +96 -110
- package/lib/components/chart/ganttChart/ganttChart.stories.tsx +135 -125
- package/lib/components/chart/heatmapChart/README.md +93 -82
- package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +122 -84
- package/lib/components/chart/lineChart/lineChart.stories.tsx +4 -10
- package/lib/components/chart/pieChart/pieChart.stories.tsx +4 -10
- package/lib/components/chart/polarAreaChart/polarAreaChart.stories.tsx +8 -14
- package/lib/components/chart/radarChart/radarChart.stories.tsx +4 -10
- package/lib/components/chart/radialBar/radialChart.stories.tsx +4 -10
- package/lib/components/chart/rangeBarChart/rangeBarChart.stories.tsx +4 -10
- package/lib/components/chart/statsCard/statsCard.stories.tsx +4 -10
- package/lib/components/chart/treemapChart/treemapChart.stories.tsx +8 -14
- package/lib/components/chat/chatAnchor/README.md +61 -79
- package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +13 -19
- package/lib/components/chat/chatBase/README.md +244 -196
- package/lib/components/chat/chatBase/chatBase.stories.tsx +828 -250
- package/lib/components/chat/chatDashboard/README.md +89 -129
- package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +406 -117
- package/lib/components/data/dataGraph/README.md +135 -1
- package/lib/components/data/dataGraph/dataGraph.stories.tsx +90 -77
- package/lib/components/data/dataReference/README.md +141 -43
- package/lib/components/data/dataReference/dataReference.stories.tsx +111 -45
- package/lib/components/data/dataTemplate/README.md +88 -61
- package/lib/components/data/dataTemplate/dataModule.stories.tsx +4 -10
- package/lib/components/data/dataTemplate/dataTemplate.stories.tsx +4 -10
- package/lib/components/data/dataWrapper/README.md +48 -46
- package/lib/components/data/dataWrapper/dataWrapper.stories.tsx +258 -489
- package/lib/components/data/queryForm/queryForm.stories.tsx +4 -10
- package/lib/components/data/readQuery/readQuery.stories.tsx +4 -9
- package/lib/components/data/readQueryHoc/readQueryHoc.stories.tsx +4 -10
- package/lib/components/data/writeQuery/writeQuery.stories.tsx +4 -8
- package/lib/components/design/actionSection/actionSection.stories.tsx +6 -10
- package/lib/components/design/banner/banner.stories.tsx +4 -10
- package/lib/components/design/bannerAction/bannerAction.stories.tsx +4 -10
- package/lib/components/design/blogSection/blogSection.stories.tsx +4 -10
- package/lib/components/design/carouselSection/carouselSection.stories.tsx +4 -10
- package/lib/components/design/designList/designList.stories.tsx +4 -10
- package/lib/components/design/faq/faq.stories.tsx +4 -10
- package/lib/components/design/fileGrid/fileGrid.stories.tsx +4 -10
- package/lib/components/design/fileViewer/fileViewer.stories.tsx +4 -10
- package/lib/components/design/footerSection/footerSection.stories.tsx +4 -10
- package/lib/components/design/googleReview/googleReview.stories.tsx +4 -10
- package/lib/components/design/heroSection/heroSection.stories.tsx +4 -10
- package/lib/components/design/iconList/iconList.stories.tsx +4 -10
- package/lib/components/design/imageGallery/imageGallery.stories.tsx +4 -10
- package/lib/components/design/imageZoom/imageZoom.stories.tsx +4 -10
- package/lib/components/design/leftContent/leftContent.stories.tsx +4 -10
- package/lib/components/design/logoCloud/logoCloud.stories.tsx +4 -10
- package/lib/components/design/onboarding/onboarding.stories.tsx +4 -10
- package/lib/components/design/pricing/pricing.stories.tsx +4 -10
- package/lib/components/design/rightContent/rightContent.stories.tsx +4 -10
- package/lib/components/design/sectionList/sectionList.stories.tsx +4 -10
- package/lib/components/design/teams/teams.stories.tsx +4 -10
- package/lib/components/display/alert/alert.stories.tsx +4 -10
- package/lib/components/display/calendar/README.md +49 -0
- package/lib/components/display/calendar/calendar.stories.tsx +71 -28
- package/lib/components/display/clipboard/clipboard.stories.tsx +4 -10
- package/lib/components/display/detailList/detailList.stories.tsx +5 -10
- package/lib/components/display/drawer/drawer.stories.tsx +5 -10
- package/lib/components/display/emptyState/emptyState.stories.tsx +4 -10
- package/lib/components/display/feeds/feeds.stories.tsx +4 -10
- package/lib/components/display/flexTable/README.md +11 -7
- package/lib/components/display/flexTable/flexTable.stories.tsx +114 -11
- package/lib/components/display/grid/README.md +28 -27
- package/lib/components/display/grid/grid.stories.tsx +4 -10
- package/lib/components/display/kanban/kanban.stories.tsx +14 -20
- package/lib/components/display/list/README.md +30 -29
- package/lib/components/display/list/list.stories.tsx +4 -10
- package/lib/components/display/loading/loading.stories.tsx +5 -10
- package/lib/components/display/mapView/mapView.stories.tsx +4 -10
- package/lib/components/display/modal/modal.stories.tsx +4 -11
- package/lib/components/display/notification/notification.stories.tsx +4 -10
- package/lib/components/display/progress/progress.stories.tsx +5 -10
- package/lib/components/display/stepper/stepper.stories.tsx +5 -10
- package/lib/components/display/table/README.md +38 -37
- package/lib/components/display/table/table.stories.tsx +8 -14
- package/lib/components/display/tag/tag.stories.tsx +4 -10
- package/lib/components/input/address/address.stories.tsx +4 -10
- package/lib/components/input/canvas/canvas.stories.tsx +4 -10
- package/lib/components/input/checkbox/checkbox.stories.tsx +4 -10
- package/lib/components/input/datePicker/datePicker.stories.tsx +4 -10
- package/lib/components/input/form/form.stories.tsx +4 -10
- package/lib/components/input/markdown/markdown.stories.tsx +5 -10
- package/lib/components/input/monthYearPicker/monthYearPicker.stories.tsx +4 -10
- package/lib/components/input/netPromoterScore/netPromoterScore.stories.tsx +4 -10
- package/lib/components/input/rating/rating.stories.tsx +4 -10
- package/lib/components/input/select/README.md +30 -29
- package/lib/components/input/select/select.stories.tsx +4 -10
- package/lib/components/input/signature/signature.stories.tsx +4 -10
- package/lib/components/input/textArea/textArea.stories.tsx +4 -10
- package/lib/components/input/textInput/textinput.stories.tsx +4 -10
- package/lib/components/input/uploadInput/uploadInput.stories.tsx +135 -11
- package/lib/components/navigation/bars/README.md +90 -17
- package/lib/components/navigation/bars/bars.stories.tsx +128 -60
- package/lib/components/navigation/bottomTab/bottomTab.stories.tsx +5 -10
- package/lib/components/navigation/breadcrumb/README.md +40 -20
- package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +34 -14
- package/lib/components/navigation/header/README.md +67 -108
- package/lib/components/navigation/header/header.stories.tsx +165 -49
- package/lib/components/navigation/leftNav/README.md +121 -66
- package/lib/components/navigation/leftNav/leftNav.stories.tsx +178 -91
- package/lib/components/navigation/tabs/tabs.stories.tsx +57 -39
- package/lib/components/structure/column/column.stories.tsx +5 -10
- package/lib/components/structure/container/container.stories.tsx +5 -10
- package/lib/components/structure/link/link.stories.tsx +4 -10
- package/lib/components/structure/text/text.stories.tsx +5 -10
- package/lib/components/template/authPage/authPage.stories.tsx +6 -5
- package/lib/components/template/landingPage/landingPage.stories.tsx +8 -6
- package/lib/components/template/productListing/productListing.stories.tsx +7 -13
- package/lib/components/template/productOverview/productOverview.stories.tsx +5 -11
- package/lib/components/template/shoppingCart/shoppingCart.stories.tsx +6 -12
- package/lib/components/template/userHomePage/userHomePage.stories.tsx +7 -6
- package/lib/components/template/userRankingDashboard/userRankingDashboard.stories.tsx +5 -11
- package/package.json +1 -1
|
@@ -1,306 +1,884 @@
|
|
|
1
1
|
import { TrashIcon } from "@heroicons/react/24/outline";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { ChatBase, ChatBaseAction, Conversation } from ".";
|
|
2
|
+
import { MicrophoneIcon, CameraIcon } from "@heroicons/react/24/outline";
|
|
3
|
+
import { DocumentIcon, XMarkIcon } from "@heroicons/react/24/solid";
|
|
4
|
+
import dayjs from "../../dayjs";
|
|
5
|
+
import React, { useState } from "react";
|
|
6
|
+
import { ChatBase, ChatBaseAction, Conversation, Message, ToolCall } from ".";
|
|
7
7
|
import Logo from "../../../assets/logo.png";
|
|
8
|
+
import { StorySection } from "../../storyUtils";
|
|
8
9
|
import { TextButton } from "../../action/textButton";
|
|
9
|
-
import {
|
|
10
|
-
import README from "./README.md";
|
|
10
|
+
import { IconButton } from "../../action/iconButton";
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const meta = { title: "Chat/Chat Base" };
|
|
13
|
+
export default meta;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const botUser = {
|
|
16
|
+
id: "bot",
|
|
17
|
+
name: "Bot",
|
|
18
|
+
imageUrl:
|
|
19
|
+
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
|
|
20
|
+
};
|
|
21
|
+
const currentUser = { id: "me", name: "You" };
|
|
22
|
+
|
|
23
|
+
const seedMessages: Message[] = [
|
|
24
|
+
{
|
|
25
|
+
sender: botUser,
|
|
26
|
+
messageBody: "Hi! How can I help you? 🤖",
|
|
27
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
sender: currentUser,
|
|
31
|
+
messageBody: "My computer keeps crashing.",
|
|
32
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
sender: botUser,
|
|
36
|
+
messageBody:
|
|
37
|
+
"**Quick Tips**\n1. Restart your machine.\n2. Check for updates.\n3. Free up disk space.",
|
|
38
|
+
createdAt: dayjs("2023-11-03 12:02").toDate(),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
sender: currentUser,
|
|
42
|
+
messageBody: "Thanks, I'll try that!",
|
|
43
|
+
createdAt: dayjs("2023-11-03 12:03").toDate(),
|
|
17
44
|
},
|
|
45
|
+
{
|
|
46
|
+
sender: botUser,
|
|
47
|
+
messageBody:
|
|
48
|
+
"You can also check the [official troubleshooting guide](https://google.com) or visit [our FAQ page](https://google.com) for more help.",
|
|
49
|
+
createdAt: dayjs("2023-11-03 12:04").toDate(),
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const makeConversation = (
|
|
54
|
+
messages: Message[] = seedMessages
|
|
55
|
+
): Conversation => ({
|
|
56
|
+
id: "1",
|
|
57
|
+
members: [currentUser, botUser],
|
|
58
|
+
messageList: messages,
|
|
18
59
|
});
|
|
19
60
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
61
|
+
const toolCallSequence: {
|
|
62
|
+
content: string;
|
|
63
|
+
request: string;
|
|
64
|
+
delayMs: number;
|
|
65
|
+
}[] = [
|
|
66
|
+
{
|
|
67
|
+
content: "search_knowledge_base",
|
|
68
|
+
request: "query: order status #4821",
|
|
69
|
+
delayMs: 800,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
content: "fetch_user_context",
|
|
73
|
+
request: "userId: current_user",
|
|
74
|
+
delayMs: 1400,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
content: "generate_summary",
|
|
78
|
+
request: "summarize order + delivery info",
|
|
79
|
+
delayMs: 2200,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const seedToolMessages: Message[] = [
|
|
84
|
+
{
|
|
85
|
+
sender: botUser,
|
|
86
|
+
messageBody:
|
|
87
|
+
"Hi! I'm an AI assistant. Ask me anything and I'll show you which tools I use to answer.",
|
|
88
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
sender: { id: "me", name: "You" },
|
|
92
|
+
messageBody: "What's the status of my latest order?",
|
|
93
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
sender: botUser,
|
|
97
|
+
messageBody:
|
|
98
|
+
"Your latest order #4821 is currently **out for delivery** and should arrive today by 6 PM.",
|
|
99
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
100
|
+
toolReport: {
|
|
101
|
+
model: "gemini-3-flash-preview",
|
|
102
|
+
tools: [
|
|
103
|
+
{
|
|
104
|
+
content: "search_orders",
|
|
105
|
+
request: "query: latest order for user",
|
|
106
|
+
response: { success: true, output: "Order #4821 found" },
|
|
107
|
+
status: "done",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
content: "fetch_delivery_status",
|
|
111
|
+
request: "orderId: 4821",
|
|
112
|
+
response: { success: true, output: "Out for delivery, ETA 6 PM" },
|
|
113
|
+
status: "done",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
sender: { id: "me", name: "You" },
|
|
120
|
+
messageBody: "Can you summarise my account activity?",
|
|
121
|
+
createdAt: dayjs("2023-11-03 12:02").toDate(),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
sender: botUser,
|
|
125
|
+
messageBody:
|
|
126
|
+
"Sorry, I couldn't retrieve your account activity right now. Please try again later.",
|
|
127
|
+
createdAt: dayjs("2023-11-03 12:02").toDate(),
|
|
128
|
+
toolReport: {
|
|
129
|
+
model: "gemini-3-flash-preview",
|
|
130
|
+
tools: [
|
|
131
|
+
{
|
|
132
|
+
content: "fetch_account_activity",
|
|
133
|
+
request: "userId: current_user",
|
|
134
|
+
response: { success: false, output: "Service unavailable" },
|
|
135
|
+
status: "error",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
];
|
|
28
141
|
|
|
29
|
-
|
|
30
|
-
|
|
142
|
+
const toolCallConversation: Conversation = {
|
|
143
|
+
id: "tool-call-demo",
|
|
144
|
+
members: [currentUser, botUser],
|
|
145
|
+
messageList: [
|
|
146
|
+
{
|
|
147
|
+
sender: currentUser,
|
|
148
|
+
messageBody: "What's my policy renewal date?",
|
|
149
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
sender: botUser,
|
|
153
|
+
messageBody: "lookup_policy_by_user",
|
|
154
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
155
|
+
isToolCall: true,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
sender: botUser,
|
|
159
|
+
messageBody: "fetch_renewal_schedule",
|
|
160
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
161
|
+
isToolCall: true,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
sender: botUser,
|
|
165
|
+
messageBody:
|
|
166
|
+
"Your policy **POL-00421** is due for renewal on **15 Jan 2025**. Would you like me to send a reminder?",
|
|
167
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
sender: currentUser,
|
|
171
|
+
messageBody:
|
|
172
|
+
"Yes please, and also check if there are any pending claims.",
|
|
173
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
sender: botUser,
|
|
177
|
+
messageBody: "schedule_renewal_reminder",
|
|
178
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
179
|
+
isToolCall: true,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
sender: botUser,
|
|
183
|
+
messageBody: "fetch_claims_by_policy",
|
|
184
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
185
|
+
isToolCall: true,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
sender: botUser,
|
|
189
|
+
messageBody:
|
|
190
|
+
"Done! Reminder set for 8 Jan 2025. You have **1 pending claim** (REF-8821) currently under review.",
|
|
191
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// INFO: generates older messages for scroll-up pagination demo
|
|
197
|
+
const generateOlderMessages = (page: number): Message[] =>
|
|
198
|
+
Array.from({ length: 10 }, (_, i) => ({
|
|
199
|
+
sender: i % 2 === 0 ? botUser : currentUser,
|
|
200
|
+
messageBody: `Older message ${i + 1} from page ${page}`,
|
|
201
|
+
createdAt: dayjs("2023-11-03 12:00")
|
|
202
|
+
.subtract(page * 10 + (10 - i), "minutes")
|
|
203
|
+
.toDate(),
|
|
204
|
+
}));
|
|
205
|
+
|
|
206
|
+
export const ChatBaseStory = () => {
|
|
207
|
+
// --- Default ---
|
|
208
|
+
const [msg1, setMsg1] = useState("");
|
|
209
|
+
const [conv1, setConv1] = useState(makeConversation());
|
|
210
|
+
|
|
211
|
+
// --- Custom header/footer ---
|
|
212
|
+
const [msg2, setMsg2] = useState("");
|
|
213
|
+
const [conv2, setConv2] = useState(makeConversation());
|
|
214
|
+
const messageLimit = 8;
|
|
215
|
+
|
|
216
|
+
// --- Search ---
|
|
217
|
+
const [msg3, setMsg3] = useState("");
|
|
31
218
|
const [search, setSearch] = useState("");
|
|
32
|
-
const [
|
|
33
|
-
|
|
34
|
-
const [file, setFile] = useState<File | any>(undefined);
|
|
35
|
-
const [loading, setLoading] = useState(false);
|
|
219
|
+
const [conv3, setConv3] = useState(makeConversation());
|
|
220
|
+
const [filteredConv, setFilteredConv] = useState<Conversation | null>(null);
|
|
36
221
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
222
|
+
// --- File upload ---
|
|
223
|
+
const [msg4, setMsg4] = useState("");
|
|
224
|
+
const [conv4, setConv4] = useState(
|
|
225
|
+
makeConversation([
|
|
41
226
|
{
|
|
42
227
|
sender: botUser,
|
|
43
|
-
messageBody:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
messageBody: "Yo, my computer's acting up.",
|
|
51
|
-
createdAt: moment(
|
|
52
|
-
"Fri Nov 03 2023 13:21:14 GMT+0800 (Malaysia Time)"
|
|
53
|
-
).toDate(),
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
sender: currentUser,
|
|
57
|
-
messageBody:
|
|
58
|
-
"It keeps saying 'Error 404: Coffee not found' when I start it up.",
|
|
59
|
-
createdAt: moment(
|
|
60
|
-
"Fri Nov 03 2023 14:21:14 GMT+0800 (Malaysia Time)"
|
|
61
|
-
).toDate(),
|
|
228
|
+
messageBody: {
|
|
229
|
+
fileName:
|
|
230
|
+
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400",
|
|
231
|
+
token: "sample-image.jpg",
|
|
232
|
+
type: "image/jpeg",
|
|
233
|
+
} as any,
|
|
234
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
62
235
|
},
|
|
63
236
|
{
|
|
64
237
|
sender: botUser,
|
|
65
|
-
messageBody:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
sender: currentUser,
|
|
73
|
-
messageBody: "Seriously?",
|
|
74
|
-
createdAt: moment(
|
|
75
|
-
"Fri Nov 03 2023 16:21:14 GMT+0800 (Malaysia Time)"
|
|
76
|
-
).toDate(),
|
|
238
|
+
messageBody: {
|
|
239
|
+
fileName:
|
|
240
|
+
"https://firebasestorage.googleapis.com/v0/b/verdant-solar-dev.appspot.com/o/preCheckList%2F0184b7ee-8bb0-4a48-992c-ac8d37f8e833%2FPre-Checklist.pdf?alt=media&token=1de8eafb-27c4-4ff7-91a3-e640e4ef07cf",
|
|
241
|
+
token: "sample-document.pdf",
|
|
242
|
+
type: "application/pdf",
|
|
243
|
+
} as any,
|
|
244
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
77
245
|
},
|
|
78
246
|
{
|
|
79
247
|
sender: botUser,
|
|
80
|
-
messageBody:
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
248
|
+
messageBody: {
|
|
249
|
+
fileName:
|
|
250
|
+
"https://docs.google.com/document/d/18eW31QU-UY_Tw2zJBzwaWTiijbuRKR7x/edit?usp=drive_link&ouid=106313527006787712257&rtpof=true&sd=true",
|
|
251
|
+
token: "sample-report.docx",
|
|
252
|
+
type: "",
|
|
253
|
+
} as any,
|
|
254
|
+
createdAt: dayjs("2023-11-03 12:02").toDate(),
|
|
85
255
|
},
|
|
256
|
+
])
|
|
257
|
+
);
|
|
258
|
+
const [files4, setFiles4] = useState<File[]>([]);
|
|
259
|
+
|
|
260
|
+
// --- Multi-line input ---
|
|
261
|
+
const [msg11, setMsg11] = useState("");
|
|
262
|
+
const [conv11, setConv11] = useState(makeConversation());
|
|
263
|
+
|
|
264
|
+
// --- Media rendering (audio / attachments[]) ---
|
|
265
|
+
const [msg12, setMsg12] = useState("");
|
|
266
|
+
const [conv12, setConv12] = useState(
|
|
267
|
+
makeConversation([
|
|
86
268
|
{
|
|
87
269
|
sender: currentUser,
|
|
88
|
-
messageBody: "
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
270
|
+
messageBody: "Here are the photos from the site visit.",
|
|
271
|
+
attachments: [
|
|
272
|
+
{
|
|
273
|
+
fileName:
|
|
274
|
+
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400",
|
|
275
|
+
token: "site-photo-1.jpg",
|
|
276
|
+
type: "image/jpeg",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
fileName:
|
|
280
|
+
"https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=400",
|
|
281
|
+
token: "site-photo-2.jpg",
|
|
282
|
+
type: "image/jpeg",
|
|
283
|
+
},
|
|
284
|
+
] as any,
|
|
285
|
+
createdAt: dayjs("2023-11-03 12:00").toDate(),
|
|
92
286
|
},
|
|
93
287
|
{
|
|
94
288
|
sender: botUser,
|
|
95
289
|
messageBody:
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
).toDate(),
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
sender: botUser,
|
|
110
|
-
messageBody: {
|
|
111
|
-
fileName:
|
|
112
|
-
"https://player.vimeo.com/external/364708250.sd.mp4?s=7382fdedda817a193bcffb6ff74624fa245ee666&profile_id=164&oauth2_token_id=57447761",
|
|
113
|
-
token: "A",
|
|
114
|
-
type: "video/mp4",
|
|
115
|
-
},
|
|
116
|
-
createdAt: moment(
|
|
117
|
-
"Fri Nov 03 2023 19:21:14 GMT+0800 (Malaysia Time)"
|
|
118
|
-
).toDate(),
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
sender: currentUser,
|
|
122
|
-
messageBody: {
|
|
123
|
-
fileName:
|
|
124
|
-
"https://images.pexels.com/photos/3726314/pexels-photo-3726314.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
|
|
125
|
-
token: "B",
|
|
126
|
-
type: "image/png",
|
|
127
|
-
},
|
|
128
|
-
createdAt: moment(
|
|
129
|
-
"Fri Nov 03 2023 19:21:18 GMT+0800 (Malaysia Time)"
|
|
130
|
-
).toDate(),
|
|
290
|
+
"Got them, thanks! I've also attached the inspection report and a voice note summary.",
|
|
291
|
+
attachments: [
|
|
292
|
+
{
|
|
293
|
+
fileName:
|
|
294
|
+
"https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3",
|
|
295
|
+
token: "voice-summary.mp3",
|
|
296
|
+
type: "audio/mpeg",
|
|
297
|
+
},
|
|
298
|
+
] as any,
|
|
299
|
+
createdAt: dayjs("2023-11-03 12:01").toDate(),
|
|
131
300
|
},
|
|
132
301
|
{
|
|
133
302
|
sender: currentUser,
|
|
134
|
-
messageBody: "
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
303
|
+
messageBody: "Perfect. Can you also send the diagram?",
|
|
304
|
+
attachments: [
|
|
305
|
+
{
|
|
306
|
+
fileName:
|
|
307
|
+
"https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=400",
|
|
308
|
+
token: "diagram.jpg",
|
|
309
|
+
type: "image/jpeg",
|
|
310
|
+
},
|
|
311
|
+
] as any,
|
|
312
|
+
createdAt: dayjs("2023-11-03 12:02").toDate(),
|
|
138
313
|
},
|
|
139
|
-
]
|
|
314
|
+
])
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// --- Loading reply ---
|
|
318
|
+
const [msg5, setMsg5] = useState("Hello, any updates?");
|
|
319
|
+
const [conv5, setConv5] = useState(makeConversation());
|
|
320
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
321
|
+
|
|
322
|
+
// --- Tool report ---
|
|
323
|
+
const [msg6, setMsg6] = useState("");
|
|
324
|
+
const [isLoading6, setIsLoading6] = useState(false);
|
|
325
|
+
const [conv6, setConv6] = useState<Conversation>({
|
|
326
|
+
id: "tool-demo",
|
|
327
|
+
members: [currentUser, botUser],
|
|
328
|
+
messageList: seedToolMessages,
|
|
140
329
|
});
|
|
141
330
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
331
|
+
// --- Scroll-up pagination ---
|
|
332
|
+
const [msg7, setMsg7] = useState("");
|
|
333
|
+
const [msgPage, setMsgPage] = useState(1);
|
|
334
|
+
const [loadingOlder, setLoadingOlder] = useState(false);
|
|
335
|
+
const [conv7, setConv7] = useState<Conversation>(
|
|
336
|
+
makeConversation(generateOlderMessages(0))
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
// --- Quick replies (footerElement) ---
|
|
340
|
+
const quickReplies = [
|
|
341
|
+
"Track my order",
|
|
342
|
+
"Speak to an agent",
|
|
343
|
+
"View FAQs",
|
|
344
|
+
"Reset password",
|
|
345
|
+
];
|
|
346
|
+
const [msg8, setMsg8] = useState("");
|
|
347
|
+
const [conv8, setConv8] = useState(makeConversation());
|
|
348
|
+
|
|
349
|
+
// --- Input actions ---
|
|
350
|
+
const [msg9, setMsg9] = useState("");
|
|
351
|
+
const [conv9, setConv9] = useState(makeConversation());
|
|
147
352
|
|
|
148
|
-
|
|
149
|
-
|
|
353
|
+
// --- File attach with URI footer ---
|
|
354
|
+
const [msg10, setMsg10] = useState("");
|
|
355
|
+
const [conv10, setConv10] = useState(makeConversation());
|
|
356
|
+
const [files10, setFiles10] = useState<File[]>([]);
|
|
357
|
+
|
|
358
|
+
const sendMessage =
|
|
359
|
+
(
|
|
360
|
+
conv: Conversation,
|
|
361
|
+
setConv: (c: Conversation) => void,
|
|
362
|
+
setMsg: (s: string) => void
|
|
363
|
+
) =>
|
|
364
|
+
(updated: Conversation) => {
|
|
365
|
+
setConv(updated);
|
|
366
|
+
setMsg("");
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const handleSearch = (searchMessage: string) => {
|
|
370
|
+
if (!searchMessage) {
|
|
150
371
|
setSearch("");
|
|
151
|
-
|
|
372
|
+
setFilteredConv(null);
|
|
152
373
|
} else {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
|
|
374
|
+
setFilteredConv({
|
|
375
|
+
...conv3,
|
|
376
|
+
messageList: conv3.messageList.filter(
|
|
377
|
+
(m) =>
|
|
378
|
+
typeof m.messageBody === "string" &&
|
|
379
|
+
m.messageBody.toLowerCase().includes(searchMessage.toLowerCase())
|
|
380
|
+
),
|
|
160
381
|
});
|
|
161
|
-
const dupConversation = {
|
|
162
|
-
id: conversation.id,
|
|
163
|
-
members: [...conversation.members],
|
|
164
|
-
messageList: [...dupMessageList],
|
|
165
|
-
};
|
|
166
|
-
setFilteredConversation(dupConversation);
|
|
167
382
|
}
|
|
168
383
|
};
|
|
169
384
|
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
385
|
+
const handleSendToolReport = (updated: Conversation) => {
|
|
386
|
+
setMsg6("");
|
|
387
|
+
setIsLoading6(true);
|
|
388
|
+
|
|
389
|
+
const pendingTools: ToolCall[] = toolCallSequence.map((t) => ({
|
|
390
|
+
content: t.content,
|
|
391
|
+
request: t.request,
|
|
392
|
+
response: null,
|
|
393
|
+
status: "pending" as const,
|
|
394
|
+
}));
|
|
395
|
+
|
|
396
|
+
const placeholderMsg: Message = {
|
|
397
|
+
sender: botUser,
|
|
398
|
+
messageBody: "",
|
|
399
|
+
createdAt: dayjs().toDate(),
|
|
400
|
+
toolReport: { model: "gemini-3-flash-preview", tools: pendingTools },
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
setConv6({
|
|
404
|
+
...updated,
|
|
405
|
+
messageList: [...updated.messageList, placeholderMsg],
|
|
176
406
|
});
|
|
177
|
-
};
|
|
178
407
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
408
|
+
toolCallSequence.forEach(({ content, delayMs }) => {
|
|
409
|
+
setTimeout(() => {
|
|
410
|
+
setConv6((prev) => {
|
|
411
|
+
const msgs = [...prev.messageList];
|
|
412
|
+
const last = { ...msgs[msgs.length - 1] };
|
|
413
|
+
last.toolReport = {
|
|
414
|
+
...last.toolReport!,
|
|
415
|
+
tools: last.toolReport!.tools.map((t) =>
|
|
416
|
+
t.content === content
|
|
417
|
+
? {
|
|
418
|
+
...t,
|
|
419
|
+
status: "done" as const,
|
|
420
|
+
response: { success: true, output: "OK" },
|
|
421
|
+
}
|
|
422
|
+
: t
|
|
423
|
+
),
|
|
424
|
+
};
|
|
425
|
+
msgs[msgs.length - 1] = last;
|
|
426
|
+
return { ...prev, messageList: msgs };
|
|
427
|
+
});
|
|
428
|
+
}, delayMs);
|
|
429
|
+
});
|
|
186
430
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
fileName: fileUrl,
|
|
198
|
-
token: uuidv4(),
|
|
199
|
-
type: file.type,
|
|
200
|
-
};
|
|
201
|
-
dupConversation.messageList.push({
|
|
202
|
-
sender: currentUser,
|
|
203
|
-
messageBody: fileObj,
|
|
204
|
-
createdAt: moment().toDate(),
|
|
431
|
+
const totalDelay =
|
|
432
|
+
toolCallSequence[toolCallSequence.length - 1].delayMs + 400;
|
|
433
|
+
setTimeout(() => {
|
|
434
|
+
setConv6((prev) => {
|
|
435
|
+
const msgs = [...prev.messageList];
|
|
436
|
+
const last = { ...msgs[msgs.length - 1] };
|
|
437
|
+
last.messageBody =
|
|
438
|
+
"I've checked across all relevant sources. Here's a summary based on your query — everything looks good on our end!";
|
|
439
|
+
msgs[msgs.length - 1] = last;
|
|
440
|
+
return { ...prev, messageList: msgs };
|
|
205
441
|
});
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
setMessage("");
|
|
209
|
-
setFile(undefined);
|
|
210
|
-
setLoading(false);
|
|
211
|
-
}, 5000);
|
|
212
|
-
}
|
|
442
|
+
setIsLoading6(false);
|
|
443
|
+
}, totalDelay);
|
|
213
444
|
};
|
|
214
445
|
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
446
|
+
const handleSendWithReply = (updated: Conversation) => {
|
|
447
|
+
setConv5(updated);
|
|
448
|
+
setMsg5("");
|
|
449
|
+
setIsLoading(true);
|
|
450
|
+
setTimeout(() => {
|
|
451
|
+
setConv5((prev) => ({
|
|
452
|
+
...prev,
|
|
453
|
+
messageList: [
|
|
454
|
+
...prev.messageList,
|
|
455
|
+
{
|
|
456
|
+
sender: botUser,
|
|
457
|
+
messageBody: "Got it! Let me look into that for you.",
|
|
458
|
+
createdAt: dayjs().toDate(),
|
|
459
|
+
},
|
|
460
|
+
],
|
|
461
|
+
}));
|
|
462
|
+
setIsLoading(false);
|
|
463
|
+
}, 2000);
|
|
464
|
+
};
|
|
220
465
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
onClick={handleOnClearMessage}
|
|
234
|
-
className="mt-2"
|
|
235
|
-
/>
|
|
236
|
-
</div>
|
|
237
|
-
</div>
|
|
238
|
-
);
|
|
239
|
-
else return undefined;
|
|
466
|
+
// INFO: scroll-up handler — prepends older messages after a simulated delay
|
|
467
|
+
const handleLoadOlderMessages = () => {
|
|
468
|
+
setLoadingOlder(true);
|
|
469
|
+
const nextPage = msgPage + 1;
|
|
470
|
+
setTimeout(() => {
|
|
471
|
+
setConv7((prev) => ({
|
|
472
|
+
...prev,
|
|
473
|
+
messageList: [...generateOlderMessages(nextPage), ...prev.messageList],
|
|
474
|
+
}));
|
|
475
|
+
setMsgPage(nextPage);
|
|
476
|
+
setLoadingOlder(false);
|
|
477
|
+
}, 1000);
|
|
240
478
|
};
|
|
241
|
-
const footerElement = renderFooterElement();
|
|
242
479
|
|
|
243
|
-
const
|
|
480
|
+
const clearAction = (
|
|
481
|
+
setConv: (c: Conversation) => void
|
|
482
|
+
): ChatBaseAction[] => [
|
|
244
483
|
{
|
|
245
484
|
text: "Clear",
|
|
246
|
-
onClick:
|
|
247
|
-
icon: <TrashIcon className="w-3 h-3"
|
|
485
|
+
onClick: () => setConv(makeConversation([])),
|
|
486
|
+
icon: <TrashIcon className="w-3 h-3" />,
|
|
248
487
|
},
|
|
249
488
|
];
|
|
250
489
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
490
|
+
return (
|
|
491
|
+
<>
|
|
492
|
+
<StorySection
|
|
493
|
+
title="Default"
|
|
494
|
+
description="Basic chat with send and receive. Sent messages appear on the right; received messages on the left with the sender's avatar. Received messages support markdown rendering."
|
|
495
|
+
>
|
|
496
|
+
<ChatBase
|
|
497
|
+
open
|
|
498
|
+
value={msg1}
|
|
499
|
+
currentUser={currentUser}
|
|
500
|
+
conversation={conv1}
|
|
501
|
+
onSendMessage={sendMessage(conv1, setConv1, setMsg1)}
|
|
502
|
+
onChangeMessage={(e) => setMsg1(e.target.value)}
|
|
503
|
+
additionalAction={clearAction(setConv1)}
|
|
504
|
+
sizeClassName="h-[500px] w-96"
|
|
505
|
+
disableMobileResize
|
|
506
|
+
/>
|
|
507
|
+
</StorySection>
|
|
257
508
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
509
|
+
<StorySection
|
|
510
|
+
title="Custom Header & Footer"
|
|
511
|
+
description="headerElement replaces the default blue header bar. footerElement renders above the message input — useful for limit warnings, disclaimers, or action prompts."
|
|
512
|
+
>
|
|
513
|
+
<ChatBase
|
|
514
|
+
open
|
|
515
|
+
value={msg2}
|
|
516
|
+
currentUser={currentUser}
|
|
517
|
+
conversation={conv2}
|
|
518
|
+
onSendMessage={sendMessage(conv2, setConv2, setMsg2)}
|
|
519
|
+
onChangeMessage={(e) => setMsg2(e.target.value)}
|
|
520
|
+
headerElement={
|
|
521
|
+
<div className="p-3 bg-primary-500 rounded-t-md flex items-center">
|
|
522
|
+
<img src={Logo} alt="Logo" className="h-8" />
|
|
523
|
+
<span className="ml-3 text-white text-sm font-medium">
|
|
524
|
+
Support Chat
|
|
525
|
+
</span>
|
|
526
|
+
</div>
|
|
527
|
+
}
|
|
528
|
+
footerElement={
|
|
529
|
+
conv2.messageList.length >= messageLimit ? (
|
|
530
|
+
<div className="p-2">
|
|
531
|
+
<div className="p-2 text-xs border rounded-md bg-primary-50 border-primary-300">
|
|
532
|
+
Message limit reached ({messageLimit}).{" "}
|
|
533
|
+
<TextButton
|
|
534
|
+
text="Clear"
|
|
535
|
+
onClick={() => setConv2(makeConversation([]))}
|
|
536
|
+
className="inline"
|
|
537
|
+
/>
|
|
538
|
+
</div>
|
|
539
|
+
</div>
|
|
540
|
+
) : undefined
|
|
541
|
+
}
|
|
542
|
+
disableMessageInput={conv2.messageList.length >= messageLimit}
|
|
543
|
+
sizeClassName="h-[500px] w-96"
|
|
544
|
+
disableMobileResize
|
|
545
|
+
/>
|
|
546
|
+
</StorySection>
|
|
273
547
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
548
|
+
<StorySection
|
|
549
|
+
title="Message Search"
|
|
550
|
+
description="onSearch filters the conversation to matching messages. The search box is opened via the ⋮ actions menu. onChangeSearch updates the search input value."
|
|
551
|
+
>
|
|
552
|
+
<ChatBase
|
|
553
|
+
open
|
|
554
|
+
value={msg3}
|
|
555
|
+
currentUser={currentUser}
|
|
556
|
+
conversation={filteredConv ?? conv3}
|
|
557
|
+
searchMessage={search}
|
|
558
|
+
onSendMessage={sendMessage(conv3, setConv3, setMsg3)}
|
|
559
|
+
onChangeMessage={(e) => setMsg3(e.target.value)}
|
|
560
|
+
onSearch={handleSearch}
|
|
561
|
+
onChangeSearch={(e) => setSearch(e.target.value)}
|
|
562
|
+
additionalAction={clearAction(setConv3)}
|
|
563
|
+
sizeClassName="h-[500px] w-96"
|
|
564
|
+
disableMobileResize
|
|
565
|
+
/>
|
|
566
|
+
</StorySection>
|
|
567
|
+
|
|
568
|
+
<StorySection
|
|
569
|
+
title="File Upload"
|
|
570
|
+
description="fileUpload.allowFileUpload=true shows a paperclip button. Selected files appear as chips in footerElement. onSendMessage fires for both text and file sends — the consumer handles upload logic there."
|
|
571
|
+
>
|
|
572
|
+
<ChatBase
|
|
573
|
+
open
|
|
574
|
+
value={msg4}
|
|
575
|
+
currentUser={currentUser}
|
|
576
|
+
conversation={conv4}
|
|
577
|
+
fileUpload={{
|
|
578
|
+
allowFileUpload: true,
|
|
579
|
+
hasFiles: files4.length > 0,
|
|
580
|
+
multiple: true,
|
|
581
|
+
acceptFileType: ".jpg,.jpeg,.png,.mp4,.webm,.pdf,.docx",
|
|
582
|
+
onChangeUpload: (e) => {
|
|
583
|
+
if (e?.target?.files?.length > 0)
|
|
584
|
+
setFiles4((prev) => [
|
|
585
|
+
...prev,
|
|
586
|
+
...Array.from(e.target.files as FileList),
|
|
587
|
+
]);
|
|
588
|
+
},
|
|
589
|
+
}}
|
|
590
|
+
onSendMessage={(updated) => {
|
|
591
|
+
const newMessages = files4.map((f) => ({
|
|
592
|
+
sender: currentUser,
|
|
593
|
+
messageBody: {
|
|
594
|
+
fileName: URL.createObjectURL(f),
|
|
595
|
+
token: f.name,
|
|
596
|
+
type: f.type,
|
|
597
|
+
},
|
|
598
|
+
createdAt: dayjs().toDate(),
|
|
599
|
+
}));
|
|
600
|
+
setConv4({
|
|
601
|
+
...updated,
|
|
602
|
+
messageList: [...updated.messageList, ...newMessages],
|
|
603
|
+
});
|
|
604
|
+
setFiles4([]);
|
|
605
|
+
setMsg4("");
|
|
606
|
+
}}
|
|
607
|
+
onChangeMessage={(e) => setMsg4(e.target.value)}
|
|
608
|
+
footerElement={
|
|
609
|
+
files4.length > 0 ? (
|
|
610
|
+
<div className="flex flex-wrap gap-2 px-2 pb-1 pt-1">
|
|
611
|
+
{files4.map((f, i) => (
|
|
612
|
+
<span
|
|
613
|
+
key={i}
|
|
614
|
+
className="inline-flex items-center gap-1.5 rounded-full border border-primary-300 bg-primary-50 py-1 pl-2.5 pr-1.5 text-xs text-primary-700"
|
|
615
|
+
>
|
|
616
|
+
<DocumentIcon className="h-3.5 w-3.5 flex-shrink-0 text-primary-500" />
|
|
617
|
+
<span className="max-w-[140px] truncate">{f.name}</span>
|
|
618
|
+
<button
|
|
619
|
+
onClick={() =>
|
|
620
|
+
setFiles4((prev) => prev.filter((_, idx) => idx !== i))
|
|
621
|
+
}
|
|
622
|
+
className="ml-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full hover:bg-primary-200 transition-colors"
|
|
623
|
+
>
|
|
624
|
+
<XMarkIcon className="h-3 w-3 text-primary-500" />
|
|
625
|
+
</button>
|
|
626
|
+
</span>
|
|
627
|
+
))}
|
|
628
|
+
</div>
|
|
629
|
+
) : undefined
|
|
630
|
+
}
|
|
631
|
+
sizeClassName="h-[500px] w-96"
|
|
632
|
+
disableMobileResize
|
|
633
|
+
/>
|
|
634
|
+
</StorySection>
|
|
635
|
+
|
|
636
|
+
<StorySection
|
|
637
|
+
title="Loading Reply"
|
|
638
|
+
description="isLoadingReply=true shows an animated typing indicator above the input and disables the message input while waiting for a response. Send a message to see the effect."
|
|
639
|
+
>
|
|
640
|
+
<ChatBase
|
|
641
|
+
open
|
|
642
|
+
value={msg5}
|
|
643
|
+
currentUser={currentUser}
|
|
644
|
+
conversation={conv5}
|
|
645
|
+
isLoadingReply={isLoading}
|
|
646
|
+
onSendMessage={handleSendWithReply}
|
|
647
|
+
onChangeMessage={(e) => setMsg5(e.target.value)}
|
|
648
|
+
sizeClassName="h-[500px] w-96"
|
|
649
|
+
disableMobileResize
|
|
650
|
+
/>
|
|
651
|
+
</StorySection>
|
|
652
|
+
|
|
653
|
+
<StorySection
|
|
654
|
+
title="Tool Report"
|
|
655
|
+
description="When a message has a toolReport, a compact log panel is shown above the message body. It displays the model used and each tool call with a status dot: yellow (pending), green (done), red (error). Send a message to see the simulated tool-call flow."
|
|
656
|
+
>
|
|
657
|
+
<ChatBase
|
|
658
|
+
open
|
|
659
|
+
value={msg6}
|
|
660
|
+
currentUser={currentUser}
|
|
661
|
+
conversation={conv6}
|
|
662
|
+
isLoadingReply={isLoading6}
|
|
663
|
+
onSendMessage={handleSendToolReport}
|
|
664
|
+
onChangeMessage={(e) => setMsg6(e.target.value)}
|
|
665
|
+
sizeClassName="h-[500px] w-96"
|
|
666
|
+
disableMobileResize
|
|
667
|
+
/>
|
|
668
|
+
</StorySection>
|
|
669
|
+
|
|
670
|
+
<StorySection
|
|
671
|
+
title="Tool Call Messages"
|
|
672
|
+
description="Messages with isToolCall=true are rendered as centered inline chips instead of chat bubbles. This is used when the chat history includes tool invocation records (e.g. author: 'tool' from an AI pipeline). They sit between user and assistant messages to show what happened behind the scenes."
|
|
673
|
+
>
|
|
674
|
+
<ChatBase
|
|
675
|
+
open
|
|
676
|
+
value=""
|
|
677
|
+
currentUser={currentUser}
|
|
678
|
+
conversation={toolCallConversation}
|
|
679
|
+
sizeClassName="h-[500px] w-96"
|
|
680
|
+
disableMobileResize
|
|
681
|
+
hideAdditionalAction
|
|
682
|
+
/>
|
|
683
|
+
</StorySection>
|
|
684
|
+
|
|
685
|
+
<StorySection
|
|
686
|
+
title="Scroll-Up Pagination"
|
|
687
|
+
description="onMessageListScrollTop fires when scrolled near the top. isLoadingMessages shows a spinner at the top while older messages are being fetched. Scroll up to see the effect."
|
|
688
|
+
>
|
|
689
|
+
<ChatBase
|
|
690
|
+
open
|
|
691
|
+
value={msg7}
|
|
692
|
+
currentUser={currentUser}
|
|
693
|
+
conversation={conv7}
|
|
694
|
+
isLoadingMessages={loadingOlder}
|
|
695
|
+
onMessageListScrollTop={handleLoadOlderMessages}
|
|
696
|
+
onSendMessage={(updated) => {
|
|
697
|
+
setConv7(updated);
|
|
698
|
+
setMsg7("");
|
|
699
|
+
}}
|
|
700
|
+
onChangeMessage={(e) => setMsg7(e.target.value)}
|
|
701
|
+
sizeClassName="h-[500px] w-96"
|
|
702
|
+
disableMobileResize
|
|
703
|
+
/>
|
|
704
|
+
</StorySection>
|
|
705
|
+
|
|
706
|
+
<StorySection
|
|
707
|
+
title="Quick Replies (footerElement)"
|
|
708
|
+
description="footerElement renders above the message input. Pass quick-reply chips here so users can tap a preset prompt instead of typing. Clicking a chip fills the input — the user still hits send."
|
|
709
|
+
>
|
|
710
|
+
<ChatBase
|
|
711
|
+
open
|
|
712
|
+
value={msg8}
|
|
713
|
+
currentUser={currentUser}
|
|
714
|
+
conversation={conv8}
|
|
715
|
+
onSendMessage={(updated) => {
|
|
716
|
+
setConv8(updated);
|
|
717
|
+
setMsg8("");
|
|
718
|
+
}}
|
|
719
|
+
onChangeMessage={(e) => setMsg8(e.target.value)}
|
|
720
|
+
footerElement={
|
|
721
|
+
<div className="flex flex-wrap gap-2 px-2 pb-1 pt-1">
|
|
722
|
+
{quickReplies.map((reply) => (
|
|
723
|
+
<button
|
|
724
|
+
key={reply}
|
|
725
|
+
onClick={() => setMsg8(reply)}
|
|
726
|
+
className="text-xs px-3 py-1 rounded-full border border-primary-300 text-primary-600 bg-primary-50 hover:bg-primary-100 transition-colors"
|
|
727
|
+
>
|
|
728
|
+
{reply}
|
|
729
|
+
</button>
|
|
730
|
+
))}
|
|
731
|
+
</div>
|
|
732
|
+
}
|
|
733
|
+
sizeClassName="h-[500px] w-96"
|
|
734
|
+
disableMobileResize
|
|
735
|
+
/>
|
|
736
|
+
</StorySection>
|
|
737
|
+
|
|
738
|
+
<StorySection
|
|
739
|
+
title="File Attach with Chips Footer"
|
|
740
|
+
description="Attach multiple files via the paperclip. Each file appears as a dismissible chip in footerElement — click × to remove individually. onSendMessage handles both text and files together."
|
|
741
|
+
>
|
|
742
|
+
<ChatBase
|
|
743
|
+
open
|
|
744
|
+
value={msg10}
|
|
745
|
+
currentUser={currentUser}
|
|
746
|
+
conversation={conv10}
|
|
747
|
+
fileUpload={{
|
|
748
|
+
allowFileUpload: true,
|
|
749
|
+
hasFiles: files10.length > 0,
|
|
750
|
+
multiple: true,
|
|
751
|
+
acceptFileType: ".jpg,.jpeg,.png,.mp4,.webm,.pdf,.docx",
|
|
752
|
+
onChangeUpload: (e) => {
|
|
753
|
+
if (e?.target?.files?.length > 0)
|
|
754
|
+
setFiles10((prev) => [
|
|
755
|
+
...prev,
|
|
756
|
+
...Array.from(e.target.files as FileList),
|
|
757
|
+
]);
|
|
758
|
+
},
|
|
759
|
+
}}
|
|
760
|
+
onSendMessage={(updated) => {
|
|
761
|
+
const newMessages = files10.map((f) => ({
|
|
762
|
+
sender: currentUser,
|
|
763
|
+
messageBody: {
|
|
764
|
+
fileName: URL.createObjectURL(f),
|
|
765
|
+
token: f.name,
|
|
766
|
+
type: f.type,
|
|
767
|
+
},
|
|
768
|
+
createdAt: dayjs().toDate(),
|
|
769
|
+
}));
|
|
770
|
+
setConv10({
|
|
771
|
+
...updated,
|
|
772
|
+
messageList: [...updated.messageList, ...newMessages],
|
|
773
|
+
});
|
|
774
|
+
setFiles10([]);
|
|
775
|
+
setMsg10("");
|
|
776
|
+
}}
|
|
777
|
+
onChangeMessage={(e) => setMsg10(e.target.value)}
|
|
778
|
+
footerElement={
|
|
779
|
+
files10.length > 0 ? (
|
|
780
|
+
<div className="flex flex-wrap gap-2 px-2 pb-1 pt-1">
|
|
781
|
+
{files10.map((f, i) => (
|
|
782
|
+
<span
|
|
783
|
+
key={i}
|
|
784
|
+
className="inline-flex items-center gap-1.5 rounded-full border border-primary-300 bg-primary-50 py-1 pl-2.5 pr-1.5 text-xs text-primary-700"
|
|
785
|
+
>
|
|
786
|
+
<DocumentIcon className="h-3.5 w-3.5 flex-shrink-0 text-primary-500" />
|
|
787
|
+
<span className="max-w-[180px] truncate">{f.name}</span>
|
|
788
|
+
<button
|
|
789
|
+
onClick={() =>
|
|
790
|
+
setFiles10((prev) => prev.filter((_, idx) => idx !== i))
|
|
791
|
+
}
|
|
792
|
+
className="ml-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full hover:bg-primary-200 transition-colors"
|
|
793
|
+
>
|
|
794
|
+
<XMarkIcon className="h-3 w-3 text-primary-500" />
|
|
795
|
+
</button>
|
|
796
|
+
</span>
|
|
797
|
+
))}
|
|
798
|
+
</div>
|
|
799
|
+
) : undefined
|
|
800
|
+
}
|
|
801
|
+
sizeClassName="h-[500px] w-96"
|
|
802
|
+
disableMobileResize
|
|
803
|
+
/>
|
|
804
|
+
</StorySection>
|
|
805
|
+
|
|
806
|
+
<StorySection
|
|
807
|
+
title="Custom Input Actions (inputActions)"
|
|
808
|
+
description="inputActions renders extra icon buttons in the input row between the paperclip and the send button. Useful for voice input, camera, or any custom LLM action."
|
|
809
|
+
>
|
|
810
|
+
<ChatBase
|
|
811
|
+
open
|
|
812
|
+
value={msg9}
|
|
813
|
+
currentUser={currentUser}
|
|
814
|
+
conversation={conv9}
|
|
815
|
+
onSendMessage={(updated) => {
|
|
816
|
+
setConv9(updated);
|
|
817
|
+
setMsg9("");
|
|
818
|
+
}}
|
|
819
|
+
onChangeMessage={(e) => setMsg9(e.target.value)}
|
|
820
|
+
fileUpload={{
|
|
821
|
+
allowFileUpload: true,
|
|
822
|
+
hasFiles: false,
|
|
823
|
+
onChangeUpload: () => {},
|
|
824
|
+
}}
|
|
825
|
+
inputActions={[
|
|
826
|
+
<IconButton
|
|
827
|
+
key="mic"
|
|
828
|
+
onClick={() => alert("Voice input triggered")}
|
|
829
|
+
className="focus:ring-transparent flex flex-col justify-center"
|
|
830
|
+
icon={<MicrophoneIcon className="w-5 h-5 text-primary-500" />}
|
|
831
|
+
/>,
|
|
832
|
+
<IconButton
|
|
833
|
+
key="camera"
|
|
834
|
+
onClick={() => alert("Camera triggered")}
|
|
835
|
+
className="focus:ring-transparent flex flex-col justify-center"
|
|
836
|
+
icon={<CameraIcon className="w-5 h-5 text-primary-500" />}
|
|
837
|
+
/>,
|
|
838
|
+
]}
|
|
839
|
+
sizeClassName="h-[500px] w-96"
|
|
840
|
+
disableMobileResize
|
|
841
|
+
/>
|
|
842
|
+
</StorySection>
|
|
843
|
+
|
|
844
|
+
<StorySection
|
|
845
|
+
title="Multi-line Input (Email Composer)"
|
|
846
|
+
description="multiLine=true expands the textarea to 5 rows and moves action buttons to a bottom-right row. Enter adds a new line instead of sending — use the send button to submit. Ideal for composing emails."
|
|
847
|
+
>
|
|
848
|
+
<ChatBase
|
|
849
|
+
open
|
|
850
|
+
value={msg11}
|
|
851
|
+
currentUser={currentUser}
|
|
852
|
+
conversation={conv11}
|
|
853
|
+
multiLine={5}
|
|
854
|
+
onSendMessage={(updated) => {
|
|
855
|
+
setConv11(updated);
|
|
856
|
+
setMsg11("");
|
|
857
|
+
}}
|
|
858
|
+
onChangeMessage={(e) => setMsg11(e.target.value)}
|
|
859
|
+
sizeClassName="h-[500px] w-96"
|
|
860
|
+
disableMobileResize
|
|
861
|
+
/>
|
|
862
|
+
</StorySection>
|
|
863
|
+
|
|
864
|
+
<StorySection
|
|
865
|
+
title="Media Rendering (Text + Attachments)"
|
|
866
|
+
description="Messages with both a text messageBody and an attachments[] array — images, PDFs, and audio files rendered inline beneath the message text. Simulates real-world scenarios like sending a caption with photos or a bot reply with supporting files."
|
|
867
|
+
>
|
|
868
|
+
<ChatBase
|
|
869
|
+
open
|
|
870
|
+
value={msg12}
|
|
871
|
+
currentUser={currentUser}
|
|
872
|
+
conversation={conv12}
|
|
873
|
+
onSendMessage={(updated) => {
|
|
874
|
+
setConv12(updated);
|
|
875
|
+
setMsg12("");
|
|
876
|
+
}}
|
|
877
|
+
onChangeMessage={(e) => setMsg12(e.target.value)}
|
|
878
|
+
sizeClassName="h-[500px] w-96"
|
|
879
|
+
disableMobileResize
|
|
880
|
+
/>
|
|
881
|
+
</StorySection>
|
|
882
|
+
</>
|
|
305
883
|
);
|
|
306
|
-
}
|
|
884
|
+
};
|