@banbox/chat 1.0.7 → 1.0.9

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 (43) hide show
  1. package/dist/index.cjs +1236 -235
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +15 -3
  4. package/dist/index.d.ts +15 -3
  5. package/dist/index.js +1160 -160
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/chat/InboxPopup.tsx +105 -42
  9. package/src/chat/SinglePopup.tsx +59 -14
  10. package/src/icons/index.tsx +55 -0
  11. package/src/index.ts +14 -12
  12. package/src/modals/ChatAddressModal.tsx +844 -0
  13. package/src/modals/{chat/ChatConfirmModal.tsx → ChatConfirmModal.tsx} +2 -2
  14. package/src/modals/ChatTranslateSettingsModal.tsx +182 -0
  15. package/src/styles/index.build.css +15 -0
  16. package/src/styles/index.css +10 -2
  17. package/src/ui/{chat/AttachmentPreviewStrip.tsx → AttachmentPreviewStrip.tsx} +2 -2
  18. package/src/ui/{chat/ChatComposerBar.tsx → ChatComposerBar.tsx} +2 -2
  19. package/src/ui/{chat/ChatFooter.tsx → ChatFooter.tsx} +102 -8
  20. package/src/ui/{chat/ChatHeader.tsx → ChatHeader.tsx} +7 -4
  21. package/src/ui/{chat/ChatIdentity.tsx → ChatIdentity.tsx} +2 -2
  22. package/src/ui/{chat/ChatInquiryBar.tsx → ChatInquiryBar.tsx} +1 -1
  23. package/src/ui/ChatKebabMenu.tsx +125 -0
  24. package/src/ui/{chat/ChatListHeader.tsx → ChatListHeader.tsx} +49 -25
  25. package/src/ui/{chat/ChatMessageItem.tsx → ChatMessageItem.tsx} +1 -1
  26. package/src/ui/ChatScroll.tsx +59 -0
  27. package/src/ui/{chat/ChatSpinner.tsx → ChatSpinner.tsx} +1 -1
  28. package/src/ui/{chat/ChatThreadItem.tsx → ChatThreadItem.tsx} +9 -16
  29. package/src/ui/{chat/MessageHoverActions.tsx → MessageHoverActions.tsx} +2 -2
  30. package/src/ui/{chat/ReplyCard.tsx → ReplyCard.tsx} +2 -2
  31. package/src/ui/{chat/TypingIndicator.tsx → TypingIndicator.tsx} +1 -1
  32. package/src/ui/{chat/drop-up → drop-up}/BusinessCardDropup.tsx +15 -3
  33. package/src/ui/{chat/drop-up → drop-up}/EmojiDropup.tsx +1 -1
  34. package/src/ui/{chat/message-items → message-items}/ChatAddressCard.tsx +4 -4
  35. package/src/ui/{chat/message-items → message-items}/ChatBubbleFiles.tsx +1 -1
  36. package/src/ui/{chat/message-items → message-items}/ChatBubbleImages.tsx +2 -2
  37. package/src/ui/{chat/message-items → message-items}/ChatBusinessCard.tsx +1 -1
  38. package/src/ui/{chat/scrollToMessage.ts → scrollToMessage.ts} +1 -1
  39. package/src/ui/{chat/types.ts → types.ts} +2 -2
  40. package/src/modals/chat/ChatTranslateSettingsModal.tsx +0 -180
  41. package/src/ui/chat/ChatScroll.tsx +0 -52
  42. /package/src/ui/{chat/message-items → message-items}/ChatBubbleAudio.tsx +0 -0
  43. /package/src/ui/{chat/message-items → message-items}/ChatBubbleText.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banbox/chat",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Banbox Chat UI components — reusable across all Banbox React/Next.js projects",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -6,19 +6,20 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
6
6
 
7
7
  import { useChatUI } from "../contexts/ChatUIContext";
8
8
  import { useGallery } from "../contexts/GalleryContext";
