@waysdrop/chat 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1345 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+
21
+ // src/components/ChatWidget.tsx
22
+ import { useEffect as useEffect4, useState as useState4 } from "react";
23
+ import { injectGlobal } from "@emotion/css";
24
+
25
+ // src/lib/upload.ts
26
+ var VISITOR_ID_KEY = "waysdrop_visitor_id";
27
+ var saveVisitorId = (id) => {
28
+ localStorage.setItem(VISITOR_ID_KEY, id);
29
+ };
30
+ var loadVisitorId = () => {
31
+ return localStorage.getItem(VISITOR_ID_KEY);
32
+ };
33
+ var clearVisitorId = () => {
34
+ localStorage.removeItem(VISITOR_ID_KEY);
35
+ };
36
+ var uploadFile = async (file, config) => {
37
+ var _a, _b;
38
+ const formData = new FormData();
39
+ formData.append("files", file);
40
+ const headers = {};
41
+ if (config.token) headers["Authorization"] = `Bearer ${config.token}`;
42
+ const res = await fetch(`${config.apiUrl}/file/bulk-upload`, {
43
+ method: "POST",
44
+ headers,
45
+ body: formData
46
+ });
47
+ if (!res.ok) {
48
+ throw new Error("File upload failed");
49
+ }
50
+ const json = await res.json();
51
+ if (!json.success || !((_b = (_a = json.data) == null ? void 0 : _a.urls) == null ? void 0 : _b.length)) {
52
+ throw new Error(json.message || "File upload failed");
53
+ }
54
+ const first = json.data.urls[0];
55
+ if (typeof first !== "string") {
56
+ throw new Error((first == null ? void 0 : first.error) || "File upload failed");
57
+ }
58
+ return first;
59
+ };
60
+
61
+ // src/hooks/useChat.ts
62
+ import { useEffect, useCallback } from "react";
63
+
64
+ // src/lib/socket.ts
65
+ import { io } from "socket.io-client";
66
+ var socket = null;
67
+ var getSocket = (config) => {
68
+ if (socket) return socket;
69
+ const { serverUrl, token, visitorId } = config;
70
+ socket = io(serverUrl, {
71
+ transports: ["websocket", "polling"],
72
+ auth: __spreadValues(__spreadValues({}, token ? { token } : {}), visitorId ? { visitorId } : {}),
73
+ autoConnect: false
74
+ });
75
+ return socket;
76
+ };
77
+ var destroySocket = () => {
78
+ if (socket) {
79
+ socket.disconnect();
80
+ socket = null;
81
+ }
82
+ };
83
+
84
+ // src/store/chatStore.ts
85
+ import { create } from "zustand";
86
+ var initialState = {
87
+ status: "idle",
88
+ role: null,
89
+ visitorId: null,
90
+ chatId: null,
91
+ messages: [],
92
+ error: null,
93
+ visitorInfo: null
94
+ };
95
+ var useChatStore = create((set) => __spreadProps(__spreadValues({}, initialState), {
96
+ setStatus: (status) => set({ status }),
97
+ setRole: (role) => set({ role }),
98
+ setVisitorId: (id) => set({ visitorId: id }),
99
+ setChatId: (id) => set({ chatId: id }),
100
+ addMessage: (message) => set((state) => ({
101
+ messages: [...state.messages, message]
102
+ })),
103
+ setError: (error) => set({ error }),
104
+ setVisitorInfo: (info) => set({ visitorInfo: info }),
105
+ reset: () => set(initialState)
106
+ }));
107
+
108
+ // src/hooks/useChat.ts
109
+ var useChat = (config) => {
110
+ const {
111
+ status,
112
+ role,
113
+ visitorId,
114
+ chatId,
115
+ messages,
116
+ error,
117
+ visitorInfo,
118
+ setStatus,
119
+ setRole,
120
+ setVisitorId,
121
+ setChatId,
122
+ addMessage,
123
+ setError,
124
+ setVisitorInfo,
125
+ reset
126
+ } = useChatStore();
127
+ useEffect(() => {
128
+ const socket2 = getSocket(config);
129
+ setStatus("connecting");
130
+ socket2.connect();
131
+ socket2.on("connected", (payload) => {
132
+ setStatus("connected");
133
+ setRole(payload.role);
134
+ if (payload.role === "VISITOR") {
135
+ saveVisitorId(payload.visitorId);
136
+ setVisitorId(payload.visitorId);
137
+ }
138
+ });
139
+ socket2.on("support-message-sent", (payload) => {
140
+ setChatId(payload.chatId);
141
+ addMessage(payload.message);
142
+ });
143
+ socket2.on("support-new-message", (payload) => {
144
+ setChatId(payload.chatId);
145
+ addMessage(payload.message);
146
+ });
147
+ socket2.on("error", (err) => {
148
+ setError(err);
149
+ setStatus("error");
150
+ });
151
+ return () => {
152
+ destroySocket();
153
+ reset();
154
+ };
155
+ }, []);
156
+ const sendMessage = useCallback(
157
+ (content, info) => {
158
+ var _a, _b;
159
+ const socket2 = getSocket(config);
160
+ const dto = __spreadValues({
161
+ content,
162
+ externalId: `msg-${Date.now()}`
163
+ }, (info != null ? info : visitorInfo) ? {
164
+ email: (info != null ? info : visitorInfo).email,
165
+ name: (_a = info != null ? info : visitorInfo) == null ? void 0 : _a.name,
166
+ phone: (_b = info != null ? info : visitorInfo) == null ? void 0 : _b.phone
167
+ } : {});
168
+ socket2.emit("support-send-message", dto);
169
+ },
170
+ [config, visitorInfo]
171
+ );
172
+ const sendFile = useCallback(
173
+ async (file, info) => {
174
+ var _a, _b;
175
+ const url = await uploadFile(file, config);
176
+ const socket2 = getSocket(config);
177
+ const dto = __spreadValues({
178
+ file: url,
179
+ externalId: `file-${Date.now()}`
180
+ }, (info != null ? info : visitorInfo) ? {
181
+ email: (info != null ? info : visitorInfo).email,
182
+ name: (_a = info != null ? info : visitorInfo) == null ? void 0 : _a.name,
183
+ phone: (_b = info != null ? info : visitorInfo) == null ? void 0 : _b.phone
184
+ } : {});
185
+ socket2.emit("support-send-message", dto);
186
+ },
187
+ [config, visitorInfo]
188
+ );
189
+ return {
190
+ status,
191
+ role,
192
+ visitorId,
193
+ chatId,
194
+ messages,
195
+ error,
196
+ visitorInfo,
197
+ setVisitorInfo,
198
+ sendMessage,
199
+ sendFile
200
+ };
201
+ };
202
+
203
+ // src/components/FloatingButton.tsx
204
+ import { useEffect as useEffect2, useState } from "react";
205
+ import { css } from "@emotion/css";
206
+ import { jsx, jsxs } from "react/jsx-runtime";
207
+ var BUTTON_SIZE = 56;
208
+ var FloatingButton = ({ isOpen, onToggle }) => {
209
+ const [isMobile, setIsMobile] = useState(false);
210
+ useEffect2(() => {
211
+ const check = () => setIsMobile(window.innerWidth <= 480);
212
+ check();
213
+ window.addEventListener("resize", check);
214
+ return () => window.removeEventListener("resize", check);
215
+ }, []);
216
+ if (isMobile && isOpen) {
217
+ return /* @__PURE__ */ jsx(
218
+ "button",
219
+ {
220
+ className: styles.mobileCloseBtn,
221
+ onClick: onToggle,
222
+ type: "button",
223
+ children: /* @__PURE__ */ jsxs(
224
+ "svg",
225
+ {
226
+ width: "22",
227
+ height: "22",
228
+ viewBox: "0 0 24 24",
229
+ fill: "none",
230
+ stroke: "currentColor",
231
+ strokeWidth: "2.5",
232
+ strokeLinecap: "round",
233
+ children: [
234
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
235
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
236
+ ]
237
+ }
238
+ )
239
+ }
240
+ );
241
+ }
242
+ return /* @__PURE__ */ jsxs("button", { className: styles.btn, onClick: onToggle, type: "button", children: [
243
+ /* @__PURE__ */ jsx("span", { className: styles.icon(!isOpen), children: /* @__PURE__ */ jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M20 2H4a2 2 0 0 0-2 2v18l4-4h14a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" }) }) }),
244
+ /* @__PURE__ */ jsx("span", { className: styles.icon(isOpen), children: /* @__PURE__ */ jsxs(
245
+ "svg",
246
+ {
247
+ width: "20",
248
+ height: "20",
249
+ viewBox: "0 0 24 24",
250
+ fill: "none",
251
+ stroke: "currentColor",
252
+ strokeWidth: "2.5",
253
+ strokeLinecap: "round",
254
+ children: [
255
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
256
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
257
+ ]
258
+ }
259
+ ) })
260
+ ] });
261
+ };
262
+ var styles = {
263
+ btn: css`
264
+ position: fixed;
265
+ right: 30px;
266
+ bottom: 20px;
267
+ width: ${BUTTON_SIZE}px;
268
+ height: ${BUTTON_SIZE}px;
269
+ border-radius: 50%;
270
+ background: var(--wds-primary);
271
+ color: #fff;
272
+ border: none;
273
+ cursor: pointer;
274
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.18);
275
+ z-index: 999999;
276
+ display: flex;
277
+ align-items: center;
278
+ justify-content: center;
279
+ user-select: none;
280
+ -webkit-user-select: none;
281
+ &:hover {
282
+ opacity: 0.92;
283
+ }
284
+ `,
285
+ mobileCloseBtn: css`
286
+ position: fixed;
287
+ top: 16px;
288
+ right: 20px;
289
+ width: ${BUTTON_SIZE}px;
290
+ height: ${BUTTON_SIZE}px;
291
+ border-radius: 50%;
292
+ background: var(--wds-primary);
293
+ color: #fff;
294
+ border: none;
295
+ cursor: pointer;
296
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
297
+ z-index: 999999;
298
+ display: flex;
299
+ align-items: center;
300
+ justify-content: center;
301
+ `,
302
+ icon: (visible) => css`
303
+ position: absolute;
304
+ display: flex;
305
+ align-items: center;
306
+ justify-content: center;
307
+ transition:
308
+ opacity 0.2s,
309
+ transform 0.2s;
310
+ opacity: ${visible ? 1 : 0};
311
+ transform: ${visible ? "scale(1)" : "scale(0.6) rotate(30deg)"};
312
+ pointer-events: ${visible ? "auto" : "none"};
313
+ `
314
+ };
315
+
316
+ // src/components/ChatPanel.tsx
317
+ import { css as css2 } from "@emotion/css";
318
+ import { jsx as jsx2 } from "react/jsx-runtime";
319
+ var ChatPanel = ({ isOpen, isExpanded, children }) => {
320
+ return /* @__PURE__ */ jsx2("div", { "data-wds-root": true, className: styles2.panel(isOpen, isExpanded), children: /* @__PURE__ */ jsx2("div", { className: styles2.inner, children }) });
321
+ };
322
+ var styles2 = {
323
+ panel: (isOpen, isExpanded) => css2`
324
+ position: fixed;
325
+ bottom: 86px;
326
+ z-index: 999998;
327
+ transition: opacity 0.2s, transform 0.25s cubic-bezier(0.34, 1.2, 0.64, 1);
328
+ opacity: ${isOpen ? 1 : 0};
329
+ pointer-events: ${isOpen ? "auto" : "none"};
330
+
331
+ ${isExpanded ? `
332
+ width: min(760px, calc(100vw - 32px));
333
+ height: min(780px, calc(100vh - 120px));
334
+ border-radius: 20px;
335
+ box-shadow: 0 20px 60px rgba(0,0,0,0.2), 0 4px 16px rgba(0,0,0,0.1);
336
+ left: 50%;
337
+ right: auto;
338
+ translate: -50% 0;
339
+ transform: ${isOpen ? "scale(1)" : "scale(0.95)"};
340
+ transform-origin: bottom center;
341
+ ` : `
342
+ width: 400px;
343
+ height: 620px;
344
+ border-radius: 20px;
345
+ box-shadow: 0 12px 48px rgba(0,0,0,0.16), 0 2px 8px rgba(0,0,0,0.08);
346
+ right: 22px;
347
+ transform: ${isOpen ? "translateY(0) scale(1)" : "translateY(16px) scale(0.97)"};
348
+ `}
349
+
350
+ overflow: hidden;
351
+
352
+ @media (max-width: 480px) {
353
+ top: 0 !important;
354
+ left: 0 !important;
355
+ right: 0 !important;
356
+ bottom: 0 !important;
357
+ width: 100% !important;
358
+ height: 100% !important;
359
+ border-radius: 0 !important;
360
+ translate: none !important;
361
+ }
362
+ `,
363
+ inner: css2`
364
+ width: 100%;
365
+ height: 100%;
366
+ overflow: hidden;
367
+ border-radius: inherit;
368
+ `
369
+ };
370
+
371
+ // src/components/HomeScreen.tsx
372
+ import { css as css3 } from "@emotion/css";
373
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
374
+ var HomeScreen = ({
375
+ onStartChat,
376
+ onContinueChat,
377
+ hasHistory,
378
+ onExpand,
379
+ isExpanded
380
+ }) => {
381
+ return /* @__PURE__ */ jsxs2("div", { className: styles3.wrapper, children: [
382
+ /* @__PURE__ */ jsxs2("div", { className: styles3.topBar, children: [
383
+ /* @__PURE__ */ jsx3("div", { className: styles3.logoMark, children: /* @__PURE__ */ jsx3(
384
+ "img",
385
+ {
386
+ src: "https://cdn.waysdrop.com/bulk/horizon_20260411202129966_d25edae2.png",
387
+ alt: "Waysdrop",
388
+ style: { width: "100%", height: "100%", objectFit: "contain" }
389
+ }
390
+ ) }),
391
+ /* @__PURE__ */ jsx3("button", { className: styles3.expandBtn, onClick: onExpand, children: isExpanded ? /* @__PURE__ */ jsxs2("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
392
+ /* @__PURE__ */ jsx3("polyline", { points: "4 14 10 14 10 20" }),
393
+ /* @__PURE__ */ jsx3("polyline", { points: "20 10 14 10 14 4" }),
394
+ /* @__PURE__ */ jsx3("line", { x1: "10", y1: "14", x2: "3", y2: "21" }),
395
+ /* @__PURE__ */ jsx3("line", { x1: "21", y1: "3", x2: "14", y2: "10" })
396
+ ] }) : /* @__PURE__ */ jsxs2("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
397
+ /* @__PURE__ */ jsx3("polyline", { points: "15 3 21 3 21 9" }),
398
+ /* @__PURE__ */ jsx3("polyline", { points: "9 21 3 21 3 15" }),
399
+ /* @__PURE__ */ jsx3("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
400
+ /* @__PURE__ */ jsx3("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
401
+ ] }) })
402
+ ] }),
403
+ /* @__PURE__ */ jsxs2("div", { className: styles3.hero, children: [
404
+ /* @__PURE__ */ jsx3("h2", { className: styles3.heroTitle, children: "Hi there! \u{1F44B}" }),
405
+ /* @__PURE__ */ jsx3("p", { className: styles3.heroSub, children: "We're here to help. Ask us anything or share your feedback." })
406
+ ] }),
407
+ /* @__PURE__ */ jsxs2("div", { className: styles3.section, children: [
408
+ /* @__PURE__ */ jsx3("p", { className: styles3.sectionLabel, children: "Conversations" }),
409
+ hasHistory ? /* @__PURE__ */ jsxs2("button", { className: styles3.conversationCard, onClick: onContinueChat, children: [
410
+ /* @__PURE__ */ jsx3("div", { className: styles3.cardIcon, children: /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }) }),
411
+ /* @__PURE__ */ jsxs2("div", { className: styles3.cardBody, children: [
412
+ /* @__PURE__ */ jsx3("span", { className: styles3.cardTitle, children: "Continue conversation" }),
413
+ /* @__PURE__ */ jsx3("span", { className: styles3.cardSub, children: "Tap to resume your last chat" })
414
+ ] }),
415
+ /* @__PURE__ */ jsx3("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("polyline", { points: "9 18 15 12 9 6" }) })
416
+ ] }) : /* @__PURE__ */ jsxs2("div", { className: styles3.emptyHistory, children: [
417
+ /* @__PURE__ */ jsx3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.25 }, children: /* @__PURE__ */ jsx3("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }),
418
+ /* @__PURE__ */ jsx3("p", { children: "No previous conversations" })
419
+ ] })
420
+ ] }),
421
+ /* @__PURE__ */ jsxs2("div", { className: styles3.footer, children: [
422
+ /* @__PURE__ */ jsxs2("button", { className: styles3.startBtn, onClick: onStartChat, children: [
423
+ "New conversation",
424
+ /* @__PURE__ */ jsx3("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx3("polyline", { points: "9 18 15 12 9 6" }) })
425
+ ] }),
426
+ /* @__PURE__ */ jsx3("p", { className: styles3.poweredBy, children: "Powered by Waysdrop" })
427
+ ] })
428
+ ] });
429
+ };
430
+ var styles3 = {
431
+ wrapper: css3`
432
+ display: flex;
433
+ flex-direction: column;
434
+ height: 100%;
435
+ background: var(--wds-bg);
436
+ text-align: left;
437
+ `,
438
+ topBar: css3`
439
+ display: flex;
440
+ align-items: center;
441
+ justify-content: space-between;
442
+ padding: 18px 18px 0;
443
+ flex-shrink: 0;
444
+ `,
445
+ logoMark: css3`
446
+ width: 48px;
447
+ height: 48px;
448
+ flex-shrink: 0;
449
+ `,
450
+ expandBtn: css3`
451
+ background: none;
452
+ border: none;
453
+ cursor: pointer;
454
+ color: var(--wds-muted);
455
+ padding: 8px;
456
+ border-radius: 8px;
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: center;
460
+ transition: color 0.15s, background 0.15s;
461
+ &:hover {
462
+ color: var(--wds-fg);
463
+ background: var(--wds-muted-bg);
464
+ }
465
+ @media (max-width: 480px) {
466
+ display: none;
467
+ }
468
+ `,
469
+ hero: css3`
470
+ padding: 20px 20px 16px;
471
+ display: flex;
472
+ flex-direction: column;
473
+ align-items: flex-start;
474
+ gap: 10px;
475
+ flex-shrink: 0;
476
+ `,
477
+ heroTitle: css3`
478
+ font-size: 1.375rem;
479
+ font-weight: 700;
480
+ color: var(--wds-fg);
481
+ margin: 0;
482
+ line-height: 1.2;
483
+ text-align: left;
484
+ `,
485
+ heroSub: css3`
486
+ font-size: 0.9375rem;
487
+ color: var(--wds-muted);
488
+ margin: 0;
489
+ line-height: 1.6;
490
+ text-align: left;
491
+ `,
492
+ section: css3`
493
+ flex: 1;
494
+ padding: 4px 18px 0;
495
+ overflow-y: auto;
496
+ `,
497
+ sectionLabel: css3`
498
+ font-size: 11px;
499
+ font-weight: 600;
500
+ letter-spacing: 0.07em;
501
+ text-transform: uppercase;
502
+ color: var(--wds-muted);
503
+ margin: 0 0 12px 2px;
504
+ text-align: left;
505
+ `,
506
+ conversationCard: css3`
507
+ display: flex;
508
+ align-items: center;
509
+ gap: 14px;
510
+ width: 100%;
511
+ padding: 14px 16px;
512
+ background: var(--wds-muted-bg);
513
+ border: 1px solid var(--wds-border);
514
+ border-radius: 14px;
515
+ cursor: pointer;
516
+ text-align: left;
517
+ transition: border-color 0.15s, background 0.15s;
518
+ color: var(--wds-fg);
519
+ &:hover {
520
+ border-color: var(--wds-primary);
521
+ background: var(--wds-primary-soft);
522
+ }
523
+ `,
524
+ cardIcon: css3`
525
+ width: 38px;
526
+ height: 38px;
527
+ border-radius: 10px;
528
+ background: var(--wds-primary-soft);
529
+ color: var(--wds-primary);
530
+ display: flex;
531
+ align-items: center;
532
+ justify-content: center;
533
+ flex-shrink: 0;
534
+ `,
535
+ cardBody: css3`
536
+ flex: 1;
537
+ display: flex;
538
+ flex-direction: column;
539
+ gap: 3px;
540
+ `,
541
+ cardTitle: css3`
542
+ font-size: 0.875rem;
543
+ font-weight: 600;
544
+ color: var(--wds-fg);
545
+ text-align: left;
546
+ `,
547
+ cardSub: css3`
548
+ font-size: 12px;
549
+ color: var(--wds-muted);
550
+ text-align: left;
551
+ `,
552
+ emptyHistory: css3`
553
+ display: flex;
554
+ flex-direction: column;
555
+ align-items: center;
556
+ gap: 10px;
557
+ padding: 40px 0;
558
+ color: var(--wds-muted);
559
+ font-size: 12px;
560
+ `,
561
+ footer: css3`
562
+ padding: 18px 18px 16px;
563
+ display: flex;
564
+ flex-direction: column;
565
+ gap: 12px;
566
+ align-items: center;
567
+ flex-shrink: 0;
568
+ border-top: 1px solid var(--wds-border);
569
+ `,
570
+ startBtn: css3`
571
+ display: flex;
572
+ align-items: center;
573
+ justify-content: center;
574
+ gap: 6px;
575
+ width: 100%;
576
+ padding: 13px 16px;
577
+ background: var(--wds-primary);
578
+ color: #fff;
579
+ border: none;
580
+ border-radius: 14px;
581
+ font-size: 0.9375rem;
582
+ font-weight: 600;
583
+ cursor: pointer;
584
+ transition: opacity 0.15s;
585
+ font-family: inherit;
586
+ &:hover { opacity: 0.88; }
587
+ `,
588
+ poweredBy: css3`
589
+ font-size: 11px;
590
+ color: var(--wds-muted);
591
+ margin: 0;
592
+ opacity: 0.7;
593
+ `
594
+ };
595
+
596
+ // src/components/ChatScreen.tsx
597
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
598
+ import { css as css7, keyframes as keyframes2 } from "@emotion/css";
599
+
600
+ // src/components/MessageBubble.tsx
601
+ import { css as css5 } from "@emotion/css";
602
+
603
+ // src/components/MarkdownRenderer.tsx
604
+ import ReactMarkdown from "react-markdown";
605
+ import { css as css4 } from "@emotion/css";
606
+ import { jsx as jsx4 } from "react/jsx-runtime";
607
+ var markdownStyle = css4`
608
+ font-size: 0.875rem;
609
+ line-height: 1.6;
610
+ color: inherit;
611
+ text-align: left;
612
+
613
+ p {
614
+ margin: 0 0 0.6rem 0;
615
+ text-align: left;
616
+ }
617
+ p:last-child {
618
+ margin-bottom: 0;
619
+ }
620
+ strong { font-weight: 600; }
621
+ em { font-style: italic; }
622
+
623
+ ul, ol {
624
+ margin: 0.4rem 0 0.6rem 0;
625
+ padding-left: 1.4rem;
626
+ text-align: left;
627
+ }
628
+ ul { list-style-type: disc; }
629
+ ol { list-style-type: decimal; }
630
+ li {
631
+ margin-bottom: 0.3rem;
632
+ }
633
+ li > ul, li > ol {
634
+ margin: 0.25rem 0 0.25rem 0;
635
+ padding-left: 1.2rem;
636
+ }
637
+ li:last-child { margin-bottom: 0; }
638
+
639
+ hr {
640
+ border: none;
641
+ border-top: 1px solid rgba(255, 255, 255, 0.25);
642
+ margin: 0.75rem 0;
643
+ }
644
+
645
+ code {
646
+ font-family: monospace;
647
+ font-size: 0.8rem;
648
+ background: rgba(255,255,255,0.15);
649
+ border-radius: 4px;
650
+ padding: 0.1rem 0.35rem;
651
+ }
652
+ pre {
653
+ background: rgba(255,255,255,0.1);
654
+ border-radius: 8px;
655
+ padding: 0.75rem 1rem;
656
+ overflow-x: auto;
657
+ margin: 0.5rem 0;
658
+ }
659
+ pre code {
660
+ background: none;
661
+ padding: 0;
662
+ font-size: 0.78rem;
663
+ }
664
+ blockquote {
665
+ border-left: 3px solid rgba(255,255,255,0.25);
666
+ margin: 0.5rem 0;
667
+ padding-left: 0.75rem;
668
+ opacity: 0.8;
669
+ }
670
+ a {
671
+ color: rgba(255,255,255,0.85);
672
+ text-decoration: underline;
673
+ }
674
+ h1, h2, h3 {
675
+ font-weight: 600;
676
+ margin: 0.6rem 0 0.3rem;
677
+ line-height: 1.3;
678
+ text-align: left;
679
+ }
680
+ h1 { font-size: 1rem; }
681
+ h2 { font-size: 0.95rem; }
682
+ h3 { font-size: 0.875rem; }
683
+ `;
684
+ var MarkdownRenderer = ({ content }) => {
685
+ return /* @__PURE__ */ jsx4("div", { className: markdownStyle, children: /* @__PURE__ */ jsx4(ReactMarkdown, { children: content }) });
686
+ };
687
+
688
+ // src/components/MessageBubble.tsx
689
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
690
+ function formatTime(iso) {
691
+ return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
692
+ }
693
+ var MessageBubble = ({ message }) => {
694
+ const isCustomer = message.direction === "OUTBOUND";
695
+ const isBot = message.senderRole === "BOT";
696
+ return /* @__PURE__ */ jsx5("div", { className: styles4.row(isCustomer), children: /* @__PURE__ */ jsxs3("div", { className: styles4.group(isCustomer), children: [
697
+ isBot && /* @__PURE__ */ jsx5("span", { className: styles4.botLabel, children: "Ways AI" }),
698
+ /* @__PURE__ */ jsxs3("div", { className: styles4.bubble(isCustomer), children: [
699
+ message.file && /* @__PURE__ */ jsx5("a", { href: message.file, target: "_blank", rel: "noreferrer", className: styles4.fileLink, children: "\u{1F4CE} View attachment" }),
700
+ message.content && (isBot ? /* @__PURE__ */ jsx5(MarkdownRenderer, { content: message.content }) : /* @__PURE__ */ jsx5("p", { className: styles4.text, children: message.content }))
701
+ ] }),
702
+ /* @__PURE__ */ jsx5("span", { className: styles4.time, children: formatTime(message.createdAt) })
703
+ ] }) });
704
+ };
705
+ var styles4 = {
706
+ row: (isCustomer) => css5`
707
+ display: flex;
708
+ width: 100%;
709
+ justify-content: ${isCustomer ? "flex-start" : "flex-end"};
710
+ `,
711
+ group: (isCustomer) => css5`
712
+ display: flex;
713
+ flex-direction: column;
714
+ max-width: 78%;
715
+ gap: 4px;
716
+ align-items: ${isCustomer ? "flex-start" : "flex-end"};
717
+ `,
718
+ botLabel: css5`
719
+ font-size: 10px;
720
+ color: var(--wds-muted);
721
+ padding: 0 4px;
722
+ font-weight: 500;
723
+ letter-spacing: 0.03em;
724
+ `,
725
+ bubble: (isCustomer) => css5`
726
+ border-radius: 18px;
727
+ padding: 10px 14px;
728
+ word-break: break-word;
729
+ text-align: left;
730
+ border-top-left-radius: ${isCustomer ? "4px" : "18px"};
731
+ border-top-right-radius: ${isCustomer ? "18px" : "4px"};
732
+ background: ${isCustomer ? "var(--wds-muted-bg)" : "var(--wds-primary)"};
733
+ color: ${isCustomer ? "var(--wds-fg)" : "#fff"};
734
+ `,
735
+ text: css5`
736
+ font-size: 0.875rem;
737
+ line-height: 1.5;
738
+ margin: 0;
739
+ white-space: pre-wrap;
740
+ text-align: left;
741
+ `,
742
+ fileLink: css5`
743
+ font-size: 0.875rem;
744
+ color: inherit;
745
+ text-decoration: underline;
746
+ display: block;
747
+ text-align: left;
748
+ `,
749
+ time: css5`
750
+ font-size: 10px;
751
+ color: var(--wds-muted);
752
+ padding: 0 4px;
753
+ `
754
+ };
755
+
756
+ // src/components/ChatInput.tsx
757
+ import { useState as useState2, useRef, useCallback as useCallback2 } from "react";
758
+ import { css as css6, keyframes } from "@emotion/css";
759
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
760
+ var spin = keyframes`
761
+ to { transform: rotate(360deg); }
762
+ `;
763
+ var pulse = keyframes`
764
+ 0%, 100% { opacity: 0.4; transform: scaleY(0.6); }
765
+ 50% { opacity: 1; transform: scaleY(1); }
766
+ `;
767
+ var ChatInput = ({ onSendText, onSendFile, disabled, isThinking }) => {
768
+ const [value, setValue] = useState2("");
769
+ const [uploading, setUploading] = useState2(false);
770
+ const [pendingFile, setPendingFile] = useState2(null);
771
+ const [pendingPreview, setPendingPreview] = useState2(null);
772
+ const textareaRef = useRef(null);
773
+ const fileInputRef = useRef(null);
774
+ const handleSend = useCallback2(() => {
775
+ var _a;
776
+ const trimmed = value.trim();
777
+ if (!trimmed && !pendingFile || disabled || uploading) return;
778
+ if (pendingFile) {
779
+ setUploading(true);
780
+ onSendFile(pendingFile).finally(() => {
781
+ setUploading(false);
782
+ setPendingFile(null);
783
+ setPendingPreview(null);
784
+ });
785
+ }
786
+ if (trimmed) {
787
+ onSendText(trimmed);
788
+ setValue("");
789
+ }
790
+ (_a = textareaRef.current) == null ? void 0 : _a.focus();
791
+ }, [value, pendingFile, disabled, uploading, onSendText, onSendFile]);
792
+ const handleKeyDown = (e) => {
793
+ if (e.key === "Enter" && !e.shiftKey) {
794
+ e.preventDefault();
795
+ handleSend();
796
+ }
797
+ };
798
+ const handleFileChange = (e) => {
799
+ var _a;
800
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
801
+ if (!file) return;
802
+ if (!file.type.startsWith("image/")) return;
803
+ setPendingFile(file);
804
+ const reader = new FileReader();
805
+ reader.onload = (ev) => {
806
+ var _a2;
807
+ return setPendingPreview((_a2 = ev.target) == null ? void 0 : _a2.result);
808
+ };
809
+ reader.readAsDataURL(file);
810
+ e.target.value = "";
811
+ };
812
+ const canSend = (value.trim() || pendingFile) && !disabled && !uploading;
813
+ return /* @__PURE__ */ jsxs4("div", { className: styles5.wrapper, children: [
814
+ isThinking && /* @__PURE__ */ jsxs4("div", { className: styles5.thinkingBar, children: [
815
+ /* @__PURE__ */ jsxs4("div", { className: styles5.thinkingDots, children: [
816
+ /* @__PURE__ */ jsx6("span", { className: styles5.dot(0) }),
817
+ /* @__PURE__ */ jsx6("span", { className: styles5.dot(1) }),
818
+ /* @__PURE__ */ jsx6("span", { className: styles5.dot(2) })
819
+ ] }),
820
+ /* @__PURE__ */ jsx6("span", { className: styles5.thinkingLabel, children: "Ways AI is typing..." })
821
+ ] }),
822
+ pendingPreview && /* @__PURE__ */ jsxs4("div", { className: styles5.imagePreview, children: [
823
+ /* @__PURE__ */ jsx6("img", { src: pendingPreview, alt: "attachment", className: styles5.previewImg }),
824
+ /* @__PURE__ */ jsx6("button", { className: styles5.removeFile, onClick: () => {
825
+ setPendingFile(null);
826
+ setPendingPreview(null);
827
+ }, children: /* @__PURE__ */ jsxs4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", children: [
828
+ /* @__PURE__ */ jsx6("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
829
+ /* @__PURE__ */ jsx6("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
830
+ ] }) })
831
+ ] }),
832
+ /* @__PURE__ */ jsxs4("div", { className: styles5.inputRow, children: [
833
+ /* @__PURE__ */ jsx6(
834
+ "button",
835
+ {
836
+ className: styles5.attachBtn,
837
+ onClick: () => {
838
+ var _a;
839
+ return (_a = fileInputRef.current) == null ? void 0 : _a.click();
840
+ },
841
+ disabled: disabled || uploading,
842
+ type: "button",
843
+ children: /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
844
+ /* @__PURE__ */ jsx6("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" }),
845
+ /* @__PURE__ */ jsx6("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
846
+ /* @__PURE__ */ jsx6("polyline", { points: "21 15 16 10 5 21" })
847
+ ] })
848
+ }
849
+ ),
850
+ /* @__PURE__ */ jsx6(
851
+ "input",
852
+ {
853
+ ref: fileInputRef,
854
+ type: "file",
855
+ accept: "image/*",
856
+ className: styles5.hiddenInput,
857
+ onChange: handleFileChange
858
+ }
859
+ ),
860
+ /* @__PURE__ */ jsx6(
861
+ "textarea",
862
+ {
863
+ ref: textareaRef,
864
+ className: styles5.textarea,
865
+ value,
866
+ onChange: (e) => setValue(e.target.value),
867
+ onKeyDown: handleKeyDown,
868
+ placeholder: "Type a message...",
869
+ disabled: disabled || uploading,
870
+ rows: 1
871
+ }
872
+ ),
873
+ /* @__PURE__ */ jsx6("button", { className: styles5.sendBtn, onClick: handleSend, disabled: !canSend, type: "button", children: uploading ? /* @__PURE__ */ jsx6("span", { className: styles5.spinner }) : /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
874
+ /* @__PURE__ */ jsx6("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
875
+ /* @__PURE__ */ jsx6("polyline", { points: "5 12 12 5 19 12" })
876
+ ] }) })
877
+ ] }),
878
+ /* @__PURE__ */ jsx6("p", { className: styles5.hint, children: "Enter to send \xB7 Shift+Enter for new line" })
879
+ ] });
880
+ };
881
+ var styles5 = {
882
+ wrapper: css6`
883
+ padding: 8px 12px 10px;
884
+ border-top: 1px solid var(--wds-border);
885
+ display: flex;
886
+ flex-direction: column;
887
+ gap: 6px;
888
+ background: var(--wds-bg);
889
+ flex-shrink: 0;
890
+ `,
891
+ thinkingBar: css6`
892
+ display: flex;
893
+ align-items: center;
894
+ gap: 8px;
895
+ padding: 6px 2px 2px;
896
+ `,
897
+ thinkingDots: css6`
898
+ display: flex;
899
+ align-items: center;
900
+ gap: 3px;
901
+ `,
902
+ dot: (i) => css6`
903
+ width: 5px;
904
+ height: 5px;
905
+ border-radius: 50%;
906
+ background: var(--wds-primary);
907
+ animation: ${pulse} 1.2s ease-in-out infinite;
908
+ animation-delay: ${i * 0.18}s;
909
+ display: inline-block;
910
+ `,
911
+ thinkingLabel: css6`
912
+ font-size: 11px;
913
+ color: var(--wds-muted);
914
+ `,
915
+ imagePreview: css6`
916
+ position: relative;
917
+ width: fit-content;
918
+ margin: 0 2px;
919
+ `,
920
+ previewImg: css6`
921
+ width: 72px;
922
+ height: 72px;
923
+ object-fit: cover;
924
+ border-radius: 10px;
925
+ border: 1px solid var(--wds-border);
926
+ display: block;
927
+ `,
928
+ removeFile: css6`
929
+ position: absolute;
930
+ top: -6px;
931
+ right: -6px;
932
+ width: 18px;
933
+ height: 18px;
934
+ border-radius: 50%;
935
+ background: var(--wds-fg);
936
+ color: var(--wds-bg);
937
+ border: none;
938
+ cursor: pointer;
939
+ display: flex;
940
+ align-items: center;
941
+ justify-content: center;
942
+ padding: 0;
943
+ `,
944
+ inputRow: css6`
945
+ display: flex;
946
+ align-items: center;
947
+ gap: 6px;
948
+ background: var(--wds-muted-bg);
949
+ border: 1px solid var(--wds-border);
950
+ border-radius: 14px;
951
+ padding: 4px 4px 4px 8px;
952
+ transition: border-color 0.15s;
953
+ &:focus-within {
954
+ border-color: var(--wds-primary);
955
+ background: var(--wds-bg);
956
+ }
957
+ `,
958
+ attachBtn: css6`
959
+ background: none;
960
+ border: none;
961
+ cursor: pointer;
962
+ color: var(--wds-muted);
963
+ padding: 5px;
964
+ border-radius: 8px;
965
+ display: flex;
966
+ align-items: center;
967
+ justify-content: center;
968
+ flex-shrink: 0;
969
+ transition: color 0.15s;
970
+ &:hover:not(:disabled) {
971
+ color: var(--wds-primary);
972
+ }
973
+ &:disabled {
974
+ opacity: 0.35;
975
+ cursor: not-allowed;
976
+ }
977
+ `,
978
+ hiddenInput: css6`
979
+ display: none;
980
+ `,
981
+ textarea: css6`
982
+ flex: 1;
983
+ resize: none;
984
+ border: none;
985
+ padding: 6px 0;
986
+ font-size: 0.875rem;
987
+ font-family: inherit;
988
+ line-height: 1.5;
989
+ background: transparent;
990
+ color: var(--wds-fg);
991
+ outline: none;
992
+ max-height: 120px;
993
+ overflow-y: auto;
994
+ &::placeholder { color: var(--wds-muted); }
995
+ &:disabled {
996
+ opacity: 0.5;
997
+ cursor: not-allowed;
998
+ }
999
+ `,
1000
+ sendBtn: css6`
1001
+ background: var(--wds-primary);
1002
+ border: none;
1003
+ border-radius: 10px;
1004
+ width: 34px;
1005
+ height: 34px;
1006
+ display: flex;
1007
+ align-items: center;
1008
+ justify-content: center;
1009
+ cursor: pointer;
1010
+ color: #fff;
1011
+ flex-shrink: 0;
1012
+ transition: opacity 0.15s;
1013
+ &:disabled {
1014
+ opacity: 0.35;
1015
+ cursor: not-allowed;
1016
+ }
1017
+ &:hover:not(:disabled) { opacity: 0.85; }
1018
+ `,
1019
+ spinner: css6`
1020
+ width: 14px;
1021
+ height: 14px;
1022
+ border: 2px solid rgba(255,255,255,0.3);
1023
+ border-top-color: #fff;
1024
+ border-radius: 50%;
1025
+ animation: ${spin} 0.7s linear infinite;
1026
+ display: block;
1027
+ `,
1028
+ hint: css6`
1029
+ font-size: 10px;
1030
+ color: var(--wds-muted);
1031
+ margin: 0;
1032
+ text-align: center;
1033
+ opacity: 0.7;
1034
+ text-align: center;
1035
+ `
1036
+ };
1037
+
1038
+ // src/components/ChatScreen.tsx
1039
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1040
+ var ChatScreen = ({
1041
+ messages,
1042
+ onSendText,
1043
+ onSendFile,
1044
+ onBack,
1045
+ onExpand,
1046
+ isExpanded,
1047
+ status,
1048
+ error
1049
+ }) => {
1050
+ const bottomRef = useRef2(null);
1051
+ const scrollRef = useRef2(null);
1052
+ const connected = status === "connected";
1053
+ const [showScrollBtn, setShowScrollBtn] = useState3(false);
1054
+ const lastMsg = messages[messages.length - 1];
1055
+ const isThinking = connected && (lastMsg == null ? void 0 : lastMsg.direction) === "INBOUND";
1056
+ useEffect3(() => {
1057
+ var _a;
1058
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
1059
+ }, [messages]);
1060
+ const handleScroll = () => {
1061
+ const el = scrollRef.current;
1062
+ if (!el) return;
1063
+ setShowScrollBtn(el.scrollHeight - el.scrollTop - el.clientHeight > 80);
1064
+ };
1065
+ return /* @__PURE__ */ jsxs5("div", { className: styles6.wrapper, children: [
1066
+ /* @__PURE__ */ jsxs5("div", { className: styles6.header, children: [
1067
+ /* @__PURE__ */ jsx7("button", { className: styles6.iconBtn, onClick: onBack, children: /* @__PURE__ */ jsx7("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("polyline", { points: "15 18 9 12 15 6" }) }) }),
1068
+ /* @__PURE__ */ jsxs5("div", { className: styles6.headerCenter, children: [
1069
+ /* @__PURE__ */ jsx7("span", { className: styles6.headerTitle, children: "Support" }),
1070
+ /* @__PURE__ */ jsx7("span", { className: styles6.statusDot(connected) }),
1071
+ /* @__PURE__ */ jsx7("span", { className: styles6.statusLabel, children: connected ? "Online" : "Connecting..." })
1072
+ ] }),
1073
+ /* @__PURE__ */ jsx7("button", { className: styles6.expandBtn, onClick: onExpand, children: isExpanded ? /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1074
+ /* @__PURE__ */ jsx7("polyline", { points: "4 14 10 14 10 20" }),
1075
+ /* @__PURE__ */ jsx7("polyline", { points: "20 10 14 10 14 4" }),
1076
+ /* @__PURE__ */ jsx7("line", { x1: "10", y1: "14", x2: "3", y2: "21" }),
1077
+ /* @__PURE__ */ jsx7("line", { x1: "21", y1: "3", x2: "14", y2: "10" })
1078
+ ] }) : /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1079
+ /* @__PURE__ */ jsx7("polyline", { points: "15 3 21 3 21 9" }),
1080
+ /* @__PURE__ */ jsx7("polyline", { points: "9 21 3 21 3 15" }),
1081
+ /* @__PURE__ */ jsx7("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
1082
+ /* @__PURE__ */ jsx7("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
1083
+ ] }) })
1084
+ ] }),
1085
+ /* @__PURE__ */ jsxs5("div", { className: styles6.messagesWrap, children: [
1086
+ /* @__PURE__ */ jsxs5("div", { className: styles6.messages, ref: scrollRef, onScroll: handleScroll, children: [
1087
+ error && /* @__PURE__ */ jsx7("div", { className: styles6.errorBanner, children: error.message }),
1088
+ messages.length === 0 && connected && /* @__PURE__ */ jsx7("div", { className: styles6.emptyState, children: /* @__PURE__ */ jsx7("p", { children: "Send a message to start the conversation." }) }),
1089
+ messages.map((msg) => /* @__PURE__ */ jsx7(MessageBubble, { message: msg }, msg.id)),
1090
+ /* @__PURE__ */ jsx7("div", { ref: bottomRef })
1091
+ ] }),
1092
+ showScrollBtn && /* @__PURE__ */ jsx7("button", { className: styles6.scrollBtn, onClick: () => {
1093
+ var _a;
1094
+ return (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
1095
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("polyline", { points: "6 9 12 15 18 9" }) }) })
1096
+ ] }),
1097
+ /* @__PURE__ */ jsx7(
1098
+ ChatInput,
1099
+ {
1100
+ onSendText,
1101
+ onSendFile,
1102
+ disabled: !connected,
1103
+ isThinking
1104
+ }
1105
+ )
1106
+ ] });
1107
+ };
1108
+ var pulse2 = keyframes2`
1109
+ 0%, 100% { opacity: 1; }
1110
+ 50% { opacity: 0.4; }
1111
+ `;
1112
+ var styles6 = {
1113
+ wrapper: css7`
1114
+ display: flex;
1115
+ flex-direction: column;
1116
+ height: 100%;
1117
+ background: var(--wds-bg);
1118
+ `,
1119
+ header: css7`
1120
+ display: flex;
1121
+ align-items: center;
1122
+ gap: 10px;
1123
+ padding: 12px 14px;
1124
+ border-bottom: 1px solid var(--wds-border);
1125
+ flex-shrink: 0;
1126
+ `,
1127
+ headerCenter: css7`
1128
+ flex: 1;
1129
+ display: flex;
1130
+ align-items: center;
1131
+ gap: 6px;
1132
+ `,
1133
+ headerTitle: css7`
1134
+ font-size: 0.875rem;
1135
+ font-weight: 600;
1136
+ color: var(--wds-fg);
1137
+ `,
1138
+ statusDot: (connected) => css7`
1139
+ width: 7px;
1140
+ height: 7px;
1141
+ border-radius: 50%;
1142
+ flex-shrink: 0;
1143
+ background: ${connected ? "#22c55e" : "#f59e0b"};
1144
+ animation: ${connected ? "none" : `${pulse2} 1.2s ease-in-out infinite`};
1145
+ `,
1146
+ statusLabel: css7`
1147
+ font-size: 11px;
1148
+ color: var(--wds-muted);
1149
+ `,
1150
+ iconBtn: css7`
1151
+ background: none;
1152
+ border: none;
1153
+ cursor: pointer;
1154
+ color: var(--wds-muted);
1155
+ padding: 6px;
1156
+ border-radius: 8px;
1157
+ display: flex;
1158
+ align-items: center;
1159
+ justify-content: center;
1160
+ flex-shrink: 0;
1161
+ transition: color 0.15s, background 0.15s;
1162
+ &:hover {
1163
+ color: var(--wds-fg);
1164
+ background: var(--wds-muted-bg);
1165
+ }
1166
+ `,
1167
+ expandBtn: css7`
1168
+ background: none;
1169
+ border: none;
1170
+ cursor: pointer;
1171
+ color: var(--wds-muted);
1172
+ padding: 6px;
1173
+ border-radius: 8px;
1174
+ display: flex;
1175
+ align-items: center;
1176
+ justify-content: center;
1177
+ flex-shrink: 0;
1178
+ transition: color 0.15s, background 0.15s;
1179
+ &:hover {
1180
+ color: var(--wds-fg);
1181
+ background: var(--wds-muted-bg);
1182
+ }
1183
+ @media (max-width: 480px) {
1184
+ display: none;
1185
+ }
1186
+ `,
1187
+ messagesWrap: css7`
1188
+ flex: 1;
1189
+ position: relative;
1190
+ overflow: hidden;
1191
+ `,
1192
+ messages: css7`
1193
+ position: absolute;
1194
+ inset: 0;
1195
+ overflow-y: auto;
1196
+ padding: 16px 14px;
1197
+ display: flex;
1198
+ flex-direction: column;
1199
+ gap: 10px;
1200
+ `,
1201
+ errorBanner: css7`
1202
+ background: oklch(0.9705 0.0129 17.04);
1203
+ color: oklch(0.5054 0.1905 27.51);
1204
+ border: 1px solid oklch(0.8845 0.0592 18.27);
1205
+ border-radius: 8px;
1206
+ padding: 8px 12px;
1207
+ font-size: 12px;
1208
+ text-align: center;
1209
+ `,
1210
+ emptyState: css7`
1211
+ flex: 1;
1212
+ display: flex;
1213
+ align-items: center;
1214
+ justify-content: center;
1215
+ color: var(--wds-muted);
1216
+ font-size: 13px;
1217
+ text-align: center;
1218
+ padding: 40px 20px;
1219
+ `,
1220
+ scrollBtn: css7`
1221
+ position: absolute;
1222
+ bottom: 12px;
1223
+ left: 50%;
1224
+ transform: translateX(-50%);
1225
+ width: 30px;
1226
+ height: 30px;
1227
+ border-radius: 50%;
1228
+ background: var(--wds-bg);
1229
+ border: 1px solid var(--wds-border);
1230
+ color: var(--wds-muted);
1231
+ display: flex;
1232
+ align-items: center;
1233
+ justify-content: center;
1234
+ cursor: pointer;
1235
+ box-shadow: 0 2px 8px rgba(0,0,0,0.12);
1236
+ transition: color 0.15s, border-color 0.15s;
1237
+ &:hover {
1238
+ color: var(--wds-primary);
1239
+ border-color: var(--wds-primary);
1240
+ }
1241
+ `
1242
+ };
1243
+
1244
+ // src/components/ChatWidget.tsx
1245
+ import { Fragment, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1246
+ var ChatWidget = ({ config }) => {
1247
+ var _a, _b;
1248
+ const [isOpen, setIsOpen] = useState4(false);
1249
+ const [isExpanded, setIsExpanded] = useState4(false);
1250
+ const [view, setView] = useState4("home");
1251
+ const reset = useChatStore((s) => s.reset);
1252
+ const resolvedConfig = __spreadProps(__spreadValues({}, config), {
1253
+ visitorId: (_b = (_a = config.visitorId) != null ? _a : loadVisitorId()) != null ? _b : void 0
1254
+ });
1255
+ const hasHistory = !!resolvedConfig.visitorId;
1256
+ const { status, messages, error, sendMessage, sendFile } = useChat(resolvedConfig);
1257
+ const handleNewConversation = () => {
1258
+ reset();
1259
+ setView("chat");
1260
+ };
1261
+ useEffect4(() => {
1262
+ const isMobile = window.innerWidth <= 480;
1263
+ if (isMobile) {
1264
+ document.body.style.overflow = isOpen ? "hidden" : "";
1265
+ }
1266
+ return () => {
1267
+ document.body.style.overflow = "";
1268
+ };
1269
+ }, [isOpen]);
1270
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
1271
+ /* @__PURE__ */ jsx8(
1272
+ FloatingButton,
1273
+ {
1274
+ isOpen,
1275
+ onToggle: () => setIsOpen((p) => !p)
1276
+ }
1277
+ ),
1278
+ /* @__PURE__ */ jsx8(
1279
+ ChatPanel,
1280
+ {
1281
+ isOpen,
1282
+ isExpanded,
1283
+ children: view === "home" ? /* @__PURE__ */ jsx8(
1284
+ HomeScreen,
1285
+ {
1286
+ onStartChat: handleNewConversation,
1287
+ onContinueChat: () => setView("chat"),
1288
+ hasHistory,
1289
+ onExpand: () => setIsExpanded((p) => !p),
1290
+ isExpanded
1291
+ }
1292
+ ) : /* @__PURE__ */ jsx8(
1293
+ ChatScreen,
1294
+ {
1295
+ messages,
1296
+ onSendText: sendMessage,
1297
+ onSendFile: sendFile,
1298
+ onBack: () => setView("home"),
1299
+ onExpand: () => setIsExpanded((p) => !p),
1300
+ isExpanded,
1301
+ status,
1302
+ error
1303
+ }
1304
+ )
1305
+ }
1306
+ )
1307
+ ] });
1308
+ };
1309
+ injectGlobal`
1310
+ :root {
1311
+ --wds-primary: oklch(0.5811 0.2268 259.15);
1312
+ --wds-primary-soft: oklch(0.5811 0.2268 259.15 / 0.12);
1313
+ --wds-primary-border: oklch(0.7695 0.1177 255.22 / 0.4);
1314
+ --wds-bg: oklch(1 0 0);
1315
+ --wds-fg: oklch(0.145 0 0);
1316
+ --wds-muted: oklch(0.556 0 0);
1317
+ --wds-muted-bg: oklch(0.97 0 0);
1318
+ --wds-border: oklch(0.922 0 0);
1319
+ }
1320
+
1321
+ @media (prefers-color-scheme: dark) {
1322
+ :root {
1323
+ --wds-bg: oklch(0.145 0 0);
1324
+ --wds-fg: oklch(0.985 0 0);
1325
+ --wds-muted: oklch(0.708 0 0);
1326
+ --wds-muted-bg: oklch(0.205 0 0);
1327
+ --wds-border: oklch(1 0 0 / 10%);
1328
+ --wds-primary-soft: oklch(0.5811 0.2268 259.15 / 0.15);
1329
+ }
1330
+ }
1331
+
1332
+ [data-wds-root] * {
1333
+ box-sizing: border-box;
1334
+ text-align: left;
1335
+ }
1336
+ `;
1337
+ export {
1338
+ ChatWidget,
1339
+ clearVisitorId,
1340
+ loadVisitorId,
1341
+ saveVisitorId,
1342
+ useChat,
1343
+ useChatStore
1344
+ };
1345
+ //# sourceMappingURL=index.js.map