@locdo.tech/botiq-chat-sdk 0.1.0 → 0.3.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-6G46H82M.js} +198 -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-6G46H82M.js";
|
|
2
2
|
export { e as init };
|
|
@@ -1,69 +1,106 @@
|
|
|
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 = "https://bot-q-backend.vercel.app", 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
|
+
"nunito",
|
|
31
|
+
"dm-sans",
|
|
32
|
+
"raleway",
|
|
33
|
+
"lato",
|
|
34
|
+
"playfair"
|
|
35
|
+
]);
|
|
36
|
+
function o(e) {
|
|
37
|
+
let t = e.colors;
|
|
38
|
+
if (![
|
|
39
|
+
t.primary,
|
|
40
|
+
t.header,
|
|
41
|
+
t.userBubble,
|
|
42
|
+
t.botBubble,
|
|
43
|
+
t.background,
|
|
44
|
+
t.text
|
|
45
|
+
].every((e) => typeof e == "string" && n.test(e)) || !r.has(e.layout.position) || !i.has(e.layout.buttonShape) || !a.has(e.font)) return !1;
|
|
46
|
+
let { width: o, height: s } = e.layout;
|
|
47
|
+
return !(!Number.isFinite(o) || o < 280 || o > 800 || !Number.isFinite(s) || s < 200 || s > 900);
|
|
48
|
+
}
|
|
49
|
+
async function s(e, n) {
|
|
50
|
+
try {
|
|
51
|
+
let r = await fetch(`${n}/widget/meta`, {
|
|
52
|
+
headers: { "X-Api-Key": e },
|
|
53
|
+
signal: AbortSignal.timeout(5e3)
|
|
54
|
+
});
|
|
55
|
+
if (!r.ok) return t;
|
|
56
|
+
let i = await r.json();
|
|
57
|
+
return !i?.design?.colors || !i?.design?.layout || !i?.design?.content || !o(i.design) ? t : {
|
|
58
|
+
name: typeof i.name == "string" && i.name.length > 0 ? i.name : "BotIQ",
|
|
59
|
+
design: i.design
|
|
60
|
+
};
|
|
61
|
+
} catch {
|
|
62
|
+
return t;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function c(t) {
|
|
26
66
|
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"
|
|
67
|
+
apiKey: t.apiKey,
|
|
68
|
+
apiUrl: e
|
|
32
69
|
};
|
|
33
70
|
}
|
|
34
71
|
//#endregion
|
|
35
72
|
//#region src/core/session.ts
|
|
36
|
-
var
|
|
37
|
-
function
|
|
73
|
+
var l = "botiq:sessionId", u = "botiq:history:", d = 10;
|
|
74
|
+
function f() {
|
|
38
75
|
try {
|
|
39
|
-
let e = localStorage.getItem(
|
|
40
|
-
return e || (e = crypto.randomUUID(), localStorage.setItem(
|
|
76
|
+
let e = localStorage.getItem(l);
|
|
77
|
+
return e || (e = crypto.randomUUID(), localStorage.setItem(l, e)), e;
|
|
41
78
|
} catch {
|
|
42
79
|
return crypto.randomUUID();
|
|
43
80
|
}
|
|
44
81
|
}
|
|
45
|
-
function
|
|
82
|
+
function p(e) {
|
|
46
83
|
try {
|
|
47
|
-
let t = localStorage.getItem(
|
|
84
|
+
let t = localStorage.getItem(u + e);
|
|
48
85
|
return t ? JSON.parse(t) : [];
|
|
49
86
|
} catch {
|
|
50
87
|
return [];
|
|
51
88
|
}
|
|
52
89
|
}
|
|
53
|
-
function
|
|
90
|
+
function m(e, t) {
|
|
54
91
|
try {
|
|
55
|
-
let n = [...
|
|
56
|
-
localStorage.setItem(
|
|
92
|
+
let n = [...p(e), ...t].slice(-d);
|
|
93
|
+
localStorage.setItem(u + e, JSON.stringify(n));
|
|
57
94
|
} catch {}
|
|
58
95
|
}
|
|
59
96
|
//#endregion
|
|
60
97
|
//#region src/core/api.ts
|
|
61
|
-
var
|
|
98
|
+
var h = {
|
|
62
99
|
401: "API key không hợp lệ.",
|
|
63
100
|
403: "Widget không được phép trên trang này.",
|
|
64
101
|
429: "Bot đã đạt giới hạn tin nhắn tháng này."
|
|
65
102
|
};
|
|
66
|
-
async function
|
|
103
|
+
async function g(e, t, n, r, i) {
|
|
67
104
|
try {
|
|
68
105
|
let a = await fetch(`${e}/widget/chat`, {
|
|
69
106
|
method: "POST",
|
|
@@ -77,35 +114,35 @@ async function u(e, t, n, r, i) {
|
|
|
77
114
|
history: i
|
|
78
115
|
})
|
|
79
116
|
});
|
|
80
|
-
return a.ok ? (await a.json()).reply || "Không có phản hồi từ bot." :
|
|
117
|
+
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
118
|
} catch {
|
|
82
119
|
return "Xin lỗi, không thể kết nối. Vui lòng thử lại.";
|
|
83
120
|
}
|
|
84
121
|
}
|
|
85
122
|
//#endregion
|
|
86
123
|
//#region src/core/state.ts
|
|
87
|
-
var
|
|
124
|
+
var _ = {
|
|
88
125
|
messages: [],
|
|
89
126
|
isLoading: !1,
|
|
90
127
|
isOpen: !1
|
|
91
|
-
},
|
|
92
|
-
function
|
|
93
|
-
return
|
|
128
|
+
}, v = /* @__PURE__ */ new Set();
|
|
129
|
+
function y() {
|
|
130
|
+
return _;
|
|
94
131
|
}
|
|
95
|
-
function
|
|
96
|
-
Object.assign(
|
|
132
|
+
function b(e) {
|
|
133
|
+
Object.assign(_, e);
|
|
97
134
|
let t = {
|
|
98
|
-
...
|
|
99
|
-
messages: [...
|
|
135
|
+
..._,
|
|
136
|
+
messages: [..._.messages]
|
|
100
137
|
};
|
|
101
|
-
|
|
138
|
+
v.forEach((e) => e(t));
|
|
102
139
|
}
|
|
103
|
-
function
|
|
104
|
-
return
|
|
140
|
+
function x(e) {
|
|
141
|
+
return v.add(e), () => v.delete(e);
|
|
105
142
|
}
|
|
106
143
|
//#endregion
|
|
107
144
|
//#region src/core/styles.ts
|
|
108
|
-
var
|
|
145
|
+
var S = {
|
|
109
146
|
inter: {
|
|
110
147
|
url: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap",
|
|
111
148
|
family: "'Inter', system-ui, -apple-system, sans-serif"
|
|
@@ -117,10 +154,30 @@ var g = {
|
|
|
117
154
|
poppins: {
|
|
118
155
|
url: "https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap",
|
|
119
156
|
family: "'Poppins', system-ui, -apple-system, sans-serif"
|
|
157
|
+
},
|
|
158
|
+
nunito: {
|
|
159
|
+
url: "https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600&display=swap",
|
|
160
|
+
family: "'Nunito', system-ui, -apple-system, sans-serif"
|
|
161
|
+
},
|
|
162
|
+
"dm-sans": {
|
|
163
|
+
url: "https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600&display=swap",
|
|
164
|
+
family: "'DM Sans', system-ui, -apple-system, sans-serif"
|
|
165
|
+
},
|
|
166
|
+
raleway: {
|
|
167
|
+
url: "https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600&display=swap",
|
|
168
|
+
family: "'Raleway', system-ui, -apple-system, sans-serif"
|
|
169
|
+
},
|
|
170
|
+
lato: {
|
|
171
|
+
url: "https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap",
|
|
172
|
+
family: "'Lato', system-ui, -apple-system, sans-serif"
|
|
173
|
+
},
|
|
174
|
+
playfair: {
|
|
175
|
+
url: "https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600&display=swap",
|
|
176
|
+
family: "'Playfair Display', Georgia, serif"
|
|
120
177
|
}
|
|
121
178
|
};
|
|
122
|
-
function
|
|
123
|
-
let { colors: t, layout: n, font: r } = e, i =
|
|
179
|
+
function C(e) {
|
|
180
|
+
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
181
|
return `
|
|
125
182
|
@import url('${i.url}');
|
|
126
183
|
|
|
@@ -218,6 +275,7 @@ function _(e) {
|
|
|
218
275
|
align-items: center;
|
|
219
276
|
justify-content: center;
|
|
220
277
|
flex-shrink: 0;
|
|
278
|
+
overflow: hidden;
|
|
221
279
|
}
|
|
222
280
|
|
|
223
281
|
.avatar svg { width: 20px; height: 20px; fill: #fff; }
|
|
@@ -377,6 +435,23 @@ function _(e) {
|
|
|
377
435
|
|
|
378
436
|
.input-row { display: flex; align-items: flex-end; gap: 8px; }
|
|
379
437
|
|
|
438
|
+
/* Subtle attribution — half-opacity, hover lifts to full. Doesn't steal focus
|
|
439
|
+
from tenant branding but reminds the customer this is powered by BotIQ. */
|
|
440
|
+
.botiq-badge {
|
|
441
|
+
display: block;
|
|
442
|
+
text-align: center;
|
|
443
|
+
font-size: 10px;
|
|
444
|
+
font-weight: 400;
|
|
445
|
+
color: var(--color-text);
|
|
446
|
+
opacity: 0.4;
|
|
447
|
+
text-decoration: none;
|
|
448
|
+
margin-top: 6px;
|
|
449
|
+
letter-spacing: 0.02em;
|
|
450
|
+
transition: opacity .15s;
|
|
451
|
+
}
|
|
452
|
+
.botiq-badge:hover { opacity: 0.85; }
|
|
453
|
+
.botiq-badge-name { font-weight: 600; color: var(--color-primary); }
|
|
454
|
+
|
|
380
455
|
.input {
|
|
381
456
|
flex: 1;
|
|
382
457
|
min-height: 38px;
|
|
@@ -420,23 +495,26 @@ function _(e) {
|
|
|
420
495
|
}
|
|
421
496
|
//#endregion
|
|
422
497
|
//#region src/core/ui.ts
|
|
423
|
-
var
|
|
424
|
-
function
|
|
498
|
+
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>";
|
|
499
|
+
function A(e) {
|
|
500
|
+
return !e || e.type === "icon" ? D : e.type === "emoji" ? `<span style="font-size:22px;line-height:1;display:flex;align-items:center;justify-content:center;width:100%;height:100%">${j(e.value)}</span>` : e.type === "initials" ? `<div style="width:100%;height:100%;border-radius:50%;background:${e.bgColor ?? "#F97316"};display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff">${j(e.value.slice(0, 2).toUpperCase())}</div>` : e.type === "image" ? `<img src="${j(e.value)}" style="width:100%;height:100%;object-fit:cover;border-radius:50%" alt="" />` : D;
|
|
501
|
+
}
|
|
502
|
+
function j(e) {
|
|
425
503
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
426
504
|
}
|
|
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
|
-
`,
|
|
505
|
+
function M(e, t, n, r) {
|
|
506
|
+
let { name: i, design: a } = t, o, s, c, l, u, d, { content: f } = a;
|
|
507
|
+
function p(e) {
|
|
508
|
+
e.setAttribute("data-position", a.layout.position), o = e.attachShadow({ mode: "open" });
|
|
509
|
+
let t = document.createElement("style");
|
|
510
|
+
t.textContent = C(a), o.appendChild(t), s = document.createElement("button"), s.className = "bubble", s.setAttribute("aria-label", `Open ${i} chat`), s.innerHTML = `
|
|
511
|
+
<span class="icon-chat">${T}</span>
|
|
512
|
+
<span class="icon-close">${E}</span>
|
|
513
|
+
`, 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
514
|
<div class="chat-header">
|
|
437
|
-
<div class="avatar">${
|
|
515
|
+
<div class="avatar">${A(a.avatar)}</div>
|
|
438
516
|
<div class="header-text">
|
|
439
|
-
<span class="bot-name">${
|
|
517
|
+
<span class="bot-name">${j(i)}</span>
|
|
440
518
|
<span class="bot-status">Trực tuyến</span>
|
|
441
519
|
</div>
|
|
442
520
|
<button class="close-btn" aria-label="Close chat">×</button>
|
|
@@ -446,108 +524,101 @@ function w(e, t, n, r) {
|
|
|
446
524
|
<div class="input-row">
|
|
447
525
|
<textarea
|
|
448
526
|
class="input"
|
|
449
|
-
placeholder="${
|
|
527
|
+
placeholder="${j(f.placeholder)}"
|
|
450
528
|
rows="1"
|
|
451
529
|
maxlength="2000"
|
|
452
530
|
aria-label="Message input"
|
|
453
531
|
></textarea>
|
|
454
532
|
<button class="send-btn" aria-label="Send message">
|
|
455
|
-
${
|
|
533
|
+
${O}
|
|
456
534
|
</button>
|
|
457
535
|
</div>
|
|
536
|
+
<a class="botiq-badge" href="${w}" target="_blank" rel="noopener noreferrer">
|
|
537
|
+
Powered by <span class="botiq-badge-name">BotIQ</span>
|
|
538
|
+
</a>
|
|
458
539
|
</div>
|
|
459
|
-
`,
|
|
460
|
-
|
|
461
|
-
}),
|
|
462
|
-
e.key === "Enter" && !e.shiftKey && (e.preventDefault(),
|
|
463
|
-
}),
|
|
540
|
+
`, c.querySelector(".close-btn").addEventListener("click", r), l = c.querySelector("#messages-container"), u = c.querySelector(".input"), d = c.querySelector(".send-btn"), u.addEventListener("input", () => {
|
|
541
|
+
u.style.height = "auto", u.style.height = Math.min(u.scrollHeight, 100) + "px";
|
|
542
|
+
}), u.addEventListener("keydown", (e) => {
|
|
543
|
+
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), m());
|
|
544
|
+
}), d.addEventListener("click", m), o.appendChild(c);
|
|
464
545
|
}
|
|
465
|
-
function
|
|
466
|
-
let e =
|
|
467
|
-
!e ||
|
|
546
|
+
function m() {
|
|
547
|
+
let e = u.value.trim();
|
|
548
|
+
!e || d.disabled || (u.value = "", u.style.height = "auto", n(e));
|
|
468
549
|
}
|
|
469
|
-
function
|
|
550
|
+
function h(e) {
|
|
470
551
|
if (e.messages.length === 0 && !e.isLoading) {
|
|
471
|
-
let e =
|
|
472
|
-
|
|
552
|
+
let e = f.suggestionChips.length > 0 ? `<div class="chips">${f.suggestionChips.map((e) => `<button class="chip">${j(e)}</button>`).join("")}</div>` : "";
|
|
553
|
+
l.innerHTML = `
|
|
473
554
|
<div class="empty-state">
|
|
474
|
-
${
|
|
475
|
-
<span class="greeting">${
|
|
555
|
+
${k}
|
|
556
|
+
<span class="greeting">${j(f.greeting)}</span>
|
|
476
557
|
${e}
|
|
477
558
|
</div>
|
|
478
|
-
`,
|
|
559
|
+
`, l.querySelectorAll(".chip").forEach((e) => {
|
|
479
560
|
e.addEventListener("click", () => n(e.textContent ?? ""));
|
|
480
561
|
});
|
|
481
562
|
return;
|
|
482
563
|
}
|
|
483
564
|
let t = e.messages.map((e) => `
|
|
484
565
|
<div class="message ${e.role}">
|
|
485
|
-
<div class="message-bubble">${
|
|
566
|
+
<div class="message-bubble">${j(e.content)}</div>
|
|
486
567
|
</div>
|
|
487
568
|
`).join(""), r = e.isLoading ? "<div class=\"typing\"><span></span><span></span><span></span></div>" : "";
|
|
488
|
-
|
|
569
|
+
l.innerHTML = t + r, l.scrollTop = l.scrollHeight;
|
|
489
570
|
}
|
|
490
|
-
function
|
|
491
|
-
|
|
571
|
+
function g(e) {
|
|
572
|
+
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
573
|
}
|
|
493
574
|
return {
|
|
494
|
-
mount:
|
|
495
|
-
update:
|
|
575
|
+
mount: p,
|
|
576
|
+
update: g
|
|
496
577
|
};
|
|
497
578
|
}
|
|
498
579
|
//#endregion
|
|
499
580
|
//#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 = {
|
|
581
|
+
function N(e) {
|
|
582
|
+
if (!e.apiKey) return console.warn("[BotIQ] apiKey is required"), () => void 0;
|
|
583
|
+
let t = c(e), n = f();
|
|
584
|
+
b({ messages: p(n) });
|
|
585
|
+
let r = document.createElement("div");
|
|
586
|
+
document.body.appendChild(r);
|
|
587
|
+
let i = () => void 0, a = !1;
|
|
588
|
+
s(t.apiKey, t.apiUrl).then((e) => {
|
|
589
|
+
if (a) return;
|
|
590
|
+
let n = M(t, e, o, l);
|
|
591
|
+
n.mount(r), i = x((e) => n.update(e)), n.update(y());
|
|
592
|
+
});
|
|
593
|
+
function o(e) {
|
|
594
|
+
let r = y();
|
|
595
|
+
if (r.isLoading) return;
|
|
596
|
+
let i = {
|
|
526
597
|
role: "user",
|
|
527
598
|
content: e
|
|
528
599
|
};
|
|
529
|
-
|
|
530
|
-
messages: [...
|
|
600
|
+
b({
|
|
601
|
+
messages: [...r.messages, i],
|
|
531
602
|
isLoading: !0
|
|
532
|
-
}),
|
|
603
|
+
}), g(t.apiUrl, t.apiKey, n, e, r.messages).then((e) => {
|
|
533
604
|
let t = {
|
|
534
605
|
role: "assistant",
|
|
535
606
|
content: e
|
|
536
607
|
};
|
|
537
|
-
|
|
538
|
-
messages: [...
|
|
608
|
+
b({
|
|
609
|
+
messages: [...y().messages, t],
|
|
539
610
|
isLoading: !1
|
|
540
|
-
}),
|
|
611
|
+
}), m(n, [i, t]);
|
|
541
612
|
}).catch(() => {
|
|
542
|
-
|
|
613
|
+
b({ isLoading: !1 });
|
|
543
614
|
});
|
|
544
615
|
}
|
|
545
|
-
function
|
|
546
|
-
|
|
616
|
+
function l() {
|
|
617
|
+
b({ isOpen: !y().isOpen });
|
|
547
618
|
}
|
|
548
619
|
return () => {
|
|
549
|
-
|
|
620
|
+
a = !0, i(), r.remove();
|
|
550
621
|
};
|
|
551
622
|
}
|
|
552
623
|
//#endregion
|
|
553
|
-
export {
|
|
624
|
+
export { N 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-6G46H82M.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.3.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
|
+
}
|