9
- import ChatConfirmModal from "../modals/chat/ChatConfirmModal";
10
- import ChatFooter from "../ui/chat/ChatFooter";
11
- import ChatHeader from "../ui/chat/ChatHeader";
12
- import ChatIdentity from "../ui/chat/ChatIdentity";
13
- import ChatInquiryBar from "../ui/chat/ChatInquiryBar";
14
- import ChatListHeader from "../ui/chat/ChatListHeader";
15
- import ChatMessageItem from "../ui/chat/ChatMessageItem";
16
- import ChatScroll from "../ui/chat/ChatScroll";
17
- import type { ChatThreadStatus } from "../ui/chat/ChatThreadItem";
18
- import ChatThreadItem from "../ui/chat/ChatThreadItem";
19
- import TypingIndicator from "../ui/chat/TypingIndicator";
9
+ import ChatConfirmModal from "../modals/ChatConfirmModal";
10
+ import ChatFooter from "../ui/ChatFooter";
11
+ import ChatHeader from "../ui/ChatHeader";
12
+ import ChatIdentity from "../ui/ChatIdentity";
13
+ import ChatInquiryBar from "../ui/ChatInquiryBar";
14
+ import ChatListHeader from "../ui/ChatListHeader";
15
+ import ChatMessageItem from "../ui/ChatMessageItem";
16
+ import ChatScroll from "../ui/ChatScroll";
17
+ import type { ChatThreadStatus } from "../ui/ChatThreadItem";
18
+ import ChatThreadItem from "../ui/ChatThreadItem";
19
+ import TypingIndicator from "../ui/TypingIndicator";
20
20
  import ChatImagePreviewModal from "./ChatImagePreviewModal";
21
21
 
22
+ import ChatKebabMenu from "../ui/ChatKebabMenu";
22
23
  import type { ChatAdapter, ChatUICallbacks } from "../adapter/types";
23
24
  import type { Message, MessageRef, Thread } from "../types";
24
25
 
@@ -31,13 +32,16 @@ export type ChatTheme =
31
32
  export type InboxPopupProps = {
32
33
  adapter: ChatAdapter;
33
34
  uiCallbacks?: ChatUICallbacks;
34
- /** Dynamic theme: "marketplace" (orange), "admin" (black), or custom object */
35
35
  theme?: ChatTheme;
36
36
  };
37
37
 
38
38
  /* ─── Helpers ─── */
39
39
  const avatarBgByInitial: Record<string, string> = {
40
- K: "#FFE7DB", A: "#FFE5DA", F: "#E8F7FF", B: "#F0EDEB", b: "#F0EDEB",
40
+ K: "#FFE7DB",
41
+ A: "#FFF1EC",
42
+ F: "#E8F7FF",
43
+ B: "#F0EDEB",
44
+ b: "#F0EDEB",
41
45
  };
42
46
 
43
47
  const GRADIENT_BORDER =
@@ -51,7 +55,6 @@ function getThemeAttr(theme?: ChatTheme): string {
51
55
 
52
56
  function getThemeVars(theme?: ChatTheme): React.CSSProperties {
53
57
  if (!theme || theme === "marketplace" || theme === "admin") return {};
54
- // Custom theme object — set CSS vars directly
55
58
  const vars: Record<string, string> = {};
56
59
  if (theme.primary) vars["--color-banbox-primary"] = theme.primary;
57
60
  if (theme.primaryActive) vars["--color-banbox-primary-active"] = theme.primaryActive;
@@ -77,7 +80,10 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
77
80
  let rafId = 0;
78
81
  rafId = requestAnimationFrame(refreshThreads);
79
82
  const unsub = adapter.threads.subscribe(refreshThreads);
80
- return () => { cancelAnimationFrame(rafId); unsub(); };
83
+ return () => {
84
+ cancelAnimationFrame(rafId);
85
+ unsub();
86
+ };
81
87
  }, [adapter, reference, refreshThreads]);
82
88
 
83
89
  /* ─── Active thread & messages ─── */
@@ -111,9 +117,18 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
111
117
  const online = Boolean(activeThread?.online);
112
118
  const isVerified = Boolean(activeThread?.badge);
113
119
  const avatarBg = avatarBgByInitial[initial] ?? "#FFF1EC";
114
-
115
- const idLabel = activeThread?.orderId ? "Order ID" : activeThread?.inquiryId ? "Inquiry ID" : undefined;
116
- const idButtonLabel = activeThread?.orderId ? "View Order" : activeThread?.inquiryId ? "View Inquiry" : undefined;
120
+ const initialSrc = activeThread?.avatarSrc ?? "/chat/banbox_chat_logo.png";
121
+
122
+ const idLabel = activeThread?.orderId
123
+ ? "Order ID"
124
+ : activeThread?.inquiryId
125
+ ? "Inquiry ID"
126
+ : undefined;
127
+ const idButtonLabel = activeThread?.orderId
128
+ ? "View Order"
129
+ : activeThread?.inquiryId
130
+ ? "View Inquiry"
131
+ : undefined;
117
132
  const idValue = activeThread?.orderId ?? activeThread?.inquiryId ?? undefined;
