blixify-ui-web-mcp 0.1.3 → 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.
Files changed (126) hide show
  1. package/lib/components/action/button/README.md +12 -11
  2. package/lib/components/action/button/button.stories.tsx +4 -10
  3. package/lib/components/action/buttonGroup/buttonGroup.stories.tsx +4 -10
  4. package/lib/components/action/iconButton/iconButton.stories.tsx +4 -10
  5. package/lib/components/action/radioList/radioList.stories.tsx +4 -10
  6. package/lib/components/action/slider/slider.stories.tsx +4 -10
  7. package/lib/components/action/textButton/textButton.stories.tsx +4 -10
  8. package/lib/components/action/toggle/toggle.stories.tsx +4 -10
  9. package/lib/components/advanced/collapse/collapse.stories.tsx +4 -10
  10. package/lib/components/advanced/infoWindow/infoWindow.stories.tsx +4 -10
  11. package/lib/components/advanced/popUp/README.md +21 -7
  12. package/lib/components/advanced/popUp/popUp.stories.tsx +4 -10
  13. package/lib/components/auth/bareSignIn/bareSignIn.stories.tsx +30 -10
  14. package/lib/components/auth/bareSignUp/bareSignUp.stories.tsx +4 -10
  15. package/lib/components/auth/signIn/signIn.stories.tsx +4 -10
  16. package/lib/components/auth/signUp/signUp.stories.tsx +4 -10
  17. package/lib/components/chart/areaChart/areaChart.stories.tsx +7 -13
  18. package/lib/components/chart/barChart/barChart.stories.tsx +7 -13
  19. package/lib/components/chart/boxPlotChart/boxPlotChart.stories.tsx +8 -14
  20. package/lib/components/chart/bubbleChart/bubbleChart.stories.tsx +8 -14
  21. package/lib/components/chart/card/card.stories.tsx +4 -10
  22. package/lib/components/chart/ganttChart/ganttChart.stories.tsx +4 -10
  23. package/lib/components/chart/heatmapChart/heatmapChart.stories.tsx +4 -10
  24. package/lib/components/chart/lineChart/lineChart.stories.tsx +4 -10
  25. package/lib/components/chart/pieChart/pieChart.stories.tsx +4 -10
  26. package/lib/components/chart/polarAreaChart/polarAreaChart.stories.tsx +8 -14
  27. package/lib/components/chart/radarChart/radarChart.stories.tsx +4 -10
  28. package/lib/components/chart/radialBar/radialChart.stories.tsx +4 -10
  29. package/lib/components/chart/rangeBarChart/rangeBarChart.stories.tsx +4 -10
  30. package/lib/components/chart/statsCard/statsCard.stories.tsx +4 -10
  31. package/lib/components/chart/treemapChart/treemapChart.stories.tsx +8 -14
  32. package/lib/components/chat/chatAnchor/chatAnchor.stories.tsx +13 -19
  33. package/lib/components/chat/chatBase/README.md +103 -22
  34. package/lib/components/chat/chatBase/chatBase.stories.tsx +652 -52
  35. package/lib/components/chat/chatDashboard/README.md +17 -15
  36. package/lib/components/chat/chatDashboard/chatDashboard.stories.tsx +356 -44
  37. package/lib/components/data/dataGraph/dataGraph.stories.tsx +4 -10
  38. package/lib/components/data/dataReference/dataReference.stories.tsx +4 -10
  39. package/lib/components/data/dataTemplate/README.md +88 -61
  40. package/lib/components/data/dataTemplate/dataModule.stories.tsx +4 -10
  41. package/lib/components/data/dataTemplate/dataTemplate.stories.tsx +4 -10
  42. package/lib/components/data/dataWrapper/README.md +46 -45
  43. package/lib/components/data/dataWrapper/dataWrapper.stories.tsx +258 -489
  44. package/lib/components/data/queryForm/queryForm.stories.tsx +4 -10
  45. package/lib/components/data/readQuery/readQuery.stories.tsx +4 -9
  46. package/lib/components/data/readQueryHoc/readQueryHoc.stories.tsx +4 -10
  47. package/lib/components/data/writeQuery/writeQuery.stories.tsx +4 -8
  48. package/lib/components/design/actionSection/actionSection.stories.tsx +6 -10
  49. package/lib/components/design/banner/banner.stories.tsx +4 -10
  50. package/lib/components/design/bannerAction/bannerAction.stories.tsx +4 -10
  51. package/lib/components/design/blogSection/blogSection.stories.tsx +4 -10
  52. package/lib/components/design/carouselSection/carouselSection.stories.tsx +4 -10
  53. package/lib/components/design/designList/designList.stories.tsx +4 -10
  54. package/lib/components/design/faq/faq.stories.tsx +4 -10
  55. package/lib/components/design/fileGrid/fileGrid.stories.tsx +4 -10
  56. package/lib/components/design/fileViewer/fileViewer.stories.tsx +4 -10
  57. package/lib/components/design/footerSection/footerSection.stories.tsx +4 -10
  58. package/lib/components/design/googleReview/googleReview.stories.tsx +4 -10
  59. package/lib/components/design/heroSection/heroSection.stories.tsx +4 -10
  60. package/lib/components/design/iconList/iconList.stories.tsx +4 -10
  61. package/lib/components/design/imageGallery/imageGallery.stories.tsx +4 -10
  62. package/lib/components/design/imageZoom/imageZoom.stories.tsx +4 -10
  63. package/lib/components/design/leftContent/leftContent.stories.tsx +4 -10
  64. package/lib/components/design/logoCloud/logoCloud.stories.tsx +4 -10
  65. package/lib/components/design/onboarding/onboarding.stories.tsx +4 -10
  66. package/lib/components/design/pricing/pricing.stories.tsx +4 -10
  67. package/lib/components/design/rightContent/rightContent.stories.tsx +4 -10
  68. package/lib/components/design/sectionList/sectionList.stories.tsx +4 -10
  69. package/lib/components/design/teams/teams.stories.tsx +4 -10
  70. package/lib/components/display/alert/alert.stories.tsx +4 -10
  71. package/lib/components/display/calendar/README.md +49 -0
  72. package/lib/components/display/calendar/calendar.stories.tsx +71 -28
  73. package/lib/components/display/clipboard/clipboard.stories.tsx +4 -10
  74. package/lib/components/display/detailList/detailList.stories.tsx +5 -10
  75. package/lib/components/display/drawer/drawer.stories.tsx +5 -10
  76. package/lib/components/display/emptyState/emptyState.stories.tsx +4 -10
  77. package/lib/components/display/feeds/feeds.stories.tsx +4 -10
  78. package/lib/components/display/flexTable/README.md +11 -7
  79. package/lib/components/display/flexTable/flexTable.stories.tsx +114 -11
  80. package/lib/components/display/grid/README.md +28 -27
  81. package/lib/components/display/grid/grid.stories.tsx +4 -10
  82. package/lib/components/display/kanban/kanban.stories.tsx +14 -20
  83. package/lib/components/display/list/README.md +30 -29
  84. package/lib/components/display/list/list.stories.tsx +4 -10
  85. package/lib/components/display/loading/loading.stories.tsx +5 -10
  86. package/lib/components/display/mapView/mapView.stories.tsx +4 -10
  87. package/lib/components/display/modal/modal.stories.tsx +4 -11
  88. package/lib/components/display/notification/notification.stories.tsx +4 -10
  89. package/lib/components/display/progress/progress.stories.tsx +5 -10
  90. package/lib/components/display/stepper/stepper.stories.tsx +5 -10
  91. package/lib/components/display/table/README.md +38 -37
  92. package/lib/components/display/table/table.stories.tsx +8 -14
  93. package/lib/components/display/tag/tag.stories.tsx +4 -10
  94. package/lib/components/input/address/address.stories.tsx +4 -10
  95. package/lib/components/input/canvas/canvas.stories.tsx +4 -10
  96. package/lib/components/input/checkbox/checkbox.stories.tsx +4 -10
  97. package/lib/components/input/datePicker/datePicker.stories.tsx +4 -10
  98. package/lib/components/input/form/form.stories.tsx +4 -10
  99. package/lib/components/input/markdown/markdown.stories.tsx +5 -10
  100. package/lib/components/input/monthYearPicker/monthYearPicker.stories.tsx +4 -10
  101. package/lib/components/input/netPromoterScore/netPromoterScore.stories.tsx +4 -10
  102. package/lib/components/input/rating/rating.stories.tsx +4 -10
  103. package/lib/components/input/select/README.md +30 -29
  104. package/lib/components/input/select/select.stories.tsx +4 -10
  105. package/lib/components/input/signature/signature.stories.tsx +4 -10
  106. package/lib/components/input/textArea/textArea.stories.tsx +4 -10
  107. package/lib/components/input/textInput/textinput.stories.tsx +4 -10
  108. package/lib/components/input/uploadInput/uploadInput.stories.tsx +135 -11
  109. package/lib/components/navigation/bars/bars.stories.tsx +4 -10
  110. package/lib/components/navigation/bottomTab/bottomTab.stories.tsx +5 -10
  111. package/lib/components/navigation/breadcrumb/breadcrumb.stories.tsx +4 -10
  112. package/lib/components/navigation/header/header.stories.tsx +24 -12
  113. package/lib/components/navigation/leftNav/leftNav.stories.tsx +4 -10
  114. package/lib/components/navigation/tabs/tabs.stories.tsx +4 -10
  115. package/lib/components/structure/column/column.stories.tsx +5 -10
  116. package/lib/components/structure/container/container.stories.tsx +5 -10
  117. package/lib/components/structure/link/link.stories.tsx +4 -10
  118. package/lib/components/structure/text/text.stories.tsx +5 -10
  119. package/lib/components/template/authPage/authPage.stories.tsx +6 -5
  120. package/lib/components/template/landingPage/landingPage.stories.tsx +8 -6
  121. package/lib/components/template/productListing/productListing.stories.tsx +7 -13
  122. package/lib/components/template/productOverview/productOverview.stories.tsx +5 -11
  123. package/lib/components/template/shoppingCart/shoppingCart.stories.tsx +6 -12
  124. package/lib/components/template/userHomePage/userHomePage.stories.tsx +7 -6
  125. package/lib/components/template/userRankingDashboard/userRankingDashboard.stories.tsx +5 -11
  126. package/package.json +1 -1
