ss-support-widget 1.0.9 → 1.0.10
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/x-bot-widget.js +168 -0
- package/index.html +15 -17
- package/package.json +31 -29
- package/readme.md +7 -7
- package/src/api/service.ts +80 -0
- package/src/{sse.ts → api/sse.ts} +58 -55
- package/src/authentification/token.ts +57 -0
- package/src/components/ChatWidget.tsx +488 -0
- package/src/components/chatbot-element.tsx +53 -0
- package/src/constants.ts +3 -0
- package/src/containers/App.tsx +187 -0
- package/src/{hooks.ts → hooks/hooks.ts} +31 -31
- package/src/hooks/useSignalR.ts +222 -0
- package/src/main.ts +38 -0
- package/src/services/chatConfiguration.ts +11 -0
- package/src/storage/session-storage.ts +53 -0
- package/src/types.ts +29 -0
- package/src/utils/deviceInfo.ts +13 -0
- package/src/utils/script-utils.ts +11 -0
- package/ss-support-widget-1.0.4.tgz +0 -0
- package/tsconfig.json +13 -13
- package/vite.config.ts +22 -22
- package/dist/chat-bot-widget.js +0 -162
- package/src/App.tsx +0 -106
- package/src/ChatWidget.tsx +0 -428
- package/src/MsgDelta.tsx +0 -6
- package/src/element.tsx +0 -100
- package/src/service.ts +0 -68
- package/src/session-storage.ts +0 -17
package/src/App.tsx
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
// App.tsx
|
|
2
|
-
import React, { useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { Box, Button, IconButton, Paper, TextField, Typography } from "@mui/material";
|
|
4
|
-
import CloseIcon from "@mui/icons-material/Close";
|
|
5
|
-
import { streamChat } from "./sse";
|
|
6
|
-
import { Config } from "./element";
|
|
7
|
-
import { MsgDelta } from "./MsgDelta";
|
|
8
|
-
import ReactMarkdown from "react-markdown";
|
|
9
|
-
import { getHistoryMessages } from "./service";
|
|
10
|
-
import { getThreadId, saveThreadId } from "./session-storage";
|
|
11
|
-
import ChatWidget from "./ChatWidget";
|
|
12
|
-
|
|
13
|
-
export type Msg = { id: string; role: string | "user" | "assistant"; text: string, timestamp?: string; };
|
|
14
|
-
export default function App({ config }: { config: Config }) {
|
|
15
|
-
const [open, setOpen] = useState(false);
|
|
16
|
-
const [input, setInput] = useState("");
|
|
17
|
-
const [threadId, setThreadId] = useState(getThreadId() || config.threadId || "");
|
|
18
|
-
const [msgs, setMsgs] = useState<Msg[]>([]);
|
|
19
|
-
const [sending, setSending] = useState(false);
|
|
20
|
-
const [hideChat, setHideChat] = useState(false);
|
|
21
|
-
|
|
22
|
-
const anchorSx = useMemo(
|
|
23
|
-
() => ({
|
|
24
|
-
position: "fixed",
|
|
25
|
-
right: 16,
|
|
26
|
-
bottom: 16,
|
|
27
|
-
zIndex: 2147483647,
|
|
28
|
-
fontFamily: "system-ui",
|
|
29
|
-
}),
|
|
30
|
-
[]
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
const path = window.location.pathname;
|
|
35
|
-
if (config.hideChatForUrls?.some(u => path.startsWith(u))) {
|
|
36
|
-
setHideChat(true);
|
|
37
|
-
} else {
|
|
38
|
-
setHideChat(false);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
getHistoryMessages(config,
|
|
44
|
-
getThreadId() || config.threadId || "",
|
|
45
|
-
(delta) => {
|
|
46
|
-
const msgs = delta.map((m: MsgDelta) => ({ id: m.id, role: m.role, text: m.text }))
|
|
47
|
-
setMsgs((prev) => [...prev, ...msgs]);
|
|
48
|
-
});
|
|
49
|
-
}, []);
|
|
50
|
-
|
|
51
|
-
function upsertAssistantDelta(delta: MsgDelta) {
|
|
52
|
-
setMsgs((prev) => {
|
|
53
|
-
const last = prev[prev.length - 1];
|
|
54
|
-
if (!last || last.role !== "assistant" || last.id !== delta.id) {
|
|
55
|
-
return [...prev, { id: delta.id, role: delta.role, text: delta.text }];
|
|
56
|
-
}
|
|
57
|
-
const updated = prev.slice();
|
|
58
|
-
updated[updated.length - 1] = { ...last, text: last.text + delta.text };
|
|
59
|
-
return updated;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function send() {
|
|
66
|
-
const text = input.trim();
|
|
67
|
-
if (!text || sending) return;
|
|
68
|
-
|
|
69
|
-
setSending(true);
|
|
70
|
-
setInput("");
|
|
71
|
-
setMsgs((m) => [...m, { id: crypto.randomUUID(), role: "user", text }]);
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
await streamChat({
|
|
75
|
-
url: config.apiBaseUrl + "api/chat-support",
|
|
76
|
-
token: config.token,
|
|
77
|
-
clientId: config.clientId,
|
|
78
|
-
threadId: threadId,
|
|
79
|
-
body: text,
|
|
80
|
-
onDelta: (delta) => {
|
|
81
|
-
upsertAssistantDelta(delta)
|
|
82
|
-
setThreadId(delta.threadId);
|
|
83
|
-
saveThreadId(delta.threadId);
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
} catch {
|
|
87
|
-
setMsgs((m) => [...m, { id: crypto.randomUUID(), role: "assistant", text: "Eroare la trimitere." }]);
|
|
88
|
-
} finally {
|
|
89
|
-
setSending(false);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
hideChat
|
|
95
|
-
? <></>
|
|
96
|
-
: <ChatWidget
|
|
97
|
-
anchorSx={anchorSx}
|
|
98
|
-
open={open}
|
|
99
|
-
setOpen={setOpen}
|
|
100
|
-
msgs={msgs}
|
|
101
|
-
input={input}
|
|
102
|
-
setInput={setInput}
|
|
103
|
-
send={send}
|
|
104
|
-
sending={sending} />
|
|
105
|
-
);
|
|
106
|
-
}
|
package/src/ChatWidget.tsx
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from "react";
|
|
2
|
-
import {
|
|
3
|
-
Avatar,
|
|
4
|
-
Badge,
|
|
5
|
-
Box,
|
|
6
|
-
Button,
|
|
7
|
-
Chip,
|
|
8
|
-
IconButton,
|
|
9
|
-
Paper,
|
|
10
|
-
TextField,
|
|
11
|
-
Typography,
|
|
12
|
-
} from "@mui/material";
|
|
13
|
-
import { useTheme, useMediaQuery } from "@mui/material";
|
|
14
|
-
import CloseIcon from "@mui/icons-material/Close";
|
|
15
|
-
import SendRoundedIcon from "@mui/icons-material/SendRounded";
|
|
16
|
-
import ChatBubbleOutlineRoundedIcon from "@mui/icons-material/ChatBubbleOutlineRounded";
|
|
17
|
-
import SmartToyRoundedIcon from "@mui/icons-material/SmartToyRounded";
|
|
18
|
-
import PersonRoundedIcon from "@mui/icons-material/PersonRounded";
|
|
19
|
-
import ReactMarkdown from "react-markdown";
|
|
20
|
-
import { Msg } from "./App";
|
|
21
|
-
import { useViewportHeightVar } from "./hooks";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export function ChatWidget({
|
|
25
|
-
open,
|
|
26
|
-
setOpen,
|
|
27
|
-
msgs,
|
|
28
|
-
input,
|
|
29
|
-
setInput,
|
|
30
|
-
sending,
|
|
31
|
-
send,
|
|
32
|
-
anchorSx,
|
|
33
|
-
}: {
|
|
34
|
-
open: boolean;
|
|
35
|
-
setOpen: (v: boolean) => void;
|
|
36
|
-
msgs: Msg[];
|
|
37
|
-
input: string;
|
|
38
|
-
setInput: (v: string) => void;
|
|
39
|
-
sending: boolean;
|
|
40
|
-
send: () => void;
|
|
41
|
-
anchorSx?: any;
|
|
42
|
-
}) {
|
|
43
|
-
const endRef = useRef<HTMLDivElement | null>(null);
|
|
44
|
-
const theme = useTheme();
|
|
45
|
-
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
|
46
|
-
useViewportHeightVar();
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
endRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
50
|
-
}, [msgs.length, sending]);
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
if (!open) return;
|
|
54
|
-
endRef.current?.scrollIntoView({ behavior: "auto", block: "end" });
|
|
55
|
-
}, [open]);
|
|
56
|
-
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (!(open && isMobile)) return;
|
|
59
|
-
|
|
60
|
-
const scrollY = window.scrollY;
|
|
61
|
-
|
|
62
|
-
document.body.style.position = "fixed";
|
|
63
|
-
document.body.style.top = `-${scrollY}px`;
|
|
64
|
-
document.body.style.left = "0";
|
|
65
|
-
document.body.style.right = "0";
|
|
66
|
-
document.body.style.width = "100%";
|
|
67
|
-
|
|
68
|
-
return () => {
|
|
69
|
-
document.body.style.position = "";
|
|
70
|
-
document.body.style.top = "";
|
|
71
|
-
document.body.style.left = "";
|
|
72
|
-
document.body.style.right = "";
|
|
73
|
-
document.body.style.width = "";
|
|
74
|
-
window.scrollTo(0, scrollY);
|
|
75
|
-
};
|
|
76
|
-
}, [open, isMobile]);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<Box sx={anchorSx}>
|
|
82
|
-
{!open && (
|
|
83
|
-
<Button
|
|
84
|
-
variant="contained"
|
|
85
|
-
onClick={() => setOpen(true)}
|
|
86
|
-
startIcon={<ChatBubbleOutlineRoundedIcon />}
|
|
87
|
-
sx={{
|
|
88
|
-
borderRadius: 999,
|
|
89
|
-
px: 2,
|
|
90
|
-
py: 1,
|
|
91
|
-
textTransform: "none",
|
|
92
|
-
boxShadow: "0 10px 30px rgba(0,0,0,.18)",
|
|
93
|
-
}}
|
|
94
|
-
>
|
|
95
|
-
Chat
|
|
96
|
-
</Button>
|
|
97
|
-
)}
|
|
98
|
-
|
|
99
|
-
{open && (
|
|
100
|
-
<Paper
|
|
101
|
-
elevation={isMobile ? 0 : 10}
|
|
102
|
-
sx={{
|
|
103
|
-
width: isMobile ? "100vw" : 380,
|
|
104
|
-
height: isMobile ? "calc(var(--vh, 1vh) * 100)" : 560,
|
|
105
|
-
"@supports (height: 100dvh)": {
|
|
106
|
-
height: isMobile ? "100dvh" : 560,
|
|
107
|
-
},
|
|
108
|
-
maxWidth: "100vw",
|
|
109
|
-
maxHeight: "100dvh",
|
|
110
|
-
borderRadius: isMobile ? 0 : 4,
|
|
111
|
-
position: isMobile ? "fixed" : "relative",
|
|
112
|
-
inset: isMobile ? 0 : "auto",
|
|
113
|
-
overflow: "hidden",
|
|
114
|
-
border: isMobile ? "none" : "1px solid",
|
|
115
|
-
borderColor: "divider",
|
|
116
|
-
boxShadow: isMobile ? "none" : "0 18px 60px rgba(0,0,0,.28)",
|
|
117
|
-
}}
|
|
118
|
-
>
|
|
119
|
-
<Box sx={{ display: "flex", flexDirection: "column", height: "100%" }}>
|
|
120
|
-
|
|
121
|
-
<Box
|
|
122
|
-
sx={{
|
|
123
|
-
px: 1.75,
|
|
124
|
-
py: 1.25,
|
|
125
|
-
display: "flex",
|
|
126
|
-
alignItems: "center",
|
|
127
|
-
gap: 1,
|
|
128
|
-
borderBottom: "1px solid",
|
|
129
|
-
borderColor: "divider",
|
|
130
|
-
background:
|
|
131
|
-
"linear-gradient(135deg, rgba(25,118,210,.18), rgba(156,39,176,.14))",
|
|
132
|
-
backdropFilter: isMobile ? "none" : "blur(10px)",
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
<Badge
|
|
136
|
-
overlap="circular"
|
|
137
|
-
variant="dot"
|
|
138
|
-
color="success"
|
|
139
|
-
sx={{
|
|
140
|
-
"& .MuiBadge-badge": {
|
|
141
|
-
width: 10,
|
|
142
|
-
height: 10,
|
|
143
|
-
borderRadius: 999,
|
|
144
|
-
border: "2px solid",
|
|
145
|
-
borderColor: "background.paper",
|
|
146
|
-
},
|
|
147
|
-
}}
|
|
148
|
-
>
|
|
149
|
-
<Avatar sx={{ bgcolor: "primary.main" }}>
|
|
150
|
-
<SmartToyRoundedIcon />
|
|
151
|
-
</Avatar>
|
|
152
|
-
</Badge>
|
|
153
|
-
|
|
154
|
-
<Box sx={{ flex: 1, minWidth: 0 }}>
|
|
155
|
-
<Typography variant="subtitle1" sx={{ fontWeight: 700, lineHeight: 1.1 }}>
|
|
156
|
-
Asistent
|
|
157
|
-
</Typography>
|
|
158
|
-
<Typography variant="caption" sx={{ color: "text.secondary" }}>
|
|
159
|
-
Online
|
|
160
|
-
</Typography>
|
|
161
|
-
</Box>
|
|
162
|
-
|
|
163
|
-
<IconButton
|
|
164
|
-
size="small"
|
|
165
|
-
onClick={() => setOpen(false)}
|
|
166
|
-
sx={{
|
|
167
|
-
bgcolor: "rgba(255,255,255,.55)",
|
|
168
|
-
border: "1px solid",
|
|
169
|
-
borderColor: "divider",
|
|
170
|
-
"&:hover": { bgcolor: "rgba(255,255,255,.75)" },
|
|
171
|
-
}}
|
|
172
|
-
>
|
|
173
|
-
<CloseIcon fontSize="small" />
|
|
174
|
-
</IconButton>
|
|
175
|
-
</Box>
|
|
176
|
-
|
|
177
|
-
<Box
|
|
178
|
-
sx={{
|
|
179
|
-
flex: 1,
|
|
180
|
-
minHeight: 0,
|
|
181
|
-
position: "relative",
|
|
182
|
-
// height: 300,
|
|
183
|
-
overflow: "auto",
|
|
184
|
-
px: 1.5,
|
|
185
|
-
py: 1.5,
|
|
186
|
-
bgcolor: "background.default",
|
|
187
|
-
backgroundImage:
|
|
188
|
-
"radial-gradient(circle at 20% 10%, rgba(25,118,210,.10), transparent 45%)," +
|
|
189
|
-
"radial-gradient(circle at 80% 20%, rgba(156,39,176,.10), transparent 50%)," +
|
|
190
|
-
"radial-gradient(circle at 40% 90%, rgba(0,200,83,.08), transparent 45%)",
|
|
191
|
-
"&::-webkit-scrollbar": { width: 10 },
|
|
192
|
-
"&::-webkit-scrollbar-thumb": {
|
|
193
|
-
backgroundColor: "rgba(0,0,0,.18)",
|
|
194
|
-
borderRadius: 999,
|
|
195
|
-
border: "3px solid transparent",
|
|
196
|
-
backgroundClip: "content-box",
|
|
197
|
-
},
|
|
198
|
-
overscrollBehavior: "contain",
|
|
199
|
-
WebkitOverflowScrolling: "touch",
|
|
200
|
-
}}
|
|
201
|
-
>
|
|
202
|
-
{msgs.map((m) => {
|
|
203
|
-
const isUser = m.role === "user";
|
|
204
|
-
return (
|
|
205
|
-
<Box
|
|
206
|
-
key={m.id}
|
|
207
|
-
sx={{
|
|
208
|
-
display: "flex",
|
|
209
|
-
justifyContent: isUser ? "flex-end" : "flex-start",
|
|
210
|
-
gap: 1,
|
|
211
|
-
mb: 1.2,
|
|
212
|
-
}}
|
|
213
|
-
>
|
|
214
|
-
{!isUser && (
|
|
215
|
-
<Avatar
|
|
216
|
-
sx={{
|
|
217
|
-
width: 30,
|
|
218
|
-
height: 30,
|
|
219
|
-
mt: 0.25,
|
|
220
|
-
bgcolor: "primary.main",
|
|
221
|
-
}}
|
|
222
|
-
>
|
|
223
|
-
<SmartToyRoundedIcon sx={{ fontSize: 18 }} />
|
|
224
|
-
</Avatar>
|
|
225
|
-
)}
|
|
226
|
-
|
|
227
|
-
<Box sx={{ maxWidth: "78%" }}>
|
|
228
|
-
{m.timestamp && (
|
|
229
|
-
<Box sx={{ display: "flex", justifyContent: isUser ? "flex-end" : "flex-start", mb: 0.5 }}>
|
|
230
|
-
<Chip size="small" label={m.timestamp} variant="outlined" sx={{ height: 22 }} />
|
|
231
|
-
</Box>
|
|
232
|
-
)}
|
|
233
|
-
|
|
234
|
-
<Box
|
|
235
|
-
sx={{
|
|
236
|
-
position: "relative",
|
|
237
|
-
px: 1.4,
|
|
238
|
-
py: 1.05,
|
|
239
|
-
borderRadius: 3,
|
|
240
|
-
border: "1px solid",
|
|
241
|
-
borderColor: "divider",
|
|
242
|
-
bgcolor: isUser ? "primary.main" : "background.paper",
|
|
243
|
-
color: isUser ? "primary.contrastText" : "text.primary",
|
|
244
|
-
boxShadow: isUser
|
|
245
|
-
? "0 10px 30px rgba(25,118,210,.22)"
|
|
246
|
-
: "0 10px 30px rgba(0,0,0,.10)",
|
|
247
|
-
whiteSpace: "pre-wrap",
|
|
248
|
-
"&:before": {
|
|
249
|
-
content: '""',
|
|
250
|
-
position: "absolute",
|
|
251
|
-
top: 14,
|
|
252
|
-
width: 10,
|
|
253
|
-
height: 10,
|
|
254
|
-
transform: "rotate(45deg)",
|
|
255
|
-
bgcolor: isUser ? "primary.main" : "background.paper",
|
|
256
|
-
borderLeft: isUser ? "none" : "1px solid",
|
|
257
|
-
borderTop: isUser ? "none" : "1px solid",
|
|
258
|
-
borderColor: "divider",
|
|
259
|
-
right: isUser ? -5 : "auto",
|
|
260
|
-
left: isUser ? "auto" : -5,
|
|
261
|
-
},
|
|
262
|
-
"& p": { margin: 0 },
|
|
263
|
-
"& a": { color: "inherit", textDecoration: "underline" },
|
|
264
|
-
"& code": {
|
|
265
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
266
|
-
fontSize: "0.9em",
|
|
267
|
-
padding: "2px 6px",
|
|
268
|
-
borderRadius: 8,
|
|
269
|
-
backgroundColor: isUser ? "rgba(255,255,255,.14)" : "rgba(0,0,0,.06)",
|
|
270
|
-
},
|
|
271
|
-
"& pre": {
|
|
272
|
-
margin: 0,
|
|
273
|
-
marginTop: 8,
|
|
274
|
-
padding: 12,
|
|
275
|
-
overflow: "auto",
|
|
276
|
-
borderRadius: 12,
|
|
277
|
-
backgroundColor: isUser ? "rgba(255,255,255,.12)" : "rgba(0,0,0,.05)",
|
|
278
|
-
},
|
|
279
|
-
}}
|
|
280
|
-
>
|
|
281
|
-
<ReactMarkdown>{m.text}</ReactMarkdown>
|
|
282
|
-
</Box>
|
|
283
|
-
</Box>
|
|
284
|
-
|
|
285
|
-
{isUser && (
|
|
286
|
-
<Avatar
|
|
287
|
-
sx={{
|
|
288
|
-
width: 30,
|
|
289
|
-
height: 30,
|
|
290
|
-
mt: 0.25,
|
|
291
|
-
bgcolor: "grey.900",
|
|
292
|
-
}}
|
|
293
|
-
>
|
|
294
|
-
<PersonRoundedIcon sx={{ fontSize: 18 }} />
|
|
295
|
-
</Avatar>
|
|
296
|
-
)}
|
|
297
|
-
</Box>
|
|
298
|
-
);
|
|
299
|
-
})}
|
|
300
|
-
|
|
301
|
-
{sending && (
|
|
302
|
-
<Box sx={{ display: "flex", alignItems: "flex-end", gap: 1, mb: 1.2 }}>
|
|
303
|
-
<Avatar sx={{ width: 30, height: 30, bgcolor: "primary.main" }}>
|
|
304
|
-
<SmartToyRoundedIcon sx={{ fontSize: 18 }} />
|
|
305
|
-
</Avatar>
|
|
306
|
-
|
|
307
|
-
<Box
|
|
308
|
-
sx={{
|
|
309
|
-
px: 1.4,
|
|
310
|
-
py: 1.1,
|
|
311
|
-
borderRadius: 3,
|
|
312
|
-
border: "1px solid",
|
|
313
|
-
borderColor: "divider",
|
|
314
|
-
bgcolor: "background.paper",
|
|
315
|
-
boxShadow: "0 10px 30px rgba(0,0,0,.10)",
|
|
316
|
-
display: "flex",
|
|
317
|
-
alignItems: "center",
|
|
318
|
-
gap: 0.6,
|
|
319
|
-
}}
|
|
320
|
-
>
|
|
321
|
-
<Box
|
|
322
|
-
sx={{
|
|
323
|
-
width: 6,
|
|
324
|
-
height: 6,
|
|
325
|
-
borderRadius: 999,
|
|
326
|
-
bgcolor: "text.secondary",
|
|
327
|
-
animation: "dot 1.1s infinite ease-in-out",
|
|
328
|
-
"@keyframes dot": {
|
|
329
|
-
"0%, 80%, 100%": { transform: "scale(0.6)", opacity: 0.4 },
|
|
330
|
-
"40%": { transform: "scale(1)", opacity: 1 },
|
|
331
|
-
},
|
|
332
|
-
}}
|
|
333
|
-
/>
|
|
334
|
-
<Box
|
|
335
|
-
sx={{
|
|
336
|
-
width: 6,
|
|
337
|
-
height: 6,
|
|
338
|
-
borderRadius: 999,
|
|
339
|
-
bgcolor: "text.secondary",
|
|
340
|
-
animation: "dot 1.1s infinite ease-in-out",
|
|
341
|
-
animationDelay: "0.15s",
|
|
342
|
-
"@keyframes dot": {
|
|
343
|
-
"0%, 80%, 100%": { transform: "scale(0.6)", opacity: 0.4 },
|
|
344
|
-
"40%": { transform: "scale(1)", opacity: 1 },
|
|
345
|
-
},
|
|
346
|
-
}}
|
|
347
|
-
/>
|
|
348
|
-
<Box
|
|
349
|
-
sx={{
|
|
350
|
-
width: 6,
|
|
351
|
-
height: 6,
|
|
352
|
-
borderRadius: 999,
|
|
353
|
-
bgcolor: "text.secondary",
|
|
354
|
-
animation: "dot 1.1s infinite ease-in-out",
|
|
355
|
-
animationDelay: "0.3s",
|
|
356
|
-
"@keyframes dot": {
|
|
357
|
-
"0%, 80%, 100%": { transform: "scale(0.6)", opacity: 0.4 },
|
|
358
|
-
"40%": { transform: "scale(1)", opacity: 1 },
|
|
359
|
-
},
|
|
360
|
-
}}
|
|
361
|
-
/>
|
|
362
|
-
</Box>
|
|
363
|
-
</Box>
|
|
364
|
-
)}
|
|
365
|
-
|
|
366
|
-
<div ref={endRef} />
|
|
367
|
-
</Box>
|
|
368
|
-
|
|
369
|
-
<Box
|
|
370
|
-
sx={{
|
|
371
|
-
position: "sticky",
|
|
372
|
-
bottom: 0,
|
|
373
|
-
zIndex: 2,
|
|
374
|
-
p: 1.25,
|
|
375
|
-
borderTop: "1px solid",
|
|
376
|
-
borderColor: "divider",
|
|
377
|
-
display: "flex",
|
|
378
|
-
gap: 1,
|
|
379
|
-
bgcolor: "background.paper",
|
|
380
|
-
}}
|
|
381
|
-
>
|
|
382
|
-
<TextField
|
|
383
|
-
size="small"
|
|
384
|
-
fullWidth
|
|
385
|
-
value={input}
|
|
386
|
-
disabled={sending}
|
|
387
|
-
onChange={(e) => setInput(e.target.value)}
|
|
388
|
-
onKeyDown={(e) => {
|
|
389
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
390
|
-
e.preventDefault();
|
|
391
|
-
send();
|
|
392
|
-
}
|
|
393
|
-
}}
|
|
394
|
-
placeholder="Scrie un mesaj"
|
|
395
|
-
multiline
|
|
396
|
-
maxRows={4}
|
|
397
|
-
sx={{
|
|
398
|
-
"& .MuiOutlinedInput-root": {
|
|
399
|
-
borderRadius: 999,
|
|
400
|
-
pr: 0.75,
|
|
401
|
-
backgroundColor: "rgba(0,0,0,.02)",
|
|
402
|
-
},
|
|
403
|
-
}}
|
|
404
|
-
InputProps={{
|
|
405
|
-
endAdornment: (
|
|
406
|
-
<IconButton
|
|
407
|
-
size="small"
|
|
408
|
-
onClick={send}
|
|
409
|
-
disabled={sending || !input.trim()}
|
|
410
|
-
sx={{
|
|
411
|
-
ml: 0.5,
|
|
412
|
-
borderRadius: 999,
|
|
413
|
-
}}
|
|
414
|
-
>
|
|
415
|
-
<SendRoundedIcon />
|
|
416
|
-
</IconButton>
|
|
417
|
-
),
|
|
418
|
-
}}
|
|
419
|
-
/>
|
|
420
|
-
</Box>
|
|
421
|
-
|
|
422
|
-
</Box>
|
|
423
|
-
</Paper>
|
|
424
|
-
)}
|
|
425
|
-
</Box>
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
export default ChatWidget;
|
package/src/MsgDelta.tsx
DELETED
package/src/element.tsx
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { createRoot, Root } from "react-dom/client";
|
|
3
|
-
import createCache from "@emotion/cache";
|
|
4
|
-
import { CacheProvider } from "@emotion/react";
|
|
5
|
-
import { CssBaseline, ThemeProvider, createTheme } from "@mui/material";
|
|
6
|
-
import App from "./App";
|
|
7
|
-
import { ensureViewportMeta } from "./hooks";
|
|
8
|
-
import { getApiStatus, getClientActivityStatus, getHideChatForUrls } from "./service";
|
|
9
|
-
|
|
10
|
-
export type Config = {
|
|
11
|
-
clientId: string;
|
|
12
|
-
apiBaseUrl: string;
|
|
13
|
-
threadId?: string;
|
|
14
|
-
token?: string;
|
|
15
|
-
userId?: string;
|
|
16
|
-
hideChatForUrls?: string[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const cfg = (window as any).ChatBotConfig;
|
|
20
|
-
const chatBotComponentName = `chat-bot-${cfg.clientId}`;
|
|
21
|
-
|
|
22
|
-
class ChatBotElement extends HTMLElement {
|
|
23
|
-
private root?: Root;
|
|
24
|
-
private mountEl?: HTMLDivElement;
|
|
25
|
-
|
|
26
|
-
connectedCallback() {
|
|
27
|
-
const cfg = this.readConfig();
|
|
28
|
-
if (!cfg) return;
|
|
29
|
-
|
|
30
|
-
const shadow = this.attachShadow({ mode: "open" });
|
|
31
|
-
|
|
32
|
-
const styleHost = document.createElement("style");
|
|
33
|
-
styleHost.textContent = `
|
|
34
|
-
:host { all: initial; }
|
|
35
|
-
`;
|
|
36
|
-
shadow.appendChild(styleHost);
|
|
37
|
-
|
|
38
|
-
this.mountEl = document.createElement("div");
|
|
39
|
-
shadow.appendChild(this.mountEl);
|
|
40
|
-
|
|
41
|
-
ensureViewportMeta();
|
|
42
|
-
|
|
43
|
-
const cache = createCache({
|
|
44
|
-
key: "chatbot",
|
|
45
|
-
container: shadow as unknown as HTMLElement,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const theme = createTheme();
|
|
49
|
-
|
|
50
|
-
this.root = createRoot(this.mountEl);
|
|
51
|
-
this.root.render(
|
|
52
|
-
<CacheProvider value={cache}>
|
|
53
|
-
<ThemeProvider theme={theme}>
|
|
54
|
-
<CssBaseline />
|
|
55
|
-
<App config={cfg} />
|
|
56
|
-
</ThemeProvider>
|
|
57
|
-
</CacheProvider>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
disconnectedCallback() {
|
|
62
|
-
this.root?.unmount();
|
|
63
|
-
this.root = undefined;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private readConfig(): Config | null {
|
|
67
|
-
const raw = (window as any).ChatBotConfig as Partial<Config> | undefined;
|
|
68
|
-
|
|
69
|
-
const clientId = this.getAttribute("client-id") || raw?.clientId || "";
|
|
70
|
-
const apiBaseUrl = this.getAttribute("api-base-url") || raw?.apiBaseUrl || "";
|
|
71
|
-
const threadId = this.getAttribute("thread-id") || raw?.threadId || "";
|
|
72
|
-
|
|
73
|
-
const token = this.getAttribute("token") || raw?.token || undefined;
|
|
74
|
-
const userId = this.getAttribute("user-id") || raw?.userId || undefined;
|
|
75
|
-
|
|
76
|
-
const hideChatForUrls = raw?.hideChatForUrls || []
|
|
77
|
-
|
|
78
|
-
if (!clientId || !apiBaseUrl) return null;
|
|
79
|
-
return { clientId, apiBaseUrl, threadId, token, userId, hideChatForUrls };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!customElements.get(chatBotComponentName)) {
|
|
84
|
-
customElements.define(chatBotComponentName, ChatBotElement);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
(async function autoMount() {
|
|
88
|
-
if (!cfg) return;
|
|
89
|
-
if (document.querySelector(chatBotComponentName)) return;
|
|
90
|
-
|
|
91
|
-
var isHealty = await getApiStatus(cfg)
|
|
92
|
-
if (!isHealty) return;
|
|
93
|
-
|
|
94
|
-
var isClientActive = await getClientActivityStatus(cfg)
|
|
95
|
-
if (!isClientActive) return;
|
|
96
|
-
|
|
97
|
-
cfg.hideChatForUrls = await getHideChatForUrls(cfg)
|
|
98
|
-
const el = document.createElement(chatBotComponentName);
|
|
99
|
-
document.body.appendChild(el);
|
|
100
|
-
})();
|