@xcelsior/ui-chat 1.0.5 → 1.0.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - updated version
8
+ - 97bcd49: initial version
9
+
10
+ ## 1.0.6
11
+
12
+ ### Patch Changes
13
+
14
+ - improve minimize
15
+
3
16
  ## 1.0.2
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -62,6 +62,88 @@ function App() {
62
62
  }
63
63
  ```
64
64
 
65
+ ### Chat Widget States (Bubble Mode)
66
+
67
+ The chat widget supports three states in popover mode:
68
+
69
+ - **`open`**: Fully open with chat interface visible
70
+ - **`minimized`**: Show bubble button only (default)
71
+ - **`closed`**: Fully hidden, no bubble visible
72
+
73
+ #### Uncontrolled Mode (Default)
74
+
75
+ By default, the widget starts in `minimized` state and manages its own state:
76
+
77
+ ```tsx
78
+ import { ChatWidget } from '@xcelsior/ui-chat';
79
+
80
+ function App() {
81
+ return (
82
+ <ChatWidget
83
+ config={chatConfig}
84
+ // Starts minimized by default
85
+ />
86
+ );
87
+ }
88
+ ```
89
+
90
+ You can also set a different default state:
91
+
92
+ ```tsx
93
+ <ChatWidget
94
+ config={chatConfig}
95
+ defaultState="open" // or "minimized" or "closed"
96
+ />
97
+ ```
98
+
99
+ #### Controlled Mode
100
+
101
+ Control the widget state externally:
102
+
103
+ ```tsx
104
+ import { ChatWidget, ChatWidgetState } from '@xcelsior/ui-chat';
105
+ import { useState } from 'react';
106
+
107
+ function App() {
108
+ const [chatState, setChatState] = useState<ChatWidgetState>('minimized');
109
+
110
+ return (
111
+ <>
112
+ <button onClick={() => setChatState('open')}>
113
+ Open Chat
114
+ </button>
115
+ <button onClick={() => setChatState('minimized')}>
116
+ Minimize Chat
117
+ </button>
118
+ <button onClick={() => setChatState('closed')}>
119
+ Close Chat
120
+ </button>
121
+
122
+ <ChatWidget
123
+ config={chatConfig}
124
+ state={chatState}
125
+ onStateChange={setChatState}
126
+ />
127
+ </>
128
+ );
129
+ }
130
+ ```
131
+
132
+ #### Listen to State Changes (Semi-controlled)
133
+
134
+ You can listen to state changes while keeping the widget uncontrolled:
135
+
136
+ ```tsx
137
+ <ChatWidget
138
+ config={chatConfig}
139
+ defaultState="minimized"
140
+ onStateChange={(state) => {
141
+ console.log('Chat state changed to:', state);
142
+ // Save to localStorage, analytics, etc.
143
+ }}
144
+ />
145
+ ```
146
+
65
147
  ### With File Upload
66
148
 
67
149
  The file upload feature uses a presigned URL approach for secure, direct-to-S3 uploads:
package/dist/index.d.mts CHANGED
@@ -125,8 +125,52 @@ interface ChatWidgetProps {
125
125
  * External WebSocket connection (for agents with global connection)
126
126
  */
127
127
  externalWebSocket?: WebSocket | null;
128
+ /**
129
+ * Callback when user wants to minimize the widget
130
+ */
131
+ onMinimize?: () => void;
132
+ /**
133
+ * Callback when user wants to close the widget
134
+ */
135
+ onClose?: () => void;
128
136
  }
129
- declare function ChatWidget({ config, className, variant, externalWebSocket, }: ChatWidgetProps): react_jsx_runtime.JSX.Element | null;
137
+ declare function ChatWidget({ config, className, variant, externalWebSocket, onMinimize, onClose, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
138
+
139
+ type ChatWidgetState = 'open' | 'minimized' | 'closed' | 'undefined';
140
+ interface UseChatWidgetStateOptions {
141
+ /**
142
+ * Controlled state. When provided, the component is controlled.
143
+ */
144
+ state?: ChatWidgetState;
145
+ /**
146
+ * Default state for uncontrolled mode
147
+ * @default 'minimized'
148
+ */
149
+ defaultState?: ChatWidgetState;
150
+ /**
151
+ * Callback when state changes
152
+ */
153
+ onStateChange?: (state: ChatWidgetState) => void;
154
+ }
155
+ interface UseChatWidgetStateReturn {
156
+ /**
157
+ * Current state of the widget
158
+ */
159
+ currentState: ChatWidgetState;
160
+ /**
161
+ * Function to update the state
162
+ */
163
+ setState: (newState: ChatWidgetState) => void;
164
+ /**
165
+ * Whether the state is controlled
166
+ */
167
+ isControlled: boolean;
168
+ }
169
+ /**
170
+ * Hook to manage chat widget state (controlled vs uncontrolled)
171
+ * Encapsulates the logic for handling both controlled and uncontrolled state patterns
172
+ */
173
+ declare function useChatWidgetState({ state: controlledState, defaultState, onStateChange, }: UseChatWidgetStateOptions): UseChatWidgetStateReturn;
130
174
 
131
175
  interface ChatWidgetWrapperProps {
132
176
  /**
@@ -149,6 +193,22 @@ interface ChatWidgetWrapperProps {
149
193
  * Callback when user submits the pre-chat form
150
194
  */
151
195
  onPreChatSubmit?: (user: IUser) => void;
196
+ /**
197
+ * Controlled state. When provided, the component is controlled.
198
+ * - 'open': Fully open with chat interface
199
+ * - 'minimized': Show bubble button only
200
+ * - 'closed': Fully hidden
201
+ */
202
+ state?: ChatWidgetState;
203
+ /**
204
+ * Default state for uncontrolled mode
205
+ * @default 'minimized'
206
+ */
207
+ defaultState?: ChatWidgetState;
208
+ /**
209
+ * Callback when state changes
210
+ */
211
+ onStateChange?: (state: ChatWidgetState) => void;
152
212
  }
153
213
  /**
154
214
  * Chat component that handles:
@@ -156,7 +216,7 @@ interface ChatWidgetWrapperProps {
156
216
  * - Pre-chat form for collecting user information
157
217
  * - Session persistence in localStorage
158
218
  */
159
- declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
219
+ declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, state, defaultState, onStateChange, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
160
220
 
161
221
  interface ChatHeaderProps {
162
222
  agent?: IUser;
@@ -215,11 +275,19 @@ interface PreChatFormProps {
215
275
  className?: string;
216
276
  initialName?: string;
217
277
  initialEmail?: string;
278
+ /**
279
+ * Callback when user wants to minimize the form
280
+ */
281
+ onMinimize: () => void;
282
+ /**
283
+ * Callback when user wants to close the form
284
+ */
285
+ onClose: () => void;
218
286
  }
219
287
  /**
220
288
  * PreChatForm component for collecting user information before starting chat
221
289
  */
222
- declare function PreChatForm({ onSubmit, className, initialName, initialEmail, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
290
+ declare function PreChatForm({ onSubmit, className, initialName, initialEmail, onMinimize, onClose, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
223
291
 
224
292
  interface UseWebSocketReturn {
225
293
  isConnected: boolean;
@@ -266,4 +334,4 @@ declare function fetchMessages(baseUrl: string, params: FetchMessagesParams, hea
266
334
  nextPageToken: string | undefined;
267
335
  }>;
268
336
 
269
- export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, type ConversationChannel, type ConversationPriority, type ConversationStatus, type IApiResponse, type IChatConfig, type IConversation, type IFileUploadConfig, type IMessage, type IReadMessageData, type ISendMessageData, type ITypingData, type IUploadedFile, type IUser, type IWebSocketMessage, MessageItem, MessageList, type MessageStatus, type MessageType, PreChatForm, TypingIndicator, fetchMessages, useFileUpload, useMessages, useTypingIndicator, useWebSocket };
337
+ export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, type ChatWidgetState, type ConversationChannel, type ConversationPriority, type ConversationStatus, type IApiResponse, type IChatConfig, type IConversation, type IFileUploadConfig, type IMessage, type IReadMessageData, type ISendMessageData, type ITypingData, type IUploadedFile, type IUser, type IWebSocketMessage, MessageItem, MessageList, type MessageStatus, type MessageType, PreChatForm, TypingIndicator, type UseChatWidgetStateOptions, type UseChatWidgetStateReturn, fetchMessages, useChatWidgetState, useFileUpload, useMessages, useTypingIndicator, useWebSocket };
package/dist/index.d.ts CHANGED
@@ -125,8 +125,52 @@ interface ChatWidgetProps {
125
125
  * External WebSocket connection (for agents with global connection)
126
126
  */
127
127
  externalWebSocket?: WebSocket | null;
128
+ /**
129
+ * Callback when user wants to minimize the widget
130
+ */
131
+ onMinimize?: () => void;
132
+ /**
133
+ * Callback when user wants to close the widget
134
+ */
135
+ onClose?: () => void;
128
136
  }
129
- declare function ChatWidget({ config, className, variant, externalWebSocket, }: ChatWidgetProps): react_jsx_runtime.JSX.Element | null;
137
+ declare function ChatWidget({ config, className, variant, externalWebSocket, onMinimize, onClose, }: ChatWidgetProps): react_jsx_runtime.JSX.Element;
138
+
139
+ type ChatWidgetState = 'open' | 'minimized' | 'closed' | 'undefined';
140
+ interface UseChatWidgetStateOptions {
141
+ /**
142
+ * Controlled state. When provided, the component is controlled.
143
+ */
144
+ state?: ChatWidgetState;
145
+ /**
146
+ * Default state for uncontrolled mode
147
+ * @default 'minimized'
148
+ */
149
+ defaultState?: ChatWidgetState;
150
+ /**
151
+ * Callback when state changes
152
+ */
153
+ onStateChange?: (state: ChatWidgetState) => void;
154
+ }
155
+ interface UseChatWidgetStateReturn {
156
+ /**
157
+ * Current state of the widget
158
+ */
159
+ currentState: ChatWidgetState;
160
+ /**
161
+ * Function to update the state
162
+ */
163
+ setState: (newState: ChatWidgetState) => void;
164
+ /**
165
+ * Whether the state is controlled
166
+ */
167
+ isControlled: boolean;
168
+ }
169
+ /**
170
+ * Hook to manage chat widget state (controlled vs uncontrolled)
171
+ * Encapsulates the logic for handling both controlled and uncontrolled state patterns
172
+ */
173
+ declare function useChatWidgetState({ state: controlledState, defaultState, onStateChange, }: UseChatWidgetStateOptions): UseChatWidgetStateReturn;
130
174
 
131
175
  interface ChatWidgetWrapperProps {
132
176
  /**
@@ -149,6 +193,22 @@ interface ChatWidgetWrapperProps {
149
193
  * Callback when user submits the pre-chat form
150
194
  */
151
195
  onPreChatSubmit?: (user: IUser) => void;
196
+ /**
197
+ * Controlled state. When provided, the component is controlled.
198
+ * - 'open': Fully open with chat interface
199
+ * - 'minimized': Show bubble button only
200
+ * - 'closed': Fully hidden
201
+ */
202
+ state?: ChatWidgetState;
203
+ /**
204
+ * Default state for uncontrolled mode
205
+ * @default 'minimized'
206
+ */
207
+ defaultState?: ChatWidgetState;
208
+ /**
209
+ * Callback when state changes
210
+ */
211
+ onStateChange?: (state: ChatWidgetState) => void;
152
212
  }
153
213
  /**
154
214
  * Chat component that handles:
@@ -156,7 +216,7 @@ interface ChatWidgetWrapperProps {
156
216
  * - Pre-chat form for collecting user information
157
217
  * - Session persistence in localStorage
158
218
  */
159
- declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
219
+ declare function Chat({ config, className, storageKeyPrefix, onPreChatSubmit, state, defaultState, onStateChange, }: ChatWidgetWrapperProps): react_jsx_runtime.JSX.Element | null;
160
220
 
161
221
  interface ChatHeaderProps {
162
222
  agent?: IUser;
@@ -215,11 +275,19 @@ interface PreChatFormProps {
215
275
  className?: string;
216
276
  initialName?: string;
217
277
  initialEmail?: string;
278
+ /**
279
+ * Callback when user wants to minimize the form
280
+ */
281
+ onMinimize: () => void;
282
+ /**
283
+ * Callback when user wants to close the form
284
+ */
285
+ onClose: () => void;
218
286
  }
219
287
  /**
220
288
  * PreChatForm component for collecting user information before starting chat
221
289
  */
222
- declare function PreChatForm({ onSubmit, className, initialName, initialEmail, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
290
+ declare function PreChatForm({ onSubmit, className, initialName, initialEmail, onMinimize, onClose, }: PreChatFormProps): react_jsx_runtime.JSX.Element;
223
291
 
224
292
  interface UseWebSocketReturn {
225
293
  isConnected: boolean;
@@ -266,4 +334,4 @@ declare function fetchMessages(baseUrl: string, params: FetchMessagesParams, hea
266
334
  nextPageToken: string | undefined;
267
335
  }>;
268
336
 
269
- export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, type ConversationChannel, type ConversationPriority, type ConversationStatus, type IApiResponse, type IChatConfig, type IConversation, type IFileUploadConfig, type IMessage, type IReadMessageData, type ISendMessageData, type ITypingData, type IUploadedFile, type IUser, type IWebSocketMessage, MessageItem, MessageList, type MessageStatus, type MessageType, PreChatForm, TypingIndicator, fetchMessages, useFileUpload, useMessages, useTypingIndicator, useWebSocket };
337
+ export { Chat, ChatHeader, ChatInput, ChatWidget, type ChatWidgetProps, type ChatWidgetState, type ConversationChannel, type ConversationPriority, type ConversationStatus, type IApiResponse, type IChatConfig, type IConversation, type IFileUploadConfig, type IMessage, type IReadMessageData, type ISendMessageData, type ITypingData, type IUploadedFile, type IUser, type IWebSocketMessage, MessageItem, MessageList, type MessageStatus, type MessageType, PreChatForm, TypingIndicator, type UseChatWidgetStateOptions, type UseChatWidgetStateReturn, fetchMessages, useChatWidgetState, useFileUpload, useMessages, useTypingIndicator, useWebSocket };
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  PreChatForm: () => PreChatForm,
40
40
  TypingIndicator: () => TypingIndicator,
41
41
  fetchMessages: () => fetchMessages,
42
+ useChatWidgetState: () => useChatWidgetState,
42
43
  useFileUpload: () => useFileUpload,
43
44
  useMessages: () => useMessages,
44
45
  useTypingIndicator: () => useTypingIndicator,
@@ -619,7 +620,7 @@ function MessageItem({
619
620
  {
620
621
  className: `rounded-2xl px-4 py-2 ${isOwnMessage ? "bg-blue-600 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"}`,
621
622
  children: [
622
- message.messageType === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "prose prose-sm dark:prose-invert max-w-none", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
623
+ message.messageType === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
623
624
  import_react_markdown.default,
624
625
  {
625
626
  components: {
@@ -648,7 +649,7 @@ function MessageItem({
648
649
  },
649
650
  children: message.content
650
651
  }
651
- ) }),
652
+ ),
652
653
  message.messageType === "image" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
653
654
  "img",
654
655
  {
@@ -1136,10 +1137,10 @@ function ChatWidget({
1136
1137
  config,
1137
1138
  className = "",
1138
1139
  variant = "popover",
1139
- externalWebSocket
1140
+ externalWebSocket,
1141
+ onMinimize,
1142
+ onClose
1140
1143
  }) {
1141
- const [isMinimized, setIsMinimized] = (0, import_react8.useState)(false);
1142
- const [isClosed, setIsClosed] = (0, import_react8.useState)(false);
1143
1144
  const isFullPage = variant === "fullPage";
1144
1145
  const websocket = useWebSocket(config, externalWebSocket);
1145
1146
  const { messages, addMessage, isLoading, loadMore, hasMore, isLoadingMore } = useMessages(
@@ -1191,28 +1192,6 @@ function ChatWidget({
1191
1192
  config.toast?.error(websocket.error.message || "An error occurred");
1192
1193
  }
1193
1194
  }, [websocket.error, config]);
1194
- if (!isFullPage) {
1195
- if (isClosed) {
1196
- return null;
1197
- }
1198
- if (isMinimized) {
1199
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: `fixed bottom-4 right-4 z-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1200
- "button",
1201
- {
1202
- type: "button",
1203
- onClick: () => setIsMinimized(false),
1204
- className: "h-14 w-14 rounded-full bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-lg hover:shadow-xl transition-all flex items-center justify-center relative",
1205
- "aria-label": "Open chat",
1206
- children: [
1207
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-2xl", children: "\u{1F4AC}" }),
1208
- messages.some(
1209
- (msg) => msg.senderId !== config.currentUser.email && msg.status !== "read"
1210
- ) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "absolute -top-1 -right-1 h-5 w-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center", children: "!" })
1211
- ]
1212
- }
1213
- ) });
1214
- }
1215
- }
1216
1195
  const containerClasses = isFullPage ? `flex flex-col bg-white dark:bg-gray-900 h-full ${className}` : `fixed bottom-4 right-4 z-50 flex flex-col bg-white dark:bg-gray-900 rounded-lg shadow-2xl overflow-hidden ${className}`;
