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