@ihoomanai/chat-widget 3.0.19 → 3.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +41 -45
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +41 -45
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/index.umd.js +41 -45
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/dist/widget.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/widget.ts +35 -43
package/src/widget.ts
CHANGED
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
WidgetView,
|
|
18
18
|
} from './types';
|
|
19
19
|
|
|
20
|
-
const VERSION = '3.0.
|
|
20
|
+
const VERSION = '3.0.21';
|
|
21
21
|
const STORAGE_PREFIX = 'ihooman_chat_';
|
|
22
22
|
const DEFAULT_SERVER_URL = 'https://api.ihooman.ai';
|
|
23
23
|
|
|
@@ -91,6 +91,7 @@ let state: WidgetState = {
|
|
|
91
91
|
|
|
92
92
|
let currentView: WidgetView = 'chat';
|
|
93
93
|
let isLiveAgentMode = false;
|
|
94
|
+
let isConversationClosed = false;
|
|
94
95
|
let shownProactiveIds: string[] = [];
|
|
95
96
|
let proactiveCooldowns: Record<string, number> = {};
|
|
96
97
|
let proactiveCheckInterval: ReturnType<typeof setInterval> | null = null;
|
|
@@ -334,6 +335,8 @@ function generateStyles(): string {
|
|
|
334
335
|
.ihooman-input-btn.send:hover { opacity: 0.9; }
|
|
335
336
|
.ihooman-input-btn.send:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
336
337
|
.ihooman-input-btn svg { width: 16px; height: 16px; }
|
|
338
|
+
.ihooman-input:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
339
|
+
.ihooman-input-btn.attach:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
337
340
|
.ihooman-file-input { display: none; }
|
|
338
341
|
.ihooman-powered { text-align: center; padding: 8px; font-size: 11px; color: ${mutedColor}; background: ${bgColor}; }
|
|
339
342
|
.ihooman-powered a { color: ${primaryColor}; text-decoration: none; }
|
|
@@ -684,23 +687,11 @@ function addMessage(content: string, sender: 'user' | 'bot' = 'bot', metadata: M
|
|
|
684
687
|
`<button class="ihooman-quick-reply" data-text="${escapeHtml(qr.text)}">${escapeHtml(qr.text)}</button>`
|
|
685
688
|
).join('')}</div>`;
|
|
686
689
|
}
|
|
687
|
-
|
|
688
|
-
// Feedback buttons for bot messages
|
|
689
|
-
let feedbackHtml = '';
|
|
690
|
-
if (sender === 'bot' && !metadata?.is_system_message) {
|
|
691
|
-
feedbackHtml = `
|
|
692
|
-
<div class="ihooman-feedback-btns">
|
|
693
|
-
<button class="ihooman-feedback-btn" data-feedback="up" title="Helpful">${icons.thumbUp}</button>
|
|
694
|
-
<button class="ihooman-feedback-btn" data-feedback="down" title="Not helpful">${icons.thumbDown}</button>
|
|
695
|
-
</div>
|
|
696
|
-
`;
|
|
697
|
-
}
|
|
698
690
|
|
|
699
691
|
el.innerHTML = `
|
|
700
692
|
<div class="ihooman-message-content">${parseMarkdown(content)}</div>
|
|
701
693
|
${escalationButtonsHtml}
|
|
702
694
|
${quickRepliesHtml}
|
|
703
|
-
${feedbackHtml}
|
|
704
695
|
${config.showTimestamps ? `<div class="ihooman-message-time">${formatTime(message.timestamp)}</div>` : ''}
|
|
705
696
|
`;
|
|
706
697
|
|
|
@@ -723,16 +714,6 @@ function addMessage(content: string, sender: 'user' | 'bot' = 'bot', metadata: M
|
|
|
723
714
|
});
|
|
724
715
|
});
|
|
725
716
|
|
|
726
|
-
// Feedback listeners
|
|
727
|
-
el.querySelectorAll('.ihooman-feedback-btn').forEach(btn => {
|
|
728
|
-
btn.addEventListener('click', () => {
|
|
729
|
-
const feedback = (btn as HTMLElement).dataset.feedback;
|
|
730
|
-
el.querySelectorAll('.ihooman-feedback-btn').forEach(b => b.classList.remove('active'));
|
|
731
|
-
btn.classList.add('active');
|
|
732
|
-
submitFeedback(message.id, feedback === 'up' ? 'positive' : 'negative');
|
|
733
|
-
});
|
|
734
|
-
});
|
|
735
|
-
|
|
736
717
|
const typing = elements.messages.querySelector('.ihooman-typing');
|
|
737
718
|
if (typing) typing.remove();
|
|
738
719
|
|
|
@@ -1049,22 +1030,6 @@ function stopLiveAgentPolling(): void {
|
|
|
1049
1030
|
}
|
|
1050
1031
|
}
|
|
1051
1032
|
|
|
1052
|
-
async function submitFeedback(messageId: string, feedbackType: 'positive' | 'negative'): Promise<void> {
|
|
1053
|
-
try {
|
|
1054
|
-
await fetch(`${config.serverUrl}/api/v1/feedback/submit?widget_id=${encodeURIComponent(config.widgetId)}`, {
|
|
1055
|
-
method: 'POST',
|
|
1056
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1057
|
-
body: JSON.stringify({
|
|
1058
|
-
message_id: messageId,
|
|
1059
|
-
session_id: state.sessionId,
|
|
1060
|
-
feedback_type: feedbackType,
|
|
1061
|
-
}),
|
|
1062
|
-
});
|
|
1063
|
-
} catch (error) {
|
|
1064
|
-
console.error('Error submitting feedback:', error);
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
1033
|
async function handleSurveySubmit(): Promise<void> {
|
|
1069
1034
|
const starsContainer = elements.surveyView?.querySelector('.ihooman-survey-stars');
|
|
1070
1035
|
const activeStars = starsContainer?.querySelectorAll('.ihooman-survey-star.active');
|
|
@@ -1081,10 +1046,11 @@ async function handleSurveySubmit(): Promise<void> {
|
|
|
1081
1046
|
method: 'POST',
|
|
1082
1047
|
headers: { 'Content-Type': 'application/json' },
|
|
1083
1048
|
body: JSON.stringify({
|
|
1084
|
-
|
|
1049
|
+
survey_config_id: config.surveyConfig?.id,
|
|
1085
1050
|
session_id: state.sessionId,
|
|
1051
|
+
visitor_id: state.visitorId,
|
|
1086
1052
|
rating,
|
|
1087
|
-
comment,
|
|
1053
|
+
comment: comment || null,
|
|
1088
1054
|
}),
|
|
1089
1055
|
});
|
|
1090
1056
|
|
|
@@ -1170,7 +1136,26 @@ function hideTyping(): void {
|
|
|
1170
1136
|
if (typing) typing.remove();
|
|
1171
1137
|
}
|
|
1172
1138
|
|
|
1139
|
+
function disableInput(): void {
|
|
1140
|
+
if (elements.input) {
|
|
1141
|
+
elements.input.disabled = true;
|
|
1142
|
+
elements.input.placeholder = 'Conversation closed — start a new chat to continue';
|
|
1143
|
+
}
|
|
1144
|
+
if (elements.sendBtn) elements.sendBtn.disabled = true;
|
|
1145
|
+
if (elements.attachBtn) elements.attachBtn.disabled = true;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
function enableInput(): void {
|
|
1149
|
+
if (elements.input) {
|
|
1150
|
+
elements.input.disabled = false;
|
|
1151
|
+
elements.input.placeholder = config.placeholder || 'Type a message...';
|
|
1152
|
+
}
|
|
1153
|
+
if (elements.sendBtn) elements.sendBtn.disabled = !elements.input?.value.trim();
|
|
1154
|
+
if (elements.attachBtn) elements.attachBtn.disabled = false;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1173
1157
|
function handleSendClick(): void {
|
|
1158
|
+
if (isConversationClosed) return;
|
|
1174
1159
|
const content = elements.input?.value.trim();
|
|
1175
1160
|
if (!content) return;
|
|
1176
1161
|
|
|
@@ -1200,6 +1185,7 @@ function hidePresetQuestions(): void {
|
|
|
1200
1185
|
}
|
|
1201
1186
|
|
|
1202
1187
|
async function sendMessageToServer(content: string): Promise<void> {
|
|
1188
|
+
if (isConversationClosed) return;
|
|
1203
1189
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
1204
1190
|
ws.send(JSON.stringify({ type: 'message', content }));
|
|
1205
1191
|
return;
|
|
@@ -1543,10 +1529,12 @@ function connectWebSocket(chatEndpoint?: string): void {
|
|
|
1543
1529
|
emit('escalation:accepted', { type: 'live_agent' });
|
|
1544
1530
|
}
|
|
1545
1531
|
if (metadata.closed_by_agent) {
|
|
1546
|
-
// Agent has closed the conversation -
|
|
1532
|
+
// Agent has closed the conversation - disable input, require new conversation
|
|
1547
1533
|
isLiveAgentMode = false;
|
|
1534
|
+
isConversationClosed = true;
|
|
1548
1535
|
stopLiveAgentPolling();
|
|
1549
1536
|
updateStatusBar('hidden');
|
|
1537
|
+
disableInput();
|
|
1550
1538
|
emit('escalation:end', { type: 'live_agent' });
|
|
1551
1539
|
}
|
|
1552
1540
|
|
|
@@ -1578,8 +1566,10 @@ function connectWebSocket(chatEndpoint?: string): void {
|
|
|
1578
1566
|
emit('escalation:accepted', { type: 'live_agent' });
|
|
1579
1567
|
} else if (data.status === 'disconnected') {
|
|
1580
1568
|
isLiveAgentMode = false;
|
|
1569
|
+
isConversationClosed = true;
|
|
1581
1570
|
stopLiveAgentPolling();
|
|
1582
1571
|
updateStatusBar('hidden');
|
|
1572
|
+
disableInput();
|
|
1583
1573
|
emit('escalation:end', { type: 'live_agent' });
|
|
1584
1574
|
}
|
|
1585
1575
|
} else if (data.type === 'error') {
|
|
@@ -1700,6 +1690,7 @@ function startNewConversation(): void {
|
|
|
1700
1690
|
state.visitorId = null;
|
|
1701
1691
|
state.messages = [];
|
|
1702
1692
|
isLiveAgentMode = false;
|
|
1693
|
+
isConversationClosed = false;
|
|
1703
1694
|
|
|
1704
1695
|
storage('session_id', null);
|
|
1705
1696
|
state.visitorId = generateId('v_');
|
|
@@ -1709,6 +1700,7 @@ function startNewConversation(): void {
|
|
|
1709
1700
|
|
|
1710
1701
|
updateStatusBar('hidden');
|
|
1711
1702
|
stopLiveAgentPolling();
|
|
1703
|
+
enableInput();
|
|
1712
1704
|
|
|
1713
1705
|
reconnectAttempts = 0;
|
|
1714
1706
|
|
|
@@ -1761,7 +1753,7 @@ function destroy(): void {
|
|
|
1761
1753
|
}
|
|
1762
1754
|
|
|
1763
1755
|
function sendMessage(content: string): void {
|
|
1764
|
-
if (!content.trim()) return;
|
|
1756
|
+
if (!content.trim() || isConversationClosed) return;
|
|
1765
1757
|
if (elements.input) elements.input.value = content;
|
|
1766
1758
|
handleSendClick();
|
|
1767
1759
|
}
|