@mobil80-dev/chatbot-widget 1.0.9 → 2.0.1
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/README.md +66 -17
- package/index.js +297 -214
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,33 +4,60 @@ A lightweight JavaScript chatbot widget powered by VaultChat, designed to be emb
|
|
|
4
4
|
|
|
5
5
|
## ✨ Features
|
|
6
6
|
|
|
7
|
-
- Floating chat button
|
|
7
|
+
- Floating chat button (fully customizable)
|
|
8
8
|
- Chat card UI
|
|
9
|
-
- API-based chatbot responses
|
|
10
|
-
-
|
|
11
|
-
- No
|
|
9
|
+
- API-based chatbot responses
|
|
10
|
+
- Light & Dark theme support
|
|
11
|
+
- No iframe
|
|
12
|
+
- No dependencies
|
|
12
13
|
- Easy setup
|
|
13
14
|
|
|
14
15
|
## 📦 Installation
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
```bash
|
|
16
18
|
npm install @mobil80-dev/chatbot-widget
|
|
17
19
|
```
|
|
18
20
|
|
|
21
|
+
## 🎨 Theme (Light / Dark)
|
|
22
|
+
|
|
23
|
+
VaultChat supports light and dark themes to match your application UI.
|
|
24
|
+
This helps improve text visibility and appearance when used inside dark-themed websites.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
theme: 'light' | 'dark'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## ✅ Supported Button Types
|
|
31
|
+
|
|
32
|
+
You can use text, image, or custom HTML as the floating button.
|
|
33
|
+
|
|
34
|
+
## ✅ Supported Button Shapes
|
|
35
|
+
|
|
36
|
+
You can use circle, square, pill as the floating button shape.
|
|
37
|
+
|
|
19
38
|
## 🌐 HTML / CSS
|
|
39
|
+
|
|
20
40
|
### 🚀 Usage
|
|
41
|
+
|
|
21
42
|
```
|
|
22
43
|
<script type="module">
|
|
23
44
|
import VaultChat from 'https://unpkg.com/@mobil80-dev/chatbot-widget/dist/index.js'
|
|
24
45
|
|
|
25
46
|
VaultChat.init({
|
|
26
47
|
apiKey: 'YOUR_API_KEY',
|
|
27
|
-
primaryColor: '#7c3aed'
|
|
48
|
+
primaryColor: '#7c3aed',
|
|
49
|
+
theme: 'light' | 'dark'
|
|
50
|
+
buttonContent: '💬', // content | image Url | emote
|
|
51
|
+
buttonType: 'text', // text | image
|
|
52
|
+
buttonShape: 'circle' // circle | square | pill
|
|
28
53
|
})
|
|
29
54
|
</script>
|
|
30
55
|
```
|
|
31
56
|
|
|
32
57
|
## 🧩 Vue
|
|
58
|
+
|
|
33
59
|
### 🚀 Usage (Vue 3)
|
|
60
|
+
|
|
34
61
|
```
|
|
35
62
|
import { onMounted } from 'vue'
|
|
36
63
|
import VaultChat from '@mobil80-dev/chatbot-widget'
|
|
@@ -38,21 +65,31 @@ import VaultChat from '@mobil80-dev/chatbot-widget'
|
|
|
38
65
|
onMounted(() => {
|
|
39
66
|
VaultChat.init({
|
|
40
67
|
apiKey: 'YOUR_API_KEY',
|
|
41
|
-
primaryColor: '#7c3aed'
|
|
68
|
+
primaryColor: '#7c3aed',
|
|
69
|
+
theme: 'light' | 'dark'
|
|
70
|
+
buttonContent: '💬', // content | image Url | emote
|
|
71
|
+
buttonType: 'test', // text | image
|
|
72
|
+
buttonShape: 'circle' // circle | square | pill
|
|
42
73
|
})
|
|
43
74
|
})
|
|
44
75
|
```
|
|
76
|
+
|
|
45
77
|
## ⚛️ React
|
|
46
78
|
|
|
47
79
|
### 🚀 Usage
|
|
48
|
-
|
|
80
|
+
|
|
81
|
+
```
|
|
49
82
|
import { useEffect } from 'react'
|
|
50
83
|
import VaultChat from '@mobil80-dev/chatbot-widget'
|
|
51
84
|
|
|
52
85
|
useEffect(() => {
|
|
53
86
|
VaultChat.init({
|
|
54
87
|
apiKey: 'YOUR_API_KEY',
|
|
55
|
-
primaryColor: '#7c3aed'
|
|
88
|
+
primaryColor: '#7c3aed',
|
|
89
|
+
theme: 'light' | 'dark'
|
|
90
|
+
buttonContent: '💬', // content | image Url | emote
|
|
91
|
+
buttonType: 'text', // text | image
|
|
92
|
+
buttonShape: 'circle' // circle | square | pill
|
|
56
93
|
})
|
|
57
94
|
}, [])
|
|
58
95
|
```
|
|
@@ -60,29 +97,41 @@ useEffect(() => {
|
|
|
60
97
|
## 🅰️ Angular
|
|
61
98
|
|
|
62
99
|
### 🚀 Usage
|
|
63
|
-
|
|
100
|
+
|
|
101
|
+
```
|
|
64
102
|
import VaultChat from '@mobil80-dev/chatbot-widget'
|
|
65
103
|
|
|
66
104
|
VaultChat.init({
|
|
67
105
|
apiKey: 'YOUR_API_KEY',
|
|
68
|
-
primaryColor: '#7c3aed'
|
|
106
|
+
primaryColor: '#7c3aed',
|
|
107
|
+
theme: 'light' | 'dark'
|
|
108
|
+
buttonContent: '💬', // content | image Url | emote
|
|
109
|
+
buttonType: 'text', // text | image
|
|
110
|
+
buttonShape: 'circle' // circle | square | pill
|
|
69
111
|
})
|
|
70
112
|
|
|
71
113
|
```
|
|
72
114
|
|
|
73
|
-
|
|
74
115
|
📌 Call this inside ngOnInit() of your root or layout component.
|
|
75
116
|
|
|
76
|
-
## ⚙️ Configuration
|
|
117
|
+
## ⚙️ Configuration Reference
|
|
118
|
+
|
|
77
119
|
```
|
|
78
|
-
Option
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
| Option | Type | Description |
|
|
121
|
+
| ------------- | ------ | ----------------------------------- |
|
|
122
|
+
| apiKey | string | Your VaultChat API key |
|
|
123
|
+
| primaryColor | string | Primary UI color |
|
|
124
|
+
| theme | string | `light` or `dark` |
|
|
125
|
+
| buttonContent | string | text, image URL, |
|
|
126
|
+
| buttonType | string | `text` | `image` |
|
|
127
|
+
| buttonShape | string | `circle` | `square` | `pill` |
|
|
128
|
+
|
|
81
129
|
|
|
82
130
|
```
|
|
131
|
+
|
|
83
132
|
## 📄 License
|
|
84
133
|
|
|
85
134
|
```
|
|
86
135
|
MIT
|
|
87
136
|
Mobil80
|
|
88
|
-
```
|
|
137
|
+
```
|
package/index.js
CHANGED
|
@@ -1,232 +1,315 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
: config.theme === 'light'
|
|
10
|
-
? 'light'
|
|
11
|
-
: window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
12
|
-
? 'dark'
|
|
13
|
-
: 'light'
|
|
14
|
-
|
|
15
|
-
const colors =
|
|
16
|
-
theme === 'dark'
|
|
17
|
-
? {
|
|
18
|
-
cardBg: '#0f172a',
|
|
19
|
-
headerBorder: '#1e293b',
|
|
20
|
-
text: '#e5e7eb',
|
|
21
|
-
mutedText: '#94a3b8',
|
|
22
|
-
inputBg: '#020617',
|
|
23
|
-
inputBorder: '#334155',
|
|
24
|
-
botBubble: '#1e293b',
|
|
25
|
-
userText: '#ffffff',
|
|
26
|
-
messagesBg: '#020617'
|
|
27
|
-
}
|
|
28
|
-
: {
|
|
29
|
-
cardBg: '#ffffff',
|
|
30
|
-
headerBorder: '#e5e7eb',
|
|
31
|
-
text: '#0f172a',
|
|
32
|
-
mutedText: '#64748b',
|
|
33
|
-
inputBg: '#ffffff',
|
|
34
|
-
inputBorder: '#cbd5f5',
|
|
35
|
-
botBubble: '#e5e7eb',
|
|
36
|
-
userText: '#ffffff',
|
|
37
|
-
messagesBg: '#f8fafc'
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const primaryColor = config.primaryColor || '#2563eb'
|
|
41
|
-
|
|
42
|
-
/* ============================
|
|
43
|
-
FLOATING BUTTON
|
|
44
|
-
============================ */
|
|
45
|
-
const button = document.createElement('div')
|
|
46
|
-
|
|
47
|
-
if (config.buttonHtml) {
|
|
48
|
-
button.innerHTML = config.buttonHtml
|
|
49
|
-
} else if (config.buttonIconUrl) {
|
|
50
|
-
const img = document.createElement('img')
|
|
51
|
-
img.src = config.buttonIconUrl
|
|
52
|
-
img.style.width = '26px'
|
|
53
|
-
img.style.height = '26px'
|
|
54
|
-
img.style.objectFit = 'contain'
|
|
55
|
-
button.appendChild(img)
|
|
56
|
-
} else if (config.buttonText) {
|
|
57
|
-
button.textContent = config.buttonText
|
|
58
|
-
} else {
|
|
59
|
-
button.textContent = '💬'
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
Object.assign(button.style, {
|
|
63
|
-
position: 'fixed',
|
|
64
|
-
bottom: '20px',
|
|
65
|
-
right: '20px',
|
|
66
|
-
width: '56px',
|
|
67
|
-
height: '56px',
|
|
68
|
-
borderRadius: '50%',
|
|
69
|
-
background: primaryColor,
|
|
70
|
-
color: '#fff',
|
|
71
|
-
display: 'flex',
|
|
72
|
-
alignItems: 'center',
|
|
73
|
-
justifyContent: 'center',
|
|
74
|
-
cursor: 'pointer',
|
|
75
|
-
fontSize: '24px',
|
|
76
|
-
zIndex: 999999
|
|
77
|
-
})
|
|
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
|
|
78
9
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<div style="
|
|
86
|
-
padding:12px;
|
|
87
|
-
border-bottom:1px solid ${colors.headerBorder};
|
|
88
|
-
color:${colors.mutedText};
|
|
89
|
-
font-size:14px;
|
|
90
|
-
user-select:none">
|
|
91
|
-
Powered by <strong>VaultChat</strong>
|
|
92
|
-
</div>
|
|
93
|
-
|
|
94
|
-
<div id="vc-messages" style="
|
|
95
|
-
flex:1;
|
|
96
|
-
padding:12px;
|
|
97
|
-
overflow-y:auto;
|
|
98
|
-
background:${colors.messagesBg};
|
|
99
|
-
color:${colors.text}">
|
|
100
|
-
</div>
|
|
101
|
-
|
|
102
|
-
<div style="
|
|
103
|
-
display:flex;
|
|
104
|
-
padding:10px;
|
|
105
|
-
border-top:1px solid ${colors.headerBorder};
|
|
106
|
-
background:${colors.cardBg}">
|
|
107
|
-
<input
|
|
108
|
-
id="vc-input"
|
|
109
|
-
placeholder="Type your message..."
|
|
110
|
-
style="
|
|
111
|
-
flex:1;
|
|
112
|
-
padding:10px;
|
|
113
|
-
border-radius:8px;
|
|
114
|
-
border:1px solid ${colors.inputBorder};
|
|
115
|
-
background:${colors.inputBg};
|
|
116
|
-
color:${colors.text};
|
|
117
|
-
outline:none"
|
|
118
|
-
/>
|
|
119
|
-
<button id="vc-send" style="
|
|
120
|
-
margin-left:8px;
|
|
121
|
-
padding:0 14px;
|
|
122
|
-
border:none;
|
|
123
|
-
border-radius:8px;
|
|
124
|
-
background:${primaryColor};
|
|
125
|
-
color:white;
|
|
126
|
-
cursor:pointer">
|
|
127
|
-
➤
|
|
128
|
-
</button>
|
|
129
|
-
</div>
|
|
130
|
-
`
|
|
131
|
-
|
|
132
|
-
Object.assign(card.style, {
|
|
133
|
-
position: 'fixed',
|
|
134
|
-
bottom: '90px',
|
|
135
|
-
right: '20px',
|
|
136
|
-
width: '400px',
|
|
137
|
-
height: '420px',
|
|
138
|
-
background: colors.cardBg,
|
|
139
|
-
borderRadius: '12px',
|
|
140
|
-
boxShadow: '0 10px 30px rgba(0,0,0,.3)',
|
|
141
|
-
zIndex: 999999,
|
|
142
|
-
display: 'none',
|
|
143
|
-
flexDirection: 'column'
|
|
144
|
-
})
|
|
10
|
+
/* =========================================================
|
|
11
|
+
STYLE INJECTION (ONCE)
|
|
12
|
+
========================================================= */
|
|
13
|
+
function injectStylesOnce () {
|
|
14
|
+
if (stylesInjected) return
|
|
15
|
+
stylesInjected = true
|
|
145
16
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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);
|
|
150
25
|
}
|
|
151
26
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const input = card.querySelector('#vc-input')
|
|
160
|
-
const sendBtn = card.querySelector('#vc-send')
|
|
161
|
-
|
|
162
|
-
function addUserMessage (text) {
|
|
163
|
-
const div = document.createElement('div')
|
|
164
|
-
div.style.cssText = `
|
|
165
|
-
background:${primaryColor};
|
|
166
|
-
color:${colors.userText};
|
|
167
|
-
padding:8px 12px;
|
|
168
|
-
border-radius:12px;
|
|
169
|
-
max-width:80%;
|
|
170
|
-
margin:8px 0 8px auto`
|
|
171
|
-
div.textContent = text
|
|
172
|
-
messages.appendChild(div)
|
|
173
|
-
messages.scrollTop = messages.scrollHeight
|
|
27
|
+
.vc-loader {
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
gap: 6px;
|
|
30
|
+
padding: 8px 12px;
|
|
31
|
+
border-radius: 12px;
|
|
32
|
+
max-width: 80%;
|
|
33
|
+
margin-bottom: 8px;
|
|
174
34
|
}
|
|
35
|
+
.vc-loader span {
|
|
36
|
+
width: 6px;
|
|
37
|
+
height: 6px;
|
|
38
|
+
background: var(--vc-text-muted);
|
|
39
|
+
border-radius: 50%;
|
|
40
|
+
animation: vc-bounce 1.4s infinite ease-in-out both;
|
|
41
|
+
}
|
|
42
|
+
.vc-loader span:nth-child(1) { animation-delay: -0.32s; }
|
|
43
|
+
.vc-loader span:nth-child(2) { animation-delay: -0.16s; }
|
|
175
44
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
background:${colors.botBubble};
|
|
180
|
-
color:${colors.text};
|
|
181
|
-
padding:8px 12px;
|
|
182
|
-
border-radius:12px;
|
|
183
|
-
max-width:80%;
|
|
184
|
-
margin-bottom:8px`
|
|
185
|
-
div.textContent = text
|
|
186
|
-
messages.appendChild(div)
|
|
187
|
-
messages.scrollTop = messages.scrollHeight
|
|
45
|
+
@keyframes vc-bounce {
|
|
46
|
+
0%, 80%, 100% { transform: scale(0); }
|
|
47
|
+
40% { transform: scale(1); }
|
|
188
48
|
}
|
|
49
|
+
`
|
|
50
|
+
document.head.appendChild(style)
|
|
51
|
+
}
|
|
189
52
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
53
|
+
/* =========================================================
|
|
54
|
+
BUTTON
|
|
55
|
+
========================================================= */
|
|
56
|
+
function getButton () {
|
|
57
|
+
if (vcButton) return vcButton
|
|
193
58
|
|
|
194
|
-
|
|
195
|
-
|
|
59
|
+
vcButton = document.createElement('div')
|
|
60
|
+
vcButton.className = 'vc-fab'
|
|
61
|
+
document.body.appendChild(vcButton)
|
|
196
62
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
addBotMessage(
|
|
200
|
-
'⚠️ API key not configured. Please add apiKey in VaultChat.init().'
|
|
201
|
-
)
|
|
202
|
-
return
|
|
203
|
-
}
|
|
63
|
+
return vcButton
|
|
64
|
+
}
|
|
204
65
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
66
|
+
function updateButton (config) {
|
|
67
|
+
const button = getButton()
|
|
68
|
+
button.innerHTML = ''
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
buttonType = 'text',
|
|
72
|
+
buttonContent = 'Chat',
|
|
73
|
+
buttonShape = 'circle',
|
|
74
|
+
primaryColor = '#2563eb'
|
|
75
|
+
} = config
|
|
76
|
+
|
|
77
|
+
const isText = buttonType === 'text'
|
|
78
|
+
|
|
79
|
+
if (buttonType === 'image') {
|
|
80
|
+
const img = document.createElement('img')
|
|
81
|
+
img.src = buttonContent
|
|
82
|
+
img.style.width = '26px'
|
|
83
|
+
img.style.height = '26px'
|
|
84
|
+
img.style.objectFit = 'contain'
|
|
85
|
+
button.appendChild(img)
|
|
86
|
+
} else {
|
|
87
|
+
button.textContent = buttonContent
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const borderRadius =
|
|
91
|
+
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
|
+
|
|
122
|
+
vcCard = document.createElement('div')
|
|
123
|
+
document.body.appendChild(vcCard)
|
|
124
|
+
return vcCard
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function updateCard (config) {
|
|
128
|
+
const card = getCard()
|
|
129
|
+
|
|
130
|
+
const theme =
|
|
131
|
+
config.theme === 'dark'
|
|
132
|
+
? 'dark'
|
|
133
|
+
: config.theme === 'light'
|
|
134
|
+
? 'light'
|
|
135
|
+
: window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
136
|
+
? 'dark'
|
|
137
|
+
: 'light'
|
|
138
|
+
|
|
139
|
+
const isDark = theme === 'dark'
|
|
140
|
+
|
|
141
|
+
const colors = isDark
|
|
142
|
+
? {
|
|
143
|
+
cardBg: '#0f172a',
|
|
144
|
+
headerBorder: '#1e293b',
|
|
145
|
+
text: '#e5e7eb',
|
|
146
|
+
mutedText: '#94a3b8',
|
|
147
|
+
inputBg: '#020617',
|
|
148
|
+
inputBorder: '#334155',
|
|
149
|
+
botBubble: '#1e293b',
|
|
150
|
+
userText: '#ffffff',
|
|
151
|
+
messagesBg: '#020617'
|
|
152
|
+
}
|
|
153
|
+
: {
|
|
154
|
+
cardBg: '#ffffff',
|
|
155
|
+
headerBorder: '#e5e7eb',
|
|
156
|
+
text: '#0f172a',
|
|
157
|
+
mutedText: '#64748b',
|
|
158
|
+
inputBg: '#ffffff',
|
|
159
|
+
inputBorder: '#cbd5f5',
|
|
160
|
+
botBubble: '#e5e7eb',
|
|
161
|
+
userText: '#ffffff',
|
|
162
|
+
messagesBg: '#f8fafc'
|
|
223
163
|
}
|
|
164
|
+
|
|
165
|
+
const primaryColor = config.primaryColor || '#2563eb'
|
|
166
|
+
|
|
167
|
+
card.innerHTML = `
|
|
168
|
+
<div style="padding:12px;border-bottom:1px solid ${colors.headerBorder};color:${colors.mutedText}">
|
|
169
|
+
Powered by <strong>VaultChat</strong>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div id="vc-messages" style="
|
|
173
|
+
flex:1;
|
|
174
|
+
padding:12px;
|
|
175
|
+
overflow-y:auto;
|
|
176
|
+
background:${colors.messagesBg};
|
|
177
|
+
color:${colors.text}">
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div style="display:flex;padding:10px;border-top:1px solid ${colors.headerBorder};background:${colors.cardBg}">
|
|
181
|
+
<input id="vc-input" placeholder="Type your message..."
|
|
182
|
+
style="flex:1;padding:10px;border-radius:8px;border:1px solid ${colors.inputBorder};
|
|
183
|
+
background:${colors.inputBg};color:${colors.text}" />
|
|
184
|
+
<button id="vc-send" style="
|
|
185
|
+
margin-left:8px;padding:0 14px;border:none;border-radius:8px;
|
|
186
|
+
background:${primaryColor};color:white;cursor:pointer">➤</button>
|
|
187
|
+
</div>
|
|
188
|
+
`
|
|
189
|
+
|
|
190
|
+
Object.assign(card.style, {
|
|
191
|
+
position: 'fixed',
|
|
192
|
+
bottom: '90px',
|
|
193
|
+
right: '20px',
|
|
194
|
+
width: '400px',
|
|
195
|
+
height: '420px',
|
|
196
|
+
background: colors.cardBg,
|
|
197
|
+
borderRadius: '12px',
|
|
198
|
+
boxShadow: '0 10px 30px rgba(0,0,0,.3)',
|
|
199
|
+
zIndex: 999999,
|
|
200
|
+
display: isOpen ? 'flex' : 'none',
|
|
201
|
+
flexDirection: 'column'
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
card.style.setProperty('--vc-text-muted', colors.mutedText)
|
|
205
|
+
card.style.setProperty('--vc-bot-bg', colors.botBubble)
|
|
206
|
+
|
|
207
|
+
const messages = card.querySelector('#vc-messages')
|
|
208
|
+
const input = card.querySelector('#vc-input')
|
|
209
|
+
const sendBtn = card.querySelector('#vc-send')
|
|
210
|
+
|
|
211
|
+
function sendMessage () {
|
|
212
|
+
const text = input.value.trim()
|
|
213
|
+
if (!text) return
|
|
214
|
+
|
|
215
|
+
const userMsg = document.createElement('div')
|
|
216
|
+
userMsg.style.cssText = `
|
|
217
|
+
background:${config.primaryColor || '#2563eb'};
|
|
218
|
+
color:${config.theme === 'dark' ? '#fff' : '#fff'};
|
|
219
|
+
padding:8px 12px;
|
|
220
|
+
border-radius:12px;
|
|
221
|
+
max-width:80%;
|
|
222
|
+
margin:8px 0 8px auto`
|
|
223
|
+
userMsg.textContent = text
|
|
224
|
+
messages.appendChild(userMsg)
|
|
225
|
+
input.value = ''
|
|
226
|
+
messages.scrollTop = messages.scrollHeight
|
|
227
|
+
|
|
228
|
+
if (!config.apiKey) {
|
|
229
|
+
addBotMessage('⚠️ API key not configured')
|
|
230
|
+
return
|
|
224
231
|
}
|
|
225
232
|
|
|
226
|
-
sendBtn
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
showLoader(messages, sendBtn)
|
|
234
|
+
|
|
235
|
+
fetch('https://api.vaultchat.io/askChatbot', {
|
|
236
|
+
method: 'POST',
|
|
237
|
+
headers: { 'Content-Type': 'application/json' },
|
|
238
|
+
body: JSON.stringify({ api_key: config.apiKey, question: text })
|
|
229
239
|
})
|
|
240
|
+
.then(res => res.json())
|
|
241
|
+
.then(data => {
|
|
242
|
+
hideLoader(sendBtn)
|
|
243
|
+
addBotMessage(
|
|
244
|
+
data?.data?.blocks?.map(b => b.text).join('\n') || 'No response'
|
|
245
|
+
)
|
|
246
|
+
})
|
|
247
|
+
.catch(() => {
|
|
248
|
+
hideLoader(sendBtn)
|
|
249
|
+
addBotMessage('⚠️ Something went wrong')
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
sendBtn.onclick = sendMessage
|
|
253
|
+
input.addEventListener('keydown', e => {
|
|
254
|
+
if (e.key === 'Enter') sendMessage()
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
function addBotMessage (text) {
|
|
258
|
+
const div = document.createElement('div')
|
|
259
|
+
div.style.cssText = `
|
|
260
|
+
background:${colors.botBubble};
|
|
261
|
+
color:${colors.text};
|
|
262
|
+
padding:8px 12px;
|
|
263
|
+
border-radius:12px;
|
|
264
|
+
max-width:80%;
|
|
265
|
+
margin-bottom:8px`
|
|
266
|
+
div.textContent = text
|
|
267
|
+
messages.appendChild(div)
|
|
268
|
+
messages.scrollTop = messages.scrollHeight
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* =========================================================
|
|
273
|
+
LOADER
|
|
274
|
+
========================================================= */
|
|
275
|
+
function showLoader (container, sendBtn) {
|
|
276
|
+
loaderEl = document.createElement('div')
|
|
277
|
+
loaderEl.className = 'vc-loader'
|
|
278
|
+
loaderEl.innerHTML = '<span></span><span></span><span></span>'
|
|
279
|
+
container.appendChild(loaderEl)
|
|
280
|
+
|
|
281
|
+
if (sendBtn) {
|
|
282
|
+
sendBtn.disabled = true
|
|
283
|
+
sendBtn.style.opacity = '0.6'
|
|
284
|
+
sendBtn.style.cursor = 'not-allowed'
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function hideLoader (sendBtn) {
|
|
289
|
+
loaderEl?.remove()
|
|
290
|
+
loaderEl = null
|
|
291
|
+
|
|
292
|
+
if (sendBtn) {
|
|
293
|
+
sendBtn.disabled = false
|
|
294
|
+
sendBtn.style.opacity = '1'
|
|
295
|
+
sendBtn.style.cursor = 'pointer'
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* =========================================================
|
|
300
|
+
PUBLIC API
|
|
301
|
+
========================================================= */
|
|
302
|
+
const VaultChat = {
|
|
303
|
+
init (config = {}) {
|
|
304
|
+
injectStylesOnce()
|
|
305
|
+
updateButton(config)
|
|
306
|
+
updateCard(config)
|
|
307
|
+
|
|
308
|
+
const button = getButton()
|
|
309
|
+
button.onclick = () => {
|
|
310
|
+
isOpen = !isOpen
|
|
311
|
+
vcCard.style.display = isOpen ? 'flex' : 'none'
|
|
312
|
+
}
|
|
230
313
|
}
|
|
231
314
|
}
|
|
232
315
|
|