@waysdrop/chat 1.0.1 → 1.0.4

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/README.md CHANGED
@@ -43,10 +43,12 @@ Mount `<ChatWidget />` once at the root of your app. It renders a floating butto
43
43
 
44
44
  ```ts
45
45
  type ChatConfig = {
46
- serverUrl: string // Socket.IO server URL
47
- apiUrl: string // REST base URL (used for file uploads)
48
- token?: string // JWT for authenticated users — omit for visitor flow
49
- visitorId?: string // Pass a returning visitor's ID to restore chat history
46
+ serverUrl: string // Socket.IO server URL
47
+ apiUrl: string // REST base URL (used for file uploads)
48
+ token?: string // JWT for authenticated users — omit for visitor flow
49
+ visitorId?: string // Pass a returning visitor's ID to restore chat history
50
+ theme?: 'light' | 'dark' | 'system' // defaults to 'system'
51
+ primaryColor?: string // any valid CSS color — defaults to Waysdrop blue
50
52
  }
51
53
  ```
52
54
 
@@ -60,7 +62,20 @@ saveVisitorId(id) // writes to localStorage
60
62
  clearVisitorId() // clears — use on logout
61
63
  ```
62
64
 
63
- **Authenticated flow** — pass a `token` (JWT). The socket server resolves the user from it. No `visitorId` needed.
65
+ **Authenticated flow** — pass a `token` (JWT). The socket server resolves the user from it. No `visitorId` needed. If the token changes at runtime (user logs in after mount), the widget automatically destroys the old socket connection and reconnects with the new token.
66
+
67
+ **Theming** — `theme` controls the color scheme. `system` follows the OS `prefers-color-scheme`. `light` and `dark` force it regardless of the OS setting. `primaryColor` accepts any valid CSS color value:
68
+
69
+ ```tsx
70
+ <ChatWidget
71
+ config={{
72
+ serverUrl: '...',
73
+ apiUrl: '...',
74
+ theme: 'dark',
75
+ primaryColor: '#7c3aed',
76
+ }}
77
+ />
78
+ ```
64
79
 
65
80
  ---
66
81
 
@@ -81,11 +96,11 @@ const {
81
96
  visitorInfo, // VisitorInfo | null
82
97
  setVisitorInfo,
83
98
  sendMessage, // (content: string, info?: VisitorInfo) => void
84
- sendFile, // (file: File, info?: VisitorInfo) => Promise<void>
99
+ sendFile, // (file: File, content?: string, info?: VisitorInfo) => Promise<void>
85
100
  } = useChat(config)
86
101
  ```
87
102
 
88
- `sendMessage` and `sendFile` accept an optional `VisitorInfo` argument for the first message in a visitor session (name, email, phone). After the first message, `visitorInfo` is stored in the Zustand store and reused automatically.
103
+ `sendMessage` and `sendFile` accept an optional `VisitorInfo` argument for the first message in a visitor session. `sendFile` also accepts an optional `content` string to send text alongside the file in a single message.
89
104
 
90
105
  ---
91
106
 
@@ -104,7 +119,7 @@ type ChatMessage = {
104
119
  }
105
120
 
106
121
  type VisitorInfo = {
107
- email: string // required
122
+ email: string
108
123
  name?: string
109
124
  phone?: string
110
125
  }
package/dist/index.cjs CHANGED
@@ -63,12 +63,15 @@ var import_css8 = require("@emotion/css");
63
63
  // src/lib/upload.ts
64
64
  var VISITOR_ID_KEY = "waysdrop_visitor_id";
65
65
  var saveVisitorId = (id) => {
66
+ if (typeof window === "undefined") return;
66
67
  localStorage.setItem(VISITOR_ID_KEY, id);
67
68
  };
68
69
  var loadVisitorId = () => {
70
+ if (typeof window === "undefined") return null;
69
71
  return localStorage.getItem(VISITOR_ID_KEY);
70
72
  };
71
73
  var clearVisitorId = () => {
74
+ if (typeof window === "undefined") return;
72
75
  localStorage.removeItem(VISITOR_ID_KEY);
73
76
  };
