@mobil80-dev/chatbot-widget 2.0.3 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +301 -0
- package/dist/index.umd.js +309 -0
- package/package.json +17 -6
- package/rollup.config.js +14 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/* =========================================================
|
|
2
|
+
GLOBAL SINGLETON REFERENCES
|
|
3
|
+
========================================================= */
|
|
4
|
+
let vcButton = null;
|
|
5
|
+
let vcCard = null;
|
|
6
|
+
let loaderEl = null;
|
|
7
|
+
let stylesInjected = false;
|
|
8
|
+
let isOpen = false;
|
|
9
|
+
|
|
10
|
+
/* =========================================================
|
|
11
|
+
STYLE INJECTION (ONCE)
|
|
12
|
+
========================================================= */
|
|
13
|
+
function injectStylesOnce() {
|
|
14
|
+
if (stylesInjected) return
|
|
15
|
+
stylesInjected = true;
|
|
16
|
+
|
|
17
|
+
const style = document.createElement('style');
|
|
18
|
+
style.innerHTML = `
|
|
19
|
+
.vc-fab {
|
|
20
|
+
box-shadow: 0 8px 20px rgba(0,0,0,.25);
|
|
21
|
+
transition: box-shadow .2s ease, transform .2s ease;
|
|
22
|
+
}
|
|
23
|
+
.vc-fab:hover {
|
|
24
|
+
box-shadow: 0 12px 28px rgba(0,0,0,.3);
|
|
25
|
+
}
|
|
26
|
+
.vc-loader {
|
|
27
|
+
display: inline-flex;
|
|
28
|
+
gap: 6px;
|
|
29
|
+
padding: 8px 12px;
|
|
30
|
+
border-radius: 12px;
|
|
31
|
+
max-width: 80%;
|
|
32
|
+
margin-bottom: 8px;
|
|
33
|
+
}
|
|
34
|
+
.vc-loader span {
|
|
35
|
+
width: 6px;
|
|
36
|
+
height: 6px;
|
|
37
|
+
background: var(--vc-text-muted);
|
|
38
|
+
border-radius: 50%;
|
|
39
|
+
animation: vc-bounce 1.4s infinite ease-in-out both;
|
|
40
|
+
}
|
|
41
|
+
.vc-loader span:nth-child(1) { animation-delay: -0.32s; }
|
|
42
|
+
.vc-loader span:nth-child(2) { animation-delay: -0.16s; }
|
|
43
|
+
@keyframes vc-bounce {
|
|
44
|
+
0%, 80%, 100% { transform: scale(0); }
|
|
45
|
+
40% { transform: scale(1); }
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
document.head.appendChild(style);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* =========================================================
|
|
52
|
+
HELPER TO RESOLVE CONTAINER
|
|
53
|
+
========================================================= */
|
|
54
|
+
function getAttachContainer(selector) {
|
|
55
|
+
if (!selector) return document.body
|
|
56
|
+
if (typeof selector === 'string') {
|
|
57
|
+
const el = document.querySelector(selector);
|
|
58
|
+
return el || document.body
|
|
59
|
+
}
|
|
60
|
+
return selector // accept DOM element directly
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* =========================================================
|
|
64
|
+
BUTTON
|
|
65
|
+
========================================================= */
|
|
66
|
+
function getButton() {
|
|
67
|
+
if (vcButton) return vcButton
|
|
68
|
+
vcButton = document.createElement('div');
|
|
69
|
+
vcButton.className = 'vc-fab';
|
|
70
|
+
return vcButton
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function updateButton(config) {
|
|
74
|
+
const button = getButton();
|
|
75
|
+
button.innerHTML = '';
|
|
76
|
+
|
|
77
|
+
const { buttonType = 'text', buttonContent = '🤖', buttonShape = 'circle', primaryColor = '#2563eb' } = config;
|
|
78
|
+
const isText = buttonType === 'text';
|
|
79
|
+
|
|
80
|
+
if (buttonType === 'image') {
|
|
81
|
+
const img = document.createElement('img');
|
|
82
|
+
img.src = buttonContent;
|
|
83
|
+
img.style.width = '26px';
|
|
84
|
+
img.style.height = '26px';
|
|
85
|
+
img.style.objectFit = 'contain';
|
|
86
|
+
button.appendChild(img);
|
|
87
|
+
} else {
|
|
88
|
+
button.textContent = buttonContent;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const borderRadius = buttonShape === 'pill' ? '999px' : buttonShape === 'square' ? '8px' : '50%';
|
|
92
|
+
|
|
93
|
+
Object.assign(button.style, {
|
|
94
|
+
position: 'fixed',
|
|
95
|
+
bottom: '20px',
|
|
96
|
+
right: '20px',
|
|
97
|
+
width: isText ? 'auto' : '56px',
|
|
98
|
+
height: isText ? 'auto' : '56px',
|
|
99
|
+
minWidth: '56px',
|
|
100
|
+
minHeight: '56px',
|
|
101
|
+
padding: isText ? '10px 16px' : '0',
|
|
102
|
+
borderRadius,
|
|
103
|
+
background: primaryColor,
|
|
104
|
+
color: '#fff',
|
|
105
|
+
display: 'flex',
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
justifyContent: 'center',
|
|
108
|
+
cursor: 'pointer',
|
|
109
|
+
fontSize: '18px',
|
|
110
|
+
fontWeight: '500',
|
|
111
|
+
userSelect: 'none',
|
|
112
|
+
zIndex: 999999
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* =========================================================
|
|
117
|
+
CARD
|
|
118
|
+
========================================================= */
|
|
119
|
+
function getCard() {
|
|
120
|
+
if (vcCard) return vcCard
|
|
121
|
+
vcCard = document.createElement('div');
|
|
122
|
+
return vcCard
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function updateCard(config) {
|
|
126
|
+
const card = getCard();
|
|
127
|
+
|
|
128
|
+
const theme = config.theme === 'dark' ? 'dark' : config.theme === 'light' ? 'light' :
|
|
129
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
130
|
+
const isDark = theme === 'dark';
|
|
131
|
+
|
|
132
|
+
const colors = isDark
|
|
133
|
+
? { cardBg: '#0f172a', headerBorder: '#1e293b', text: '#e5e7eb', mutedText: '#94a3b8', inputBg: '#020617', inputBorder: '#334155', botBubble: '#1e293b', userText: '#ffffff', messagesBg: '#020617' }
|
|
134
|
+
: { cardBg: '#ffffff', headerBorder: '#e5e7eb', text: '#0f172a', mutedText: '#64748b', inputBg: '#ffffff', inputBorder: '#cbd5f5', botBubble: '#e5e7eb', userText: '#ffffff', messagesBg: '#f8fafc' };
|
|
135
|
+
|
|
136
|
+
const primaryColor = config.primaryColor || '#2563eb';
|
|
137
|
+
|
|
138
|
+
card.innerHTML = `
|
|
139
|
+
<div style="padding:12px;border-bottom:1px solid ${colors.headerBorder};color:${colors.mutedText}">
|
|
140
|
+
Powered by <strong>VaultChat</strong>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div id="vc-messages" style="
|
|
144
|
+
flex:1;
|
|
145
|
+
padding:12px;
|
|
146
|
+
overflow-y:auto;
|
|
147
|
+
background:${colors.messagesBg};
|
|
148
|
+
color:${colors.text}">
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div style="display:flex;padding:10px;border-top:1px solid ${colors.headerBorder};background:${colors.cardBg}">
|
|
152
|
+
<input id="vc-input" placeholder="Type your message..."
|
|
153
|
+
style="flex:1;padding:10px;border-radius:8px;border:1px solid ${colors.inputBorder};
|
|
154
|
+
background:${colors.inputBg};color:${colors.text}" />
|
|
155
|
+
<button id="vc-send" style="
|
|
156
|
+
margin-left:8px;padding:0 14px;border:none;border-radius:8px;
|
|
157
|
+
background:${primaryColor};color:white;cursor:pointer">➤</button>
|
|
158
|
+
</div>
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
Object.assign(card.style, {
|
|
162
|
+
position: 'fixed',
|
|
163
|
+
bottom: '90px',
|
|
164
|
+
right: '20px',
|
|
165
|
+
width: '400px',
|
|
166
|
+
height: '420px',
|
|
167
|
+
background: colors.cardBg,
|
|
168
|
+
borderRadius: '12px',
|
|
169
|
+
boxShadow: '0 10px 30px rgba(0,0,0,.3)',
|
|
170
|
+
zIndex: 999999,
|
|
171
|
+
display: isOpen ? 'flex' : 'none',
|
|
172
|
+
flexDirection: 'column'
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
card.style.setProperty('--vc-text-muted', colors.mutedText);
|
|
176
|
+
card.style.setProperty('--vc-bot-bg', colors.botBubble);
|
|
177
|
+
|
|
178
|
+
const messages = card.querySelector('#vc-messages');
|
|
179
|
+
const input = card.querySelector('#vc-input');
|
|
180
|
+
const sendBtn = card.querySelector('#vc-send');
|
|
181
|
+
|
|
182
|
+
// send message
|
|
183
|
+
async function sendMessage() {
|
|
184
|
+
const text = input.value.trim();
|
|
185
|
+
if (!text) return
|
|
186
|
+
|
|
187
|
+
const userMsg = document.createElement('div');
|
|
188
|
+
userMsg.style.cssText = `
|
|
189
|
+
background:${primaryColor};
|
|
190
|
+
color:${colors.userText};
|
|
191
|
+
padding:8px 12px;
|
|
192
|
+
border-radius:12px;
|
|
193
|
+
max-width:80%;
|
|
194
|
+
margin:8px 0 8px auto`;
|
|
195
|
+
userMsg.textContent = text;
|
|
196
|
+
messages.appendChild(userMsg);
|
|
197
|
+
|
|
198
|
+
input.value = '';
|
|
199
|
+
messages.scrollTop = messages.scrollHeight;
|
|
200
|
+
|
|
201
|
+
if (!config.apiKey) {
|
|
202
|
+
addBotMessage('⚠️ API key not configured');
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
showLoader(messages, sendBtn);
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
const res = await fetch('https://api.vaultchat.io/askChatbot', {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
headers: { 'Content-Type': 'application/json' },
|
|
212
|
+
body: JSON.stringify({ api_key: config.apiKey, question: text })
|
|
213
|
+
});
|
|
214
|
+
const data = await res.json();
|
|
215
|
+
hideLoader(sendBtn);
|
|
216
|
+
addBotMessage(data?.data?.blocks?.map(b => b.text).join('\n') || 'No response');
|
|
217
|
+
} catch {
|
|
218
|
+
hideLoader(sendBtn);
|
|
219
|
+
addBotMessage('⚠️ Something went wrong');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function addBotMessage(text) {
|
|
224
|
+
const div = document.createElement('div');
|
|
225
|
+
div.style.cssText = `
|
|
226
|
+
background:${colors.botBubble};
|
|
227
|
+
color:${colors.text};
|
|
228
|
+
padding:8px 12px;
|
|
229
|
+
border-radius:12px;
|
|
230
|
+
max-width:80%;
|
|
231
|
+
margin-bottom:8px`;
|
|
232
|
+
div.textContent = text;
|
|
233
|
+
messages.appendChild(div);
|
|
234
|
+
messages.scrollTop = messages.scrollHeight;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// events
|
|
238
|
+
sendBtn.onclick = sendMessage;
|
|
239
|
+
input.addEventListener('keydown', e => { if (e.key === 'Enter') sendMessage(); });
|
|
240
|
+
|
|
241
|
+
return { messages, input, sendBtn }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* =========================================================
|
|
245
|
+
LOADER
|
|
246
|
+
========================================================= */
|
|
247
|
+
function showLoader(container, sendBtn) {
|
|
248
|
+
loaderEl = document.createElement('div');
|
|
249
|
+
loaderEl.className = 'vc-loader';
|
|
250
|
+
loaderEl.innerHTML = '<span></span><span></span><span></span>';
|
|
251
|
+
container.appendChild(loaderEl);
|
|
252
|
+
|
|
253
|
+
if (sendBtn) {
|
|
254
|
+
sendBtn.disabled = true;
|
|
255
|
+
sendBtn.style.opacity = '0.6';
|
|
256
|
+
sendBtn.style.cursor = 'not-allowed';
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function hideLoader(sendBtn) {
|
|
261
|
+
loaderEl?.remove();
|
|
262
|
+
loaderEl = null;
|
|
263
|
+
|
|
264
|
+
if (sendBtn) {
|
|
265
|
+
sendBtn.disabled = false;
|
|
266
|
+
sendBtn.style.opacity = '1';
|
|
267
|
+
sendBtn.style.cursor = 'pointer';
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* =========================================================
|
|
272
|
+
PUBLIC API
|
|
273
|
+
========================================================= */
|
|
274
|
+
const VaultChat = {
|
|
275
|
+
init(config = {}) {
|
|
276
|
+
injectStylesOnce();
|
|
277
|
+
updateButton(config);
|
|
278
|
+
|
|
279
|
+
const mountEl = getAttachContainer(config.attachToElement);
|
|
280
|
+
mountEl.appendChild(vcButton);
|
|
281
|
+
|
|
282
|
+
updateCard(config);
|
|
283
|
+
mountEl.appendChild(vcCard);
|
|
284
|
+
|
|
285
|
+
vcButton.onclick = () => {
|
|
286
|
+
isOpen = !isOpen;
|
|
287
|
+
vcCard.style.display = isOpen ? 'flex' : 'none';
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
destroy() {
|
|
292
|
+
vcButton?.remove();
|
|
293
|
+
vcCard?.remove();
|
|
294
|
+
vcButton = null;
|
|
295
|
+
vcCard = null;
|
|
296
|
+
loaderEl = null;
|
|
297
|
+
isOpen = false;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
export { VaultChat as default };
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.VaultChat = factory());
|
|
5
|
+
})(this, (function () { 'use strict';
|
|
6
|
+
|
|
7
|
+
/* =========================================================
|
|
8
|
+
GLOBAL SINGLETON REFERENCES
|
|
9
|
+
========================================================= */
|
|
10
|
+
let vcButton = null;
|
|
11
|
+
let vcCard = null;
|
|
12
|
+
let loaderEl = null;
|
|
13
|
+
let stylesInjected = false;
|
|
14
|
+
let isOpen = false;
|
|
15
|
+
|
|
16
|
+
/* =========================================================
|
|
17
|
+
STYLE INJECTION (ONCE)
|
|
18
|
+
========================================================= */
|
|
19
|
+
function injectStylesOnce() {
|
|
20
|
+
if (stylesInjected) return
|
|
21
|
+
stylesInjected = true;
|
|
22
|
+
|
|
23
|
+
const style = document.createElement('style');
|
|
24
|
+
style.innerHTML = `
|
|
25
|
+
.vc-fab {
|
|
26
|
+
box-shadow: 0 8px 20px rgba(0,0,0,.25);
|
|
27
|
+
transition: box-shadow .2s ease, transform .2s ease;
|
|
28
|
+
}
|
|
29
|
+
.vc-fab:hover {
|
|
30
|
+
box-shadow: 0 12px 28px rgba(0,0,0,.3);
|
|
31
|
+
}
|
|
32
|
+
.vc-loader {
|
|
33
|
+
display: inline-flex;
|
|
34
|
+
gap: 6px;
|
|
35
|
+
padding: 8px 12px;
|
|
36
|
+
border-radius: 12px;
|
|
37
|
+
max-width: 80%;
|
|
38
|
+
margin-bottom: 8px;
|
|
39
|
+
}
|
|
40
|
+
.vc-loader span {
|
|
41
|
+
width: 6px;
|
|
42
|
+
height: 6px;
|
|
43
|
+
background: var(--vc-text-muted);
|
|
44
|
+
border-radius: 50%;
|
|
45
|
+
animation: vc-bounce 1.4s infinite ease-in-out both;
|
|
46
|
+
}
|
|
47
|
+
.vc-loader span:nth-child(1) { animation-delay: -0.32s; }
|
|
48
|
+
.vc-loader span:nth-child(2) { animation-delay: -0.16s; }
|
|
49
|
+
@keyframes vc-bounce {
|
|
50
|
+
0%, 80%, 100% { transform: scale(0); }
|
|
51
|
+
40% { transform: scale(1); }
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
document.head.appendChild(style);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* =========================================================
|
|
58
|
+
HELPER TO RESOLVE CONTAINER
|
|
59
|
+
========================================================= */
|
|
60
|
+
function getAttachContainer(selector) {
|
|
61
|
+
if (!selector) return document.body
|
|
62
|
+
if (typeof selector === 'string') {
|
|
63
|
+
const el = document.querySelector(selector);
|
|
64
|
+
return el || document.body
|
|
65
|
+
}
|
|
66
|
+
return selector // accept DOM element directly
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* =========================================================
|
|
70
|
+
BUTTON
|
|
71
|
+
========================================================= */
|
|
72
|
+
function getButton() {
|
|
73
|
+
if (vcButton) return vcButton
|
|
74
|
+
vcButton = document.createElement('div');
|
|
75
|
+
vcButton.className = 'vc-fab';
|
|
76
|
+
return vcButton
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function updateButton(config) {
|
|
80
|
+
const button = getButton();
|
|
81
|
+
button.innerHTML = '';
|
|
82
|
+
|
|
83
|
+
const { buttonType = 'text', buttonContent = '🤖', buttonShape = 'circle', primaryColor = '#2563eb' } = config;
|
|
84
|
+
const isText = buttonType === 'text';
|
|
85
|
+
|
|
86
|
+
if (buttonType === 'image') {
|
|
87
|
+
const img = document.createElement('img');
|
|
88
|
+
img.src = buttonContent;
|
|
89
|
+
img.style.width = '26px';
|
|
90
|
+
img.style.height = '26px';
|
|
91
|
+
img.style.objectFit = 'contain';
|
|
92
|
+
button.appendChild(img);
|
|
93
|
+
} else {
|
|
94
|
+
button.textContent = buttonContent;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const borderRadius = buttonShape === 'pill' ? '999px' : buttonShape === 'square' ? '8px' : '50%';
|
|
98
|
+
|
|
99
|
+
Object.assign(button.style, {
|
|
100
|
+
position: 'fixed',
|
|
101
|
+
bottom: '20px',
|
|
102
|
+
right: '20px',
|
|
103
|
+
width: isText ? 'auto' : '56px',
|
|
104
|
+
height: isText ? 'auto' : '56px',
|
|
105
|
+
minWidth: '56px',
|
|
106
|
+
minHeight: '56px',
|
|
107
|
+
padding: isText ? '10px 16px' : '0',
|
|
108
|
+
borderRadius,
|
|
109
|
+
background: primaryColor,
|
|
110
|
+
color: '#fff',
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
justifyContent: 'center',
|
|
114
|
+
cursor: 'pointer',
|
|
115
|
+
fontSize: '18px',
|
|
116
|
+
fontWeight: '500',
|
|
117
|
+
userSelect: 'none',
|
|
118
|
+
zIndex: 999999
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* =========================================================
|
|
123
|
+
CARD
|
|
124
|
+
========================================================= */
|
|
125
|
+
function getCard() {
|
|
126
|
+
if (vcCard) return vcCard
|
|
127
|
+
vcCard = document.createElement('div');
|
|
128
|
+
return vcCard
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function updateCard(config) {
|
|
132
|
+
const card = getCard();
|
|
133
|
+
|
|
134
|
+
const theme = config.theme === 'dark' ? 'dark' : config.theme === 'light' ? 'light' :
|
|
135
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
136
|
+
const isDark = theme === 'dark';
|
|
137
|
+
|
|
138
|
+
const colors = isDark
|
|
139
|
+
? { cardBg: '#0f172a', headerBorder: '#1e293b', text: '#e5e7eb', mutedText: '#94a3b8', inputBg: '#020617', inputBorder: '#334155', botBubble: '#1e293b', userText: '#ffffff', messagesBg: '#020617' }
|
|
140
|
+
: { cardBg: '#ffffff', headerBorder: '#e5e7eb', text: '#0f172a', mutedText: '#64748b', inputBg: '#ffffff', inputBorder: '#cbd5f5', botBubble: '#e5e7eb', userText: '#ffffff', messagesBg: '#f8fafc' };
|
|
141
|
+
|
|
142
|
+
const primaryColor = config.primaryColor || '#2563eb';
|
|
143
|
+
|
|
144
|
+
card.innerHTML = `
|
|
145
|
+
<div style="padding:12px;border-bottom:1px solid ${colors.headerBorder};color:${colors.mutedText}">
|
|
146
|
+
Powered by <strong>VaultChat</strong>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div id="vc-messages" style="
|
|
150
|
+
flex:1;
|
|
151
|
+
padding:12px;
|
|
152
|
+
overflow-y:auto;
|
|
153
|
+
background:${colors.messagesBg};
|
|
154
|
+
color:${colors.text}">
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div style="display:flex;padding:10px;border-top:1px solid ${colors.headerBorder};background:${colors.cardBg}">
|
|
158
|
+
<input id="vc-input" placeholder="Type your message..."
|
|
159
|
+
style="flex:1;padding:10px;border-radius:8px;border:1px solid ${colors.inputBorder};
|
|
160
|
+
background:${colors.inputBg};color:${colors.text}" />
|
|
161
|
+
<button id="vc-send" style="
|
|
162
|
+
margin-left:8px;padding:0 14px;border:none;border-radius:8px;
|
|
163
|
+
background:${primaryColor};color:white;cursor:pointer">➤</button>
|
|
164
|
+
</div>
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
Object.assign(card.style, {
|
|
168
|
+
position: 'fixed',
|
|
169
|
+
bottom: '90px',
|
|
170
|
+
right: '20px',
|
|
171
|
+
width: '400px',
|
|
172
|
+
height: '420px',
|
|
173
|
+
background: colors.cardBg,
|
|
174
|
+
borderRadius: '12px',
|
|
175
|
+
boxShadow: '0 10px 30px rgba(0,0,0,.3)',
|
|
176
|
+
zIndex: 999999,
|
|
177
|
+
display: isOpen ? 'flex' : 'none',
|
|
178
|
+
flexDirection: 'column'
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
card.style.setProperty('--vc-text-muted', colors.mutedText);
|
|
182
|
+
card.style.setProperty('--vc-bot-bg', colors.botBubble);
|
|
183
|
+
|
|
184
|
+
const messages = card.querySelector('#vc-messages');
|
|
185
|
+
const input = card.querySelector('#vc-input');
|
|
186
|
+
const sendBtn = card.querySelector('#vc-send');
|
|
187
|
+
|
|
188
|
+
// send message
|
|
189
|
+
async function sendMessage() {
|
|
190
|
+
const text = input.value.trim();
|
|
191
|
+
if (!text) return
|
|
192
|
+
|
|
193
|
+
const userMsg = document.createElement('div');
|
|
194
|
+
userMsg.style.cssText = `
|
|
195
|
+
background:${primaryColor};
|
|
196
|
+
color:${colors.userText};
|
|
197
|
+
padding:8px 12px;
|
|
198
|
+
border-radius:12px;
|
|
199
|
+
max-width:80%;
|
|
200
|
+
margin:8px 0 8px auto`;
|
|
201
|
+
userMsg.textContent = text;
|
|
202
|
+
messages.appendChild(userMsg);
|
|
203
|
+
|
|
204
|
+
input.value = '';
|
|
205
|
+
messages.scrollTop = messages.scrollHeight;
|
|
206
|
+
|
|
207
|
+
if (!config.apiKey) {
|
|
208
|
+
addBotMessage('⚠️ API key not configured');
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
showLoader(messages, sendBtn);
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const res = await fetch('https://api.vaultchat.io/askChatbot', {
|
|
216
|
+
method: 'POST',
|
|
217
|
+
headers: { 'Content-Type': 'application/json' },
|
|
218
|
+
body: JSON.stringify({ api_key: config.apiKey, question: text })
|
|
219
|
+
});
|
|
220
|
+
const data = await res.json();
|
|
221
|
+
hideLoader(sendBtn);
|
|
222
|
+
addBotMessage(data?.data?.blocks?.map(b => b.text).join('\n') || 'No response');
|
|
223
|
+
} catch {
|
|
224
|
+
hideLoader(sendBtn);
|
|
225
|
+
addBotMessage('⚠️ Something went wrong');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function addBotMessage(text) {
|
|
230
|
+
const div = document.createElement('div');
|
|
231
|
+
div.style.cssText = `
|
|
232
|
+
background:${colors.botBubble};
|
|
233
|
+
color:${colors.text};
|
|
234
|
+
padding:8px 12px;
|
|
235
|
+
border-radius:12px;
|
|
236
|
+
max-width:80%;
|
|
237
|
+
margin-bottom:8px`;
|
|
238
|
+
div.textContent = text;
|
|
239
|
+
messages.appendChild(div);
|
|
240
|
+
messages.scrollTop = messages.scrollHeight;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// events
|
|
244
|
+
sendBtn.onclick = sendMessage;
|
|
245
|
+
input.addEventListener('keydown', e => { if (e.key === 'Enter') sendMessage(); });
|
|
246
|
+
|
|
247
|
+
return { messages, input, sendBtn }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* =========================================================
|
|
251
|
+
LOADER
|
|
252
|
+
========================================================= */
|
|
253
|
+
function showLoader(container, sendBtn) {
|
|
254
|
+
loaderEl = document.createElement('div');
|
|
255
|
+
loaderEl.className = 'vc-loader';
|
|
256
|
+
loaderEl.innerHTML = '<span></span><span></span><span></span>';
|
|
257
|
+
container.appendChild(loaderEl);
|
|
258
|
+
|
|
259
|
+
if (sendBtn) {
|
|
260
|
+
sendBtn.disabled = true;
|
|
261
|
+
sendBtn.style.opacity = '0.6';
|
|
262
|
+
sendBtn.style.cursor = 'not-allowed';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function hideLoader(sendBtn) {
|
|
267
|
+
loaderEl?.remove();
|
|
268
|
+
loaderEl = null;
|
|
269
|
+
|
|
270
|
+
if (sendBtn) {
|
|
271
|
+
sendBtn.disabled = false;
|
|
272
|
+
sendBtn.style.opacity = '1';
|
|
273
|
+
sendBtn.style.cursor = 'pointer';
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* =========================================================
|
|
278
|
+
PUBLIC API
|
|
279
|
+
========================================================= */
|
|
280
|
+
const VaultChat = {
|
|
281
|
+
init(config = {}) {
|
|
282
|
+
injectStylesOnce();
|
|
283
|
+
updateButton(config);
|
|
284
|
+
|
|
285
|
+
const mountEl = getAttachContainer(config.attachToElement);
|
|
286
|
+
mountEl.appendChild(vcButton);
|
|
287
|
+
|
|
288
|
+
updateCard(config);
|
|
289
|
+
mountEl.appendChild(vcCard);
|
|
290
|
+
|
|
291
|
+
vcButton.onclick = () => {
|
|
292
|
+
isOpen = !isOpen;
|
|
293
|
+
vcCard.style.display = isOpen ? 'flex' : 'none';
|
|
294
|
+
};
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
destroy() {
|
|
298
|
+
vcButton?.remove();
|
|
299
|
+
vcCard?.remove();
|
|
300
|
+
vcButton = null;
|
|
301
|
+
vcCard = null;
|
|
302
|
+
loaderEl = null;
|
|
303
|
+
isOpen = false;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return VaultChat;
|
|
308
|
+
|
|
309
|
+
}));
|
package/package.json
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobil80-dev/chatbot-widget",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Drop-in JavaScript chat widget for websites (no iframe, no framework)",
|
|
5
|
-
"
|
|
5
|
+
"author": "NanthaGopal",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"unpkg": "dist/index.umd.js",
|
|
11
|
+
"jsdelivr": "dist/index.umd.js",
|
|
12
|
+
|
|
6
13
|
"type": "module",
|
|
14
|
+
|
|
7
15
|
"keywords": [
|
|
8
16
|
"chat",
|
|
9
17
|
"chatbot",
|
|
@@ -11,9 +19,12 @@
|
|
|
11
19
|
"javascript",
|
|
12
20
|
"vaultchat"
|
|
13
21
|
],
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "rollup -c"
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"rollup": "^4.54.0"
|
|
18
29
|
}
|
|
19
30
|
}
|