ajaxter-chat 3.0.16 → 3.1.0

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.
Files changed (147) hide show
  1. package/dist/components/BlockList/index.d.ts +1 -0
  2. package/dist/components/BlockList/index.d.ts.map +1 -0
  3. package/dist/components/BlockList/index.js +55 -28
  4. package/dist/components/BlockList/index.js.map +1 -0
  5. package/dist/components/CallScreen/index.d.ts +3 -0
  6. package/dist/components/CallScreen/index.d.ts.map +1 -0
  7. package/dist/components/CallScreen/index.js +107 -29
  8. package/dist/components/CallScreen/index.js.map +1 -0
  9. package/dist/components/ChatScreen/index.d.ts +1 -0
  10. package/dist/components/ChatScreen/index.d.ts.map +1 -0
  11. package/dist/components/ChatScreen/index.js +493 -294
  12. package/dist/components/ChatScreen/index.js.map +1 -0
  13. package/dist/components/ChatWidget.d.ts +1 -0
  14. package/dist/components/ChatWidget.d.ts.map +1 -0
  15. package/dist/components/ChatWidget.js +359 -250
  16. package/dist/components/ChatWidget.js.map +1 -0
  17. package/dist/components/EmojiPicker/index.d.ts +1 -0
  18. package/dist/components/EmojiPicker/index.d.ts.map +1 -0
  19. package/dist/components/EmojiPicker/index.js +19 -7
  20. package/dist/components/EmojiPicker/index.js.map +1 -0
  21. package/dist/components/ErrorBoundary/index.d.ts +20 -0
  22. package/dist/components/ErrorBoundary/index.d.ts.map +1 -0
  23. package/dist/components/ErrorBoundary/index.js +76 -0
  24. package/dist/components/ErrorBoundary/index.js.map +1 -0
  25. package/dist/components/HomeScreen/index.d.ts +1 -0
  26. package/dist/components/HomeScreen/index.d.ts.map +1 -0
  27. package/dist/components/HomeScreen/index.js +236 -158
  28. package/dist/components/HomeScreen/index.js.map +1 -0
  29. package/dist/components/MaintenanceView/index.d.ts +1 -0
  30. package/dist/components/MaintenanceView/index.d.ts.map +1 -0
  31. package/dist/components/MaintenanceView/index.js +28 -12
  32. package/dist/components/MaintenanceView/index.js.map +1 -0
  33. package/dist/components/MiniCallBar/index.d.ts +15 -0
  34. package/dist/components/MiniCallBar/index.d.ts.map +1 -0
  35. package/dist/components/MiniCallBar/index.js +116 -0
  36. package/dist/components/MiniCallBar/index.js.map +1 -0
  37. package/dist/components/PermissionsGateScreen/index.d.ts +1 -0
  38. package/dist/components/PermissionsGateScreen/index.d.ts.map +1 -0
  39. package/dist/components/PermissionsGateScreen/index.js +82 -28
  40. package/dist/components/PermissionsGateScreen/index.js.map +1 -0
  41. package/dist/components/RecentChatsScreen/index.d.ts +1 -0
  42. package/dist/components/RecentChatsScreen/index.d.ts.map +1 -0
  43. package/dist/components/RecentChatsScreen/index.js +79 -19
  44. package/dist/components/RecentChatsScreen/index.js.map +1 -0
  45. package/dist/components/SlideNavMenu.d.ts +1 -0
  46. package/dist/components/SlideNavMenu.d.ts.map +1 -0
  47. package/dist/components/SlideNavMenu.js +82 -63
  48. package/dist/components/SlideNavMenu.js.map +1 -0
  49. package/dist/components/Tabs/BottomTabs.d.ts +1 -0
  50. package/dist/components/Tabs/BottomTabs.d.ts.map +1 -0
  51. package/dist/components/Tabs/BottomTabs.js +34 -19
  52. package/dist/components/Tabs/BottomTabs.js.map +1 -0
  53. package/dist/components/TicketDetailScreen/index.d.ts +1 -0
  54. package/dist/components/TicketDetailScreen/index.d.ts.map +1 -0
  55. package/dist/components/TicketDetailScreen/index.js +66 -27
  56. package/dist/components/TicketDetailScreen/index.js.map +1 -0
  57. package/dist/components/TicketFormScreen/index.d.ts +1 -0
  58. package/dist/components/TicketFormScreen/index.d.ts.map +1 -0
  59. package/dist/components/TicketFormScreen/index.js +99 -49
  60. package/dist/components/TicketFormScreen/index.js.map +1 -0
  61. package/dist/components/TicketScreen/index.d.ts +1 -0
  62. package/dist/components/TicketScreen/index.d.ts.map +1 -0
  63. package/dist/components/TicketScreen/index.js +95 -26
  64. package/dist/components/TicketScreen/index.js.map +1 -0
  65. package/dist/components/UserListScreen/index.d.ts +1 -0
  66. package/dist/components/UserListScreen/index.d.ts.map +1 -0
  67. package/dist/components/UserListScreen/index.js +127 -53
  68. package/dist/components/UserListScreen/index.js.map +1 -0
  69. package/dist/components/ViewerBlockedScreen/index.d.ts +1 -0
  70. package/dist/components/ViewerBlockedScreen/index.d.ts.map +1 -0
  71. package/dist/components/ViewerBlockedScreen/index.js +113 -61
  72. package/dist/components/ViewerBlockedScreen/index.js.map +1 -0
  73. package/dist/config/index.d.ts +1 -0
  74. package/dist/config/index.d.ts.map +1 -0
  75. package/dist/config/index.js +7 -2
  76. package/dist/config/index.js.map +1 -0
  77. package/dist/hooks/useChat.d.ts +9 -1
  78. package/dist/hooks/useChat.d.ts.map +1 -0
  79. package/dist/hooks/useChat.js +60 -18
  80. package/dist/hooks/useChat.js.map +1 -0
  81. package/dist/hooks/useRemoteConfig.d.ts +1 -0
  82. package/dist/hooks/useRemoteConfig.d.ts.map +1 -0
  83. package/dist/hooks/useRemoteConfig.js +12 -8
  84. package/dist/hooks/useRemoteConfig.js.map +1 -0
  85. package/dist/hooks/useSocket.d.ts +40 -0
  86. package/dist/hooks/useSocket.d.ts.map +1 -0
  87. package/dist/hooks/useSocket.js +190 -0
  88. package/dist/hooks/useSocket.js.map +1 -0
  89. package/dist/hooks/useWebRTC.d.ts +10 -2
  90. package/dist/hooks/useWebRTC.d.ts.map +1 -0
  91. package/dist/hooks/useWebRTC.js +101 -69
  92. package/dist/hooks/useWebRTC.js.map +1 -0
  93. package/dist/index.d.ts +6 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +67 -21
  96. package/dist/index.js.map +1 -0
  97. package/dist/types/index.d.ts +1 -0
  98. package/dist/types/index.d.ts.map +1 -0
  99. package/dist/types/index.js +3 -1
  100. package/dist/types/index.js.map +1 -0
  101. package/dist/utils/chat.d.ts +1 -0
  102. package/dist/utils/chat.d.ts.map +1 -0
  103. package/dist/utils/chat.js +17 -7
  104. package/dist/utils/chat.js.map +1 -0
  105. package/dist/utils/fileName.d.ts +1 -0
  106. package/dist/utils/fileName.d.ts.map +1 -0
  107. package/dist/utils/fileName.js +5 -1
  108. package/dist/utils/fileName.js.map +1 -0
  109. package/dist/utils/messageSound.d.ts +1 -0
  110. package/dist/utils/messageSound.d.ts.map +1 -0
  111. package/dist/utils/messageSound.js +9 -3
  112. package/dist/utils/messageSound.js.map +1 -0
  113. package/dist/utils/presenceStatus.d.ts +1 -0
  114. package/dist/utils/presenceStatus.d.ts.map +1 -0
  115. package/dist/utils/presenceStatus.js +11 -4
  116. package/dist/utils/presenceStatus.js.map +1 -0
  117. package/dist/utils/privacyConsent.d.ts +1 -0
  118. package/dist/utils/privacyConsent.d.ts.map +1 -0
  119. package/dist/utils/privacyConsent.js +9 -3
  120. package/dist/utils/privacyConsent.js.map +1 -0
  121. package/dist/utils/reenableRequest.d.ts +1 -0
  122. package/dist/utils/reenableRequest.d.ts.map +1 -0
  123. package/dist/utils/reenableRequest.js +5 -1
  124. package/dist/utils/reenableRequest.js.map +1 -0
  125. package/dist/utils/theme.d.ts +1 -0
  126. package/dist/utils/theme.d.ts.map +1 -0
  127. package/dist/utils/theme.js +10 -4
  128. package/dist/utils/theme.js.map +1 -0
  129. package/dist/utils/widgetPermissions.d.ts +1 -0
  130. package/dist/utils/widgetPermissions.d.ts.map +1 -0
  131. package/dist/utils/widgetPermissions.js +13 -5
  132. package/dist/utils/widgetPermissions.js.map +1 -0
  133. package/dist/utils/widgetSession.d.ts +1 -0
  134. package/dist/utils/widgetSession.d.ts.map +1 -0
  135. package/dist/utils/widgetSession.js +9 -3
  136. package/dist/utils/widgetSession.js.map +1 -0
  137. package/package.json +3 -3
  138. package/src/components/CallScreen/index.tsx +23 -1
  139. package/src/components/ChatScreen/index.tsx +2 -1
  140. package/src/components/ChatWidget.tsx +314 -263
  141. package/src/components/ErrorBoundary/index.tsx +62 -0
  142. package/src/components/HomeScreen/index.tsx +0 -3
  143. package/src/components/MiniCallBar/index.tsx +150 -0
  144. package/src/hooks/useChat.ts +59 -12
  145. package/src/hooks/useSocket.ts +228 -0
  146. package/src/hooks/useWebRTC.ts +99 -64
  147. package/src/index.ts +7 -2