74
77
  var uploadFile = async (file, config) => {
@@ -102,8 +105,11 @@ var import_react = require("react");
102
105
  // src/lib/socket.ts
103
106
  var import_socket = require("socket.io-client");
104
107
  var socket = null;
105
- var getSocket = (config) => {
106
- if (socket) return socket;
108
+ var createSocket = (config) => {
109
+ if (socket) {
110
+ socket.disconnect();
111
+ socket = null;
112
+ }
107
113
  const { serverUrl, token, visitorId } = config;
108
114
  socket = (0, import_socket.io)(serverUrl, {
109
115
  transports: ["websocket", "polling"],
@@ -112,6 +118,10 @@ var getSocket = (config) => {
112
118
  });
113
119
  return socket;
114
120
  };
121
+ var getSocket = (config) => {
122
+ if (socket) return socket;
123
+ return createSocket(config);
124
+ };
115
125
  var destroySocket = () => {
116
126
  if (socket) {
117
127
  socket.disconnect();
@@ -163,7 +173,7 @@ var useChat = (config) => {
163
173
  reset
164
174
  } = useChatStore();
165
175
  (0, import_react.useEffect)(() => {
166
- const socket2 = getSocket(config);
176
+ const socket2 = createSocket(config);
167
177
  setStatus("connecting");
168
178
  socket2.connect();
169
179
  socket2.on("connected", (payload) => {
@@ -190,7 +200,7 @@ var useChat = (config) => {
190
200
  destroySocket();
191
201
  reset();
192
202
  };
193
- }, []);
203
+ }, [config.token]);
194
204
  const sendMessage = (0, import_react.useCallback)(
195
205
  (content, info) => {
196
206
  var _a, _b;
@@ -208,14 +218,15 @@ var useChat = (config) => {
208
218
  [config, visitorInfo]
209
219
  );
210
220
  const sendFile = (0, import_react.useCallback)(
211
- async (file, info) => {
221
+ async (file, content, info) => {
212
222
  var _a, _b;
213
223
  const url = await uploadFile(file, config);
214
224
  const socket2 = getSocket(config);
215
- const dto = __spreadValues({
216
- file: url,
225
+ const dto = __spreadValues(__spreadProps(__spreadValues({
226
+ file: url
227
+ }, content ? { content } : {}), {
217
228
  externalId: `file-${Date.now()}`
218
- }, (info != null ? info : visitorInfo) ? {
229
+ }), (info != null ? info : visitorInfo) ? {
219
230
  email: (info != null ? info : visitorInfo).email,
220
231
  name: (_a = info != null ? info : visitorInfo) == null ? void 0 : _a.name,
221
232
  phone: (_b = info != null ? info : visitorInfo) == null ? void 0 : _b.phone
@@ -734,7 +745,7 @@ var MessageBubble = ({ message }) => {
734
745
  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
746
  isBot && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: styles4.botLabel, children: "Ways AI" }),
736
747
  /* @__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" }),
748
+ message.file && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: message.file, target: "_blank", rel: "noreferrer", className: styles4.fileLink(!!message.content), children: "\u{1F4CE} View attachment" }),
738
749
  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
750
  ] }),
740
751
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: styles4.time, children: formatTime(message.createdAt) })
@@ -777,12 +788,13 @@ var styles4 = {
777
788
  white-space: pre-wrap;
778
789
  text-align: left;
779
790
  `,
780
- fileLink: import_css5.css`
791
+ fileLink: (hasContent) => import_css5.css`
781
792
  font-size: 0.875rem;
782
793
  color: inherit;
783
794
  text-decoration: underline;
784
795
  display: block;
785
796
  text-align: left;
797
+ ${hasContent ? "margin-bottom: 6px;" : ""}
786
798
  `,
787
799
  time: import_css5.css`
788
800
  font-size: 10px;
@@ -815,13 +827,13 @@ var ChatInput = ({ onSendText, onSendFile, disabled, isThinking }) => {
815
827
  if (!trimmed && !pendingFile || disabled || uploading) return;
816
828
  if (pendingFile) {
817
829
  setUploading(true);
818
- onSendFile(pendingFile).finally(() => {
830
+ onSendFile(pendingFile, trimmed || void 0).finally(() => {
819
831
  setUploading(false);
820
832
  setPendingFile(null);
821
833
  setPendingPreview(null);
834
+ setValue("");
822
835
  });
823
- }
824
- if (trimmed) {
836
+ } else if (trimmed) {
825
837
  onSendText(trimmed);
826
838
  setValue("");
827
839
  }
@@ -1102,35 +1114,102 @@ var ChatScreen = ({
1102
1114
  };
1103
1115
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: styles6.wrapper, children: [
1104
1116
  /* @__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" }) }) }),
1117
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { className: styles6.iconBtn, onClick: onBack, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1118
+ "svg",
1119
+ {
1120
+ width: "18",
1121
+ height: "18",
1122
+ viewBox: "0 0 24 24",
1123
+ fill: "none",
1124
+ stroke: "currentColor",
1125
+ strokeWidth: "2",
1126
+ strokeLinecap: "round",
1127
+ strokeLinejoin: "round",
1128
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "15 18 9 12 15 6" })
1129
+ }
1130
+ ) }),
1106
1131
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: styles6.headerCenter, children: [
1107
1132
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: styles6.headerTitle, children: "Support" }),
1108
1133
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: styles6.statusDot(connected) }),
1109
1134
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: styles6.statusLabel, children: connected ? "Online" : "Connecting..." })
1110
1135
  ] }),
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
- ] }) })
1136
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { className: styles6.expandBtn, onClick: onExpand, children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1137
+ "svg",
1138
+ {
1139
+ width: "16",
1140
+ height: "16",
1141
+ viewBox: "0 0 24 24",
1142
+ fill: "none",
1143
+ stroke: "currentColor",
1144
+ strokeWidth: "2",
1145
+ strokeLinecap: "round",
1146
+ strokeLinejoin: "round",
1147
+ children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "4 14 10 14 10 20" }),
1149
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "20 10 14 10 14 4" }),
1150
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "10", y1: "14", x2: "3", y2: "21" }),
1151
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" })
1152
+ ]
1153
+ }
1154
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1155
+ "svg",
1156
+ {
1157
+ width: "16",
1158
+ height: "16",
1159
+ viewBox: "0 0 24 24",
1160
+ fill: "none",
1161
+ stroke: "currentColor",
1162
+ strokeWidth: "2",
1163
+ strokeLinecap: "round",
1164
+ strokeLinejoin: "round",
1165
+ children: [
1166
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "15 3 21 3 21 9" }),
1167
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "9 21 3 21 3 15" }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "21", y1: "3", x2: "14", y2: "10" }),
1169
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "3", y1: "21", x2: "10", y2: "14" })
1170
+ ]
1171
+ }
1172
+ ) })
1122
1173
  ] }),
1123
1174
  /* @__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" }) }) })
1175
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1176
+ "div",
1177
+ {
1178
+ className: styles6.messages,
1179
+ ref: scrollRef,
1180
+ onScroll: handleScroll,
1181
+ children: [
1182
+ error && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: styles6.errorBanner, children: error.message }),
1183
+ 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." }) }),
1184
+ messages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageBubble, { message: msg }, msg.id)),
1185
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { ref: bottomRef })
1186
+ ]
1187
+ }
1188
+ ),
1189
+ showScrollBtn && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1190
+ "button",
1191
+ {
1192
+ className: styles6.scrollBtn,
1193
+ onClick: () => {
1194
+ var _a;
1195
+ return (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
1196
+ },
1197
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1198
+ "svg",
1199
+ {
1200
+ width: "14",
1201
+ height: "14",
1202
+ viewBox: "0 0 24 24",
1203
+ fill: "none",
1204
+ stroke: "currentColor",
1205
+ strokeWidth: "2.5",
1206
+ strokeLinecap: "round",
1207
+ strokeLinejoin: "round",
1208
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "6 9 12 15 18 9" })
1209
+ }
1210
+ )
1211
+ }
1212
+ )
1134
1213
  ] }),
1135
1214
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1136
1215
  ChatInput,
@@ -1196,7 +1275,9 @@ var styles6 = {
1196
1275
  align-items: center;
1197
1276
  justify-content: center;
1198
1277
  flex-shrink: 0;
1199
- transition: color 0.15s, background 0.15s;
1278
+ transition:
1279
+ color 0.15s,
1280
+ background 0.15s;
1200
1281
  &:hover {
1201
1282
  color: var(--wds-fg);
1202
1283
  background: var(--wds-muted-bg);
@@ -1213,7 +1294,9 @@ var styles6 = {
1213
1294
  align-items: center;
1214
1295
  justify-content: center;
1215
1296
  flex-shrink: 0;
1216
- transition: color 0.15s, background 0.15s;
1297
+ transition:
1298
+ color 0.15s,
1299
+ background 0.15s;
1217
1300
  &:hover {
1218
1301
  color: var(--wds-fg);
1219
1302
  background: var(--wds-muted-bg);
@@ -1270,8 +1353,10 @@ var styles6 = {
1270
1353
  align-items: center;
1271
1354
  justify-content: center;
1272
1355
  cursor: pointer;
1273
- box-shadow: 0 2px 8px rgba(0,0,0,0.12);
1274
- transition: color 0.15s, border-color 0.15s;
1356
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
1357
+ transition:
1358
+ color 0.15s,
1359
+ border-color 0.15s;
1275
1360
  &:hover {
1276
1361
  color: var(--wds-primary);
1277
1362
  border-color: var(--wds-primary);
@@ -1281,16 +1366,74 @@ var styles6 = {
1281
1366
 
1282
1367
  // src/components/ChatWidget.tsx
1283
1368
  var import_jsx_runtime8 = require("react/jsx-runtime");
1369
+ var LIGHT_VARS = `
1370
+ --wds-bg: oklch(1 0 0);
1371
+ --wds-fg: oklch(0.145 0 0);
1372
+ --wds-muted: oklch(0.556 0 0);
1373
+ --wds-muted-bg: oklch(0.97 0 0);
1374
+ --wds-border: oklch(0.922 0 0);
1375
+ `;
1376
+ var DARK_VARS = `
1377
+ --wds-bg: oklch(0.145 0 0);
1378
+ --wds-fg: oklch(0.985 0 0);
1379
+ --wds-muted: oklch(0.708 0 0);
1380
+ --wds-muted-bg: oklch(0.205 0 0);
1381
+ --wds-border: oklch(1 0 0 / 10%);
1382
+ `;
1383
+ function buildPrimaryVars(color) {
1384
+ return `
1385
+ --wds-primary: ${color};
1386
+ --wds-primary-soft: color-mix(in oklch, ${color} 12%, transparent);
1387
+ --wds-primary-border: color-mix(in oklch, ${color} 40%, transparent);
1388
+ `;
1389
+ }
1390
+ var DEFAULT_PRIMARY = "oklch(0.5811 0.2268 259.15)";
1391
+ function buildThemeStyle(theme, primaryColor) {
1392
+ const primary = buildPrimaryVars(primaryColor);
1393
+ if (theme === "light") {
1394
+ return `
1395
+ [data-wds-root] {
1396
+ ${LIGHT_VARS}
1397
+ ${primary}
1398
+ }
1399
+ `;
1400
+ }
1401
+ if (theme === "dark") {
1402
+ return `
1403
+ [data-wds-root] {
1404
+ ${DARK_VARS}
1405
+ ${primary}
1406
+ }
1407
+ `;
1408
+ }
1409
+ return `
1410
+ [data-wds-root] {
1411
+ ${LIGHT_VARS}
1412
+ ${primary}
1413
+ }
1414
+ @media (prefers-color-scheme: dark) {
1415
+ [data-wds-root] {
1416
+ ${DARK_VARS}
1417
+ --wds-primary-soft: color-mix(in oklch, ${primaryColor} 15%, transparent);
1418
+ }
1419
+ }
1420
+ `;
1421
+ }
1284
1422
  var ChatWidget = ({ config }) => {
1285
- var _a, _b;
1286
1423
  const [isOpen, setIsOpen] = (0, import_react5.useState)(false);
1287
1424
  const [isExpanded, setIsExpanded] = (0, import_react5.useState)(false);
1288
1425
  const [view, setView] = (0, import_react5.useState)("home");
1426
+ const [visitorId] = (0, import_react5.useState)(
1427
+ () => {
1428
+ var _a, _b;
1429
+ return (_b = (_a = config.visitorId) != null ? _a : loadVisitorId()) != null ? _b : void 0;
1430
+ }
1431
+ );
1289
1432
  const reset = useChatStore((s) => s.reset);
1290
1433
  const resolvedConfig = __spreadProps(__spreadValues({}, config), {
1291
- visitorId: (_b = (_a = config.visitorId) != null ? _a : loadVisitorId()) != null ? _b : void 0
1434
+ visitorId
1292
1435
  });
1293
- const hasHistory = !!resolvedConfig.visitorId;
1436
+ const hasHistory = !!visitorId;
1294
1437
  const { status, messages, error, sendMessage, sendFile } = useChat(resolvedConfig);
1295
1438
  const handleNewConversation = () => {
1296
1439
  reset();
@@ -1305,6 +1448,19 @@ var ChatWidget = ({ config }) => {
1305
1448
  document.body.style.overflow = "";
1306
1449
  };
1307
1450
  }, [isOpen]);
1451
+ (0, import_react5.useEffect)(() => {
1452
+ var _a, _b;
1453
+ const theme = (_a = config.theme) != null ? _a : "system";
1454
+ const primaryColor = (_b = config.primaryColor) != null ? _b : DEFAULT_PRIMARY;
1455
+ const style = buildThemeStyle(theme, primaryColor);
1456
+ const el = document.createElement("style");
1457
+ el.setAttribute("data-wds-theme", "");
1458
+ el.textContent = style;
1459
+ document.head.appendChild(el);
1460
+ return () => {
1461
+ document.head.removeChild(el);
1462
+ };
1463
+ }, [config.theme, config.primaryColor]);
1308
1464
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1309
1465
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1310
1466
  FloatingButton,
@@ -1345,28 +1501,6 @@ var ChatWidget = ({ config }) => {
1345
1501
  ] });
1346
1502
  };
1347
1503
  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
1504
  [data-wds-root] * {
1371
1505
  box-sizing: border-box;
1372
1506
  text-align: left;