@locdo.tech/botiq-chat-sdk 0.1.0 → 0.2.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.
- package/LICENSE +21 -21
- package/README.md +19 -35
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/{npm-Dbo9SB2H.js → npm-Bo1fPZM3.js} +169 -127
- package/dist/sdk/react.js +1 -1
- package/dist/sdk/vue.d.ts +2 -39
- package/dist/sdk/vue.js +6 -30
- package/package.json +87 -87
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 BotIQ (LocDo.Tech)
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BotIQ (LocDo.Tech)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -21,13 +21,7 @@ You also need a **Widget Embed Key** (`sk-biq-...`) issued from your BotIQ admin
|
|
|
21
21
|
```ts
|
|
22
22
|
import { init } from '@locdo.tech/botiq-chat-sdk';
|
|
23
23
|
|
|
24
|
-
const dispose = init({
|
|
25
|
-
apiKey: 'sk-biq-xxxxxxxxxxxxxxxx',
|
|
26
|
-
apiUrl: 'https://api.botiq.vn',
|
|
27
|
-
botName: 'SpaBot',
|
|
28
|
-
position: 'bottom-right',
|
|
29
|
-
primaryColor: '#F97316',
|
|
30
|
-
});
|
|
24
|
+
const dispose = init({ apiKey: 'sk-biq-xxxxxxxxxxxxxxxx' });
|
|
31
25
|
|
|
32
26
|
// Later, to remove the widget:
|
|
33
27
|
dispose();
|
|
@@ -43,19 +37,11 @@ dispose();
|
|
|
43
37
|
import { BotIQWidget } from '@locdo.tech/botiq-chat-sdk/react';
|
|
44
38
|
|
|
45
39
|
export function App() {
|
|
46
|
-
return
|
|
47
|
-
<BotIQWidget
|
|
48
|
-
apiKey="sk-biq-xxxxxxxxxxxxxxxx"
|
|
49
|
-
apiUrl="https://api.botiq.vn"
|
|
50
|
-
botName="SpaBot"
|
|
51
|
-
position="bottom-right"
|
|
52
|
-
primaryColor="#F97316"
|
|
53
|
-
/>
|
|
54
|
-
);
|
|
40
|
+
return <BotIQWidget apiKey="sk-biq-xxxxxxxxxxxxxxxx" />;
|
|
55
41
|
}
|
|
56
42
|
```
|
|
57
43
|
|
|
58
|
-
> The widget reinitialises only when `apiKey` changes.
|
|
44
|
+
> The widget reinitialises only when `apiKey` changes.
|
|
59
45
|
|
|
60
46
|
---
|
|
61
47
|
|
|
@@ -67,13 +53,7 @@ import { BotIQWidget } from '@locdo.tech/botiq-chat-sdk/vue';
|
|
|
67
53
|
</script>
|
|
68
54
|
|
|
69
55
|
<template>
|
|
70
|
-
<BotIQWidget
|
|
71
|
-
api-key="sk-biq-xxxxxxxxxxxxxxxx"
|
|
72
|
-
api-url="https://api.botiq.vn"
|
|
73
|
-
bot-name="SpaBot"
|
|
74
|
-
position="bottom-right"
|
|
75
|
-
primary-color="#F97316"
|
|
76
|
-
/>
|
|
56
|
+
<BotIQWidget api-key="sk-biq-xxxxxxxxxxxxxxxx" />
|
|
77
57
|
</template>
|
|
78
58
|
```
|
|
79
59
|
|
|
@@ -81,15 +61,11 @@ import { BotIQWidget } from '@locdo.tech/botiq-chat-sdk/vue';
|
|
|
81
61
|
|
|
82
62
|
## Configuration
|
|
83
63
|
|
|
84
|
-
| Prop | Type | Required |
|
|
85
|
-
|
|
86
|
-
| `apiKey` | `string` | ✅ |
|
|
87
|
-
| `apiUrl` | `string` | ✅ | — |
|
|
88
|
-
| `botName` | `string` | ✅ | — |
|
|
89
|
-
| `position` | `'bottom-right' \| 'bottom-left'` | — | `'bottom-right'` |
|
|
90
|
-
| `primaryColor` | `string` (hex) | — | `'#F97316'` |
|
|
64
|
+
| Prop | Type | Required |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `apiKey` | `string` | ✅ |
|
|
91
67
|
|
|
92
|
-
The
|
|
68
|
+
That's it. The bot name, colors, layout, position, greeting message, and all other visual options are configured by your admin/tenant in the BotIQ dashboard and fetched at runtime from `/widget/meta` — keeping integration code dead simple and letting non-developers tweak the UI without re-deploying.
|
|
93
69
|
|
|
94
70
|
---
|
|
95
71
|
|
|
@@ -101,9 +77,6 @@ For static HTML (no bundler), use the CDN build:
|
|
|
101
77
|
<script
|
|
102
78
|
src="https://bot-q-frontend.vercel.app/widget.js"
|
|
103
79
|
data-api-key="sk-biq-xxxxxxxxxxxxxxxx"
|
|
104
|
-
data-api-url="https://api.botiq.vn"
|
|
105
|
-
data-bot-name="SpaBot"
|
|
106
|
-
data-position="bottom-right"
|
|
107
80
|
></script>
|
|
108
81
|
```
|
|
109
82
|
|
|
@@ -117,6 +90,17 @@ The Widget Embed Key (`sk-biq-...`) is **safe to expose** in HTML/source. Each k
|
|
|
117
90
|
|
|
118
91
|
---
|
|
119
92
|
|
|
93
|
+
## Migrating from 0.1.x
|
|
94
|
+
|
|
95
|
+
`0.2.0` removes the following props from `init()`, `<BotIQWidget>`, and the `<script>` tag — they were redundant because the same values come from your bot config on the backend:
|
|
96
|
+
|
|
97
|
+
- `apiUrl` — production URL is now baked into the bundle
|
|
98
|
+
- `botName`, `position`, `primaryColor`, `data-bot-name`, `data-position`, `data-primary-color`, `data-api-url`
|
|
99
|
+
|
|
100
|
+
If you were setting these from snippet config, the dashboard values now win. Visit **Admin → Bots → Design** to confirm the bot name and colors look right after upgrading.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
120
104
|
## License
|
|
121
105
|
|
|
122
106
|
MIT © [BotIQ (LocDo.Tech)](https://botiq.vn)
|
package/dist/sdk/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./npm-
|
|
1
|
+
import { t as e } from "./npm-Bo1fPZM3.js";
|
|
2
2
|
export { e as init };
|
|
@@ -1,69 +1,101 @@
|
|
|
1
1
|
//#region src/core/config.ts
|
|
2
|
-
var e = {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
|
|
2
|
+
var e = "http://localhost:3001", t = {
|
|
3
|
+
name: "BotIQ",
|
|
4
|
+
design: {
|
|
5
|
+
colors: {
|
|
6
|
+
primary: "#F97316",
|
|
7
|
+
header: "#F97316",
|
|
8
|
+
userBubble: "#F97316",
|
|
9
|
+
botBubble: "#1A1A1A",
|
|
10
|
+
background: "#000000",
|
|
11
|
+
text: "#FFFFFF"
|
|
12
|
+
},
|
|
13
|
+
layout: {
|
|
14
|
+
position: "bottom-right",
|
|
15
|
+
buttonShape: "circle",
|
|
16
|
+
width: 360,
|
|
17
|
+
height: 520
|
|
18
|
+
},
|
|
19
|
+
content: {
|
|
20
|
+
greeting: "Xin chào! Tôi có thể giúp gì cho bạn?",
|
|
21
|
+
suggestionChips: [],
|
|
22
|
+
placeholder: "Nhắn tin..."
|
|
23
|
+
},
|
|
24
|
+
font: "inter"
|
|
25
|
+
}
|
|
26
|
+
}, n = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/, r = new Set(["bottom-right", "bottom-left"]), i = new Set(["circle", "square"]), a = new Set([
|
|
27
|
+
"inter",
|
|
28
|
+
"plus-jakarta",
|
|
29
|
+
"poppins"
|
|
30
|
+
]);
|
|
31
|
+
function o(e) {
|
|
32
|
+
let t = e.colors;
|
|
33
|
+
if (![
|
|
34
|
+
t.primary,
|
|
35
|
+
t.header,
|
|
36
|
+
t.userBubble,
|
|
37
|
+
t.botBubble,
|
|
38
|
+
t.background,
|
|
39
|
+
t.text
|
|
40
|
+
].every((e) => typeof e == "string" && n.test(e)) || !r.has(e.layout.position) || !i.has(e.layout.buttonShape) || !a.has(e.font)) return !1;
|
|
41
|
+
let { width: o, height: s } = e.layout;
|
|
42
|
+
return !(!Number.isFinite(o) || o < 280 || o > 800 || !Number.isFinite(s) || s < 200 || s > 900);
|
|
43
|
+
}
|
|
44
|
+
async function s(e, n) {
|
|
45
|
+
try {
|
|
46
|
+
let r = await fetch(`${n}/widget/meta`, {
|
|
47
|
+
headers: { "X-Api-Key": e },
|
|
48
|
+
signal: AbortSignal.timeout(5e3)
|
|
49
|
+
});
|
|
50
|
+
if (!r.ok) return t;
|
|
51
|
+
let i = await r.json();
|
|
52
|
+
return !i?.design?.colors || !i?.design?.layout || !i?.design?.content || !o(i.design) ? t : {
|
|
53
|
+
name: typeof i.name == "string" && i.name.length > 0 ? i.name : "BotIQ",
|
|
54
|
+
design: i.design
|
|
55
|
+
};
|
|
56
|
+
} catch {
|
|
57
|
+
return t;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function c(t) {
|
|
26
61
|
return {
|
|
27
|
-
apiKey:
|
|
28
|
-
apiUrl:
|
|
29
|
-
botName: e.botName ?? "BotIQ",
|
|
30
|
-
position: e.position === "bottom-left" ? "bottom-left" : "bottom-right",
|
|
31
|
-
primaryColor: t.test(n) ? n : "#F97316"
|
|
62
|
+
apiKey: t.apiKey,
|
|
63
|
+
apiUrl: e
|
|
32
64
|
};
|
|
33
65
|
}
|
|
34
66
|
//#endregion
|
|
35
67
|
//#region src/core/session.ts
|
|
36
|
-
var
|
|
37
|
-
function
|
|
68
|
+
var l = "botiq:sessionId", u = "botiq:history:", d = 10;
|
|
69
|
+
function f() {
|
|
38
70
|
try {
|
|
39
|
-
let e = localStorage.getItem(
|
|
40
|
-
return e || (e = crypto.randomUUID(), localStorage.setItem(
|
|
71
|
+
let e = localStorage.getItem(l);
|
|
72
|
+
return e || (e = crypto.randomUUID(), localStorage.setItem(l, e)), e;
|
|
41
73
|
} catch {
|
|
42
74
|
return crypto.randomUUID();
|
|
43
75
|
}
|
|
44
76
|
}
|
|
45
|
-
function
|
|
77
|
+
function p(e) {
|
|
46
78
|
try {
|
|
47
|
-
let t = localStorage.getItem(
|
|
79
|
+
let t = localStorage.getItem(u + e);
|
|
48
80
|
return t ? JSON.parse(t) : [];
|
|
49
81
|
} catch {
|
|
50
82
|
return [];
|
|
51
83
|
}
|
|
52
84
|
}
|
|
53
|
-
function
|
|
85
|
+
function m(e, t) {
|
|
54
86
|
try {
|
|
55
|
-
let n = [...
|
|
56
|
-
localStorage.setItem(
|
|
87
|
+
let n = [...p(e), ...t].slice(-d);
|
|
88
|
+
localStorage.setItem(u + e, JSON.stringify(n));
|
|
57
89
|
} catch {}
|
|
58
90
|
}
|
|
59
91
|
//#endregion
|
|
60
92
|
//#region src/core/api.ts
|
|
61
|
-
var
|
|
93
|
+
var h = {
|
|
62
94
|
401: "API key không hợp lệ.",
|
|
63
95
|
403: "Widget không được phép trên trang này.",
|
|
64
96
|
429: "Bot đã đạt giới hạn tin nhắn tháng này."
|
|
65
97
|
};
|
|
66
|
-
async function
|
|
98
|
+
async function g(e, t, n, r, i) {
|
|
67
99
|
try {
|
|
68
100
|
let a = await fetch(`${e}/widget/chat`, {
|
|
69
101
|
method: "POST",
|
|
@@ -77,35 +109,35 @@ async function u(e, t, n, r, i) {
|
|
|
77
109
|
history: i
|
|
78
110
|
})
|
|
79
111
|
});
|
|
80
|
-
return a.ok ? (await a.json()).reply || "Không có phản hồi từ bot." :
|
|
112
|
+
return a.ok ? (await a.json()).reply || "Không có phản hồi từ bot." : h[a.status] ?? "Đang gặp sự cố kỹ thuật, vui lòng liên hệ trực tiếp.";
|
|
81
113
|
} catch {
|
|
82
114
|
return "Xin lỗi, không thể kết nối. Vui lòng thử lại.";
|
|
83
115
|
}
|
|
84
116
|
}
|
|
85
117
|
//#endregion
|
|
86
118
|
//#region src/core/state.ts
|
|
87
|
-
var
|
|
119
|
+
var _ = {
|
|
88
120
|
messages: [],
|
|
89
121
|
isLoading: !1,
|
|
90
122
|
isOpen: !1
|
|
91
|
-
},
|
|
92
|
-
function
|
|
93
|
-
return
|
|
123
|
+
}, v = /* @__PURE__ */ new Set();
|
|
124
|
+
function y() {
|
|
125
|
+
return _;
|
|
94
126
|
}
|
|
95
|
-
function
|
|
96
|
-
Object.assign(
|
|
127
|
+
function b(e) {
|
|
128
|
+
Object.assign(_, e);
|
|
97
129
|
let t = {
|
|
98
|
-
...
|
|
99
|
-
messages: [...
|
|
130
|
+
..._,
|
|
131
|
+
messages: [..._.messages]
|
|
100
132
|
};
|
|
101
|
-
|
|
133
|
+
v.forEach((e) => e(t));
|
|
102
134
|
}
|
|
103
|
-
function
|
|
104
|
-
return
|
|
135
|
+
function x(e) {
|
|
136
|
+
return v.add(e), () => v.delete(e);
|
|
105
137
|
}
|
|
106
138
|
//#endregion
|
|
107
139
|
//#region src/core/styles.ts
|
|
108
|
-
var
|
|
140
|
+
var S = {
|
|
109
141
|
inter: {
|
|
110
142
|
url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
|
|
111
143
|
family: "'Inter', system-ui, -apple-system, sans-serif"
|
|
@@ -119,8 +151,8 @@ var g = {
|
|
|
119
151
|
family: "'Poppins', system-ui, -apple-system, sans-serif"
|
|
120
152
|
}
|
|
121
153
|
};
|
|
122
|
-
function
|
|
123
|
-
let { colors: t, layout: n, font: r } = e, i =
|
|
154
|
+
function C(e) {
|
|
155
|
+
let { colors: t, layout: n, font: r } = e, i = S[r] ?? S.inter, a = n.buttonShape === "square" ? "14px" : "50%", o = n.position === "bottom-left" ? "right: auto; left: 24px;" : "right: 24px;", s = n.position === "bottom-left" ? "bottom left" : "bottom right", c = n.position === "bottom-left" ? "right: auto; left: 0;" : "right: 0;";
|
|
124
156
|
return `
|
|
125
157
|
@import url('${i.url}');
|
|
126
158
|
|
|
@@ -377,6 +409,23 @@ function _(e) {
|
|
|
377
409
|
|
|
378
410
|
.input-row { display: flex; align-items: flex-end; gap: 8px; }
|
|
379
411
|
|
|
412
|
+
/* Subtle attribution — half-opacity, hover lifts to full. Doesn't steal focus
|
|
413
|
+
from tenant branding but reminds the customer this is powered by BotIQ. */
|
|
414
|
+
.botiq-badge {
|
|
415
|
+
display: block;
|
|
416
|
+
text-align: center;
|
|
417
|
+
font-size: 10px;
|
|
418
|
+
font-weight: 400;
|
|
419
|
+
color: var(--color-text);
|
|
420
|
+
opacity: 0.4;
|
|
421
|
+
text-decoration: none;
|
|
422
|
+
margin-top: 6px;
|
|
423
|
+
letter-spacing: 0.02em;
|
|
424
|
+
transition: opacity .15s;
|
|
425
|
+
}
|
|
426
|
+
.botiq-badge:hover { opacity: 0.85; }
|
|
427
|
+
.botiq-badge-name { font-weight: 600; color: var(--color-primary); }
|
|
428
|
+
|
|
380
429
|
.input {
|
|
381
430
|
flex: 1;
|
|
382
431
|
min-height: 38px;
|
|
@@ -420,23 +469,23 @@ function _(e) {
|
|
|
420
469
|
}
|
|
421
470
|
//#endregion
|
|
422
471
|
//#region src/core/ui.ts
|
|
423
|
-
var
|
|
424
|
-
function
|
|
472
|
+
var w = "https://bot-q-frontend.vercel.app/", T = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 2H4a2 2 0 00-2 2v18l4-4h14a2 2 0 002-2V4a2 2 0 00-2-2z\"/>\n</svg>", E = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n</svg>", D = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 2a2 2 0 012 2c0 .74-.4 1.38-1 1.72V7h3a3 3 0 013 3v8a3 3 0 01-3 3H8a3 3 0 01-3-3v-8a3 3 0 013-3h3V5.72A2 2 0 0110 4a2 2 0 012-2zm-2 9a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm4 0a1.5 1.5 0 100 3 1.5 1.5 0 000-3z\"/>\n</svg>", O = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/>\n</svg>", k = "<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M20 2H4a2 2 0 00-2 2v18l4-4h14a2 2 0 002-2V4a2 2 0 00-2-2zm-2 10H6v-2h12v2zm0-3H6V7h12v2z\"/>\n</svg>";
|
|
473
|
+
function A(e) {
|
|
425
474
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
426
475
|
}
|
|
427
|
-
function
|
|
428
|
-
let i, a, o, s, c, l, { content:
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
let
|
|
432
|
-
|
|
433
|
-
<span class="icon-chat">${
|
|
434
|
-
<span class="icon-close">${
|
|
435
|
-
`,
|
|
476
|
+
function j(e, t, n, r) {
|
|
477
|
+
let { name: i, design: a } = t, o, s, c, l, u, d, { content: f } = a;
|
|
478
|
+
function p(e) {
|
|
479
|
+
e.setAttribute("data-position", a.layout.position), o = e.attachShadow({ mode: "open" });
|
|
480
|
+
let t = document.createElement("style");
|
|
481
|
+
t.textContent = C(a), o.appendChild(t), s = document.createElement("button"), s.className = "bubble", s.setAttribute("aria-label", `Open ${i} chat`), s.innerHTML = `
|
|
482
|
+
<span class="icon-chat">${T}</span>
|
|
483
|
+
<span class="icon-close">${E}</span>
|
|
484
|
+
`, s.addEventListener("click", r), o.appendChild(s), c = document.createElement("div"), c.className = "chat-window", c.setAttribute("role", "dialog"), c.setAttribute("aria-label", `${i} chat`), c.innerHTML = `
|
|
436
485
|
<div class="chat-header">
|
|
437
|
-
<div class="avatar">${
|
|
486
|
+
<div class="avatar">${D}</div>
|
|
438
487
|
<div class="header-text">
|
|
439
|
-
<span class="bot-name">${
|
|
488
|
+
<span class="bot-name">${A(i)}</span>
|
|
440
489
|
<span class="bot-status">Trực tuyến</span>
|
|
441
490
|
</div>
|
|
442
491
|
<button class="close-btn" aria-label="Close chat">×</button>
|
|
@@ -446,108 +495,101 @@ function w(e, t, n, r) {
|
|
|
446
495
|
<div class="input-row">
|
|
447
496
|
<textarea
|
|
448
497
|
class="input"
|
|
449
|
-
placeholder="${
|
|
498
|
+
placeholder="${A(f.placeholder)}"
|
|
450
499
|
rows="1"
|
|
451
500
|
maxlength="2000"
|
|
452
501
|
aria-label="Message input"
|
|
453
502
|
></textarea>
|
|
454
503
|
<button class="send-btn" aria-label="Send message">
|
|
455
|
-
${
|
|
504
|
+
${O}
|
|
456
505
|
</button>
|
|
457
506
|
</div>
|
|
507
|
+
<a class="botiq-badge" href="${w}" target="_blank" rel="noopener noreferrer">
|
|
508
|
+
Powered by <span class="botiq-badge-name">BotIQ</span>
|
|
509
|
+
</a>
|
|
458
510
|
</div>
|
|
459
|
-
`,
|
|
460
|
-
|
|
461
|
-
}),
|
|
462
|
-
e.key === "Enter" && !e.shiftKey && (e.preventDefault(),
|
|
463
|
-
}),
|
|
511
|
+
`, c.querySelector(".close-btn").addEventListener("click", r), l = c.querySelector("#messages-container"), u = c.querySelector(".input"), d = c.querySelector(".send-btn"), u.addEventListener("input", () => {
|
|
512
|
+
u.style.height = "auto", u.style.height = Math.min(u.scrollHeight, 100) + "px";
|
|
513
|
+
}), u.addEventListener("keydown", (e) => {
|
|
514
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), m());
|
|
515
|
+
}), d.addEventListener("click", m), o.appendChild(c);
|
|
464
516
|
}
|
|
465
|
-
function
|
|
466
|
-
let e =
|
|
467
|
-
!e ||
|
|
517
|
+
function m() {
|
|
518
|
+
let e = u.value.trim();
|
|
519
|
+
!e || d.disabled || (u.value = "", u.style.height = "auto", n(e));
|
|
468
520
|
}
|
|
469
|
-
function
|
|
521
|
+
function h(e) {
|
|
470
522
|
if (e.messages.length === 0 && !e.isLoading) {
|
|
471
|
-
let e =
|
|
472
|
-
|
|
523
|
+
let e = f.suggestionChips.length > 0 ? `<div class="chips">${f.suggestionChips.map((e) => `<button class="chip">${A(e)}</button>`).join("")}</div>` : "";
|
|
524
|
+
l.innerHTML = `
|
|
473
525
|
<div class="empty-state">
|
|
474
|
-
${
|
|
475
|
-
<span class="greeting">${
|
|
526
|
+
${k}
|
|
527
|
+
<span class="greeting">${A(f.greeting)}</span>
|
|
476
528
|
${e}
|
|
477
529
|
</div>
|
|
478
|
-
`,
|
|
530
|
+
`, l.querySelectorAll(".chip").forEach((e) => {
|
|
479
531
|
e.addEventListener("click", () => n(e.textContent ?? ""));
|
|
480
532
|
});
|
|
481
533
|
return;
|
|
482
534
|
}
|
|
483
535
|
let t = e.messages.map((e) => `
|
|
484
536
|
<div class="message ${e.role}">
|
|
485
|
-
<div class="message-bubble">${
|
|
537
|
+
<div class="message-bubble">${A(e.content)}</div>
|
|
486
538
|
</div>
|
|
487
539
|
`).join(""), r = e.isLoading ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "";
|
|
488
|
-
|
|
540
|
+
l.innerHTML = t + r, l.scrollTop = l.scrollHeight;
|
|
489
541
|
}
|
|
490
|
-
function
|
|
491
|
-
|
|
542
|
+
function g(e) {
|
|
543
|
+
e.isOpen ? (c.classList.add("open"), s.classList.add("open"), s.setAttribute("aria-label", `Close ${i} chat`), requestAnimationFrame(() => u.focus())) : (c.classList.remove("open"), s.classList.remove("open"), s.setAttribute("aria-label", `Open ${i} chat`)), d.disabled = e.isLoading, h(e);
|
|
492
544
|
}
|
|
493
545
|
return {
|
|
494
|
-
mount:
|
|
495
|
-
update:
|
|
546
|
+
mount: p,
|
|
547
|
+
update: g
|
|
496
548
|
};
|
|
497
549
|
}
|
|
498
550
|
//#endregion
|
|
499
551
|
//#region src/builds/npm/index.ts
|
|
500
|
-
function
|
|
501
|
-
if (!
|
|
502
|
-
let
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
let l = document.createElement("div");
|
|
517
|
-
document.body.appendChild(l);
|
|
518
|
-
let d = w(r, i, g, _);
|
|
519
|
-
d.mount(l);
|
|
520
|
-
let f = h((e) => d.update(e));
|
|
521
|
-
d.update(p());
|
|
522
|
-
function g(e) {
|
|
523
|
-
let t = p();
|
|
524
|
-
if (t.isLoading) return;
|
|
525
|
-
let n = {
|
|
552
|
+
function M(e) {
|
|
553
|
+
if (!e.apiKey) return console.warn("[BotIQ] apiKey is required"), () => void 0;
|
|
554
|
+
let t = c(e), n = f();
|
|
555
|
+
b({ messages: p(n) });
|
|
556
|
+
let r = document.createElement("div");
|
|
557
|
+
document.body.appendChild(r);
|
|
558
|
+
let i = () => void 0, a = !1;
|
|
559
|
+
s(t.apiKey, t.apiUrl).then((e) => {
|
|
560
|
+
if (a) return;
|
|
561
|
+
let n = j(t, e, o, l);
|
|
562
|
+
n.mount(r), i = x((e) => n.update(e)), n.update(y());
|
|
563
|
+
});
|
|
564
|
+
function o(e) {
|
|
565
|
+
let r = y();
|
|
566
|
+
if (r.isLoading) return;
|
|
567
|
+
let i = {
|
|
526
568
|
role: "user",
|
|
527
569
|
content: e
|
|
528
570
|
};
|
|
529
|
-
|
|
530
|
-
messages: [...
|
|
571
|
+
b({
|
|
572
|
+
messages: [...r.messages, i],
|
|
531
573
|
isLoading: !0
|
|
532
|
-
}),
|
|
574
|
+
}), g(t.apiUrl, t.apiKey, n, e, r.messages).then((e) => {
|
|
533
575
|
let t = {
|
|
534
576
|
role: "assistant",
|
|
535
577
|
content: e
|
|
536
578
|
};
|
|
537
|
-
|
|
538
|
-
messages: [...
|
|
579
|
+
b({
|
|
580
|
+
messages: [...y().messages, t],
|
|
539
581
|
isLoading: !1
|
|
540
|
-
}),
|
|
582
|
+
}), m(n, [i, t]);
|
|
541
583
|
}).catch(() => {
|
|
542
|
-
|
|
584
|
+
b({ isLoading: !1 });
|
|
543
585
|
});
|
|
544
586
|
}
|
|
545
|
-
function
|
|
546
|
-
|
|
587
|
+
function l() {
|
|
588
|
+
b({ isOpen: !y().isOpen });
|
|
547
589
|
}
|
|
548
590
|
return () => {
|
|
549
|
-
|
|
591
|
+
a = !0, i(), r.remove();
|
|
550
592
|
};
|
|
551
593
|
}
|
|
552
594
|
//#endregion
|
|
553
|
-
export {
|
|
595
|
+
export { M as t };
|
package/dist/sdk/react.js
CHANGED
package/dist/sdk/vue.d.ts
CHANGED
|
@@ -1,50 +1,13 @@
|
|
|
1
|
-
import { PropType } from 'vue';
|
|
2
1
|
import { BotIQConfig } from './index.ts';
|
|
2
|
+
export type { BotIQConfig };
|
|
3
3
|
export declare const BotIQWidget: import('vue').DefineComponent<import('vue').ExtractPropTypes<{
|
|
4
4
|
apiKey: {
|
|
5
5
|
type: StringConstructor;
|
|
6
6
|
required: true;
|
|
7
7
|
};
|
|
8
|
-
apiUrl: {
|
|
9
|
-
type: StringConstructor;
|
|
10
|
-
default: undefined;
|
|
11
|
-
};
|
|
12
|
-
botName: {
|
|
13
|
-
type: StringConstructor;
|
|
14
|
-
default: undefined;
|
|
15
|
-
};
|
|
16
|
-
position: {
|
|
17
|
-
type: PropType<BotIQConfig["position"]>;
|
|
18
|
-
default: undefined;
|
|
19
|
-
};
|
|
20
|
-
primaryColor: {
|
|
21
|
-
type: StringConstructor;
|
|
22
|
-
default: undefined;
|
|
23
|
-
};
|
|
24
8
|
}>, () => null, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
|
|
25
9
|
apiKey: {
|
|
26
10
|
type: StringConstructor;
|
|
27
11
|
required: true;
|
|
28
12
|
};
|
|
29
|
-
|
|
30
|
-
type: StringConstructor;
|
|
31
|
-
default: undefined;
|
|
32
|
-
};
|
|
33
|
-
botName: {
|
|
34
|
-
type: StringConstructor;
|
|
35
|
-
default: undefined;
|
|
36
|
-
};
|
|
37
|
-
position: {
|
|
38
|
-
type: PropType<BotIQConfig["position"]>;
|
|
39
|
-
default: undefined;
|
|
40
|
-
};
|
|
41
|
-
primaryColor: {
|
|
42
|
-
type: StringConstructor;
|
|
43
|
-
default: undefined;
|
|
44
|
-
};
|
|
45
|
-
}>> & Readonly<{}>, {
|
|
46
|
-
primaryColor: string;
|
|
47
|
-
apiUrl: string;
|
|
48
|
-
botName: string;
|
|
49
|
-
position: "bottom-right" | "bottom-left" | undefined;
|
|
50
|
-
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
13
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
package/dist/sdk/vue.js
CHANGED
|
@@ -1,40 +1,16 @@
|
|
|
1
|
-
import { t as e } from "./npm-
|
|
1
|
+
import { t as e } from "./npm-Bo1fPZM3.js";
|
|
2
2
|
import { defineComponent as t, onMounted as n, onUnmounted as r } from "vue";
|
|
3
3
|
//#region src/builds/npm/vue.ts
|
|
4
4
|
var i = t({
|
|
5
5
|
name: "BotIQWidget",
|
|
6
|
-
props: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
},
|
|
11
|
-
apiUrl: {
|
|
12
|
-
type: String,
|
|
13
|
-
default: void 0
|
|
14
|
-
},
|
|
15
|
-
botName: {
|
|
16
|
-
type: String,
|
|
17
|
-
default: void 0
|
|
18
|
-
},
|
|
19
|
-
position: {
|
|
20
|
-
type: String,
|
|
21
|
-
default: void 0
|
|
22
|
-
},
|
|
23
|
-
primaryColor: {
|
|
24
|
-
type: String,
|
|
25
|
-
default: void 0
|
|
26
|
-
}
|
|
27
|
-
},
|
|
6
|
+
props: { apiKey: {
|
|
7
|
+
type: String,
|
|
8
|
+
required: !0
|
|
9
|
+
} },
|
|
28
10
|
setup(t) {
|
|
29
11
|
let i;
|
|
30
12
|
return n(() => {
|
|
31
|
-
i = e({
|
|
32
|
-
apiKey: t.apiKey,
|
|
33
|
-
apiUrl: t.apiUrl,
|
|
34
|
-
botName: t.botName,
|
|
35
|
-
position: t.position,
|
|
36
|
-
primaryColor: t.primaryColor
|
|
37
|
-
});
|
|
13
|
+
i = e({ apiKey: t.apiKey });
|
|
38
14
|
}), r(() => {
|
|
39
15
|
i?.();
|
|
40
16
|
}), () => null;
|
package/package.json
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@locdo.tech/botiq-chat-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "BotIQ chat widget SDK — embed AI chatbot into any website with vanilla JS, React, or Vue.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"botiq",
|
|
7
|
-
"chatbot",
|
|
8
|
-
"ai",
|
|
9
|
-
"widget",
|
|
10
|
-
"react",
|
|
11
|
-
"vue",
|
|
12
|
-
"customer-support"
|
|
13
|
-
],
|
|
14
|
-
"license": "MIT",
|
|
15
|
-
"author": "BotIQ (LocDo.Tech)",
|
|
16
|
-
"homepage": "https://botiq.vn",
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/locdo-tech/botiq.git",
|
|
20
|
-
"directory": "apps/widget"
|
|
21
|
-
},
|
|
22
|
-
"bugs": {
|
|
23
|
-
"url": "https://github.com/locdo-tech/botiq/issues"
|
|
24
|
-
},
|
|
25
|
-
"type": "module",
|
|
26
|
-
"main": "./dist/sdk/index.js",
|
|
27
|
-
"module": "./dist/sdk/index.js",
|
|
28
|
-
"types": "./dist/sdk/index.d.ts",
|
|
29
|
-
"exports": {
|
|
30
|
-
".": {
|
|
31
|
-
"import": "./dist/sdk/index.js",
|
|
32
|
-
"types": "./dist/sdk/index.d.ts"
|
|
33
|
-
},
|
|
34
|
-
"./react": {
|
|
35
|
-
"import": "./dist/sdk/react.js",
|
|
36
|
-
"types": "./dist/sdk/react.d.ts"
|
|
37
|
-
},
|
|
38
|
-
"./vue": {
|
|
39
|
-
"import": "./dist/sdk/vue.js",
|
|
40
|
-
"types": "./dist/sdk/vue.d.ts"
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
"files": [
|
|
44
|
-
"dist/sdk",
|
|
45
|
-
"README.md",
|
|
46
|
-
"LICENSE"
|
|
47
|
-
],
|
|
48
|
-
"publishConfig": {
|
|
49
|
-
"access": "public"
|
|
50
|
-
},
|
|
51
|
-
"sideEffects": false,
|
|
52
|
-
"peerDependencies": {
|
|
53
|
-
"react": ">=17",
|
|
54
|
-
"vue": ">=3"
|
|
55
|
-
},
|
|
56
|
-
"peerDependenciesMeta": {
|
|
57
|
-
"react": {
|
|
58
|
-
"optional": true
|
|
59
|
-
},
|
|
60
|
-
"vue": {
|
|
61
|
-
"optional": true
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
"scripts": {
|
|
65
|
-
"dev": "vite",
|
|
66
|
-
"build:cdn": "tsc && vite build",
|
|
67
|
-
"build:sdk": "tsc -p tsconfig.sdk.json --noEmit && vite build --config vite.config.sdk.ts",
|
|
68
|
-
"build": "npm run build:cdn && npm run build:sdk",
|
|
69
|
-
"preview": "vite preview",
|
|
70
|
-
"test": "vitest run",
|
|
71
|
-
"test:watch": "vitest",
|
|
72
|
-
"prepublishOnly": "npm run build:sdk"
|
|
73
|
-
},
|
|
74
|
-
"devDependencies": {
|
|
75
|
-
"@types/react": "^19.2.14",
|
|
76
|
-
"@types/react-dom": "^19.2.3",
|
|
77
|
-
"@vitest/coverage-v8": "^4.1.6",
|
|
78
|
-
"jsdom": "^29.1.1",
|
|
79
|
-
"react": "^19.2.6",
|
|
80
|
-
"react-dom": "^19.2.6",
|
|
81
|
-
"typescript": "~6.0.2",
|
|
82
|
-
"vite": "^8.0.12",
|
|
83
|
-
"vite-plugin-dts": "^5.0.0",
|
|
84
|
-
"vitest": "^4.1.6",
|
|
85
|
-
"vue": "^3.5.34"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@locdo.tech/botiq-chat-sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "BotIQ chat widget SDK — embed AI chatbot into any website with vanilla JS, React, or Vue.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"botiq",
|
|
7
|
+
"chatbot",
|
|
8
|
+
"ai",
|
|
9
|
+
"widget",
|
|
10
|
+
"react",
|
|
11
|
+
"vue",
|
|
12
|
+
"customer-support"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "BotIQ (LocDo.Tech)",
|
|
16
|
+
"homepage": "https://botiq.vn",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/locdo-tech/botiq.git",
|
|
20
|
+
"directory": "apps/widget"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/locdo-tech/botiq/issues"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/sdk/index.js",
|
|
27
|
+
"module": "./dist/sdk/index.js",
|
|
28
|
+
"types": "./dist/sdk/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./dist/sdk/index.js",
|
|
32
|
+
"types": "./dist/sdk/index.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./react": {
|
|
35
|
+
"import": "./dist/sdk/react.js",
|
|
36
|
+
"types": "./dist/sdk/react.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"./vue": {
|
|
39
|
+
"import": "./dist/sdk/vue.js",
|
|
40
|
+
"types": "./dist/sdk/vue.d.ts"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist/sdk",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"sideEffects": false,
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": ">=17",
|
|
54
|
+
"vue": ">=3"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"react": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"vue": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"dev": "vite",
|
|
66
|
+
"build:cdn": "tsc && vite build",
|
|
67
|
+
"build:sdk": "tsc -p tsconfig.sdk.json --noEmit && vite build --config vite.config.sdk.ts",
|
|
68
|
+
"build": "npm run build:cdn && npm run build:sdk",
|
|
69
|
+
"preview": "vite preview",
|
|
70
|
+
"test": "vitest run",
|
|
71
|
+
"test:watch": "vitest",
|
|
72
|
+
"prepublishOnly": "npm run build:sdk"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/react": "^19.2.14",
|
|
76
|
+
"@types/react-dom": "^19.2.3",
|
|
77
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
78
|
+
"jsdom": "^29.1.1",
|
|
79
|
+
"react": "^19.2.6",
|
|
80
|
+
"react-dom": "^19.2.6",
|
|
81
|
+
"typescript": "~6.0.2",
|
|
82
|
+
"vite": "^8.0.12",
|
|
83
|
+
"vite-plugin-dts": "^5.0.0",
|
|
84
|
+
"vitest": "^4.1.6",
|
|
85
|
+
"vue": "^3.5.34"
|
|
86
|
+
}
|
|
87
|
+
}
|