ss-support-widget 1.0.8 → 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 -421
- 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,421 +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 prevOverflow = document.documentElement.style.overflow;
|
|
61
|
-
const prevBodyOverflow = document.body.style.overflow;
|
|
62
|
-
|
|
63
|
-
document.documentElement.style.overflow = "hidden";
|
|
64
|
-
document.body.style.overflow = "hidden";
|
|
65
|
-
|
|
66
|
-
return () => {
|
|
67
|
-
document.documentElement.style.overflow = prevOverflow;
|
|
68
|
-
document.body.style.overflow = prevBodyOverflow;
|
|
69
|
-
};
|
|
70
|
-
}, [open, isMobile]);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<Box sx={anchorSx}>
|
|
75
|
-
{!open && (
|
|
76
|
-
<Button
|
|
77
|
-
variant="contained"
|
|
78
|
-
onClick={() => setOpen(true)}
|
|
79
|
-
startIcon={<ChatBubbleOutlineRoundedIcon />}
|
|
80
|
-
sx={{
|
|
81
|
-
borderRadius: 999,
|
|
82
|
-
px: 2,
|
|
83
|
-
py: 1,
|
|
84
|
-
textTransform: "none",
|
|
85
|
-
boxShadow: "0 10px 30px rgba(0,0,0,.18)",
|
|
86
|
-
}}
|
|
87
|
-
>
|
|
88
|
-
Chat
|
|
89
|
-
</Button>
|
|
90
|
-
)}
|
|
91
|
-
|
|
92
|
-
{open && (
|
|
93
|
-
<Paper
|
|
94
|
-
elevation={isMobile ? 0 : 10}
|
|
95
|
-
sx={{
|
|
96
|
-
width: isMobile ? "100vw" : 380,
|
|
97
|
-
height: isMobile ? "calc(var(--vh, 1vh) * 100)" : 560,
|
|
98
|
-
"@supports (height: 100dvh)": {
|
|
99
|
-
height: isMobile ? "100dvh" : 560,
|
|
100
|
-
},
|
|
101
|
-
maxWidth: "100vw",
|
|
102
|
-
maxHeight: "100dvh",
|
|
103
|
-
borderRadius: isMobile ? 0 : 4,
|
|
104
|
-
position: isMobile ? "fixed" : "relative",
|
|
105
|
-
inset: isMobile ? 0 : "auto",
|
|
106
|
-
overflow: "hidden",
|
|
107
|
-
border: isMobile ? "none" : "1px solid",
|
|
108
|
-
borderColor: "divider",
|
|
109
|
-
boxShadow: isMobile ? "none" : "0 18px 60px rgba(0,0,0,.28)",
|
|
110
|
-
}}
|
|
111
|
-
>
|
|
112
|
-
<Box sx={{ display: "flex", flexDirection: "column", height: "100%" }}>
|
|
113
|
-
|
|
114
|
-
<Box
|
|
115
|
-
sx={{
|
|
116
|
-
px: 1.75,
|
|
117
|
-
py: 1.25,
|
|
118
|
-
display: "flex",
|
|
119
|
-
alignItems: "center",
|
|
120
|
-
gap: 1,
|
|
121
|
-
borderBottom: "1px solid",
|
|
122
|
-
borderColor: "divider",
|
|
123
|
-
background:
|
|
124
|
-
"linear-gradient(135deg, rgba(25,118,210,.18), rgba(156,39,176,.14))",
|
|
125
|
-
backdropFilter: isMobile ? "none" : "blur(10px)",
|
|
126
|
-
}}
|
|
127
|
-
>
|
|
128
|
-
<Badge
|
|
129
|
-
overlap="circular"
|
|
130
|
-
variant="dot"
|
|
131
|
-
color="success"
|
|
132
|
-
sx={{
|
|
133
|
-
"& .MuiBadge-badge": {
|
|
134
|
-
width: 10,
|
|
135
|
-
height: 10,
|
|
136
|
-
borderRadius: 999,
|
|
137
|
-
border: "2px solid",
|
|
138
|
-
borderColor: "background.paper",
|
|
139
|
-
},
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
142
|
-
<Avatar sx={{ bgcolor: "primary.main" }}>
|
|
143
|
-
<SmartToyRoundedIcon />
|
|
144
|
-
</Avatar>
|
|
145
|
-
</Badge>
|
|
146
|
-
|
|
147
|
-
<Box sx={{ flex: 1, minWidth: 0 }}>
|
|
148
|
-
<Typography variant="subtitle1" sx={{ fontWeight: 700, lineHeight: 1.1 }}>
|
|
149
|
-
Asistent
|
|
150
|
-
</Typography>
|
|
151
|
-
<Typography variant="caption" sx={{ color: "text.secondary" }}>
|
|
152
|
-
Online
|
|
153
|
-
</Typography>
|
|
154
|
-
</Box>
|
|
155
|
-
|
|
156
|
-
<IconButton
|
|
157
|
-
size="small"
|
|
158
|
-
onClick={() => setOpen(false)}
|
|
159
|
-
sx={{
|
|
160
|
-
bgcolor: "rgba(255,255,255,.55)",
|
|
161
|
-
border: "1px solid",
|
|
162
|
-
borderColor: "divider",
|
|
163
|
-
"&:hover": { bgcolor: "rgba(255,255,255,.75)" },
|
|
164
|
-
}}
|
|
165
|
-
>
|
|
166
|
-
<CloseIcon fontSize="small" />
|
|
167
|
-
</IconButton>
|
|
168
|
-
</Box>
|
|
169
|
-
|
|
170
|
-
<Box
|
|
171
|
-
sx={{
|
|
172
|
-
flex: 1,
|
|
173
|
-
minHeight: 0,
|
|
174
|
-
position: "relative",
|
|
175
|
-
// height: 300,
|
|
176
|
-
overflow: "auto",
|
|
177
|
-
px: 1.5,
|
|
178
|
-
py: 1.5,
|
|
179
|
-
bgcolor: "background.default",
|
|
180
|
-
backgroundImage:
|
|
181
|
-
"radial-gradient(circle at 20% 10%, rgba(25,118,210,.10), transparent 45%)," +
|
|
182
|
-
"radial-gradient(circle at 80% 20%, rgba(156,39,176,.10), transparent 50%)," +
|
|
183
|
-
"radial-gradient(circle at 40% 90%, rgba(0,200,83,.08), transparent 45%)",
|
|
184
|
-
"&::-webkit-scrollbar": { width: 10 },
|
|
185
|
-
"&::-webkit-scrollbar-thumb": {
|
|
186
|
-
backgroundColor: "rgba(0,0,0,.18)",
|
|
187
|
-
borderRadius: 999,
|
|
188
|
-
border: "3px solid transparent",
|
|
189
|
-
backgroundClip: "content-box",
|
|
190
|
-
},
|
|
191
|
-
overscrollBehavior: "contain",
|
|
192
|
-
WebkitOverflowScrolling: "touch",
|
|
193
|
-
}}
|
|
194
|
-
>
|
|
195
|
-
{msgs.map((m) => {
|
|
196
|
-
const isUser = m.role === "user";
|
|
197
|
-
return (
|
|
198
|
-
<Box
|
|
199
|
-
key={m.id}
|
|
200
|
-
sx={{
|
|
201
|
-
display: "flex",
|
|
202
|
-
justifyContent: isUser ? "flex-end" : "flex-start",
|
|
203
|
-
gap: 1,
|
|
204
|
-
mb: 1.2,
|
|
205
|
-
}}
|
|
206
|
-
>
|
|
207
|
-
{!isUser && (
|
|
208
|
-
<Avatar
|
|
209
|
-
sx={{
|
|
210
|
-
width: 30,
|
|
211
|
-
height: 30,
|
|
212
|
-
mt: 0.25,
|
|
213
|
-
bgcolor: "primary.main",
|
|
214
|
-
}}
|
|
215
|
-
>
|
|
216
|
-
<SmartToyRoundedIcon sx={{ fontSize: 18 }} />
|
|
217
|
-
</Avatar>
|
|
218
|
-
)}
|
|
219
|
-
|
|
220
|
-
<Box sx={{ maxWidth: "78%" }}>
|
|
221
|
-
{m.timestamp && (
|
|
222
|
-
<Box sx={{ display: "flex", justifyContent: isUser ? "flex-end" : "flex-start", mb: 0.5 }}>
|
|
223
|
-
<Chip size="small" label={m.timestamp} variant="outlined" sx={{ height: 22 }} />
|
|
224
|
-
</Box>
|
|
225
|
-
)}
|
|
226
|
-
|
|
227
|
-
<Box
|
|
228
|
-
sx={{
|
|
229
|
-
position: "relative",
|
|
230
|
-
px: 1.4,
|
|
231
|
-
py: 1.05,
|
|
232
|
-
borderRadius: 3,
|
|
233
|
-
border: "1px solid",
|
|
234
|
-
borderColor: "divider",
|
|
235
|
-
bgcolor: isUser ? "primary.main" : "background.paper",
|
|
236
|
-
color: isUser ? "primary.contrastText" : "text.primary",
|
|
237
|
-
boxShadow: isUser
|
|
238
|
-
? "0 10px 30px rgba(25,118,210,.22)"
|
|
239
|
-
: "0 10px 30px rgba(0,0,0,.10)",
|
|
240
|
-
whiteSpace: "pre-wrap",
|
|
241
|
-
"&:before": {
|
|
242
|
-
content: '""',
|
|
243
|
-
position: "absolute",
|
|
244
|
-
top: 14,
|
|
245
|
-
width: 10,
|
|
246
|
-
height: 10,
|
|
247
|
-
transform: "rotate(45deg)",
|
|
248
|
-
bgcolor: isUser ? "primary.main" : "background.paper",
|
|
249
|
-
borderLeft: isUser ? "none" : "1px solid",
|
|
250
|
-
borderTop: isUser ? "none" : "1px solid",
|
|
251
|
-
borderColor: "divider",
|
|
252
|
-
right: isUser ? -5 : "auto",
|
|
253
|
-
left: isUser ? "auto" : -5,
|
|
254
|
-
},
|
|
255
|
-
"& p": { margin: 0 },
|
|
256
|
-
"& a": { color: "inherit", textDecoration: "underline" },
|
|
257
|
-
"& code": {
|
|
258
|
-
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
259
|
-
fontSize: "0.9em",
|
|
260
|
-
padding: "2px 6px",
|
|
261
|
-
borderRadius: 8,
|
|
262
|
-
backgroundColor: isUser ? "rgba(255,255,255,.14)" : "rgba(0,0,0,.06)",
|
|
263
|
-
},
|
|
264
|
-
"& pre": {
|
|
265
|
-
margin: 0,
|
|
266
|
-
marginTop: 8,
|
|
267
|
-
padding: 12,
|
|
268
|
-
overflow: "auto",
|
|
269
|
-
borderRadius: 12,
|
|
270
|
-
backgroundColor: isUser ? "rgba(255,255,255,.12)" : "rgba(0,0,0,.05)",
|
|
271
|
-
},
|
|
272
|
-
}}
|
|
273
|
-
>
|
|
274
|
-
<ReactMarkdown>{m.text}</ReactMarkdown>
|
|
275
|
-
</Box>
|
|
276
|
-
</Box>
|
|
277
|
-
|
|
278
|
-
{isUser && (
|
|
279
|
-
<Avatar
|
|
280
|
-
sx={{
|
|
281
|
-
width: 30,
|
|
282
|
-
height: 30,
|
|
283
|
-
mt: 0.25,
|
|
284
|
-
bgcolor: "grey.900",
|
|
285
|
-
}}
|
|
286
|
-
>
|
|
287
|
-
<PersonRoundedIcon sx={{ fontSize: 18 }} />
|
|
288
|
-
</Avatar>
|
|
289
|
-
)}
|
|
290
|
-
</Box>
|
|
291
|
-
);
|
|
292
|
-
})}
|
|
293
|
-
|
|
294
|
-
{sending && (
|
|
295
|
-
<Box sx={{ display: "flex", alignItems: "flex-end", gap: 1, mb: 1.2 }}>
|
|
296
|
-
<Avatar sx={{ width: 30, height: 30, bgcolor: "primary.main" }}>
|
|
297
|
-
<SmartToyRoundedIcon sx={{ fontSize: 18 }} />
|
|
298
|
-
</Avatar>
|
|
299
|
-
|
|
300
|
-
<Box
|
|
301
|
-
sx={{
|
|
302
|
-
px: 1.4,
|
|
303
|
-
py: 1.1,
|
|
304
|
-
borderRadius: 3,
|
|
305
|
-
border: "1px solid",
|
|
306
|
-
borderColor: "divider",
|
|
307
|
-
bgcolor: "background.paper",
|
|
308
|
-
boxShadow: "0 10px 30px rgba(0,0,0,.10)",
|
|
309
|
-
display: "flex",
|
|
310
|
-
alignItems: "center",
|
|
311
|
-
gap: 0.6,
|
|
312
|
-
}}
|
|
313
|
-
>
|
|
314
|
-
<Box
|
|
315
|
-
sx={{
|
|
316
|
-
width: 6,
|
|
317
|
-
height: 6,
|
|
318
|
-
borderRadius: 999,
|
|
319
|
-
bgcolor: "text.secondary",
|
|
320
|
-
animation: "dot 1.1s infinite ease-in-out",
|
|
321
|
-
"@keyframes dot": {
|
|
322
|
-
"0%, 80%, 100%": { transform: "scale(0.6)", opacity: 0.4 },
|
|
323
|
-
"40%": { transform: "scale(1)", opacity: 1 },
|
|
324
|
-
},
|
|
325
|
-
}}
|
|
326
|
-
/>
|
|
327
|
-
<Box
|
|
328
|
-
sx={{
|
|
329
|
-
width: 6,
|
|
330
|
-
height: 6,
|
|
331
|
-
borderRadius: 999,
|
|
332
|
-
bgcolor: "text.secondary",
|
|
333
|
-
animation: "dot 1.1s infinite ease-in-out",
|
|
334
|
-
animationDelay: "0.15s",
|
|
335
|
-
"@keyframes dot": {
|
|
336
|
-
"0%, 80%, 100%": { transform: "scale(0.6)", opacity: 0.4 },
|
|
337
|
-
"40%": { transform: "scale(1)", opacity: 1 },
|
|
338
|
-
},
|
|
339
|
-
}}
|
|
340
|
-
/>
|
|
341
|
-
<Box
|
|
342
|
-
sx={{
|
|
343
|
-
width: 6,
|
|
344
|
-
height: 6,
|
|
345
|
-
borderRadius: 999,
|
|
346
|
-
bgcolor: "text.secondary",
|
|
347
|
-
animation: "dot 1.1s infinite ease-in-out",
|
|
348
|
-
animationDelay: "0.3s",
|
|
349
|
-
"@keyframes dot": {
|
|
350
|
-
"0%, 80%, 100%": { transform: "scale(0.6)", opacity: 0.4 },
|
|
351
|
-
"40%": { transform: "scale(1)", opacity: 1 },
|
|
352
|
-
},
|
|
353
|
-
}}
|
|
354
|
-
/>
|
|
355
|
-
</Box>
|
|
356
|
-
</Box>
|
|
357
|
-
)}
|
|
358
|
-
|
|
359
|
-
<div ref={endRef} />
|
|
360
|
-
</Box>
|
|
361
|
-
|
|
362
|
-
<Box
|
|
363
|
-
sx={{
|
|
364
|
-
position: "sticky",
|
|
365
|
-
bottom: 0,
|
|
366
|
-
zIndex: 2,
|
|
367
|
-
p: 1.25,
|
|
368
|
-
borderTop: "1px solid",
|
|
369
|
-
borderColor: "divider",
|
|
370
|
-
display: "flex",
|
|
371
|
-
gap: 1,
|
|
372
|
-
bgcolor: "background.paper",
|
|
373
|
-
}}
|
|
374
|
-
>
|
|
375
|
-
<TextField
|
|
376
|
-
size="small"
|
|
377
|
-
fullWidth
|
|
378
|
-
value={input}
|
|
379
|
-
disabled={sending}
|
|
380
|
-
onChange={(e) => setInput(e.target.value)}
|
|
381
|
-
onKeyDown={(e) => {
|
|
382
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
383
|
-
e.preventDefault();
|
|
384
|
-
send();
|
|
385
|
-
}
|
|
386
|
-
}}
|
|
387
|
-
placeholder="Scrie un mesaj"
|
|
388
|
-
multiline
|
|
389
|
-
maxRows={4}
|
|
390
|
-
sx={{
|
|
391
|
-
"& .MuiOutlinedInput-root": {
|
|
392
|
-
borderRadius: 999,
|
|
393
|
-
pr: 0.75,
|
|
394
|
-
backgroundColor: "rgba(0,0,0,.02)",
|
|
395
|
-
},
|
|
396
|
-
}}
|
|
397
|
-
InputProps={{
|
|
398
|
-
endAdornment: (
|
|
399
|
-
<IconButton
|
|
400
|
-
size="small"
|
|
401
|
-
onClick={send}
|
|
402
|
-
disabled={sending || !input.trim()}
|
|
403
|
-
sx={{
|
|
404
|
-
ml: 0.5,
|
|
405
|
-
borderRadius: 999,
|
|
406
|
-
}}
|
|
407
|
-
>
|
|
408
|
-
<SendRoundedIcon />
|
|
409
|
-
</IconButton>
|
|
410
|
-
),
|
|
411
|
-
}}
|
|
412
|
-
/>
|
|
413
|
-
</Box>
|
|
414
|
-
|
|
415
|
-
</Box>
|
|
416
|
-
</Paper>
|
|
417
|
-
)}
|
|
418
|
-
</Box>
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
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
|
-
})();
|