1217
1196
  const containerStyle = isFullPage ? void 0 : {
1218
1197
  width: "400px",
@@ -1229,8 +1208,8 @@ function ChatWidget({
1229
1208
  type: "agent",
1230
1209
  status: websocket.isConnected ? "online" : "offline"
1231
1210
  } : void 0,
1232
- onMinimize: () => setIsMinimized(true),
1233
- onClose: () => setIsClosed(true)
1211
+ onMinimize,
1212
+ onClose
1234
1213
  }
1235
1214
  ),
1236
1215
  !websocket.isConnected && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "bg-yellow-50 dark:bg-yellow-900/30 border-b border-yellow-200 dark:border-yellow-800 px-4 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -1280,7 +1259,7 @@ function ChatWidget({
1280
1259
  }
1281
1260
 
1282
1261
  // src/components/Chat.tsx
1283
- var import_react10 = require("react");
1262
+ var import_react11 = require("react");
1284
1263
 
1285
1264
  // src/components/PreChatForm.tsx
1286
1265
  var import_react9 = require("react");
@@ -1289,7 +1268,9 @@ function PreChatForm({
1289
1268
  onSubmit,
1290
1269
  className = "",
1291
1270
  initialName = "",
1292
- initialEmail = ""
1271
+ initialEmail = "",
1272
+ onMinimize,
1273
+ onClose
1293
1274
  }) {
1294
1275
  const [name, setName] = (0, import_react9.useState)(initialName);
1295
1276
  const [email, setEmail] = (0, import_react9.useState)(initialEmail);
@@ -1336,10 +1317,74 @@ function PreChatForm({
1336
1317
  maxHeight: "calc(100vh - 2rem)"
1337
1318
  },
1338
1319
  children: [
1339
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-4", children: [
1340
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-lg font-semibold", children: "Start a Conversation" }),
1341
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-blue-100 mt-1", children: "Please provide your details to continue" })
1342
- ] }),
1320
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between", children: [
1321
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex-1", children: [
1322
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-lg font-semibold", children: "Start a Conversation" }),
1323
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm text-blue-100 mt-1", children: "Please provide your details to continue" })
1324
+ ] }),
1325
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-2 ml-2", children: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1327
+ "button",
1328
+ {
1329
+ type: "button",
1330
+ onClick: onMinimize,
1331
+ className: "text-white hover:bg-white/20 rounded p-1 transition-colors",
1332
+ "aria-label": "Minimize chat",
1333
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1334
+ "svg",
1335
+ {
1336
+ className: "w-5 h-5",
1337
+ fill: "none",
1338
+ stroke: "currentColor",
1339
+ viewBox: "0 0 24 24",
1340
+ children: [
1341
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("title", { children: "Minimize" }),
1342
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1343
+ "path",
1344
+ {
1345
+ strokeLinecap: "round",
1346
+ strokeLinejoin: "round",
1347
+ strokeWidth: 2,
1348
+ d: "M20 12H4"
1349
+ }
1350
+ )
1351
+ ]
1352
+ }
1353
+ )
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1357
+ "button",
1358
+ {
1359
+ type: "button",
1360
+ onClick: onClose,
1361
+ className: "text-white hover:bg-white/20 rounded p-1 transition-colors",
1362
+ "aria-label": "Close chat",
1363
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1364
+ "svg",
1365
+ {
1366
+ className: "w-5 h-5",
1367
+ fill: "none",
1368
+ stroke: "currentColor",
1369
+ viewBox: "0 0 24 24",
1370
+ children: [
1371
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("title", { children: "Close" }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1373
+ "path",
1374
+ {
1375
+ strokeLinecap: "round",
1376
+ strokeLinejoin: "round",
1377
+ strokeWidth: 2,
1378
+ d: "M6 18L18 6M6 6l12 12"
1379
+ }
1380
+ )
1381
+ ]
1382
+ }
1383
+ )
1384
+ }
1385
+ )
1386
+ ] })
1387
+ ] }) }),
1343
1388
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: "p-6 space-y-5", children: [
1344
1389
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1345
1390
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
@@ -1459,6 +1504,32 @@ function PreChatForm({
1459
1504
  );
1460
1505
  }
1461
1506
 
1507
+ // src/hooks/useChatWidgetState.ts
1508
+ var import_react10 = require("react");
1509
+ function useChatWidgetState({
1510
+ state: controlledState,
1511
+ defaultState = "minimized",
1512
+ onStateChange
1513
+ }) {
1514
+ const [uncontrolledState, setUncontrolledState] = (0, import_react10.useState)(defaultState);
1515
+ const isControlled = controlledState !== void 0 && controlledState !== "undefined";
1516
+ const currentState = isControlled ? controlledState : uncontrolledState;
1517
+ const setState = (0, import_react10.useCallback)(
1518
+ (newValue) => {
1519
+ if (!isControlled) {
1520
+ setUncontrolledState(newValue);
1521
+ }
1522
+ onStateChange?.(newValue);
1523
+ },
1524
+ [isControlled, onStateChange]
1525
+ );
1526
+ return {
1527
+ currentState,
1528
+ setState,
1529
+ isControlled
1530
+ };
1531
+ }
1532
+
1462
1533
  // src/components/Chat.tsx
1463
1534
  var import_jsx_runtime7 = require("react/jsx-runtime");
1464
1535
  function generateSessionId() {
@@ -1471,12 +1542,20 @@ function Chat({
1471
1542
  config,
1472
1543
  className = "",
1473
1544
  storageKeyPrefix = "xcelsior_chat",
1474
- onPreChatSubmit
1545
+ onPreChatSubmit,
1546
+ state,
1547
+ defaultState = "minimized",
1548
+ onStateChange
1475
1549
  }) {
1476
- const [userInfo, setUserInfo] = (0, import_react10.useState)(null);
1477
- const [conversationId, setConversationId] = (0, import_react10.useState)("");
1478
- const [isLoading, setIsLoading] = (0, import_react10.useState)(true);
1479
- (0, import_react10.useEffect)(() => {
1550
+ const [userInfo, setUserInfo] = (0, import_react11.useState)(null);
1551
+ const [conversationId, setConversationId] = (0, import_react11.useState)("");
1552
+ const [isLoading, setIsLoading] = (0, import_react11.useState)(true);
1553
+ const { currentState, setState } = useChatWidgetState({
1554
+ state,
1555
+ defaultState,
1556
+ onStateChange
1557
+ });
1558
+ (0, import_react11.useEffect)(() => {
1480
1559
  const initializeSession = () => {
1481
1560
  try {
1482
1561
  if (config.currentUser?.email && config.currentUser?.name) {
@@ -1531,7 +1610,7 @@ function Chat({
1531
1610
  };
1532
1611
  initializeSession();
1533
1612
  }, [config, storageKeyPrefix]);
1534
- const handlePreChatSubmit = (0, import_react10.useCallback)(
1613
+ const handlePreChatSubmit = (0, import_react11.useCallback)(
1535
1614
  (name, email) => {
1536
1615
  const convId = conversationId || generateSessionId();
1537
1616
  const user = {
@@ -1560,6 +1639,21 @@ function Chat({
1560
1639
  if (isLoading) {
1561
1640
  return null;
1562
1641
  }
1642
+ if (currentState === "closed") {
1643
+ return null;
1644
+ }
1645
+ if (currentState === "minimized") {
1646
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `fixed bottom-4 right-4 z-50 ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1647
+ "button",
1648
+ {
1649
+ type: "button",
1650
+ onClick: () => setState("open"),
1651
+ className: "h-14 w-14 rounded-full bg-gradient-to-r from-blue-600 to-purple-600 text-white shadow-lg hover:shadow-xl transition-all flex items-center justify-center relative",
1652
+ "aria-label": "Open chat",
1653
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-2xl", children: "\u{1F4AC}" })
1654
+ }
1655
+ ) });
1656
+ }
1563
1657
  if (!userInfo || !userInfo.email || !userInfo.name) {
1564
1658
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1565
1659
  PreChatForm,
@@ -1567,7 +1661,9 @@ function Chat({
1567
1661
  onSubmit: handlePreChatSubmit,
1568
1662
  className,
1569
1663
  initialName: config.currentUser?.name,
1570
- initialEmail: config.currentUser?.email
1664
+ initialEmail: config.currentUser?.email,
1665
+ onClose: () => setState("closed"),
1666
+ onMinimize: () => setState("minimized")
1571
1667
  }
1572
1668
  );
1573
1669
  }
@@ -1576,7 +1672,15 @@ function Chat({
1576
1672
  conversationId,
1577
1673
  currentUser: userInfo
1578
1674
  };
1579
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatWidget, { config: fullConfig, className });
1675
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1676
+ ChatWidget,
1677
+ {
1678
+ config: fullConfig,
1679
+ className,
1680
+ onClose: () => setState("closed"),
1681
+ onMinimize: () => setState("minimized")
1682
+ }
1683
+ );
1580
1684
  }
1581
1685
 
1582
1686
  // src/components/TypingIndicator.tsx
@@ -1617,6 +1721,7 @@ function TypingIndicator({ isTyping, userName }) {
1617
1721
  PreChatForm,
1618
1722
  TypingIndicator,
1619
1723
  fetchMessages,
1724
+ useChatWidgetState,
1620
1725
  useFileUpload,
1621
1726
  useMessages,
1622
1727
  useTypingIndicator,