@@ -1,58 +1,102 @@
1
+ "use strict";
1
2
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
4
- import { loadLocalConfig } from '../config';
5
- import { mergeTheme } from '../utils/theme';
6
- import { useRemoteConfig } from '../hooks/useRemoteConfig';
7
- import { useChat } from '../hooks/useChat';
8
- import { useWebRTC } from '../hooks/useWebRTC';
9
- import { saveSession, loadSession } from '../utils/widgetSession';
10
- import { playMessageSound, getMessageSoundEnabled, setMessageSoundEnabled } from '../utils/messageSound';
11
- import { HomeScreen } from './HomeScreen';
12
- import { UserListScreen } from './UserListScreen';
13
- import { ChatScreen } from './ChatScreen';
14
- import { RecentChatsScreen } from './RecentChatsScreen';
15
- import { TicketScreen } from './TicketScreen';
16
- import { TicketDetailScreen } from './TicketDetailScreen';
17
- import { TicketFormScreen } from './TicketFormScreen';
18
- import { BlockListScreen } from './BlockList';
19
- import { CallScreen } from './CallScreen';
20
- import { MaintenanceView } from './MaintenanceView';
21
- import { BottomTabs } from './Tabs/BottomTabs';
22
- import { ViewerBlockedScreen } from './ViewerBlockedScreen';
23
- import { PermissionsGateScreen } from './PermissionsGateScreen';
24
- import { hasStoredPermissionsGrant } from '../utils/widgetPermissions';
25
- export const ChatWidget = ({ theme: localTheme, viewer }) => {
26
- var _a, _b, _c, _d, _e;
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.ChatWidget = void 0;
38
+ const react_1 = __importStar(require("react"));
39
+ const config_1 = require("../config");
40
+ const theme_1 = require("../utils/theme");
41
+ const useRemoteConfig_1 = require("../hooks/useRemoteConfig");
42
+ const useChat_1 = require("../hooks/useChat");
43
+ const useWebRTC_1 = require("../hooks/useWebRTC");
44
+ const useSocket_1 = require("../hooks/useSocket");
45
+ const widgetSession_1 = require("../utils/widgetSession");
46
+ const messageSound_1 = require("../utils/messageSound");
47
+ const ErrorBoundary_1 = require("./ErrorBoundary");
48
+ const HomeScreen_1 = require("./HomeScreen");
49
+ const UserListScreen_1 = require("./UserListScreen");
50
+ const ChatScreen_1 = require("./ChatScreen");
51
+ const RecentChatsScreen_1 = require("./RecentChatsScreen");
52
+ const TicketScreen_1 = require("./TicketScreen");
53
+ const TicketDetailScreen_1 = require("./TicketDetailScreen");
54
+ const TicketFormScreen_1 = require("./TicketFormScreen");
55
+ const BlockList_1 = require("./BlockList");
56
+ const CallScreen_1 = require("./CallScreen");
57
+ const MiniCallBar_1 = require("./MiniCallBar");
58
+ const MaintenanceView_1 = require("./MaintenanceView");
59
+ const BottomTabs_1 = require("./Tabs/BottomTabs");
60
+ const ViewerBlockedScreen_1 = require("./ViewerBlockedScreen");
61
+ const PermissionsGateScreen_1 = require("./PermissionsGateScreen");
62
+ const widgetPermissions_1 = require("../utils/widgetPermissions");
63
+ /** WebSocket server URL — override via env NEXT_PUBLIC_SOCKET_URL / REACT_APP_SOCKET_URL */
64
+ function getSocketUrl() {
65
+ var _a, _b;
66
+ if (typeof process !== 'undefined' && process.env) {
67
+ return ((_b = (_a = process.env['NEXT_PUBLIC_SOCKET_URL']) !== null && _a !== void 0 ? _a : process.env['REACT_APP_SOCKET_URL']) !== null && _b !== void 0 ? _b : 'http://localhost:3005');
68
+ }
69
+ return 'http://localhost:3005';
70
+ }
71
+ const ChatWidget = ({ theme: localTheme, viewer }) => {
72
+ var _a, _b, _c, _d, _e, _f, _g, _h;
27
73
  /* SSR guard */
28
- const [mounted, setMounted] = useState(false);
29
- useEffect(() => { setMounted(true); }, []);
30
- /* Env config */
31
- const { apiKey, widgetId } = loadLocalConfig();
32
- /* Remote config */
33
- const { data, loading: cfgLoading, error: cfgError } = useRemoteConfig(apiKey, widgetId);
34
- /* Merged theme — remote config overrides defaults, local prop overrides both */
35
- const theme = mergeTheme((data === null || data === void 0 ? void 0 : data.widget) ? { primaryColor: data.widget.primaryColor, buttonLabel: data.widget.buttonLabel, buttonPosition: data.widget.buttonPosition } : undefined, localTheme);
36
- /* Drawer open state */
37
- const [isOpen, setIsOpen] = useState(false);
38
- const [closing, setClosing] = useState(false); // for slide-out animation
39
- /* Navigation */
40
- const [activeTab, setActiveTab] = useState('home');
41
- const [screen, setScreen] = useState('home');
42
- const [userListCtx, setUserListCtx] = useState('support');
43
- const [chatReturnCtx, setChatReturnCtx] = useState('conversation');
44
- const [viewingTicketId, setViewingTicketId] = useState(null);
45
- const [messageSoundEnabled, setMessageSoundEnabledState] = useState(true);
46
- /** Stagger list animation only when opening from home burger menu */
47
- const [listEntranceAnimation, setListEntranceAnimation] = useState(false);
48
- /** Microphone, geolocation, and screen capture granted for this tab */
49
- const [permissionsOk, setPermissionsOk] = useState(false);
50
- /* App state */
51
- const [tickets, setTickets] = useState((_a = data === null || data === void 0 ? void 0 : data.sampleTickets) !== null && _a !== void 0 ? _a : []);
52
- const [recentChats, setRecentChats] = useState([]);
53
- const [blockedUids, setBlockedUids] = useState((_b = data === null || data === void 0 ? void 0 : data.blockedUsers) !== null && _b !== void 0 ? _b : []);
74
+ const [mounted, setMounted] = (0, react_1.useState)(false);
75
+ (0, react_1.useEffect)(() => { setMounted(true); }, []);
76
+ const { apiKey, widgetId } = (0, config_1.loadLocalConfig)();
77
+ const { data, loading: cfgLoading, error: cfgError } = (0, useRemoteConfig_1.useRemoteConfig)(apiKey, widgetId);
78
+ const theme = (0, theme_1.mergeTheme)((data === null || data === void 0 ? void 0 : data.widget) ? {
79
+ primaryColor: data.widget.primaryColor,
80
+ buttonLabel: data.widget.buttonLabel,
81
+ buttonPosition: data.widget.buttonPosition,
82
+ } : undefined, localTheme);
83
+ const [isOpen, setIsOpen] = (0, react_1.useState)(false);
84
+ const [closing, setClosing] = (0, react_1.useState)(false);
85
+ const [callMinimized, setCallMinimized] = (0, react_1.useState)(false);
86
+ const [activeTab, setActiveTab] = (0, react_1.useState)('home');
87
+ const [screen, setScreen] = (0, react_1.useState)('home');
88
+ const [userListCtx, setUserListCtx] = (0, react_1.useState)('support');
89
+ const [chatReturnCtx, setChatReturnCtx] = (0, react_1.useState)('conversation');
90
+ const [viewingTicketId, setViewingTicketId] = (0, react_1.useState)(null);
91
+ const [messageSoundEnabled, setMessageSoundEnabledState] = (0, react_1.useState)(true);
92
+ const [listEntranceAnimation, setListEntranceAnimation] = (0, react_1.useState)(false);
93
+ const [permissionsOk, setPermissionsOk] = (0, react_1.useState)(false);
94
+ const [socketStatus, setSocketStatus] = (0, react_1.useState)('disconnected');
95
+ const [tickets, setTickets] = (0, react_1.useState)((_a = data === null || data === void 0 ? void 0 : data.sampleTickets) !== null && _a !== void 0 ? _a : []);
96
+ const [recentChats, setRecentChats] = (0, react_1.useState)([]);
97
+ const [blockedUids, setBlockedUids] = (0, react_1.useState)((_b = data === null || data === void 0 ? void 0 : data.blockedUsers) !== null && _b !== void 0 ? _b : []);
54
98
  /* Sync remote data into local state once loaded */
55
- useEffect(() => {
99
+ (0, react_1.useEffect)(() => {
56
100
  var _a, _b, _c, _d;
57
101
  if (data) {
58
102
  setTickets(data.sampleTickets);
@@ -78,52 +122,109 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
78
122
  setRecentChats(recents);
79
123
  }
80
124
  }, [data, viewer === null || viewer === void 0 ? void 0 : viewer.projectId]);
81
- /* Chat hook */
82
- const { messages, activeUser, isPaused, isReported, selectUser, sendMessage, togglePause, reportChat, clearChat, setMessages, } = useChat();
83
- /* WebRTC hook */
84
- const { session: callSession, localVideoRef, remoteVideoRef, startCall, endCall, toggleMute, toggleCamera } = useWebRTC();
85
- /* ── Drawer open/close with slide animation ───────────────────────────── */
125
+ /* ── Socket setup ───────────────────────────────────────────────────── */
126
+ const viewerUidForSocket = ((_e = (_c = viewer === null || viewer === void 0 ? void 0 : viewer.uid) !== null && _c !== void 0 ? _c : (_d = data === null || data === void 0 ? void 0 : data.widget) === null || _d === void 0 ? void 0 : _d.viewerUid) !== null && _e !== void 0 ? _e : '').trim();
127
+ const { joinRoom, leaveRoom, emitMessage, emitPauseToggle, emitReport, emitBlock, emitUnblock, emitTransfer, emitCallOffer, emitCallAnswer, emitIceCandidate, emitCallEnd, emitAddParticipant, status: _socketStatus, } = (0, useSocket_1.useSocket)({
128
+ widgetId,
129
+ viewerUid: viewerUidForSocket,
130
+ serverUrl: getSocketUrl(),
131
+ onMessage: (msg) => {
132
+ receiveMessage(msg);
133
+ if (messageSoundEnabled && msg.senderId !== 'me')
134
+ (0, messageSound_1.playMessageSound)();
135
+ // Update recent chats last message
136
+ setRecentChats(prev => prev.map(r => r.user.uid === msg.senderId || r.user.uid === msg.receiverId
137
+ ? Object.assign(Object.assign({}, r), { lastMessage: msg.text, lastTime: msg.timestamp, unread: r.unread + 1 }) : r));
138
+ },
139
+ onMessageAck: ({ messageId, status }) => {
140
+ updateMessageStatus(messageId, status);
141
+ },
142
+ onChatPaused: (_roomId, paused) => {
143
+ // Sync pause state from remote
144
+ setRecentChats(prev => prev.map(r => activeUser && r.user.uid === activeUser.uid ? Object.assign(Object.assign({}, r), { isPaused: paused }) : r));
145
+ },
146
+ onCallOffer: async (offer, fromUid, callId) => {
147
+ var _a, _b;
148
+ // Find peer user
149
+ const allUsers = [...((_a = data === null || data === void 0 ? void 0 : data.developers) !== null && _a !== void 0 ? _a : []), ...((_b = data === null || data === void 0 ? void 0 : data.users) !== null && _b !== void 0 ? _b : [])];
150
+ const peer = allUsers.find(u => u.uid === fromUid);
151
+ if (peer) {
152
+ await acceptCall(offer, peer, callId);
153
+ setScreen('call');
154
+ setIsOpen(true);
155
+ }
156
+ },
157
+ onCallAnswer: async (answer, _fromUid, _callId) => {
158
+ await addIceCandidate({ sdpMid: '0', sdpMLineIndex: 0, candidate: '' });
159
+ // WebRTC: set remote description
160
+ void answer;
161
+ },
162
+ onIceCandidate: async (candidate) => {
163
+ await addIceCandidate(candidate);
164
+ },
165
+ onCallEnd: () => { _endCall(); },
166
+ onError: (code, message) => {
167
+ console.error('[ChatWidget] Socket error:', code, message);
168
+ },
169
+ onUserStatus: (uid, status) => {
170
+ setRecentChats(prev => prev.map(r => r.user.uid === uid ? Object.assign(Object.assign({}, r), { user: Object.assign(Object.assign({}, r.user), { status }) }) : r));
171
+ },
172
+ });
173
+ (0, react_1.useEffect)(() => { setSocketStatus(_socketStatus); }, [_socketStatus]);
174
+ /* ── Chat hook ──────────────────────────────────────────────────────── */
175
+ const { messages, activeUser, isPaused, isReported, selectUser, sendMessage, receiveMessage, updateMessageStatus, togglePause: _togglePause, reportChat: _reportChat, clearChat, setMessages, } = (0, useChat_1.useChat)([], {
176
+ onEmitMessage: (msg) => { emitMessage(msg); },
177
+ onEmitPause: (roomId, targetUid, paused) => { emitPauseToggle(roomId, targetUid, paused); },
178
+ onEmitReport: (roomId) => { emitReport(roomId); },
179
+ });
180
+ /* ── WebRTC hook ────────────────────────────────────────────────────── */
181
+ const { session: callSession, localVideoRef, remoteVideoRef, startCall: _startCall, acceptCall, addIceCandidate, endCall: _endCall, toggleMute, toggleCamera, } = (0, useWebRTC_1.useWebRTC)({
182
+ onOfferReady: (offer, toUid, callId) => { emitCallOffer(offer, toUid, callId); },
183
+ onAnswerReady: (answer, toUid, callId) => { emitCallAnswer(answer, toUid, callId); },
184
+ onIceCandidateReady: (candidate, toUid) => { emitIceCandidate(candidate, toUid); },
185
+ onCallEnded: (callId, toUid) => { emitCallEnd(callId, toUid); },
186
+ });
187
+ const callInProgress = callSession.state === 'calling' || callSession.state === 'connected';
188
+ (0, react_1.useEffect)(() => {
189
+ if (!callInProgress)
190
+ setCallMinimized(false);
191
+ }, [callInProgress]);
192
+ /* ── Drawer open/close ──────────────────────────────────────────────── */
86
193
  const openDrawer = () => {
87
194
  setClosing(false);
88
195
  setIsOpen(true);
196
+ setCallMinimized(false);
89
197
  };
90
- const persistWidgetState = useCallback(() => {
198
+ const persistWidgetState = (0, react_1.useCallback)(() => {
91
199
  var _a;
92
200
  const w = data === null || data === void 0 ? void 0 : data.widget;
93
201
  if (!w)
94
202
  return;
95
- saveSession(w.id, {
96
- screen,
97
- activeTab,
98
- userListCtx,
203
+ (0, widgetSession_1.saveSession)(w.id, {
204
+ screen, activeTab, userListCtx,
99
205
  activeUserUid: (_a = activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid) !== null && _a !== void 0 ? _a : null,
100
- messages,
101
- viewingTicketId,
102
- chatReturnCtx,
206
+ messages, viewingTicketId, chatReturnCtx,
103
207
  });
104
208
  }, [data === null || data === void 0 ? void 0 : data.widget, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx]);
105
- const closeDrawer = useCallback(() => {
209
+ const closeDrawer = (0, react_1.useCallback)(() => {
106
210
  persistWidgetState();
107
211
  setClosing(true);
108
- setTimeout(() => {
109
- setIsOpen(false);
110
- setClosing(false);
111
- }, 300);
212
+ setTimeout(() => { setIsOpen(false); setClosing(false); }, 300);
112
213
  }, [persistWidgetState]);
113
- useEffect(() => {
214
+ (0, react_1.useEffect)(() => {
114
215
  var _a;
115
216
  const id = (_a = data === null || data === void 0 ? void 0 : data.widget) === null || _a === void 0 ? void 0 : _a.id;
116
217
  if (!id)
117
218
  return;
118
- setPermissionsOk(hasStoredPermissionsGrant(id));
119
- }, [(_c = data === null || data === void 0 ? void 0 : data.widget) === null || _c === void 0 ? void 0 : _c.id]);
120
- const restoredRef = useRef(false);
121
- useEffect(() => {
219
+ setPermissionsOk((0, widgetPermissions_1.hasStoredPermissionsGrant)(id));
220
+ }, [(_f = data === null || data === void 0 ? void 0 : data.widget) === null || _f === void 0 ? void 0 : _f.id]);
221
+ const restoredRef = (0, react_1.useRef)(false);
222
+ (0, react_1.useEffect)(() => {
122
223
  var _a, _b, _c, _d, _e, _f;
123
224
  if (!(data === null || data === void 0 ? void 0 : data.widget) || restoredRef.current)
124
225
  return;
125
226
  const w = data.widget;
126
- setMessageSoundEnabledState(getMessageSoundEnabled(w.id));
227
+ setMessageSoundEnabledState((0, messageSound_1.getMessageSoundEnabled)(w.id));
127
228
  const uidForBlock = (_b = ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.uid) !== null && _a !== void 0 ? _a : w.viewerUid)) === null || _b === void 0 ? void 0 : _b.trim();
128
229
  let viewerIsBlocked = w.viewerBlocked === true;
129
230
  if (!viewerIsBlocked && uidForBlock) {
@@ -138,7 +239,7 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
138
239
  restoredRef.current = true;
139
240
  return;
140
241
  }
141
- const p = loadSession(w.id);
242
+ const p = (0, widgetSession_1.loadSession)(w.id);
142
243
  if (p) {
143
244
  setScreen(p.screen);
144
245
  setActiveTab(p.activeTab);
@@ -156,12 +257,15 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
156
257
  ? p.messages
157
258
  : ((_f = data.sampleChats[u.uid]) !== null && _f !== void 0 ? _f : []);
158
259
  selectUser(u, hist);
260
+ // Rejoin socket room
261
+ const roomId = [u.uid, viewerUidForSocket].sort().join('_');
262
+ joinRoom(roomId);
159
263
  }
160
264
  }
161
265
  }
162
266
  restoredRef.current = true;
163
- }, [data, selectUser, clearChat, viewer === null || viewer === void 0 ? void 0 : viewer.projectId, viewer === null || viewer === void 0 ? void 0 : viewer.uid]);
164
- useEffect(() => {
267
+ }, [data, selectUser, clearChat, viewer === null || viewer === void 0 ? void 0 : viewer.projectId, viewer === null || viewer === void 0 ? void 0 : viewer.uid, viewerUidForSocket, joinRoom]);
268
+ (0, react_1.useEffect)(() => {
165
269
  var _a, _b;
166
270
  if (!(data === null || data === void 0 ? void 0 : data.widget))
167
271
  return;
@@ -179,36 +283,22 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
179
283
  setActiveTab('home');
180
284
  setViewingTicketId(null);
181
285
  }, [data === null || data === void 0 ? void 0 : data.widget, data === null || data === void 0 ? void 0 : data.developers, data === null || data === void 0 ? void 0 : data.users, viewer === null || viewer === void 0 ? void 0 : viewer.uid, clearChat]);
182
- useEffect(() => {
286
+ (0, react_1.useEffect)(() => {
183
287
  if (!(data === null || data === void 0 ? void 0 : data.widget))
184
288
  return;
185
289
  persistWidgetState();
186
- }, [(_d = data === null || data === void 0 ? void 0 : data.widget) === null || _d === void 0 ? void 0 : _d.id, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx, persistWidgetState]);
187
- const incomingSoundRef = useRef(0);
188
- useEffect(() => {
189
- incomingSoundRef.current = messages.length;
190
- }, [activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid]);
191
- useEffect(() => {
192
- if (!messageSoundEnabled || !activeUser || !(data === null || data === void 0 ? void 0 : data.widget))
193
- return;
194
- if (messages.length < incomingSoundRef.current) {
195
- incomingSoundRef.current = messages.length;
196
- return;
197
- }
198
- const added = messages.slice(incomingSoundRef.current);
199
- incomingSoundRef.current = messages.length;
200
- if (added.some(m => m.senderId !== 'me'))
201
- playMessageSound();
202
- }, [messages, messageSoundEnabled, activeUser, data === null || data === void 0 ? void 0 : data.widget]);
203
- const toggleMessageSound = useCallback((enabled) => {
290
+ }, [(_g = data === null || data === void 0 ? void 0 : data.widget) === null || _g === void 0 ? void 0 : _g.id, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx, persistWidgetState]);
291
+ const incomingSoundRef = (0, react_1.useRef)(0);
292
+ (0, react_1.useEffect)(() => { incomingSoundRef.current = messages.length; }, [activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid]);
293
+ const toggleMessageSound = (0, react_1.useCallback)((enabled) => {
204
294
  const w = data === null || data === void 0 ? void 0 : data.widget;
205
295
  if (!w)
206
296
  return;
207
- setMessageSoundEnabled(w.id, enabled);
297
+ (0, messageSound_1.setMessageSoundEnabled)(w.id, enabled);
208
298
  setMessageSoundEnabledState(enabled);
209
299
  }, [data === null || data === void 0 ? void 0 : data.widget]);
210
- /* ── Navigation ──────────────────────────────────────────────────────── */
211
- const handleCardClick = useCallback((ctx, options) => {
300
+ /* ── Navigation ─────────────────────────────────────────────────────── */
301
+ const handleCardClick = (0, react_1.useCallback)((ctx, options) => {
212
302
  setListEntranceAnimation(!!(options === null || options === void 0 ? void 0 : options.fromMenu));
213
303
  if (ctx === 'ticket') {
214
304
  setActiveTab('tickets');
@@ -219,7 +309,7 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
219
309
  setScreen('user-list');
220
310
  }
221
311
  }, []);
222
- const handleNavFromMenu = useCallback((ctx) => {
312
+ const handleNavFromMenu = (0, react_1.useCallback)((ctx) => {
223
313
  setListEntranceAnimation(false);
224
314
  clearChat();
225
315
  if (ctx === 'ticket') {
@@ -231,62 +321,71 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
231
321
  setScreen('user-list');
232
322
  }
233
323
  }, [clearChat]);
234
- const listCtxForUser = useCallback((user, viewerIsDev) => {
324
+ const listCtxForUser = (0, react_1.useCallback)((user, viewerIsDev) => {
235
325
  if (viewerIsDev)
236
326
  return user.type === 'user' ? 'support' : 'conversation';
237
327
  return user.type === 'developer' ? 'support' : 'conversation';
238
328
  }, []);
239
- const handleSelectUser = useCallback((user, returnCtxOverride) => {
329
+ const handleSelectUser = (0, react_1.useCallback)((user, returnCtxOverride) => {
240
330
  var _a;
241
331
  setListEntranceAnimation(false);
242
332
  setChatReturnCtx(returnCtxOverride !== null && returnCtxOverride !== void 0 ? returnCtxOverride : userListCtx);
243
333
  const history = (_a = data === null || data === void 0 ? void 0 : data.sampleChats[user.uid]) !== null && _a !== void 0 ? _a : [];
244
334
  selectUser(user, history);
245
335
  setScreen('chat');
336
+ // Join socket room for real-time messages
337
+ const roomId = [user.uid, viewerUidForSocket].sort().join('_');
338
+ joinRoom(roomId);
246
339
  setRecentChats(prev => {
247
340
  const exists = prev.find(r => r.user.uid === user.uid);
248
341
  if (exists)
249
- return prev;
342
+ return prev.map(r => r.user.uid === user.uid ? Object.assign(Object.assign({}, r), { unread: 0 }) : r);
250
343
  return [{ id: `rc_${user.uid}`, user, lastMessage: '', lastTime: new Date().toISOString(), unread: 0, isPaused: false }, ...prev];
251
344
  });
252
- }, [data, selectUser, userListCtx]);
253
- const handleBackFromChat = useCallback(() => {
345
+ }, [data, selectUser, userListCtx, viewerUidForSocket, joinRoom]);
346
+ const handleBackFromChat = (0, react_1.useCallback)(() => {
254
347
  setListEntranceAnimation(false);
348
+ if (activeUser) {
349
+ const roomId = [activeUser.uid, viewerUidForSocket].sort().join('_');
350
+ leaveRoom(roomId);
351
+ }
255
352
  clearChat();
256
353
  setUserListCtx(chatReturnCtx);
257
354
  setScreen('user-list');
258
- }, [clearChat, chatReturnCtx]);
259
- const handleOpenTicket = useCallback((id) => {
355
+ }, [clearChat, chatReturnCtx, activeUser, viewerUidForSocket, leaveRoom]);
356
+ const handleOpenTicket = (0, react_1.useCallback)((id) => {
260
357
  setListEntranceAnimation(false);
261
358
  setViewingTicketId(id);
262
359
  setScreen('ticket-detail');
263
360
  setActiveTab('tickets');
264
361
  }, []);
265
- const handleTabChange = useCallback((tab) => {
362
+ const handleTabChange = (0, react_1.useCallback)((tab) => {
266
363
  setListEntranceAnimation(false);
267
364
  setActiveTab(tab);
268
365
  setScreen(tab === 'home' ? 'home' : tab === 'chats' ? 'recent-chats' : 'tickets');
269
366
  }, []);
270
- useEffect(() => {
367
+ (0, react_1.useEffect)(() => {
271
368
  if (!listEntranceAnimation)
272
369
  return;
273
370
  const t = window.setTimeout(() => setListEntranceAnimation(false), 520);
274
371
  return () => window.clearTimeout(t);
275
372
  }, [listEntranceAnimation]);
276
- /* ── Block/Unblock ───────────────────────────────────────────────────── */
277
- const handleBlock = useCallback(() => {
373
+ /* ── Block/Unblock ──────────────────────────────────────────────────── */
374
+ const handleBlock = (0, react_1.useCallback)(() => {
278
375
  if (!activeUser)
279
376
  return;
280
377
  setBlockedUids(prev => [...prev, activeUser.uid]);
378
+ emitBlock(activeUser.uid);
281
379
  clearChat();
282
380
  setScreen('block-list');
283
381
  setActiveTab('home');
284
- }, [activeUser, clearChat]);
285
- const handleUnblock = useCallback((uid) => {
382
+ }, [activeUser, clearChat, emitBlock]);
383
+ const handleUnblock = (0, react_1.useCallback)((uid) => {
286
384
  setBlockedUids(prev => prev.filter(id => id !== uid));
287
- }, []);
288
- /* ── Tickets ─────────────────────────────────────────────────────────── */
289
- const handleRaiseTicket = useCallback((title, desc, priority) => {
385
+ emitUnblock(uid);
386
+ }, [emitUnblock]);
387
+ /* ── Tickets ────────────────────────────────────────────────────────── */
388
+ const handleRaiseTicket = (0, react_1.useCallback)((title, desc, priority) => {
290
389
  const t = {
291
390
  id: `TKT-${String(Date.now()).slice(-4)}`,
292
391
  title, description: desc, status: 'open', priority,
@@ -299,27 +398,31 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
299
398
  setScreen('ticket-detail');
300
399
  setActiveTab('tickets');
301
400
  }, []);
302
- /* ── Pause sync back into recent chats ──────────────────────────────── */
303
- const handleTogglePause = useCallback(() => {
304
- togglePause();
401
+ /* ── Pause sync back into recent chats ─────────────────────────────── */
402
+ const handleTogglePause = (0, react_1.useCallback)(() => {
403
+ _togglePause();
305
404
  if (activeUser) {
306
405
  setRecentChats(prev => prev.map(r => r.user.uid === activeUser.uid ? Object.assign(Object.assign({}, r), { isPaused: !isPaused }) : r));
307
406
  }
308
- }, [togglePause, activeUser, isPaused]);
309
- /* ── Call ────────────────────────────────────────────────────────────── */
310
- const handleStartCall = useCallback((withVideo) => {
407
+ }, [_togglePause, activeUser, isPaused]);
408
+ /* ── Call ───────────────────────────────────────────────────────────── */
409
+ const handleStartCall = (0, react_1.useCallback)((withVideo) => {
311
410
  if (!activeUser)
312
411
  return;
313
- startCall(activeUser, withVideo);
412
+ _startCall(activeUser, withVideo);
314
413
  setScreen('call');
315
- }, [activeUser, startCall]);
316
- const handleEndCall = useCallback(() => {
317
- endCall();
414
+ }, [activeUser, _startCall]);
415
+ const handleEndCall = (0, react_1.useCallback)(() => {
416
+ _endCall();
417
+ setCallMinimized(false);
318
418
  setScreen('chat');
319
- }, [endCall]);
320
- /* ── Derived ─────────────────────────────────────────────────────────── */
321
- const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
322
- const widgetConfig = useMemo(() => {
419
+ }, [_endCall]);
420
+ const minimizeCall = (0, react_1.useCallback)(() => {
421
+ setCallMinimized(true);
422
+ closeDrawer();
423
+ }, [closeDrawer]);
424
+ /* ── Derived config (must be declared before callbacks that use it) ── */
425
+ const widgetConfig = (0, react_1.useMemo)(() => {
323
426
  var _a;
324
427
  if (!(data === null || data === void 0 ? void 0 : data.widget))
325
428
  return undefined;
@@ -333,9 +436,36 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
333
436
  }
334
437
  return w;
335
438
  }, [data === null || data === void 0 ? void 0 : data.widget, viewer]);
439
+ /* ── Transfer (developer feature) ──────────────────────────────────── */
440
+ const handleTransferToDeveloper = (0, react_1.useCallback)((dev) => {
441
+ var _a;
442
+ if (!activeUser || !widgetConfig)
443
+ return;
444
+ const agent = ((_a = widgetConfig.viewerName) === null || _a === void 0 ? void 0 : _a.trim()) || 'Agent';
445
+ const roomId = [activeUser.uid, viewerUidForSocket].sort().join('_');
446
+ const transferNote = {
447
+ id: `tr_${Date.now()}_${Math.random().toString(36).slice(2)}`,
448
+ senderId: 'me',
449
+ receiverId: dev.uid,
450
+ text: `— ${agent} transferred this conversation from ${activeUser.name} to ${dev.name} —`,
451
+ timestamp: new Date().toISOString(),
452
+ type: 'text',
453
+ status: 'sent',
454
+ };
455
+ emitTransfer(roomId, dev.uid, transferNote.text);
456
+ selectUser(dev, [...messages, transferNote]);
457
+ }, [activeUser, messages, selectUser, widgetConfig, viewerUidForSocket, emitTransfer]);
458
+ /* ── Add participant (developer feature) ────────────────────────────── */
459
+ const handleAddParticipant = (0, react_1.useCallback)((uid) => {
460
+ if (!activeUser)
461
+ return;
462
+ const roomId = [activeUser.uid, viewerUidForSocket].sort().join('_');
463
+ emitAddParticipant(roomId, uid);
464
+ }, [activeUser, viewerUidForSocket, emitAddParticipant]);
465
+ /* ── Derived ────────────────────────────────────────────────────────── */
466
+ const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
336
467
  const primaryColor = theme.primaryColor;
337
- /** All developers are listed; only end-`user` rows are filtered by `viewer.projectId`. */
338
- const allUsers = useMemo(() => {
468
+ const allUsers = (0, react_1.useMemo)(() => {
339
469
  var _a, _b;
340
470
  if (!data)
341
471
  return [];
@@ -343,10 +473,9 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
343
473
  const devs = (_b = data.developers) !== null && _b !== void 0 ? _b : [];
344
474
  if (!pid)
345
475
  return [...devs, ...data.users];
346
- const usersInProject = data.users.filter(u => u.project === pid);
347
- return [...devs, ...usersInProject];
476
+ return [...devs, ...data.users.filter(u => u.project === pid)];
348
477
  }, [data, viewer === null || viewer === void 0 ? void 0 : viewer.projectId]);
349
- const effectiveViewerBlocked = useMemo(() => {
478
+ const effectiveViewerBlocked = (0, react_1.useMemo)(() => {
350
479
  var _a, _b;
351
480
  if (!widgetConfig)
352
481
  return false;
@@ -363,61 +492,28 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
363
492
  const filteredUsers = screen === 'user-list'
364
493
  ? allUsers.filter(u => {
365
494
  if (userListCtx === 'support') {
366
- if (viewerIsDev)
367
- return u.type === 'user';
368
- return u.type === 'developer';
495
+ return viewerIsDev ? u.type === 'user' : u.type === 'developer';
369
496
  }
370
- if (viewerIsDev) {
497
+ if (viewerIsDev)
371
498
  return u.type === 'developer' && u.uid !== viewerUid;
372
- }
373
499
  return u.type === 'user';
374
500
  })
375
501
  : [];
376
- const otherDevelopers = useMemo(() => allUsers.filter(u => u.type === 'developer' && u.uid !== viewerUid), [allUsers, viewerUid]);
502
+ const otherDevelopers = (0, react_1.useMemo)(() => allUsers.filter(u => u.type === 'developer' && u.uid !== viewerUid), [allUsers, viewerUid]);
377
503
  const blockedUsers = allUsers.filter(u => blockedUids.includes(u.uid));
378
- const totalUnread = useMemo(() => recentChats.reduce((sum, c) => { var _a; return sum + Math.max(0, (_a = c.unread) !== null && _a !== void 0 ? _a : 0); }, 0), [recentChats]);
379
- const handleTransferToDeveloper = useCallback((dev) => {
380
- var _a;
381
- if (!activeUser || !widgetConfig)
382
- return;
383
- const agent = ((_a = widgetConfig.viewerName) === null || _a === void 0 ? void 0 : _a.trim()) || 'Agent';
384
- const transferNote = {
385
- id: `tr_${Date.now()}_${Math.random().toString(36).slice(2)}`,
386
- senderId: 'me',
387
- receiverId: dev.uid,
388
- text: `— ${agent} transferred this conversation from ${activeUser.name} to ${dev.name} —`,
389
- timestamp: new Date().toISOString(),
390
- type: 'text',
391
- status: 'sent',
392
- };
393
- selectUser(dev, [...messages, transferNote]);
394
- }, [activeUser, messages, selectUser, widgetConfig]);
395
- /* Position */
504
+ const totalUnread = (0, react_1.useMemo)(() => recentChats.reduce((sum, c) => { var _a; return sum + Math.max(0, (_a = c.unread) !== null && _a !== void 0 ? _a : 0); }, 0), [recentChats]);
396
505
  const posStyle = theme.buttonPosition === 'bottom-left'
397
506
  ? { left: 24, right: 'auto' }
398
507
  : { right: 24, left: 'auto' };
399
- /* No radius on top-left / bottom-left; left-docked panel keeps inner TR/BR curve */
400
508
  const drawerPosStyle = theme.buttonPosition === 'bottom-left'
401
- ? {
402
- left: 0,
403
- borderTopLeftRadius: 0,
404
- borderBottomLeftRadius: 0,
405
- borderTopRightRadius: 16,
406
- borderBottomRightRadius: 16,
407
- }
408
- : {
409
- right: 0,
410
- borderTopLeftRadius: 0,
411
- borderBottomLeftRadius: 0,
412
- };
413
- /* ── Don't render until mounted (SSR safe) ──────────────────────────── */
509
+ ? { left: 0, borderTopLeftRadius: 0, borderBottomLeftRadius: 0, borderTopRightRadius: 16, borderBottomRightRadius: 16 }
510
+ : { right: 0, borderTopLeftRadius: 0, borderBottomLeftRadius: 0 };
414
511
  if (!mounted)
415
512
  return null;
416
- return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
513
+ return (react_1.default.createElement(ErrorBoundary_1.ErrorBoundary, { primaryColor: primaryColor },
514
+ react_1.default.createElement("style", null, `
417
515
  @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
418
-
419
516
  .cw-root * { box-sizing: border-box; font-family: 'DM Sans', 'Segoe UI', sans-serif; }
420
-
421
517
  @keyframes cw-slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
422
518
  @keyframes cw-slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
423
519
  @keyframes cw-slideInLeft { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@@ -426,82 +522,95 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
426
522
  @keyframes cw-slideIn { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: translateX(0); } }
427
523
  @keyframes cw-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
428
524
  @keyframes cw-btnPop { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
429
-
525
+ @keyframes spin { to { transform: rotate(360deg); } }
430
526
  .cw-scroll::-webkit-scrollbar { width: 4px; }
431
527
  .cw-scroll::-webkit-scrollbar-track { background: transparent; }
432
528
  .cw-scroll::-webkit-scrollbar-thumb { background: #e0e0e0; border-radius: 4px; }
433
-
434
529
  .cw-drawer-enter { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideInLeft' : 'cw-slideInRight'} 0.32s cubic-bezier(0.22,1,0.36,1) both; }
435
530
  .cw-drawer-exit { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideOutLeft' : 'cw-slideOutRight'} 0.28s cubic-bezier(0.55,0,1,0.45) both; }
436
-
437
- .cw-drawer-panel {
438
- width: 30%;
439
- max-width: 100vw;
440
- min-width: 0;
441
- }
442
- @media (max-width: 1024px) {
443
- .cw-drawer-panel { width: 100%; }
444
- }
445
- ` }), !isOpen && (_jsxs("button", { className: "cw-root", type: "button", onClick: openDrawer, "aria-label": totalUnread > 0 ? `${theme.buttonLabel}, ${totalUnread} unread` : theme.buttonLabel, title: totalUnread > 0 ? `${totalUnread} unread message${totalUnread === 1 ? '' : 's'}` : theme.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24, zIndex: 9999 }, posStyle), { display: 'flex', alignItems: 'center', gap: 10, padding: '13px 22px', backgroundColor: theme.buttonColor, color: theme.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontSize: 15, fontWeight: 700, boxShadow: `0 8px 28px ${theme.buttonColor}55`, animation: 'cw-btnPop 0.4s cubic-bezier(0.34,1.56,0.64,1)', transition: 'transform 0.2s, box-shadow 0.2s' }), onMouseEnter: e => {
446
- e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
447
- e.currentTarget.style.boxShadow = `0 14px 36px ${theme.buttonColor}66`;
448
- }, onMouseLeave: e => {
449
- e.currentTarget.style.transform = 'scale(1)';
450
- e.currentTarget.style.boxShadow = `0 8px 28px ${theme.buttonColor}55`;
451
- }, children: [_jsxs("span", { style: { position: 'relative', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }, children: [_jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: theme.buttonTextColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }), totalUnread > 0 && (_jsx("span", { style: {
452
- position: 'absolute',
453
- top: -8,
454
- right: -10,
455
- minWidth: 20,
456
- height: 20,
457
- padding: '0 5px',
458
- borderRadius: 999,
459
- background: '#ef4444',
460
- color: '#fff',
461
- fontSize: 11,
462
- fontWeight: 800,
463
- lineHeight: '20px',
464
- textAlign: 'center',
465
- border: '2px solid #fff',
466
- boxSizing: 'border-box',
467
- }, children: totalUnread > 99 ? '99+' : totalUnread }))] }), _jsx("span", { children: theme.buttonLabel })] })), isOpen && (_jsx("div", { "aria-hidden": true, style: {
468
- position: 'fixed', inset: 0, zIndex: 9997,
469
- backgroundColor: 'rgba(0,0,0,0.35)',
470
- opacity: closing ? 0 : 1,
471
- transition: 'opacity 0.3s',
472
- } })), isOpen && (_jsxs("div", { className: `cw-root cw-drawer-panel ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
473
- ? '4px 0 40px rgba(0,0,0,0.18)'
474
- : '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }), children: [cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 }, children: [_jsx("div", { style: {
475
- width: 40, height: 40, borderRadius: '50%',
476
- border: `3px solid ${primaryColor}30`,
477
- borderTopColor: primaryColor,
478
- animation: 'spin 0.8s linear infinite',
479
- } }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` }), _jsx("p", { style: { fontSize: 14, color: '#7b8fa1' }, children: "Loading chat\u2026" })] })), cfgError && !cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\u26A0\uFE0F" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Could not load chat configuration" }), _jsx("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 }, children: cfgError }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), !cfgLoading && !cfgError && widgetConfig && (_jsxs(_Fragment, { children: [screen !== 'chat' && screen !== 'call' && !effectiveViewerBlocked && (_jsx("div", { style: {
480
- position: 'absolute', top: 12,
481
- right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
482
- left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
483
- zIndex: 20, display: 'flex', gap: 6,
484
- }, children: _jsx(CornerBtn, { onClick: closeDrawer, title: "Close", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) }) })), widgetConfig.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: primaryColor })), widgetConfig.status === 'DISABLE' && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\uD83D\uDD12" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Chat is disabled" }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), widgetConfig.status === 'ACTIVE' && effectiveViewerBlocked && (_jsx(ViewerBlockedScreen, { config: widgetConfig, apiKey: apiKey, onClose: closeDrawer })), widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && !permissionsOk && (_jsx(PermissionsGateScreen, { primaryColor: primaryColor, widgetId: widgetConfig.id, onGranted: () => setPermissionsOk(true) })), widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && permissionsOk && (_jsxs("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [screen === 'home' && (_jsx(HomeScreen, { config: widgetConfig, apiKey: apiKey, onNavigate: handleCardClick, onOpenTicket: handleOpenTicket, tickets: tickets })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_e = widgetConfig.viewerType) !== null && _e !== void 0 ? _e : 'user', onBack: () => { setListEntranceAnimation(false); setScreen('home'); }, onSelectUser: handleSelectUser, onBlockList: userListCtx === 'conversation' ? () => setScreen('block-list') : undefined, useHomeHeader: userListCtx === 'support' && widgetConfig.viewerType !== 'developer', animateEntrance: listEntranceAnimation })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: handleBackFromChat, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper, messageSoundEnabled: messageSoundEnabled, onToggleMessageSound: toggleMessageSound })), screen === 'call' && callSession.peer && (_jsx(CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: u => handleSelectUser(u, listCtxForUser(u, viewerIsDev)), animateEntrance: listEntranceAnimation })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, config: widgetConfig, onNewTicket: () => { setListEntranceAnimation(false); setScreen('ticket-new'); }, onSelectTicket: id => {
485
- setListEntranceAnimation(false);
486
- setViewingTicketId(id);
487
- setScreen('ticket-detail');
488
- }, animateEntrance: listEntranceAnimation })), screen === 'ticket-new' && (_jsx(TicketFormScreen, { config: widgetConfig, onSubmit: handleRaiseTicket, onCancel: () => setScreen('tickets') })), screen === 'ticket-detail' && viewingTicketId && ((() => {
489
- const t = tickets.find(x => x.id === viewingTicketId);
490
- return t ? (_jsx(TicketDetailScreen, { ticket: t, config: widgetConfig, onBack: () => { setViewingTicketId(null); setScreen('tickets'); } })) : null;
491
- })()), screen === 'block-list' && (_jsx(BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } }))] })), widgetConfig.status === 'ACTIVE' &&
492
- !effectiveViewerBlocked &&
493
- permissionsOk &&
494
- screen !== 'chat' &&
495
- screen !== 'call' &&
496
- screen !== 'user-list' &&
497
- screen !== 'block-list' &&
498
- screen !== 'ticket-detail' &&
499
- screen !== 'ticket-new' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor }))] }))] }))] }));
531
+ .cw-drawer-panel { width: 30%; max-width: 100vw; min-width: 0; }
532
+ @media (max-width: 1024px) { .cw-drawer-panel { width: 100%; } }
533
+ `),
534
+ socketStatus !== 'connected' && socketStatus !== 'disconnected' && (react_1.default.createElement("div", { style: Object.assign(Object.assign({ position: 'fixed', bottom: 80 }, posStyle), { zIndex: 9996, background: socketStatus === 'connecting' ? '#f59e0b' : '#ef4444', color: '#fff', fontSize: 11, fontWeight: 700, padding: '3px 10px', borderRadius: 20, animation: 'cw-fadeUp 0.3s ease' }) }, socketStatus === 'connecting' ? '● Connecting…' : '● Offline')),
535
+ !isOpen && callMinimized && callInProgress && callSession.peer && (react_1.default.createElement(MiniCallBar_1.MiniCallBar, { session: callSession, primaryColor: primaryColor, buttonPosition: theme.buttonPosition, onExpand: openDrawer, onEnd: handleEndCall })),
536
+ !isOpen && (react_1.default.createElement("button", { className: "cw-root", type: "button", onClick: openDrawer, "aria-label": totalUnread > 0 ? `${theme.buttonLabel}, ${totalUnread} unread` : theme.buttonLabel, title: totalUnread > 0 ? `${totalUnread} unread message${totalUnread === 1 ? '' : 's'}` : theme.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24, zIndex: 9999 }, posStyle), { display: 'flex', alignItems: 'center', gap: 10, padding: '13px 22px', backgroundColor: theme.buttonColor, color: theme.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontSize: 15, fontWeight: 700, boxShadow: `0 8px 28px ${theme.buttonColor}55`, animation: 'cw-btnPop 0.4s cubic-bezier(0.34,1.56,0.64,1)', transition: 'transform 0.2s, box-shadow 0.2s' }), onMouseEnter: e => {
537
+ e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
538
+ e.currentTarget.style.boxShadow = `0 14px 36px ${theme.buttonColor}66`;
539
+ }, onMouseLeave: e => {
540
+ e.currentTarget.style.transform = 'scale(1)';
541
+ e.currentTarget.style.boxShadow = `0 8px 28px ${theme.buttonColor}55`;
542
+ } },
543
+ react_1.default.createElement("span", { style: { position: 'relative', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' } },
544
+ react_1.default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none" },
545
+ react_1.default.createElement("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: theme.buttonTextColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })),
546
+ totalUnread > 0 && (react_1.default.createElement("span", { style: {
547
+ position: 'absolute', top: -8, right: -10,
548
+ minWidth: 20, height: 20, padding: '0 5px', borderRadius: 999,
549
+ background: '#ef4444', color: '#fff', fontSize: 11, fontWeight: 800,
550
+ lineHeight: '20px', textAlign: 'center', border: '2px solid #fff', boxSizing: 'border-box',
551
+ } }, totalUnread > 99 ? '99+' : totalUnread))),
552
+ react_1.default.createElement("span", null, theme.buttonLabel))),
553
+ isOpen && (react_1.default.createElement("div", { "aria-hidden": true, style: {
554
+ position: 'fixed', inset: 0, zIndex: 9997,
555
+ backgroundColor: 'rgba(0,0,0,0.35)',
556
+ opacity: closing ? 0 : 1,
557
+ transition: 'opacity 0.3s',
558
+ } })),
559
+ isOpen && (react_1.default.createElement("div", { className: `cw-root cw-drawer-panel ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
560
+ ? '4px 0 40px rgba(0,0,0,0.18)'
561
+ : '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }) },
562
+ cfgLoading && (react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 } },
563
+ react_1.default.createElement("div", { style: { width: 40, height: 40, borderRadius: '50%', border: `3px solid ${primaryColor}30`, borderTopColor: primaryColor, animation: 'spin 0.8s linear infinite' } }),
564
+ react_1.default.createElement("p", { style: { fontSize: 14, color: '#7b8fa1' } }, "Loading chat\u2026"))),
565
+ cfgError && !cfgLoading && (react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' } },
566
+ react_1.default.createElement("div", { style: { fontSize: 40 } }, "\u26A0\uFE0F"),
567
+ react_1.default.createElement("p", { style: { fontWeight: 700, color: '#1a2332' } }, "Could not load chat configuration"),
568
+ react_1.default.createElement("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 } }, cfgError),
569
+ react_1.default.createElement("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 } }, "Close"))),
570
+ !cfgLoading && !cfgError && widgetConfig && (react_1.default.createElement(ErrorBoundary_1.ErrorBoundary, { primaryColor: primaryColor },
571
+ screen !== 'chat' && screen !== 'call' && !effectiveViewerBlocked && (react_1.default.createElement("div", { style: {
572
+ position: 'absolute', top: 12,
573
+ right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
574
+ left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
575
+ zIndex: 20, display: 'flex', gap: 6,
576
+ } },
577
+ react_1.default.createElement(CornerBtn, { onClick: closeDrawer, title: "Close" },
578
+ react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none" },
579
+ react_1.default.createElement("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }))))),
580
+ widgetConfig.status === 'MAINTENANCE' && react_1.default.createElement(MaintenanceView_1.MaintenanceView, { primaryColor: primaryColor }),
581
+ widgetConfig.status === 'DISABLE' && (react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 } },
582
+ react_1.default.createElement("div", { style: { fontSize: 40 } }, "\uD83D\uDD12"),
583
+ react_1.default.createElement("p", { style: { fontWeight: 700, color: '#1a2332' } }, "Chat is disabled"),
584
+ react_1.default.createElement("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 } }, "Close"))),
585
+ widgetConfig.status === 'ACTIVE' && effectiveViewerBlocked && (react_1.default.createElement(ViewerBlockedScreen_1.ViewerBlockedScreen, { config: widgetConfig, apiKey: apiKey, onClose: closeDrawer })),
586
+ widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && !permissionsOk && (react_1.default.createElement(PermissionsGateScreen_1.PermissionsGateScreen, { primaryColor: primaryColor, widgetId: widgetConfig.id, onGranted: () => setPermissionsOk(true) })),
587
+ widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && permissionsOk && (react_1.default.createElement("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' } },
588
+ screen === 'home' && (react_1.default.createElement(HomeScreen_1.HomeScreen, { config: widgetConfig, apiKey: apiKey, onNavigate: handleCardClick, onOpenTicket: handleOpenTicket, tickets: tickets })),
589
+ screen === 'user-list' && (react_1.default.createElement(UserListScreen_1.UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_h = widgetConfig.viewerType) !== null && _h !== void 0 ? _h : 'user', onBack: () => { setListEntranceAnimation(false); setScreen('home'); }, onSelectUser: handleSelectUser, onBlockList: userListCtx === 'conversation' ? () => setScreen('block-list') : undefined, useHomeHeader: userListCtx === 'support' && widgetConfig.viewerType !== 'developer', animateEntrance: listEntranceAnimation })),
590
+ screen === 'chat' && activeUser && (react_1.default.createElement(ChatScreen_1.ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: handleBackFromChat, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: _reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper, messageSoundEnabled: messageSoundEnabled, onToggleMessageSound: toggleMessageSound })),
591
+ screen === 'call' && callSession.peer && (react_1.default.createElement(CallScreen_1.CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor, onMinimize: minimizeCall })),
592
+ screen === 'recent-chats' && (react_1.default.createElement(RecentChatsScreen_1.RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: u => handleSelectUser(u, listCtxForUser(u, viewerIsDev)), animateEntrance: listEntranceAnimation })),
593
+ screen === 'tickets' && (react_1.default.createElement(TicketScreen_1.TicketScreen, { tickets: tickets, config: widgetConfig, onNewTicket: () => { setListEntranceAnimation(false); setScreen('ticket-new'); }, onSelectTicket: id => {
594
+ setListEntranceAnimation(false);
595
+ setViewingTicketId(id);
596
+ setScreen('ticket-detail');
597
+ }, animateEntrance: listEntranceAnimation })),
598
+ screen === 'ticket-new' && (react_1.default.createElement(TicketFormScreen_1.TicketFormScreen, { config: widgetConfig, onSubmit: handleRaiseTicket, onCancel: () => setScreen('tickets') })),
599
+ screen === 'ticket-detail' && viewingTicketId && (() => {
600
+ const t = tickets.find(x => x.id === viewingTicketId);
601
+ return t ? (react_1.default.createElement(TicketDetailScreen_1.TicketDetailScreen, { ticket: t, config: widgetConfig, onBack: () => { setViewingTicketId(null); setScreen('tickets'); } })) : null;
602
+ })(),
603
+ screen === 'block-list' && (react_1.default.createElement(BlockList_1.BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } })))),
604
+ widgetConfig.status === 'ACTIVE' &&
605
+ !effectiveViewerBlocked && permissionsOk &&
606
+ screen !== 'chat' && screen !== 'call' &&
607
+ screen !== 'user-list' && screen !== 'block-list' &&
608
+ screen !== 'ticket-detail' && screen !== 'ticket-new' && (react_1.default.createElement(BottomTabs_1.BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor }))))))));
500
609
  };
501
- export default ChatWidget;
502
- /* ── Tiny corner button ────────────────────────────────────────────────────── */
503
- const CornerBtn = ({ onClick, title, children }) => (_jsx("button", { onClick: onClick, title: title, style: {
504
- width: 26, height: 26, borderRadius: '50%',
505
- background: 'rgba(0,0,0,0.25)', border: 'none',
610
+ exports.ChatWidget = ChatWidget;
611
+ exports.default = exports.ChatWidget;
612
+ const CornerBtn = ({ onClick, title, children }) => (react_1.default.createElement("button", { onClick: onClick, title: title, style: {
613
+ width: 26, height: 26, borderRadius: '50%', background: 'rgba(0,0,0,0.25)', border: 'none',
506
614
  display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
507
- }, children: children }));
615
+ } }, children));
616
+ //# sourceMappingURL=ChatWidget.js.map