118
133
 
119
134
  const [showDelete, setShowDelete] = useState(false);
@@ -138,13 +153,20 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
138
153
  });
139
154
 
140
155
  const handleConfirmDelete = () => {
141
- if (!activeId) { setShowDelete(false); return; }
156
+ if (!activeId) {
157
+ setShowDelete(false);
158
+ return;
159
+ }
142
160
  adapter.threads.delete(activeId);
143
161
  const nextId = threads.filter((t) => t.id !== activeId)[0]?.id;
144
162
  if (nextId) selectThread(nextId);
145
163
  setReplyTo(undefined);
146
164
  setShowDelete(false);
147
- uiCallbacks?.showToast?.({ type: "success", title: "Chat Deleted", message: "The chat has been deleted successfully." });
165
+ uiCallbacks?.showToast?.({
166
+ type: "success",
167
+ title: "Chat Deleted",
168
+ message: "The chat has been deleted successfully.",
169
+ });
148
170
  };
149
171
 
150
172
  const filteredThreads = threads.filter((t) => {
@@ -162,12 +184,12 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
162
184
  RENDER
163
185
  ══════════════════════════════════════════════════ */
164
186
  return (
165
- <div className="fixed bottom-4 right-4 z-[10002]">
187
+ <div className="fixed bottom-4 right-4 z-10002">
166
188
  {/* Backdrop */}
167
189
  <motion.button
168
190
  aria-label="Close chat"
169
191
  onClick={close}
170
- className="fixed inset-0 cursor-auto!"
192
+ className="fixed inset-0"
171
193
  style={{ background: "transparent", border: "none" }}
172
194
  initial={{ opacity: 0 }}
173
195
  animate={{ opacity: 1 }}
@@ -200,8 +222,8 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
200
222
  >
201
223
  <div className="pointer-events-none absolute inset-0 rounded-[14px] ring-1 ring-[#2F80ED]/40" />
202
224
 
203
- {/* TWO-COLUMN GRID */}
204
- <div className="grid h-full min-h-0 grid-cols-[1fr_310px]">
225
+ {/* TWO-COLUMN GRID — exact marketplace: grid-cols-[1fr_350px] */}
226
+ <div className="grid h-full min-h-0 grid-cols-[1fr_350px]">
205
227
 
206
228
  {/* ════ LEFT — chat ════ */}
207
229
  <div className="flex h-full min-h-0 flex-col border-r border-[#9BBCCF]">
@@ -211,17 +233,46 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
211
233
  <ChatHeader
212
234
  left={
213
235
  activeThread?.avatarSrc ? (
214
- <ChatIdentity variant="avatar" src={activeThread.avatarSrc} online={online} title={title} subtitle={subtitle} verified={isVerified} subtitleVariant="muted" />
236
+ <ChatIdentity
237
+ variant="avatar"
238
+ src={initialSrc}
239
+ online={online}
240
+ title={title}
241
+ subtitle={subtitle}
242
+ verified={isVerified}
243
+ subtitleVariant="muted"
244
+ />
215
245
  ) : (
216
- <ChatIdentity variant="initial" initial={initial} bg={avatarBg} online={online} title={title} subtitle={subtitle} verified={isVerified} subtitleVariant="muted" />
246
+ <ChatIdentity
247
+ variant="initial"
248
+ initial={initial}
249
+ bg={avatarBg}
250
+ online={online}
251
+ title={title}
252
+ subtitle={subtitle}
253
+ verified={isVerified}
254
+ subtitleVariant="muted"
255
+ />
217
256
  )
218
257
  }
219
258
  right={
220
- uiCallbacks?.renderKebabMenu?.({
221
- pinned: Boolean(activeThread?.pinned),
222
- onPinToggle: () => { if (activeId) adapter.threads.pin(activeId, !activeThread?.pinned); },
223
- onDelete: () => setShowDelete(true),
224
- }) ?? null
259
+ uiCallbacks?.renderKebabMenu
260
+ ? uiCallbacks.renderKebabMenu({
261
+ pinned: Boolean(activeThread?.pinned),
262
+ onPinToggle: () => {
263
+ if (activeId) adapter.threads.pin(activeId, !activeThread?.pinned);
264
+ },
265
+ onDelete: () => setShowDelete(true),
266
+ })
267
+ : (
268
+ <ChatKebabMenu
269
+ pinned={Boolean(activeThread?.pinned)}
270
+ onPinToggle={() => {
271
+ if (activeId) adapter.threads.pin(activeId, !activeThread?.pinned);
272
+ }}
273
+ onDelete={() => setShowDelete(true)}
274
+ />
275
+ )
225
276
  }
226
277
  />
227
278
  </div>
@@ -261,8 +312,12 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
261
312
  authorInitial={typeof m.author === "string" ? m.author : "U"}
262
313
  avatarBg={avatarBg}
263
314
  text={m.text ?? m.content}
264
- businessCard={m.businessCard as Parameters<typeof ChatMessageItem>[0]["businessCard"]}
265
- addressCard={m.addressCard as Parameters<typeof ChatMessageItem>[0]["addressCard"]}
315
+ businessCard={
316
+ m.businessCard as Parameters<typeof ChatMessageItem>[0]["businessCard"]
317
+ }
318
+ addressCard={
319
+ m.addressCard as Parameters<typeof ChatMessageItem>[0]["addressCard"]
320
+ }
266
321
  images={m.images}
267
322
  files={m.files}
268
323
  audio={m.audio}
@@ -297,19 +352,23 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
297
352
  </div>
298
353
  </div>
299
354
 
300
- {/* ════ RIGHT — thread list ════ */}
301
- <div className="flex h-full min-h-0 flex-col">
302
- <div className="shrink-0">
303
- <ChatListHeader onClose={close} onSearchChange={(val) => setSearchQuery(val)} />
304
- </div>
305
- <div className="flex-1 min-h-0 overflow-y-auto custom-scroll">
355
+ {/* ════ RIGHT — thread list (exact marketplace: h-full min-h-0 only) ════ */}
356
+ <div className="h-full min-h-0">
357
+ <ChatListHeader onClose={close} onSearchChange={(val) => setSearchQuery(val)} />
358
+ <div className="h-full overflow-y-auto custom-scroll py-0">
306
359
  {filteredThreads.map((t) => {
307
360
  const status: ChatThreadStatus =
308
- t.status ?? (t.unread && t.unread > 0 ? { kind: "new", count: t.unread } : { kind: "seen" });
361
+ t.status ??
362
+ (t.unread && t.unread > 0
363
+ ? { kind: "new", count: t.unread }
364
+ : { kind: "seen" });
309
365
  return (
310
366
  <ChatThreadItem
311
367
  key={t.id}
312
- onClick={() => { setReplyTo(undefined); selectThread(t.id); }}
368
+ onClick={() => {
369
+ setReplyTo(undefined);
370
+ selectThread(t.id);
371
+ }}
313
372
  active={t.id === activeId}
314
373
  pinned={Boolean(t.pinned)}
315
374
  online={t.online}
@@ -328,7 +387,11 @@ const InboxPopup: React.FC<InboxPopupProps> = ({ adapter, uiCallbacks, theme })
328
387
  </div>
329
388
 
330
389
  {/* Modals */}
331
- <ChatConfirmModal open={showDelete} onClose={() => setShowDelete(false)} onConfirm={handleConfirmDelete} />
390
+ <ChatConfirmModal
391
+ open={showDelete}
392
+ onClose={() => setShowDelete(false)}
393
+ onConfirm={handleConfirmDelete}
394
+ />
332
395
  <ChatImagePreviewModal isOpen={isGalleryOpen} onClose={closeGallery} />
333
396
  </div>
334
397
  </motion.div>
@@ -5,13 +5,17 @@ import { motion } from "framer-motion";
5
5
  import React from "react";
6
6
 
7
7
  import { useChatUI } from "../contexts/ChatUIContext";
8
+ import { useGallery } from "../contexts/GalleryContext";
8
9
  import { ChatXIcon } from "../icons";
9
- import ChatFooter from "../ui/chat/ChatFooter";
10
- import ChatHeader from "../ui/chat/ChatHeader";
11
- import ChatIdentity from "../ui/chat/ChatIdentity";
12
- import ChatMessageItem from "../ui/chat/ChatMessageItem";
13
- import ChatScroll from "../ui/chat/ChatScroll";
14
- import TypingIndicator from "../ui/chat/TypingIndicator";
10
+ import ChatKebabMenu from "../ui/ChatKebabMenu";
11
+ import ChatConfirmModal from "../modals/ChatConfirmModal";
12
+ import ChatFooter from "../ui/ChatFooter";
13
+ import ChatHeader from "../ui/ChatHeader";
14
+ import ChatIdentity from "../ui/ChatIdentity";
15
+ import ChatMessageItem from "../ui/ChatMessageItem";
16
+ import ChatScroll from "../ui/ChatScroll";
17
+ import TypingIndicator from "../ui/TypingIndicator";
18
+ import ChatImagePreviewModal from "./ChatImagePreviewModal";
15
19
 
16
20
  import type { ChatAdapter, ChatUICallbacks } from "../adapter/types";
17
21
  import type { Message, MessageRef, Reference, Thread } from "../types";
@@ -72,6 +76,7 @@ export type SinglePopupProps = {
72
76
  ══════════════════════════════════════════════════ */
73
77
  const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }) => {
74
78
  const { close, reference } = useChatUI();
79
+ const { isOpen: isGalleryOpen, closeGallery } = useGallery();
75
80
 
76
81
  const threads = adapter.threads.list(reference);
77
82
  const initialThreadId = React.useMemo(
@@ -101,6 +106,7 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }
101
106
  );
102
107
  const [scrollKey, setScrollKey] = React.useState<number>(Date.now());
103
108
  const [replyTo, setReplyTo] = React.useState<MessageRef | undefined>(undefined);
109
+ const [showDelete, setShowDelete] = React.useState(false);
104
110
 
105
111
  React.useEffect(() => {
106
112
  if (!activeId || !adapter.messages.subscribe) return;
@@ -117,7 +123,17 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }
117
123
  setReplyTo(undefined);
118
124
  }, [activeId, adapter]);
119
125
 
120
- void uiCallbacks;
126
+ const handleConfirmDelete = React.useCallback(() => {
127
+ if (!activeId) { setShowDelete(false); return; }
128
+ adapter.threads.delete(activeId);
129
+ uiCallbacks?.showToast?.({
130
+ type: "success",
131
+ title: "Chat Deleted",
132
+ message: "The chat has been deleted successfully.",
133
+ });
134
+ setShowDelete(false);
135
+ close();
136
+ }, [activeId, adapter, uiCallbacks, close]);
121
137
 
122
138
  return (
123
139
  <div className="fixed bottom-4 right-4 z-[10002]">
@@ -169,13 +185,33 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }
169
185
  />
170
186
  }
171
187
  right={
172
- <button
173
- type="button"
174
- onClick={close}
175
- className="flex h-[34px] w-[34px] items-center justify-center rounded-full bg-white text-black shadow-[0px_2px_4px_0px_#A5A3AE4D] hover:bg-black/5 hover:text-[var(--color-banbox-warning)] cursor-pointer border-none"
176
- >
177
- <ChatXIcon className="h-6 w-6" />
178
- </button>
188
+ <div className="flex items-center gap-1">
189
+ {uiCallbacks?.renderKebabMenu
190
+ ? uiCallbacks.renderKebabMenu({
191
+ pinned: Boolean(activeThread?.pinned),
192
+ onPinToggle: () => {
193
+ if (activeId) adapter.threads.pin(activeId, !activeThread?.pinned);
194
+ },
195
+ onDelete: () => setShowDelete(true),
196
+ })
197
+ : (
198
+ <ChatKebabMenu
199
+ pinned={Boolean(activeThread?.pinned)}
200
+ onPinToggle={() => {
201
+ if (activeId) adapter.threads.pin(activeId, !activeThread?.pinned);
202
+ }}
203
+ onDelete={() => setShowDelete(true)}
204
+ />
205
+ )
206
+ }
207
+ <button
208
+ type="button"
209
+ onClick={close}
210
+ className="flex h-[34px] w-[34px] items-center justify-center rounded-full bg-white text-black shadow-[0px_2px_4px_0px_#A5A3AE4D] hover:bg-black/5 hover:text-[var(--color-banbox-warning)] cursor-pointer border-none"
211
+ >
212
+ <ChatXIcon className="h-6 w-6" />
213
+ </button>
214
+ </div>
179
215
  }
180
216
  />
181
217
  </div>
@@ -233,6 +269,15 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }
233
269
  </div>
234
270
  </div>
235
271
  </motion.div>
272
+
273
+ {/* Delete confirm modal */}
274
+ <ChatConfirmModal
275
+ open={showDelete}
276
+ onClose={() => setShowDelete(false)}
277
+ onConfirm={handleConfirmDelete}
278
+ />
279
+ {/* Image gallery preview */}
280
+ <ChatImagePreviewModal isOpen={isGalleryOpen} onClose={closeGallery} />
236
281
  </div>
237
282
  );
238
283
  };
@@ -212,6 +212,20 @@ export const MapPinIcon = ({ className = "" }: IconProps) => (
212
212
  </svg>
213
213
  );
214
214
 
215
+ export const MapIcon2 = ({ className = "" }: IconProps) => (
216
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
217
+ <path d="M7.9987 1.33203C5.41203 1.33203 3.33203 3.41203 3.33203 5.9987C3.33203 9.4987 7.9987 14.6654 7.9987 14.6654C7.9987 14.6654 12.6654 9.4987 12.6654 5.9987C12.6654 3.41203 10.5854 1.33203 7.9987 1.33203Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
218
+ <circle cx="8" cy="6" r="1.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
219
+ </svg>
220
+ );
221
+
222
+ export const FlyIcon = ({ className = "" }: IconProps) => (
223
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
224
+ <path d="M16.5 1.5L8.25 9.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
225
+ <path d="M16.5 1.5L11.25 16.5L8.25 9.75L1.5 6.75L16.5 1.5Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
226
+ </svg>
227
+ );
228
+
215
229
  export const MenuIcon = ({ className }: IconProps) => (
216
230
  <svg width="24" height="24" className={className} viewBox="0 0 24 24" fill="currentColor">
217
231
  <circle cx="12" cy="5" r="2" />
@@ -246,3 +260,44 @@ export const ChatTrashIcon = ({ className }: IconProps) => (
246
260
  <path d="M7.5 5.83333V3.33333C7.5 2.8731 7.8731 2.5 8.33333 2.5H11.6667C12.1269 2.5 12.5 2.8731 12.5 3.33333V5.83333" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
247
261
  </svg>
248
262
  );
263
+
264
+ export const EditIcon = ({ className = "" }: IconProps) => (
265
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
266
+ <path d="M9 7H6C4.89543 7 4 7.89543 4 9V18C4 19.1046 4.89543 20 6 20H15C16.1046 20 17 19.1046 17 18V15" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
267
+ <path d="M9 15H12L20.5 6.49998C21.3284 5.67156 21.3284 4.32841 20.5 3.49998C19.6716 2.67156 18.3284 2.67156 17.5 3.49998L9 12V15" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
268
+ <path d="M16 5L19 8" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
269
+ </svg>
270
+ );
271
+
272
+ export const TrashIcon = ({ className }: IconProps) => (
273
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
274
+ <path d="M3.3335 5.83464H16.6668" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
275
+ <path d="M8.33317 9.16797V14.168" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
276
+ <path d="M11.6667 9.16797V14.168" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
277
+ <path d="M4.1665 5.83203L4.99984 15.832C4.99984 16.7525 5.74603 17.4987 6.6665 17.4987H13.3332C14.2536 17.4987 14.9998 16.7525 14.9998 15.832L15.8332 5.83203" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
278
+ <path d="M7.5 5.83333V3.33333C7.5 2.8731 7.8731 2.5 8.33333 2.5H11.6667C12.1269 2.5 12.5 2.8731 12.5 3.33333V5.83333" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
279
+ </svg>
280
+ );
281
+
282
+ export const EmailIcon = ({ className = "" }: IconProps) => (
283
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
284
+ <path d="M3 3H15C15.825 3 16.5 3.675 16.5 4.5V13.5C16.5 14.325 15.825 15 15 15H3C2.175 15 1.5 14.325 1.5 13.5V4.5C1.5 3.675 2.175 3 3 3Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
285
+ <path d="M16.5 4.5L9 9.75L1.5 4.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
286
+ </svg>
287
+ );
288
+
289
+ export const BadgeHomeAddrIcon = ({ className = "" }: IconProps) => (
290
+ <svg width="12" height="12" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
291
+ <path d="M2.91667 7H1.75L7 1.75L12.25 7H11.0833" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
292
+ <path d="M2.91406 7V11.0833C2.91406 11.7277 3.4364 12.25 4.08073 12.25H9.91406C10.5584 12.25 11.0807 11.7277 11.0807 11.0833V7" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
293
+ <rect x="5.83594" y="7" width="2.33333" height="2.33333" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
294
+ </svg>
295
+ );
296
+
297
+ export const CheckboxFilledIcon = ({ className = "" }: IconProps) => (
298
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
299
+ <rect width="16" height="16" rx="4" fill="currentColor" />
300
+ <path d="M14.8161 1C13.1423 2.42824 11.7356 4.37456 10.6806 6.51692C9.68193 8.56126 8.99268 10.8016 8.69729 13H4.81498V12.972L3.12701 8.56126L2.4659 6.83897L2.18457 6.09685L3.83033 6.05484C4.26639 6.05484 4.66025 6.30688 4.75871 6.68495L5.72929 8.63127L6.517 10.1995L6.728 10.6196C6.91086 10.2415 7.09372 9.87748 7.29065 9.51342C8.34563 7.49708 9.49907 5.67678 10.8635 4.13652C11.6512 3.25438 12.5093 2.45624 13.4658 1.77013C13.8456 1.50408 14.2254 1.25204 14.6333 1.014H14.8161V1Z" fill="white" />
301
+ </svg>
302
+ );
303
+
package/src/index.ts CHANGED
@@ -39,18 +39,20 @@ export { ChatUIContext } from "./contexts/ChatUIContext";
39
39
  export type { ChatUIState, ChatVariant } from "./contexts/ChatUIContext";
40
40
 
41
41
  // ── Individual UI components (advanced usage / custom layouts) ────────────────
42
- export { default as ChatFooter } from "./ui/chat/ChatFooter";
43
- export { default as ChatHeader } from "./ui/chat/ChatHeader";
44
- export { default as ChatIdentity } from "./ui/chat/ChatIdentity";
45
- export { default as ChatMessageItem } from "./ui/chat/ChatMessageItem";
46
- export { default as ChatScroll } from "./ui/chat/ChatScroll";
47
- export { default as ChatThreadItem } from "./ui/chat/ChatThreadItem";
48
- export type { ChatThreadStatus } from "./ui/chat/ChatThreadItem";
49
- export { default as ChatListHeader } from "./ui/chat/ChatListHeader";
50
- export { default as ChatInquiryBar } from "./ui/chat/ChatInquiryBar";
51
- export { default as TypingIndicator } from "./ui/chat/TypingIndicator";
52
- export { default as ReplyCard } from "./ui/chat/ReplyCard";
53
- export { default as ChatSpinner } from "./ui/chat/ChatSpinner";
42
+ export { default as ChatFooter } from "./ui/ChatFooter";
43
+ export { default as ChatHeader } from "./ui/ChatHeader";
44
+ export { default as ChatIdentity } from "./ui/ChatIdentity";
45
+ export { default as ChatMessageItem } from "./ui/ChatMessageItem";
46
+ export { default as ChatScroll } from "./ui/ChatScroll";
47
+ export { default as ChatThreadItem } from "./ui/ChatThreadItem";
48
+ export type { ChatThreadStatus } from "./ui/ChatThreadItem";
49
+ export { default as ChatListHeader } from "./ui/ChatListHeader";
50
+ export { default as ChatInquiryBar } from "./ui/ChatInquiryBar";
51
+ export { default as TypingIndicator } from "./ui/TypingIndicator";
52
+ export { default as ReplyCard } from "./ui/ReplyCard";
53
+ export { default as ChatSpinner } from "./ui/ChatSpinner";
54
+ export { default as ChatKebabMenu } from "./ui/ChatKebabMenu";
55
+ export type { ChatKebabMenuProps } from "./ui/ChatKebabMenu";
54
56
 
55
57
  // ── Utilities ─────────────────────────────────────────────────────────────────
56
58
  export { cn } from "./utils/cn";