@@ -1,21 +1,16 @@
1
1
  import { TrashIcon } from "@heroicons/react/24/outline";
2
- import { storiesOf } from "@storybook/react";
3
- import moment from "moment";
2
+ import { MicrophoneIcon, CameraIcon } from "@heroicons/react/24/outline";
3
+ import { DocumentIcon, XMarkIcon } from "@heroicons/react/24/solid";
4
+ import dayjs from "../../dayjs";
4
5
  import React, { useState } from "react";
5
- import { v4 as uuidv4 } from "uuid";
6
- import { ChatBase, ChatBaseAction, Conversation } from ".";
6
+ import { ChatBase, ChatBaseAction, Conversation, Message, ToolCall } from ".";
7
7
  import Logo from "../../../assets/logo.png";
8
8
  import { StorySection } from "../../storyUtils";
9
9
  import { TextButton } from "../../action/textButton";
10
- import README from "./README.md";
10
+ import { IconButton } from "../../action/iconButton";
11
11
 
12
- const stories = storiesOf("Chat", module);
13
-
14
- stories.addParameters({
15
- readme: {
16
- content: README,
17
- },
18
- });
12
+ const meta = { title: "Chat/Chat Base" };
13
+ export default meta;
19
14
 
20
15
  const botUser = {
21
16
  id: "bot",
@@ -25,37 +20,190 @@ const botUser = {
25
20
  };
26
21
  const currentUser = { id: "me", name: "You" };
27
22
 
28
- const seedMessages = [
23
+ const seedMessages: Message[] = [
29
24
  {
30
25
  sender: botUser,
31
26
  messageBody: "Hi! How can I help you? 🤖",
32
- createdAt: moment("2023-11-03 12:00").toDate(),
27
+ createdAt: dayjs("2023-11-03 12:00").toDate(),
33
28
  },
34
29
  {
35
30
  sender: currentUser,
36
31
  messageBody: "My computer keeps crashing.",
37
- createdAt: moment("2023-11-03 12:01").toDate(),
32
+ createdAt: dayjs("2023-11-03 12:01").toDate(),
38
33
  },
39
34
  {
40
35
  sender: botUser,
41
36
  messageBody:
42
37
  "**Quick Tips**\n1. Restart your machine.\n2. Check for updates.\n3. Free up disk space.",
43
- createdAt: moment("2023-11-03 12:02").toDate(),
38
+ createdAt: dayjs("2023-11-03 12:02").toDate(),
44
39
  },
45
40
  {
46
41
  sender: currentUser,
47
42
  messageBody: "Thanks, I'll try that!",
48
- createdAt: moment("2023-11-03 12:03").toDate(),
43
+ createdAt: dayjs("2023-11-03 12:03").toDate(),
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(),
49
50
  },
50
51
  ];
51
52
 
52
- const makeConversation = (messages = seedMessages): Conversation => ({
53
+ const makeConversation = (
54
+ messages: Message[] = seedMessages
55
+ ): Conversation => ({
53
56
  id: "1",
54
57
  members: [currentUser, botUser],
55
58
  messageList: messages,
56
59
  });
57
60
 
58
- stories.add("Chat Base", () => {
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
+ ];
141
+
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 = () => {
59
207
  // --- Default ---
60
208
  const [msg1, setMsg1] = useState("");
61
209
  const [conv1, setConv1] = useState(makeConversation());
@@ -73,14 +221,140 @@ stories.add("Chat Base", () => {
73
221
 
74
222
  // --- File upload ---
75
223
  const [msg4, setMsg4] = useState("");
76
- const [conv4, setConv4] = useState(makeConversation());
77
- const [file, setFile] = useState<File | any>(undefined);
224
+ const [conv4, setConv4] = useState(
225
+ makeConversation([
226
+ {
227
+ sender: botUser,
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(),
235
+ },
236
+ {
237
+ sender: botUser,
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(),
245
+ },
246
+ {
247
+ sender: botUser,
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(),
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([
268
+ {
269
+ sender: currentUser,
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(),
286
+ },
287
+ {
288
+ sender: botUser,
289
+ messageBody:
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(),
300
+ },
301
+ {
302
+ sender: currentUser,
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(),
313
+ },
314
+ ])
315
+ );
78
316
 
79
317
  // --- Loading reply ---
80
- const [msg5, setMsg5] = useState("");
318
+ const [msg5, setMsg5] = useState("Hello, any updates?");
81
319
  const [conv5, setConv5] = useState(makeConversation());
82
320
  const [isLoading, setIsLoading] = useState(false);
83
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,
329
+ });
330
+
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());
352
+
353
+ // --- File attach with URI footer ---
354
+ const [msg10, setMsg10] = useState("");
355
+ const [conv10, setConv10] = useState(makeConversation());
356
+ const [files10, setFiles10] = useState<File[]>([]);
357
+
84
358
  const sendMessage =
85
359
  (
86
360
  conv: Conversation,
@@ -108,6 +382,67 @@ stories.add("Chat Base", () => {
108
382
  }
109
383
  };
110
384
 
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],
406
+ });
407
+
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
+ });
430
+
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 };
441
+ });
442
+ setIsLoading6(false);
443
+ }, totalDelay);
444
+ };
445
+
111
446
  const handleSendWithReply = (updated: Conversation) => {
112
447
  setConv5(updated);
113
448
  setMsg5("");
@@ -120,7 +455,7 @@ stories.add("Chat Base", () => {
120
455
  {
121
456
  sender: botUser,
122
457
  messageBody: "Got it! Let me look into that for you.",
123
- createdAt: moment().toDate(),
458
+ createdAt: dayjs().toDate(),
124
459
  },
125
460
  ],
126
461
  }));
