lulichat 1.0.2 → 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.
@@ -1,45 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
-
3
- const useSocket = (url: string) => {
4
- const socketRef = useRef<WebSocket | null>(null);
5
-
6
- const connect = () => {
7
- socketRef.current = new WebSocket(url);
8
-
9
- socketRef.current.onopen = () => {
10
- console.log('WebSocket connected');
11
- };
12
-
13
- socketRef.current.onclose = () => {
14
- console.log('WebSocket disconnected');
15
- };
16
-
17
- socketRef.current.onerror = (error) => {
18
- console.error('WebSocket error:', error);
19
- };
20
- };
21
-
22
- const disconnect = () => {
23
- if (socketRef.current) {
24
- socketRef.current.close();
25
- }
26
- };
27
-
28
- const sendMessage = (message: string) => {
29
- if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
30
- socketRef.current.send(message);
31
- }
32
- };
33
-
34
- useEffect(() => {
35
- connect();
36
-
37
- return () => {
38
- disconnect();
39
- };
40
- }, [url]);
41
-
42
- return { sendMessage };
43
- };
44
-
45
- export default useSocket;
package/src/index.css DELETED
@@ -1,383 +0,0 @@
1
- @import url("https://fonts.googleapis.com/css2?family=Inclusive+Sans:ital@0;1&family=Outfit:wght@100..900&display=swap");
2
-
3
- :root {
4
- --background: 0 0% 100%;
5
- --foreground: 222.2 84% 4.9%;
6
-
7
- --card: 0 0% 100%;
8
- --card-foreground: 222.2 84% 4.9%;
9
-
10
- --popover: 0 0% 100%;
11
- --popover-foreground: 222.2 84% 4.9%;
12
-
13
- --primary-color: #755ae2;
14
- --primary: 251.91deg 70.1% 61.96%;
15
- --primary-foreground: 210 40% 98%;
16
-
17
- --secondary: 210 40% 96.1%;
18
- --secondary-foreground: 222.2 47.4% 11.2%;
19
-
20
- --muted: 210 40% 96.1%;
21
- --muted-foreground: 215.4 16.3% 46.9%;
22
-
23
- --accent: 210 40% 96.1%;
24
- --accent-foreground: 222.2 47.4% 11.2%;
25
-
26
- --destructive: 0 84.2% 60.2%;
27
- --destructive-foreground: 210 40% 98%;
28
-
29
- --border: 214.3 31.8% 91.4%;
30
- --input: 214.3 31.8% 91.4%;
31
- --ring: 222.2 84% 4.9%;
32
-
33
- --radius: 0.5rem;
34
- }
35
-
36
- .dark {
37
- --background: 222.2 84% 4.9%;
38
- --foreground: 210 40% 98%;
39
-
40
- --card: 222.2 84% 4.9%;
41
- --card-foreground: 210 40% 98%;
42
-
43
- --popover: 222.2 84% 4.9%;
44
- --popover-foreground: 210 40% 98%;
45
-
46
- --primary: 210 40% 98%;
47
- --primary-foreground: 222.2 47.4% 11.2%;
48
-
49
- --secondary: 217.2 32.6% 17.5%;
50
- --secondary-foreground: 210 40% 98%;
51
-
52
- --muted: 217.2 32.6% 17.5%;
53
- --muted-foreground: 215 20.2% 65.1%;
54
-
55
- --accent: 217.2 32.6% 17.5%;
56
- --accent-foreground: 210 40% 98%;
57
-
58
- --destructive: 0 62.8% 30.6%;
59
- --destructive-foreground: 210 40% 98%;
60
-
61
- --border: 217.2 32.6% 17.5%;
62
- --input: 217.2 32.6% 17.5%;
63
- --ring: 212.7 26.8% 83.9%;
64
- }
65
-
66
- .lulichat,
67
- .lulichat * {
68
- box-sizing: border-box;
69
- margin: 0;
70
- padding: 0;
71
- border: none;
72
- outline: none;
73
- }
74
-
75
- .lulichat button {
76
- appearance: none;
77
- -webkit-appearance: none;
78
- outline: none;
79
- }
80
-
81
- .lulichat {
82
- position: fixed;
83
- z-index: 50;
84
- display: flex;
85
- flex-direction: column;
86
- row-gap: 10px;
87
- font-family: "Outfit", sans-serif;
88
- font-optical-sizing: auto;
89
- font-weight: 300;
90
- max-width: 400px;
91
- }
92
-
93
- .lulichat-main {
94
- background-color: var(--background);
95
- color: var(--foreground);
96
- border-radius: 12px;
97
- border: 4px solid hsl(var(--primary));
98
- box-shadow: 4px 4px 20px 4px rgba(0, 0, 0, 0.1);
99
- display: flex;
100
- flex-direction: column;
101
- }
102
-
103
- .lulichat-bottom-right {
104
- bottom: 1rem;
105
- right: 1rem;
106
- }
107
-
108
- .lulichat-bottom-left {
109
- bottom: 1rem;
110
- left: 1rem;
111
- }
112
-
113
- .lulichat-top-right {
114
- top: 1rem;
115
- right: 1rem;
116
- }
117
-
118
- .lulichat-container-top-left {
119
- top: 1rem;
120
- left: 1rem;
121
- }
122
-
123
- /* This is Button Styles */
124
- .lulichat-btn {
125
- display: inline-flex;
126
- align-items: center;
127
- justify-content: center;
128
- gap: 0.5rem;
129
- white-space: nowrap;
130
- font-size: 1rem;
131
- font-weight: 500;
132
- transition: background-color 0.2s, color 0.2s;
133
- outline: none;
134
- cursor: pointer;
135
- /* ring-offset: 2px; */
136
- }
137
-
138
- .lulichat-btn.lulichat-contact-form-btn {
139
- background-color: #fff;
140
- color: hsl(var(--primary));
141
- }
142
-
143
- .lulichat-btn-group {
144
- display: flex;
145
- gap: 0.4rem;
146
- }
147
-
148
- .lulichat-btn:disabled {
149
- cursor: not-allowed;
150
- opacity: 0.5;
151
- }
152
-
153
- /* Btn Shape */
154
- .lulichat-btn-rounded {
155
- border-radius: 1.5rem;
156
- }
157
-
158
- .lulichat-btn-none {
159
- border-radius: 0rem;
160
- }
161
-
162
- .lulichat-btn-circle {
163
- border-radius: 50%;
164
- }
165
-
166
- .lulichat-btn:focus-visible {
167
- outline: 2px solid var(--ring);
168
- outline-offset: 2px;
169
- }
170
-
171
- .lulichat-btn:disabled {
172
- pointer-events: none;
173
- opacity: 0.5;
174
- }
175
-
176
- /* .lulichat-btn svg {
177
- pointer-events: none;
178
- width: 1rem;
179
- height: 1rem;
180
- flex-shrink: 0;
181
- } */
182
-
183
- /* Variants */
184
-
185
- .lulichat-btn-primary {
186
- background-color: hsl(var(--primary));
187
- color: #fff;
188
- }
189
-
190
- .lulichat-btn-primary:hover:not(:disabled) {
191
- background-color: #000;
192
- }
193
-
194
- .lulichat-btn-default {
195
- background: hsl(var(--primary));
196
- color: hsl(var(--primary-foreground));
197
- }
198
-
199
- .lulichat-btn-default:hover {
200
- background: rgba(var(--primary), 0.9);
201
- }
202
-
203
- .lulichat-btn-destructive {
204
- background: hsl(var(--destructive));
205
- color: hsl(var(--destructive-foreground));
206
- }
207
-
208
- .lulichat-btn-destructive:hover {
209
- background: rgba(var(--destructive), 0.9);
210
- }
211
-
212
- .lulichat-btn-outline {
213
- border: 1px solid hsl(var(--border));
214
- background: hsl(var(--background));
215
- color: inherit;
216
- }
217
- .lulichat-btn-outline:hover {
218
- background: hsl(var(--accent));
219
- color: hsl(var(--accent-foreground));
220
- }
221
-
222
- .lulichat-btn-secondary {
223
- background: hsl(var(--secondary));
224
- color: hsl(var(--secondary-foreground));
225
- }
226
- .lulichat-btn-secondary:hover {
227
- background: hsl(var(--accent));
228
- }
229
-
230
- .lulichat-btn-ghost {
231
- }
232
-
233
- .lulichat-btn-ghost:hover {
234
- background: hsl(var(--accent));
235
- color: hsl(var(--accent-foreground));
236
- }
237
-
238
- .lulichat-btn-link {
239
- color: hsl(var(--primary));
240
- text-underline-offset: 4px;
241
- }
242
- .lulichat-btn-link:hover {
243
- text-decoration: underline;
244
- }
245
-
246
- /* Sizes */
247
- .lulichat-btn-md {
248
- height: 2.5rem;
249
- padding: 0.5rem 1rem;
250
- }
251
- .lulichat-btn-sm {
252
- height: 2.25rem;
253
- padding: 0 0.75rem;
254
- }
255
- .lulichat-btn-lg {
256
- height: 2.75rem;
257
- padding: 0 2rem;
258
- }
259
-
260
- .lulichat-btn-icon {
261
- height: 2.5rem;
262
- width: 2.5rem;
263
- }
264
-
265
- /* End of Button Styles */
266
-
267
- /* Form Styles */
268
-
269
- .lulichat-contact-form {
270
- background-color: hsl(var(--primary));
271
- padding: 16px;
272
- color: #fff;
273
- }
274
-
275
- .lulichat-form-group {
276
- max-height: 0;
277
- opacity: 0;
278
- overflow: hidden;
279
- transition: max-height 0.7s cubic-bezier(0.22, 1, 0.36, 1),
280
- opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1);
281
- }
282
-
283
- .lulichat-form-group.open {
284
- opacity: 1;
285
- max-height: 500px; /* Set to a value larger than your content's height */
286
- }
287
-
288
- .lulichat-form-header {
289
- margin-bottom: 1.5rem;
290
- }
291
-
292
- .lulichat-form-item {
293
- margin-bottom: 1rem;
294
- display: block;
295
- flex-direction: column;
296
- gap: 0.5rem;
297
- }
298
-
299
- .lulichat-form-label {
300
- font-size: 1rem;
301
- font-weight: 500;
302
- display: block;
303
- margin-bottom: 0.25rem;
304
- }
305
-
306
- .lulichat-form-item input,
307
- .lulichat-form-item textarea,
308
- .lulichat-input {
309
- padding: 0.7rem 0.75rem;
310
- border: 1px solid #d1d5db;
311
- border-radius: 0.5rem;
312
- font-size: 1rem;
313
- width: 100%;
314
- transition: border-color 0.2s;
315
- transition: background-color 500ms;
316
- background-color: #fff;
317
- color: #222;
318
- }
319
-
320
- input:-internal-autofill-selected,
321
- textarea:-webkit-autofill {
322
- appearance: none;
323
- background-color: rgba(0, 0, 0, 0.2) !important;
324
- color: fieldtext !important;
325
- }
326
-
327
- .lulichat-form-item input.transparent {
328
- background-color: rgba(0, 0, 0, 0.2) !important;
329
- color: #fff;
330
- }
331
-
332
- .lulichat-form-item input.error {
333
- border-color: red;
334
- }
335
-
336
- .lulichat-form-item input.transparent:hover,
337
- .lulichat-form-item input.transparent:focus {
338
- background-color: rgba(0, 0, 0, 0.4) !important;
339
- }
340
-
341
- .lulichat-form-item input.transparent::placeholder {
342
- color: #fff;
343
- }
344
-
345
- .lulichat-form-item input:focus,
346
- .lulichat-form-item textarea:focus {
347
- border-color: #ceced2;
348
- outline: none;
349
- }
350
-
351
- .lulichat-form-item input:focus::placeholder,
352
- .lulichat-form-item textarea:focus::placeholder {
353
- opacity: 0.5;
354
- }
355
-
356
- .lulichat-form-item input[aria-invalid="true"],
357
- .lulichat-form-item textarea[aria-invalid="true"] {
358
- border-color: #ef4444;
359
- }
360
-
361
- .lulichat-form-item .error {
362
- color: #fd8989;
363
- font-size: 0.875rem;
364
- margin-top: 0.25rem;
365
- font-weight: 400;
366
- }
367
-
368
- .lulichat-tag {
369
- /* border: 1px solid hsl(var(--primary)); */
370
- padding: 5px 8px;
371
- font-weight: 500;
372
- border-radius: 24px;
373
- font-size: 0.75rem;
374
- text-transform: capitalize !important;
375
- background-color: rgb(231, 230, 230);
376
- cursor: pointer;
377
- transition: background-color 400ms, color 200ms;
378
- }
379
-
380
- .lulichat-tag:hover {
381
- background-color: var(--primary-color);
382
- color: #fff;
383
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export { LuliChat } from "./components/LuliChat";
2
- export * from "./types";
3
- export { LuliChatAPI } from "./utils/api";
4
- export { ChatSocket } from "./lib/socket";
5
- import "./index.css";
package/src/lib/socket.ts DELETED
@@ -1,96 +0,0 @@
1
- import { ChatMessage } from "../types";
2
-
3
- export class ChatSocket {
4
- private socket: WebSocket | null = null;
5
- private token: string;
6
- private sessionId: string;
7
- private onMessageCallback?: (message: ChatMessage) => void;
8
- private onStatusCallback?: (
9
- status: "connected" | "disconnected" | "error"
10
- ) => void;
11
- private reconnectAttempts = 0;
12
- private maxReconnectAttempts = 5;
13
-
14
- constructor(
15
- token: string,
16
- sessionId: string,
17
- baseUrl: string = "wss://ws.lulichat.com"
18
- ) {
19
- this.token = token;
20
- this.sessionId = sessionId;
21
- this.connect(baseUrl);
22
- }
23
-
24
- private connect(baseUrl: string) {
25
- try {
26
- this.socket = new WebSocket(
27
- `${baseUrl}/chat?token=${this.token}&session=${this.sessionId}`
28
- );
29
-
30
- this.socket.onopen = () => {
31
- console.log("Socket connected");
32
- this.reconnectAttempts = 0;
33
- this.onStatusCallback?.("connected");
34
- };
35
-
36
- this.socket.onmessage = (event) => {
37
- try {
38
- const message: ChatMessage = JSON.parse(event.data);
39
- this.onMessageCallback?.(message);
40
- } catch (error) {
41
- console.error("Failed to parse message:", error);
42
- }
43
- };
44
-
45
- this.socket.onclose = () => {
46
- console.log("Socket disconnected");
47
- this.onStatusCallback?.("disconnected");
48
- this.handleReconnect(baseUrl);
49
- };
50
-
51
- this.socket.onerror = (error) => {
52
- console.error("Socket error:", error);
53
- this.onStatusCallback?.("error");
54
- };
55
- } catch (error) {
56
- console.error("Failed to connect socket:", error);
57
- this.onStatusCallback?.("error");
58
- }
59
- }
60
-
61
- private handleReconnect(baseUrl: string) {
62
- if (this.reconnectAttempts < this.maxReconnectAttempts) {
63
- this.reconnectAttempts++;
64
- setTimeout(() => {
65
- console.log(`Reconnecting... attempt ${this.reconnectAttempts}`);
66
- this.connect(baseUrl);
67
- }, 2000 * this.reconnectAttempts);
68
- }
69
- }
70
-
71
- sendMessage(content: string) {
72
- if (this.socket?.readyState === WebSocket.OPEN) {
73
- const message = {
74
- type: "message",
75
- content,
76
- timestamp: new Date().toISOString(),
77
- };
78
- this.socket.send(JSON.stringify(message));
79
- }
80
- }
81
-
82
- onMessage(callback: (message: ChatMessage) => void) {
83
- this.onMessageCallback = callback;
84
- }
85
-
86
- onStatus(callback: (status: "connected" | "disconnected" | "error") => void) {
87
- this.onStatusCallback = callback;
88
- }
89
-
90
- disconnect() {
91
- if (this.socket) {
92
- this.socket.close();
93
- this.socket = null;
94
- }
95
- }
96
- }
package/src/main.tsx DELETED
@@ -1,15 +0,0 @@
1
- import { createRoot } from "react-dom/client";
2
- import "./index.css";
3
- import LuliChat from "lulichat";
4
-
5
- createRoot(document.getElementById("root")!).render(
6
- <LuliChat
7
- apiKey="LUa2pfSV9CPcyjDM6r3ca8m3Fv3TXxZU"
8
- companyName="LuliChat Demo"
9
- welcomeMessage="Welcome to LuliChat! How can we help you today?"
10
- primaryColor="#4f46e5"
11
- allowAnonymous={true}
12
- requireContactInfo={true}
13
- position="bottom-right"
14
- />
15
- );
@@ -1,93 +0,0 @@
1
- export interface LuliChatConfig {
2
- apiKey: string;
3
- baseUrl?: string;
4
- position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
5
- primaryColor?: string;
6
- companyName?: string;
7
- welcomeMessage?: string;
8
- requireContactInfo?: boolean;
9
- allowAnonymous?: boolean;
10
- }
11
-
12
- export interface ContactInfo {
13
- name: string;
14
- email: string;
15
- phone?: string;
16
- company?: string;
17
- }
18
-
19
- export interface ChatMessage {
20
- id: string;
21
- content: string;
22
- timestamp: Date;
23
- sender: "user" | "agent" | "system";
24
- senderName?: string;
25
- }
26
-
27
- export interface ChatSession {
28
- id: string;
29
- token: string;
30
- isActive: boolean;
31
- agentInfo?: {
32
- name: string;
33
- avatar?: string;
34
- status: "online" | "away" | "busy";
35
- };
36
- }
37
-
38
- export interface CompanyInfo {
39
- status: string;
40
- success: boolean;
41
- data: Data;
42
- }
43
-
44
- interface Data {
45
- id: number;
46
- name: string;
47
- email: null;
48
- website: null;
49
- description: null;
50
- settings: Settings;
51
- plan: null;
52
- queues: Queue[];
53
- }
54
-
55
- interface Queue {
56
- id: number;
57
- name: string;
58
- color: string;
59
- greetingMessage: string;
60
- outOfHoursMessage: string;
61
- schedules: Schedule[];
62
- companyId: number;
63
- orderQueue: null | number;
64
- leadId: null | number;
65
- mediaId: null;
66
- createdAt: string;
67
- updatedAt: string;
68
- }
69
-
70
- interface Schedule {
71
- endTime: string;
72
- startTime: string;
73
- weekdayEn: string;
74
- weekday?: string;
75
- }
76
-
77
- interface Settings {
78
- id: number;
79
- chatBotType: string;
80
- sendGreetingAccepted: string;
81
- sendMsgTransfTicket: string;
82
- sendGreetingMessageOneQueues: string;
83
- userRating: string;
84
- scheduleType: string;
85
- CheckMsgIsGroup: string;
86
- call: string;
87
- companyId: number;
88
- type: string;
89
- createdAt: string;
90
- updatedAt: string;
91
- }
92
-
93
- export type ChatState = "closed" | "contact-form" | "chat" | "loading";
package/src/utils/api.ts DELETED
@@ -1,59 +0,0 @@
1
- import { LuliChatConfig, ContactInfo, CompanyInfo } from "../types";
2
-
3
- export class LuliChatAPI {
4
- private config: LuliChatConfig;
5
- private baseUrl: string;
6
-
7
- constructor(config: LuliChatConfig) {
8
- this.config = config;
9
- this.baseUrl = config.baseUrl || "http://localhost:8000";
10
- }
11
-
12
- async getCompanyInfo(): Promise<CompanyInfo> {
13
- try {
14
- const response = await fetch(`${this.baseUrl}/companies/e/info`, {
15
- headers: {
16
- Authorization: `Bearer ${this.config.apiKey}`,
17
- "Content-Type": "application/json",
18
- },
19
- });
20
-
21
- if (!response.ok) {
22
- throw new Error(`HTTP error! status: ${response.status}`);
23
- }
24
-
25
- return await response.json();
26
- } catch (error) {
27
- console.error("Failed to fetch company info:", error);
28
- throw error;
29
- }
30
- }
31
-
32
- async submitContactInfo(
33
- contactInfo?: ContactInfo
34
- ): Promise<{ token: string; sessionId: string }> {
35
- try {
36
- const response = await fetch(`${this.baseUrl}/companies/e/chat/init`, {
37
- method: "POST",
38
- headers: {
39
- Authorization: `Bearer ${this.config.apiKey}`,
40
- "Content-Type": "application/json",
41
- },
42
- body: JSON.stringify(contactInfo || {}),
43
- });
44
-
45
- if (!response.ok) {
46
- throw new Error(`HTTP error! status: ${response.status}`);
47
- }
48
-
49
- return await response.json();
50
- } catch (error) {
51
- console.error("Failed to submit contact info:", error);
52
- throw error;
53
- }
54
- }
55
-
56
- async startAnonymousChat(): Promise<{ token: string; sessionId: string }> {
57
- return this.submitContactInfo();
58
- }
59
- }
@@ -1,5 +0,0 @@
1
- import { clsx, type ClassValue } from "clsx";
2
-
3
- export function cn(...inputs: ClassValue[]) {
4
- return clsx(inputs);
5
- }
package/src/vite-env.d.ts DELETED
@@ -1 +0,0 @@
1
- /// <reference types="vite/client" />