@channlize/react 0.1.0-alpha.1

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,1032 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ChannlizeChat: () => ChannlizeChat,
24
+ ChannlizeProvider: () => ChannlizeProvider,
25
+ ContactForm: () => ContactForm,
26
+ SupportChat: () => SupportChat,
27
+ useChannlize: () => useChannlize,
28
+ useChatSession: () => useChatSession,
29
+ useContactForm: () => useContactForm
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/context/ChannlizeContext.tsx
34
+ var import_react = require("react");
35
+ var import_core = require("@channlize/core");
36
+ var import_jsx_runtime = require("react/jsx-runtime");
37
+ var ChannlizeContext = (0, import_react.createContext)(null);
38
+ function ChannlizeProvider({ config, children }) {
39
+ const client = new import_core.ChannlizeClient(config);
40
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChannlizeContext.Provider, { value: { client, config }, children });
41
+ }
42
+ function useChannlize() {
43
+ const context = (0, import_react.useContext)(ChannlizeContext);
44
+ if (!context) {
45
+ throw new Error("useChannlize must be used within a ChannlizeProvider");
46
+ }
47
+ return context;
48
+ }
49
+
50
+ // src/hooks/useContactForm.ts
51
+ var import_react2 = require("react");
52
+ function useContactForm(options = {}) {
53
+ const { client } = useChannlize();
54
+ const [isSubmitting, setIsSubmitting] = (0, import_react2.useState)(false);
55
+ const [isSuccess, setIsSuccess] = (0, import_react2.useState)(false);
56
+ const [error, setError] = (0, import_react2.useState)(null);
57
+ const submit = (0, import_react2.useCallback)(
58
+ async (data) => {
59
+ setIsSubmitting(true);
60
+ setError(null);
61
+ setIsSuccess(false);
62
+ try {
63
+ const response = await client.submitContactForm(data);
64
+ setIsSuccess(true);
65
+ options.onSuccess?.(response);
66
+ } catch (err) {
67
+ const error2 = err;
68
+ setError(error2);
69
+ options.onError?.(error2);
70
+ } finally {
71
+ setIsSubmitting(false);
72
+ }
73
+ },
74
+ [client, options]
75
+ );
76
+ const reset = (0, import_react2.useCallback)(() => {
77
+ setIsSubmitting(false);
78
+ setIsSuccess(false);
79
+ setError(null);
80
+ }, []);
81
+ return {
82
+ submit,
83
+ isSubmitting,
84
+ isSuccess,
85
+ error,
86
+ reset
87
+ };
88
+ }
89
+
90
+ // src/hooks/useChatSession.ts
91
+ var import_react3 = require("react");
92
+ function useChatSession(options = {}) {
93
+ const { client, config } = useChannlize();
94
+ const [session, setSession] = (0, import_react3.useState)(null);
95
+ const [messages, setMessages] = (0, import_react3.useState)([]);
96
+ const [isConnecting, setIsConnecting] = (0, import_react3.useState)(false);
97
+ const [isConnected, setIsConnected] = (0, import_react3.useState)(false);
98
+ const [error, setError] = (0, import_react3.useState)(null);
99
+ const eventSourceRef = (0, import_react3.useRef)(null);
100
+ const sessionRef = (0, import_react3.useRef)(null);
101
+ const isInitializedRef = (0, import_react3.useRef)(false);
102
+ const baseUrl = config.baseUrl || "https://api.channlize.com";
103
+ const storageKey = options.storageKey || `channlize_session_${config.apiKey}`;
104
+ const persistSession = options.persistSession ?? true;
105
+ (0, import_react3.useEffect)(() => {
106
+ sessionRef.current = session;
107
+ }, [session]);
108
+ const saveSessionToStorage = (0, import_react3.useCallback)((sessionId) => {
109
+ if (persistSession && typeof window !== "undefined") {
110
+ try {
111
+ localStorage.setItem(storageKey, sessionId);
112
+ } catch (err) {
113
+ console.warn("Failed to save session to localStorage:", err);
114
+ }
115
+ }
116
+ }, [persistSession, storageKey]);
117
+ const getSessionFromStorage = (0, import_react3.useCallback)(() => {
118
+ if (persistSession && typeof window !== "undefined") {
119
+ try {
120
+ return localStorage.getItem(storageKey);
121
+ } catch (err) {
122
+ console.warn("Failed to get session from localStorage:", err);
123
+ }
124
+ }
125
+ return null;
126
+ }, [persistSession, storageKey]);
127
+ const clearSessionFromStorage = (0, import_react3.useCallback)(() => {
128
+ if (persistSession && typeof window !== "undefined") {
129
+ try {
130
+ localStorage.removeItem(storageKey);
131
+ } catch (err) {
132
+ console.warn("Failed to clear session from localStorage:", err);
133
+ }
134
+ }
135
+ }, [persistSession, storageKey]);
136
+ const connectToSSE = (0, import_react3.useCallback)((sseEndpoint) => {
137
+ if (eventSourceRef.current) {
138
+ eventSourceRef.current.close();
139
+ }
140
+ const fullUrl = sseEndpoint.startsWith("http") ? sseEndpoint : `${baseUrl}${sseEndpoint}`;
141
+ const eventSource = new EventSource(fullUrl);
142
+ eventSourceRef.current = eventSource;
143
+ eventSource.onopen = () => {
144
+ setIsConnected(true);
145
+ setError(null);
146
+ };
147
+ eventSource.addEventListener("connected", (event) => {
148
+ console.log("SSE connected:", event.data);
149
+ });
150
+ eventSource.addEventListener("message", (event) => {
151
+ try {
152
+ console.log("SSE message:", event.data);
153
+ const message = JSON.parse(event.data);
154
+ const chatMessage = {
155
+ ...message,
156
+ timestamp: message.timestamp ? new Date(message.timestamp) : /* @__PURE__ */ new Date()
157
+ };
158
+ setMessages((prev) => {
159
+ const exists = prev.some((m) => m.id === chatMessage.id);
160
+ return exists ? prev : [...prev, chatMessage];
161
+ });
162
+ options.onMessage?.(chatMessage);
163
+ } catch (err) {
164
+ console.warn("Failed to parse SSE message:", err, event.data);
165
+ }
166
+ });
167
+ eventSource.onmessage = (event) => {
168
+ try {
169
+ const data = JSON.parse(event.data);
170
+ if (data.type === "message" && data.message) {
171
+ const message = {
172
+ ...data.message,
173
+ timestamp: data.message.timestamp ? new Date(data.message.timestamp) : /* @__PURE__ */ new Date()
174
+ };
175
+ setMessages((prev) => {
176
+ const exists = prev.some((m) => m.id === message.id);
177
+ return exists ? prev : [...prev, message];
178
+ });
179
+ options.onMessage?.(message);
180
+ }
181
+ } catch (err) {
182
+ console.warn("Failed to parse default SSE message:", err);
183
+ }
184
+ };
185
+ eventSource.onerror = () => {
186
+ setIsConnected(false);
187
+ eventSource.close();
188
+ eventSourceRef.current = null;
189
+ const error2 = new Error("Connection lost");
190
+ setError(error2);
191
+ options.onError?.(error2);
192
+ };
193
+ }, [baseUrl, options.onError, options.onMessage]);
194
+ const startSession = (0, import_react3.useCallback)(async () => {
195
+ if (isConnecting || session) return;
196
+ setIsConnecting(true);
197
+ setError(null);
198
+ try {
199
+ const existingSessionId = options.sessionId || getSessionFromStorage();
200
+ if (existingSessionId) {
201
+ try {
202
+ console.log("\u{1F4E6} Resuming session from storage:", existingSessionId);
203
+ const existingSession = await client.getChatSession(existingSessionId);
204
+ const existingMessages = await client.getChatMessages(existingSessionId);
205
+ setSession(existingSession);
206
+ const normalizedMessages2 = existingMessages.map((msg) => ({
207
+ ...msg,
208
+ timestamp: msg.timestamp ? new Date(msg.timestamp) : /* @__PURE__ */ new Date()
209
+ }));
210
+ setMessages(normalizedMessages2);
211
+ const sseEndpoint = `/v1/chat/sessions/${existingSessionId}/events`;
212
+ connectToSSE(sseEndpoint);
213
+ console.log("\u2705 Session resumed successfully");
214
+ return;
215
+ } catch (err) {
216
+ console.warn("\u26A0\uFE0F Failed to resume session, creating new one:", err);
217
+ clearSessionFromStorage();
218
+ }
219
+ }
220
+ console.log("\u{1F195} Creating new chat session");
221
+ const response = await client.createChatSession(options.metadata);
222
+ setSession(response.session);
223
+ saveSessionToStorage(response.session.id);
224
+ const normalizedMessages = (response.session.messages || []).map((msg) => ({
225
+ ...msg,
226
+ timestamp: msg.timestamp ? new Date(msg.timestamp) : /* @__PURE__ */ new Date()
227
+ }));
228
+ setMessages(normalizedMessages);
229
+ if (response.sseEndpoint) {
230
+ connectToSSE(response.sseEndpoint);
231
+ }
232
+ console.log("\u2705 New session created:", response.session.id);
233
+ } catch (err) {
234
+ const error2 = err;
235
+ setError(error2);
236
+ options.onError?.(error2);
237
+ } finally {
238
+ setIsConnecting(false);
239
+ }
240
+ }, [client, connectToSSE, options.metadata, options.onError, options.sessionId, getSessionFromStorage, saveSessionToStorage, clearSessionFromStorage, isConnecting, session]);
241
+ const sendMessage = (0, import_react3.useCallback)(
242
+ async (content, attachments) => {
243
+ if (!sessionRef.current) {
244
+ throw new Error("No active chat session");
245
+ }
246
+ try {
247
+ const response = await client.sendMessage(
248
+ sessionRef.current.id,
249
+ content,
250
+ attachments
251
+ );
252
+ setMessages((prev) => {
253
+ const exists = prev.some((m) => m.id === response.message.id);
254
+ if (exists) return prev;
255
+ const message = {
256
+ ...response.message,
257
+ timestamp: response.message.timestamp ? new Date(response.message.timestamp) : /* @__PURE__ */ new Date()
258
+ };
259
+ return [...prev, message];
260
+ });
261
+ } catch (err) {
262
+ const error2 = err;
263
+ setError(error2);
264
+ options.onError?.(error2);
265
+ throw error2;
266
+ }
267
+ },
268
+ [client, options.onError]
269
+ );
270
+ const endSession = (0, import_react3.useCallback)(async () => {
271
+ if (!sessionRef.current) return;
272
+ try {
273
+ await client.closeChatSession(sessionRef.current.id);
274
+ if (eventSourceRef.current) {
275
+ eventSourceRef.current.close();
276
+ eventSourceRef.current = null;
277
+ }
278
+ clearSessionFromStorage();
279
+ setSession(null);
280
+ setMessages([]);
281
+ setIsConnected(false);
282
+ console.log("\u2705 Session ended and cleared from storage");
283
+ } catch (err) {
284
+ const error2 = err;
285
+ setError(error2);
286
+ options.onError?.(error2);
287
+ }
288
+ }, [client, options.onError, clearSessionFromStorage]);
289
+ (0, import_react3.useEffect)(() => {
290
+ if (options.autoConnect && !session && !isConnecting && !isInitializedRef.current) {
291
+ isInitializedRef.current = true;
292
+ startSession();
293
+ }
294
+ return () => {
295
+ if (eventSourceRef.current) {
296
+ eventSourceRef.current.close();
297
+ }
298
+ };
299
+ }, [options.autoConnect, session, isConnecting, startSession]);
300
+ return {
301
+ session,
302
+ messages,
303
+ isConnecting,
304
+ isConnected,
305
+ error,
306
+ sendMessage,
307
+ startSession,
308
+ endSession
309
+ };
310
+ }
311
+
312
+ // src/components/Form.tsx
313
+ var import_react4 = require("react");
314
+ var import_jsx_runtime2 = require("react/jsx-runtime");
315
+ function Form({
316
+ customFields = [],
317
+ onSuccess,
318
+ onError,
319
+ className = "",
320
+ styles = {},
321
+ submitText = "Send Message",
322
+ successMessage = "Thank you! Your message has been sent."
323
+ }) {
324
+ const [formData, setFormData] = (0, import_react4.useState)({
325
+ name: "",
326
+ email: "",
327
+ subject: "",
328
+ message: "",
329
+ customFields: {}
330
+ });
331
+ const { submit, isSubmitting, isSuccess, error, reset } = useContactForm({
332
+ onSuccess: () => {
333
+ setFormData({
334
+ name: "",
335
+ email: "",
336
+ subject: "",
337
+ message: "",
338
+ customFields: {}
339
+ });
340
+ onSuccess?.();
341
+ },
342
+ onError
343
+ });
344
+ const handleSubmit = async (e) => {
345
+ e.preventDefault();
346
+ if (!formData.name || !formData.email || !formData.message) {
347
+ return;
348
+ }
349
+ await submit(formData);
350
+ };
351
+ const handleInputChange = (field, value) => {
352
+ setFormData((prev) => ({ ...prev, [field]: value }));
353
+ };
354
+ const handleCustomFieldChange = (fieldName, value) => {
355
+ setFormData((prev) => ({
356
+ ...prev,
357
+ customFields: {
358
+ ...prev.customFields,
359
+ [fieldName]: value
360
+ }
361
+ }));
362
+ };
363
+ if (isSuccess) {
364
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `channlize-form-success ${className}`, style: styles.success, children: [
365
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: successMessage }),
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
367
+ "button",
368
+ {
369
+ type: "button",
370
+ onClick: reset,
371
+ style: styles.button,
372
+ children: "Send Another Message"
373
+ }
374
+ )
375
+ ] });
376
+ }
377
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
378
+ "form",
379
+ {
380
+ className: `channlize-form ${className}`,
381
+ style: styles.form,
382
+ onSubmit: handleSubmit,
383
+ children: [
384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "channlize-field", style: styles.field, children: [
385
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "channlize-name", style: styles.label, children: "Name *" }),
386
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
387
+ "input",
388
+ {
389
+ id: "channlize-name",
390
+ type: "text",
391
+ value: formData.name || "",
392
+ onChange: (e) => handleInputChange("name", e.target.value),
393
+ required: true,
394
+ style: styles.input
395
+ }
396
+ )
397
+ ] }),
398
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "channlize-field", style: styles.field, children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "channlize-email", style: styles.label, children: "Email *" }),
400
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
401
+ "input",
402
+ {
403
+ id: "channlize-email",
404
+ type: "email",
405
+ value: formData.email || "",
406
+ onChange: (e) => handleInputChange("email", e.target.value),
407
+ required: true,
408
+ style: styles.input
409
+ }
410
+ )
411
+ ] }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "channlize-field", style: styles.field, children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "channlize-subject", style: styles.label, children: "Subject" }),
414
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
415
+ "input",
416
+ {
417
+ id: "channlize-subject",
418
+ type: "text",
419
+ value: formData.subject || "",
420
+ onChange: (e) => handleInputChange("subject", e.target.value),
421
+ style: styles.input
422
+ }
423
+ )
424
+ ] }),
425
+ customFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "channlize-field", style: styles.field, children: [
426
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: `channlize-${field.name}`, style: styles.label, children: [
427
+ field.label,
428
+ " ",
429
+ field.required && "*"
430
+ ] }),
431
+ field.type === "select" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
432
+ "select",
433
+ {
434
+ id: `channlize-${field.name}`,
435
+ value: formData.customFields?.[field.name] || "",
436
+ onChange: (e) => handleCustomFieldChange(field.name, e.target.value),
437
+ required: field.required,
438
+ style: styles.input,
439
+ children: [
440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: "Select..." }),
441
+ field.options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: option, children: option }, option))
442
+ ]
443
+ }
444
+ ) : field.type === "textarea" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
445
+ "textarea",
446
+ {
447
+ id: `channlize-${field.name}`,
448
+ value: formData.customFields?.[field.name] || "",
449
+ onChange: (e) => handleCustomFieldChange(field.name, e.target.value),
450
+ required: field.required,
451
+ style: styles.textarea
452
+ }
453
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
454
+ "input",
455
+ {
456
+ id: `channlize-${field.name}`,
457
+ type: field.type,
458
+ value: formData.customFields?.[field.name] || "",
459
+ onChange: (e) => handleCustomFieldChange(field.name, e.target.value),
460
+ required: field.required,
461
+ style: styles.input
462
+ }
463
+ )
464
+ ] }, field.name)),
465
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "channlize-field", style: styles.field, children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "channlize-message", style: styles.label, children: "Message *" }),
467
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
468
+ "textarea",
469
+ {
470
+ id: "channlize-message",
471
+ value: formData.message || "",
472
+ onChange: (e) => handleInputChange("message", e.target.value),
473
+ required: true,
474
+ rows: 4,
475
+ style: styles.textarea
476
+ }
477
+ )
478
+ ] }),
479
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "channlize-error", style: styles.error, children: error.message }),
480
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
481
+ "button",
482
+ {
483
+ type: "submit",
484
+ disabled: isSubmitting,
485
+ style: styles.button,
486
+ children: isSubmitting ? "Sending..." : submitText
487
+ }
488
+ )
489
+ ]
490
+ }
491
+ );
492
+ }
493
+
494
+ // src/components/ContactForm.tsx
495
+ var import_jsx_runtime3 = require("react/jsx-runtime");
496
+ function ContactForm(props) {
497
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Form, { ...props });
498
+ }
499
+
500
+ // src/components/SupportChat.tsx
501
+ var import_react5 = require("react");
502
+ var import_jsx_runtime4 = require("react/jsx-runtime");
503
+ function SupportChat({
504
+ autoConnect = true,
505
+ metadata,
506
+ onMessage,
507
+ onError,
508
+ sessionId,
509
+ persistSession,
510
+ storageKey,
511
+ className = "",
512
+ styles = {},
513
+ placeholder = "Type your message...",
514
+ title = "Support Chat",
515
+ sendButtonText = "Send",
516
+ showTimestamps = true,
517
+ allowAttachments = false
518
+ }) {
519
+ const [messageInput, setMessageInput] = (0, import_react5.useState)("");
520
+ const [attachments, setAttachments] = (0, import_react5.useState)([]);
521
+ const messagesEndRef = (0, import_react5.useRef)(null);
522
+ const {
523
+ session,
524
+ messages,
525
+ isConnecting,
526
+ isConnected,
527
+ error,
528
+ sendMessage,
529
+ startSession,
530
+ endSession
531
+ } = useChatSession({
532
+ autoConnect,
533
+ metadata,
534
+ onMessage,
535
+ onError,
536
+ sessionId,
537
+ persistSession,
538
+ storageKey
539
+ });
540
+ (0, import_react5.useEffect)(() => {
541
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
542
+ }, [messages]);
543
+ const handleSubmit = async (e) => {
544
+ e.preventDefault();
545
+ if (!messageInput.trim() && attachments.length === 0) return;
546
+ try {
547
+ await sendMessage(messageInput, attachments.length > 0 ? attachments : void 0);
548
+ setMessageInput("");
549
+ setAttachments([]);
550
+ } catch (err) {
551
+ console.error("Failed to send message:", err);
552
+ }
553
+ };
554
+ const handleFileChange = (e) => {
555
+ if (e.target.files) {
556
+ setAttachments(Array.from(e.target.files));
557
+ }
558
+ };
559
+ const formatTimestamp = (date) => {
560
+ if (!date || isNaN(date.getTime())) {
561
+ return "";
562
+ }
563
+ try {
564
+ return new Intl.DateTimeFormat("en-US", {
565
+ hour: "numeric",
566
+ minute: "2-digit",
567
+ hour12: true
568
+ }).format(date);
569
+ } catch (error2) {
570
+ console.warn("Failed to format timestamp:", error2);
571
+ return "";
572
+ }
573
+ };
574
+ if (!session && !isConnecting) {
575
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `channlize-chat-container ${className}`, style: styles.container, children: [
576
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "channlize-chat-header", style: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: title }) }),
577
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "channlize-chat-start", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: startSession, style: styles.button, children: "Start Chat" }) }),
578
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "channlize-error", style: styles.error, children: error.message })
579
+ ] });
580
+ }
581
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `channlize-chat-container ${className}`, style: styles.container, children: [
582
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "channlize-chat-header", style: styles.header, children: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: title }),
584
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "channlize-chat-status", style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
585
+ isConnecting && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
586
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
587
+ display: "inline-block",
588
+ width: "8px",
589
+ height: "8px",
590
+ borderRadius: "50%",
591
+ backgroundColor: "#f59e0b"
592
+ } }),
593
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Connecting..." })
594
+ ] }),
595
+ isConnected && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
597
+ display: "inline-block",
598
+ width: "8px",
599
+ height: "8px",
600
+ borderRadius: "50%",
601
+ backgroundColor: "#10b981"
602
+ } }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Connected" })
604
+ ] }),
605
+ !isConnected && !isConnecting && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
606
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
607
+ display: "inline-block",
608
+ width: "8px",
609
+ height: "8px",
610
+ borderRadius: "50%",
611
+ backgroundColor: "#ef4444"
612
+ } }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Disconnected" })
614
+ ] })
615
+ ] }),
616
+ session && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
617
+ "button",
618
+ {
619
+ onClick: endSession,
620
+ style: { ...styles.button, fontSize: "0.875rem" },
621
+ type: "button",
622
+ children: "End Chat"
623
+ }
624
+ )
625
+ ] }),
626
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "channlize-messages", style: styles.messagesContainer, children: [
627
+ messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { textAlign: "center", padding: "2rem", color: "#666" }, children: "No messages yet. Start the conversation!" }),
628
+ messages.map((message, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
629
+ "div",
630
+ {
631
+ className: `channlize-message ${message.author === "user" ? "channlize-message-user" : "channlize-message-agent"}`,
632
+ style: {
633
+ ...styles.message,
634
+ ...message.author === "user" ? styles.userMessage : styles.agentMessage
635
+ },
636
+ children: [
637
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "channlize-message-content", children: message.content }),
638
+ showTimestamps && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "channlize-message-timestamp", style: styles.timestamp, children: formatTimestamp(message.timestamp) })
639
+ ]
640
+ },
641
+ message.id || index
642
+ )),
643
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: messagesEndRef })
644
+ ] }),
645
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "channlize-error", style: styles.error, children: error.message }),
646
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
647
+ "form",
648
+ {
649
+ className: "channlize-chat-input-container",
650
+ style: styles.inputContainer,
651
+ onSubmit: handleSubmit,
652
+ children: [
653
+ allowAttachments && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
655
+ "input",
656
+ {
657
+ type: "file",
658
+ multiple: true,
659
+ onChange: handleFileChange,
660
+ style: { fontSize: "0.875rem" }
661
+ }
662
+ ),
663
+ attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { fontSize: "0.75rem", marginTop: "0.25rem" }, children: [
664
+ attachments.length,
665
+ " file(s) selected"
666
+ ] })
667
+ ] }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "0.5rem" }, children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
670
+ "input",
671
+ {
672
+ type: "text",
673
+ value: messageInput,
674
+ onChange: (e) => setMessageInput(e.target.value),
675
+ placeholder,
676
+ disabled: !session || isConnecting,
677
+ style: { ...styles.input, flex: 1 }
678
+ }
679
+ ),
680
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
681
+ "button",
682
+ {
683
+ type: "submit",
684
+ disabled: !session || isConnecting || !messageInput.trim() && attachments.length === 0,
685
+ style: styles.button,
686
+ children: sendButtonText
687
+ }
688
+ )
689
+ ] })
690
+ ]
691
+ }
692
+ )
693
+ ] });
694
+ }
695
+
696
+ // src/components/ChannlizeChat.tsx
697
+ var import_react6 = require("react");
698
+ var import_jsx_runtime5 = require("react/jsx-runtime");
699
+ function ChannlizeChat({
700
+ config,
701
+ position = "bottom-right",
702
+ offset = { x: 24, y: 24 },
703
+ buttonElement,
704
+ buttonIcon,
705
+ buttonSize = 56,
706
+ buttonColors = {
707
+ background: "#3b82f6",
708
+ hover: "#2563eb",
709
+ text: "#ffffff"
710
+ },
711
+ chatProps = {},
712
+ defaultOpen = false,
713
+ showBadge = false,
714
+ unreadCount = 0,
715
+ zIndex = 1e3,
716
+ showOnMobile = true,
717
+ onOpen,
718
+ onClose,
719
+ enableAnimations = true,
720
+ buttonClassName = "",
721
+ chatClassName = ""
722
+ }) {
723
+ const [isOpen, setIsOpen] = (0, import_react6.useState)(defaultOpen);
724
+ const [isMounted, setIsMounted] = (0, import_react6.useState)(false);
725
+ const [isDarkMode, setIsDarkMode] = (0, import_react6.useState)(false);
726
+ (0, import_react6.useEffect)(() => {
727
+ setIsMounted(true);
728
+ }, []);
729
+ (0, import_react6.useEffect)(() => {
730
+ if (typeof window === "undefined") return;
731
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
732
+ setIsDarkMode(mediaQuery.matches);
733
+ const handler = (e) => setIsDarkMode(e.matches);
734
+ mediaQuery.addEventListener("change", handler);
735
+ return () => mediaQuery.removeEventListener("change", handler);
736
+ }, []);
737
+ if (!isMounted) {
738
+ return null;
739
+ }
740
+ const handleToggle = () => {
741
+ const newState = !isOpen;
742
+ setIsOpen(newState);
743
+ if (newState) {
744
+ onOpen?.();
745
+ } else {
746
+ onClose?.();
747
+ }
748
+ };
749
+ const positionStyles = {
750
+ position: "fixed",
751
+ zIndex,
752
+ ...position.includes("bottom") ? { bottom: `${offset.y}px` } : { top: `${offset.y}px` },
753
+ ...position.includes("right") ? { right: `${offset.x}px` } : { left: `${offset.x}px` }
754
+ };
755
+ const buttonStyle = {
756
+ width: `${buttonSize}px`,
757
+ height: `${buttonSize}px`,
758
+ borderRadius: "50%",
759
+ backgroundColor: buttonColors.background,
760
+ color: buttonColors.text,
761
+ border: "none",
762
+ cursor: "pointer",
763
+ display: "flex",
764
+ alignItems: "center",
765
+ justifyContent: "center",
766
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
767
+ transition: enableAnimations ? "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)" : "none",
768
+ transform: enableAnimations && isOpen ? "scale(0.9)" : "scale(1)"
769
+ };
770
+ const chatContainerStyle = {
771
+ ...positionStyles,
772
+ opacity: isOpen ? 1 : 0,
773
+ visibility: isOpen ? "visible" : "hidden",
774
+ transform: enableAnimations ? isOpen ? "scale(1) translateY(0)" : "scale(0.95) translateY(10px)" : "none",
775
+ transition: enableAnimations ? "opacity 0.2s, transform 0.2s, visibility 0.2s" : "none"
776
+ };
777
+ const defaultIcon = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
778
+ "svg",
779
+ {
780
+ width: buttonSize * 0.5,
781
+ height: buttonSize * 0.5,
782
+ viewBox: "0 0 24 24",
783
+ fill: "none",
784
+ stroke: "currentColor",
785
+ strokeWidth: "2",
786
+ strokeLinecap: "round",
787
+ strokeLinejoin: "round",
788
+ children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
789
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
791
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime5.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" }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M8 10h.01" }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 10h.01" }),
795
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M16 10h.01" })
796
+ ] })
797
+ }
798
+ );
799
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(ChannlizeProvider, { config, children: [
800
+ !isOpen && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
801
+ "div",
802
+ {
803
+ style: {
804
+ ...positionStyles,
805
+ display: !showOnMobile && typeof window !== "undefined" && window.innerWidth < 768 ? "none" : "block"
806
+ },
807
+ className: buttonClassName,
808
+ children: buttonElement ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { onClick: handleToggle, style: { cursor: "pointer" }, children: buttonElement }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
809
+ "button",
810
+ {
811
+ onClick: handleToggle,
812
+ style: buttonStyle,
813
+ onMouseEnter: (e) => {
814
+ if (enableAnimations) {
815
+ e.currentTarget.style.backgroundColor = buttonColors.hover || buttonColors.background || "#2563eb";
816
+ e.currentTarget.style.transform = "scale(1.05)";
817
+ }
818
+ },
819
+ onMouseLeave: (e) => {
820
+ if (enableAnimations) {
821
+ e.currentTarget.style.backgroundColor = buttonColors.background || "#3b82f6";
822
+ e.currentTarget.style.transform = "scale(1)";
823
+ }
824
+ },
825
+ "aria-label": isOpen ? "Close chat" : "Open chat",
826
+ children: [
827
+ buttonIcon || defaultIcon,
828
+ showBadge && unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
829
+ "span",
830
+ {
831
+ style: {
832
+ position: "absolute",
833
+ top: "-4px",
834
+ right: "-4px",
835
+ backgroundColor: "#ef4444",
836
+ color: "white",
837
+ borderRadius: "10px",
838
+ padding: "2px 6px",
839
+ fontSize: "12px",
840
+ fontWeight: "600",
841
+ minWidth: "20px",
842
+ textAlign: "center",
843
+ boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
844
+ },
845
+ children: unreadCount > 99 ? "99+" : unreadCount
846
+ }
847
+ )
848
+ ]
849
+ }
850
+ )
851
+ }
852
+ ),
853
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
854
+ "div",
855
+ {
856
+ style: chatContainerStyle,
857
+ className: chatClassName,
858
+ children: [
859
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
860
+ SupportChat,
861
+ {
862
+ autoConnect: isOpen,
863
+ persistSession: true,
864
+ title: "Support Chat",
865
+ placeholder: "Type your message...",
866
+ showTimestamps: true,
867
+ ...chatProps,
868
+ styles: {
869
+ container: {
870
+ width: "384px",
871
+ height: "600px",
872
+ maxHeight: "calc(100vh - 120px)",
873
+ borderRadius: "16px",
874
+ overflow: "hidden",
875
+ boxShadow: isDarkMode ? "0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.5)" : "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
876
+ backgroundColor: isDarkMode ? "#1f2937" : "#ffffff",
877
+ display: "flex",
878
+ flexDirection: "column",
879
+ position: "relative",
880
+ ...chatProps.styles?.container
881
+ },
882
+ header: {
883
+ background: "linear-gradient(135deg, #3b82f6 0%, #9333ea 100%)",
884
+ padding: "20px",
885
+ borderRadius: "16px 16px 0 0",
886
+ color: "white",
887
+ display: "flex",
888
+ alignItems: "center",
889
+ justifyContent: "space-between",
890
+ ...chatProps.styles?.header
891
+ },
892
+ messagesContainer: {
893
+ flex: 1,
894
+ overflowY: "auto",
895
+ padding: "20px",
896
+ backgroundColor: isDarkMode ? "#111827" : "#f9fafb",
897
+ ...chatProps.styles?.messagesContainer
898
+ },
899
+ inputContainer: {
900
+ padding: "16px",
901
+ backgroundColor: isDarkMode ? "#1f2937" : "white",
902
+ borderTop: `1px solid ${isDarkMode ? "#374151" : "#e5e7eb"}`,
903
+ ...chatProps.styles?.inputContainer
904
+ },
905
+ input: {
906
+ ...chatProps.styles?.input,
907
+ borderRadius: "12px",
908
+ padding: "12px 16px",
909
+ fontSize: "14px",
910
+ border: `1px solid ${isDarkMode ? "#374151" : "#d1d5db"}`,
911
+ outline: "none",
912
+ backgroundColor: isDarkMode ? "#374151" : "#ffffff",
913
+ color: isDarkMode ? "#f9fafb" : "#111827",
914
+ fontWeight: "400"
915
+ },
916
+ button: {
917
+ backgroundColor: "#3b82f6",
918
+ color: "white",
919
+ borderRadius: "12px",
920
+ padding: "12px 24px",
921
+ fontSize: "14px",
922
+ fontWeight: "500",
923
+ border: "none",
924
+ cursor: "pointer",
925
+ transition: "background-color 0.2s",
926
+ ...chatProps.styles?.button
927
+ },
928
+ userMessage: {
929
+ backgroundColor: "#3b82f6",
930
+ color: "white",
931
+ padding: "10px 14px",
932
+ borderRadius: "12px 12px 4px 12px",
933
+ marginBottom: "8px",
934
+ marginLeft: "auto",
935
+ maxWidth: "80%",
936
+ width: "fit-content",
937
+ ...chatProps.styles?.userMessage
938
+ },
939
+ agentMessage: {
940
+ backgroundColor: isDarkMode ? "#374151" : "white",
941
+ color: isDarkMode ? "#f9fafb" : "#1f2937",
942
+ padding: "10px 14px",
943
+ borderRadius: "12px 12px 12px 4px",
944
+ marginBottom: "8px",
945
+ marginRight: "auto",
946
+ maxWidth: "80%",
947
+ width: "fit-content",
948
+ boxShadow: isDarkMode ? "0 1px 2px 0 rgb(0 0 0 / 0.3)" : "0 1px 2px 0 rgb(0 0 0 / 0.05)",
949
+ ...chatProps.styles?.agentMessage
950
+ },
951
+ timestamp: {
952
+ fontSize: "11px",
953
+ opacity: 0.7,
954
+ marginTop: "4px",
955
+ color: isDarkMode ? "#d1d5db" : "inherit",
956
+ ...chatProps.styles?.timestamp
957
+ },
958
+ error: {
959
+ backgroundColor: isDarkMode ? "#7f1d1d" : "#fee2e2",
960
+ color: isDarkMode ? "#fca5a5" : "#991b1b",
961
+ padding: "12px",
962
+ margin: "8px 16px",
963
+ borderRadius: "8px",
964
+ fontSize: "14px",
965
+ ...chatProps.styles?.error
966
+ }
967
+ }
968
+ }
969
+ ),
970
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
971
+ "button",
972
+ {
973
+ onClick: handleToggle,
974
+ style: {
975
+ position: "absolute",
976
+ top: "16px",
977
+ right: "16px",
978
+ width: "32px",
979
+ height: "32px",
980
+ borderRadius: "50%",
981
+ backgroundColor: "rgba(255, 255, 255, 0.2)",
982
+ border: "none",
983
+ cursor: "pointer",
984
+ display: "flex",
985
+ alignItems: "center",
986
+ justifyContent: "center",
987
+ color: "white",
988
+ transition: "background-color 0.2s",
989
+ zIndex: 10
990
+ },
991
+ onMouseEnter: (e) => {
992
+ e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.3)";
993
+ },
994
+ onMouseLeave: (e) => {
995
+ e.currentTarget.style.backgroundColor = "rgba(255, 255, 255, 0.2)";
996
+ },
997
+ "aria-label": "Close chat",
998
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
999
+ "svg",
1000
+ {
1001
+ width: "16",
1002
+ height: "16",
1003
+ viewBox: "0 0 24 24",
1004
+ fill: "none",
1005
+ stroke: "currentColor",
1006
+ strokeWidth: "2",
1007
+ strokeLinecap: "round",
1008
+ strokeLinejoin: "round",
1009
+ children: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1011
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1012
+ ]
1013
+ }
1014
+ )
1015
+ }
1016
+ )
1017
+ ]
1018
+ }
1019
+ )
1020
+ ] });
1021
+ }
1022
+ // Annotate the CommonJS export names for ESM import in node:
1023
+ 0 && (module.exports = {
1024
+ ChannlizeChat,
1025
+ ChannlizeProvider,
1026
+ ContactForm,
1027
+ SupportChat,
1028
+ useChannlize,
1029
+ useChatSession,
1030
+ useContactForm
1031
+ });
1032
+ //# sourceMappingURL=index.js.map