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