@@ -128,6 +463,20 @@ stories.add("Chat Base", () => {
128
463
  }, 2000);
129
464
  };
130
465
 
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);
478
+ };
479
+
131
480
  const clearAction = (
132
481
  setConv: (c: Conversation) => void
133
482
  ): ChatBaseAction[] => [
@@ -218,7 +567,7 @@ stories.add("Chat Base", () => {
218
567
 
219
568
  <StorySection
220
569
  title="File Upload"
221
- description="fileUpload.allowFileUpload=true shows a paperclip button. The selected file is previewed before sending. onSendUpload is called with the current conversation when the send button is clicked."
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."
222
571
  >
223
572
  <ChatBase
224
573
  open
@@ -227,37 +576,58 @@ stories.add("Chat Base", () => {
227
576
  conversation={conv4}
228
577
  fileUpload={{
229
578
  allowFileUpload: true,
230
- file,
231
- acceptFileType: ".jpg,.jpeg,.png,.mp4,.webm",
579
+ hasFiles: files4.length > 0,
580
+ multiple: true,
581
+ acceptFileType: ".jpg,.jpeg,.png,.mp4,.webm,.pdf,.docx",
232
582
  onChangeUpload: (e) => {
233
- if (e?.target?.files?.length > 0) setFile(e.target.files[0]);
234
- else setFile(undefined);
235
- },
236
- onSendUpload: (conversation) => {
237
- if (file) {
238
- const fileUrl = URL.createObjectURL(file);
239
- setConv4({
240
- ...conversation,
241
- messageList: [
242
- ...conversation.messageList,
243
- {
244
- sender: currentUser,
245
- messageBody: {
246
- fileName: fileUrl,
247
- token: uuidv4(),
248
- type: file.type,
249
- },
250
- createdAt: moment().toDate(),
251
- },
252
- ],
253
- });
254
- setFile(undefined);
255
- setMsg4("");
256
- }
583
+ if (e?.target?.files?.length > 0)
584
+ setFiles4((prev) => [
585
+ ...prev,
586
+ ...Array.from(e.target.files as FileList),
587
+ ]);
257
588
  },
258
589
  }}
259
- onSendMessage={sendMessage(conv4, setConv4, setMsg4)}
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
+ }}
260
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
+ }
261
631
  sizeClassName="h-[500px] w-96"
262
632
  disableMobileResize
263
633
  />
@@ -279,6 +649,236 @@ stories.add("Chat Base", () => {
279
649
  disableMobileResize
280
650
  />
281
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>
282
882
  </>
283
883
  );
284
- });
884
+ };