@sevenfold/setto-client 0.23.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/chat/SettoChat.d.ts +1 -4
- package/dist/setto-client.js +828 -231
- package/dist/setto-client.js.map +1 -1
- package/package.json +1 -1
package/dist/setto-client.js
CHANGED
|
@@ -21059,7 +21059,8 @@ async function asError(res, fallback) {
|
|
|
21059
21059
|
}
|
|
21060
21060
|
}
|
|
21061
21061
|
function createApi(args) {
|
|
21062
|
-
const
|
|
21062
|
+
const apiUrl = args.apiUrl.replace(/\/+$/, "");
|
|
21063
|
+
const { supabase } = args;
|
|
21063
21064
|
const request = async (path, init = {}, label = `${init.method ?? "GET"} ${path}`) => {
|
|
21064
21065
|
const token = await bearer(supabase);
|
|
21065
21066
|
const res = await fetch(`${apiUrl}${path}`, {
|
|
@@ -22033,6 +22034,9 @@ function EditModeShell({ children, themeStore, active }) {
|
|
|
22033
22034
|
] });
|
|
22034
22035
|
}
|
|
22035
22036
|
const VISITOR_KEY = "setto-chat-visitor";
|
|
22037
|
+
const CLOSE_MS = 210;
|
|
22038
|
+
const MIN_COMPOSER_HEIGHT = 36;
|
|
22039
|
+
const MAX_COMPOSER_HEIGHT = 200;
|
|
22036
22040
|
function visitorId() {
|
|
22037
22041
|
try {
|
|
22038
22042
|
const stored = window.localStorage.getItem(VISITOR_KEY);
|
|
@@ -22057,82 +22061,316 @@ function contactTeam() {
|
|
|
22057
22061
|
if (link) link.click();
|
|
22058
22062
|
else window.location.hash = "kontakt";
|
|
22059
22063
|
}
|
|
22064
|
+
function validColor(value, fallback) {
|
|
22065
|
+
return value && (/^#[0-9a-f]{3,8}$/i.test(value) || /^rgba?\(/i.test(value)) ? value : fallback;
|
|
22066
|
+
}
|
|
22067
|
+
function chatPalette(colors) {
|
|
22068
|
+
const values = colors?.map((color) => color.value) ?? [];
|
|
22069
|
+
return {
|
|
22070
|
+
"--setto-chat-crema": validColor(values[0], "#F2EBDF"),
|
|
22071
|
+
"--setto-chat-raised": validColor(values[1], "#FAF6EE"),
|
|
22072
|
+
"--setto-chat-canvas": validColor(values[2], "#FFFFFF"),
|
|
22073
|
+
"--setto-chat-ink": validColor(values[3], "#211711"),
|
|
22074
|
+
"--setto-chat-accent": validColor(values.at(-2) ?? values.at(-1), "#C4502A")
|
|
22075
|
+
};
|
|
22076
|
+
}
|
|
22077
|
+
function MenuIcon() {
|
|
22078
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "22", height: "22", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
22079
|
+
"path",
|
|
22080
|
+
{
|
|
22081
|
+
d: "M4 7h16M4 12h16M4 17h16",
|
|
22082
|
+
fill: "none",
|
|
22083
|
+
stroke: "currentColor",
|
|
22084
|
+
strokeWidth: "2",
|
|
22085
|
+
strokeLinecap: "round"
|
|
22086
|
+
}
|
|
22087
|
+
) });
|
|
22088
|
+
}
|
|
22089
|
+
function CloseIcon({ size = 22 }) {
|
|
22090
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: size, height: size, "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
22091
|
+
"path",
|
|
22092
|
+
{
|
|
22093
|
+
d: "M6 6l12 12M18 6L6 18",
|
|
22094
|
+
fill: "none",
|
|
22095
|
+
stroke: "currentColor",
|
|
22096
|
+
strokeWidth: "2.25",
|
|
22097
|
+
strokeLinecap: "round"
|
|
22098
|
+
}
|
|
22099
|
+
) });
|
|
22100
|
+
}
|
|
22101
|
+
function PlusIcon() {
|
|
22102
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "18", height: "18", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
22103
|
+
"path",
|
|
22104
|
+
{
|
|
22105
|
+
d: "M12 5v14M5 12h14",
|
|
22106
|
+
fill: "none",
|
|
22107
|
+
stroke: "currentColor",
|
|
22108
|
+
strokeWidth: "2",
|
|
22109
|
+
strokeLinecap: "round"
|
|
22110
|
+
}
|
|
22111
|
+
) });
|
|
22112
|
+
}
|
|
22113
|
+
function ArrowIcon() {
|
|
22114
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "18", height: "18", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
22115
|
+
"path",
|
|
22116
|
+
{
|
|
22117
|
+
d: "M12 19V5M5 12l7-7 7 7",
|
|
22118
|
+
fill: "none",
|
|
22119
|
+
stroke: "currentColor",
|
|
22120
|
+
strokeWidth: "2",
|
|
22121
|
+
strokeLinejoin: "round",
|
|
22122
|
+
strokeLinecap: "round"
|
|
22123
|
+
}
|
|
22124
|
+
) });
|
|
22125
|
+
}
|
|
22126
|
+
function BaristaIcon() {
|
|
22127
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "20", height: "20", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
22128
|
+
"path",
|
|
22129
|
+
{
|
|
22130
|
+
d: "M12 2.8c.65 5.25 3.95 8.55 9.2 9.2-5.25.65-8.55 3.95-9.2 9.2-.65-5.25-3.95-8.55-9.2-9.2 5.25-.65 8.55-3.95 9.2-9.2Z",
|
|
22131
|
+
fill: "currentColor"
|
|
22132
|
+
}
|
|
22133
|
+
) });
|
|
22134
|
+
}
|
|
22135
|
+
function SiteChatComposer({
|
|
22136
|
+
value,
|
|
22137
|
+
onChange,
|
|
22138
|
+
onSubmit,
|
|
22139
|
+
busy,
|
|
22140
|
+
queued,
|
|
22141
|
+
placeholder,
|
|
22142
|
+
attachLabel,
|
|
22143
|
+
sendLabel,
|
|
22144
|
+
removeLabel,
|
|
22145
|
+
locale,
|
|
22146
|
+
attachments,
|
|
22147
|
+
onAttach,
|
|
22148
|
+
onRemoveAttachment
|
|
22149
|
+
}) {
|
|
22150
|
+
const textareaRef = useRef(null);
|
|
22151
|
+
const fileRef = useRef(null);
|
|
22152
|
+
const wasBusy = useRef(busy);
|
|
22153
|
+
const [focused, setFocused] = useState(false);
|
|
22154
|
+
useEffect(() => {
|
|
22155
|
+
const textarea = textareaRef.current;
|
|
22156
|
+
if (!textarea) return;
|
|
22157
|
+
textarea.style.height = "auto";
|
|
22158
|
+
const height = Math.min(
|
|
22159
|
+
Math.max(textarea.scrollHeight, MIN_COMPOSER_HEIGHT),
|
|
22160
|
+
MAX_COMPOSER_HEIGHT
|
|
22161
|
+
);
|
|
22162
|
+
textarea.style.height = `${height}px`;
|
|
22163
|
+
}, [value]);
|
|
22164
|
+
useEffect(() => {
|
|
22165
|
+
if (wasBusy.current && !busy) textareaRef.current?.focus();
|
|
22166
|
+
wasBusy.current = busy;
|
|
22167
|
+
}, [busy]);
|
|
22168
|
+
function send() {
|
|
22169
|
+
if (!value.trim() && attachments.length === 0) return;
|
|
22170
|
+
onSubmit();
|
|
22171
|
+
}
|
|
22172
|
+
function handleSubmit(event) {
|
|
22173
|
+
event.preventDefault();
|
|
22174
|
+
send();
|
|
22175
|
+
}
|
|
22176
|
+
function handleKeyDown(event) {
|
|
22177
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
22178
|
+
event.preventDefault();
|
|
22179
|
+
send();
|
|
22180
|
+
}
|
|
22181
|
+
}
|
|
22182
|
+
const canSend = value.trim().length > 0 || attachments.length > 0;
|
|
22183
|
+
const open = focused || value.length > 0 || attachments.length > 0;
|
|
22184
|
+
return /* @__PURE__ */ jsxs(
|
|
22185
|
+
"form",
|
|
22186
|
+
{
|
|
22187
|
+
className: `setto-chat__composer${open ? " is-open" : ""}`,
|
|
22188
|
+
onSubmit: handleSubmit,
|
|
22189
|
+
onPointerDown: (event) => {
|
|
22190
|
+
if (event.target === event.currentTarget) textareaRef.current?.focus();
|
|
22191
|
+
},
|
|
22192
|
+
children: [
|
|
22193
|
+
attachments.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "setto-chat__attachments", children: attachments.map((attachment, index) => /* @__PURE__ */ jsxs("li", { children: [
|
|
22194
|
+
/* @__PURE__ */ jsx("img", { src: attachment.preview, alt: attachment.file.name }),
|
|
22195
|
+
/* @__PURE__ */ jsx(
|
|
22196
|
+
"button",
|
|
22197
|
+
{
|
|
22198
|
+
type: "button",
|
|
22199
|
+
onClick: () => onRemoveAttachment(index),
|
|
22200
|
+
"aria-label": `${removeLabel} ${attachment.file.name}`,
|
|
22201
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { size: 12 })
|
|
22202
|
+
}
|
|
22203
|
+
)
|
|
22204
|
+
] }, `${attachment.file.name}-${attachment.preview}`)) }) : null,
|
|
22205
|
+
/* @__PURE__ */ jsx(
|
|
22206
|
+
"textarea",
|
|
22207
|
+
{
|
|
22208
|
+
ref: textareaRef,
|
|
22209
|
+
lang: norwegian(locale) ? "nb" : "en",
|
|
22210
|
+
spellCheck: true,
|
|
22211
|
+
value,
|
|
22212
|
+
onChange: (event) => onChange(event.target.value),
|
|
22213
|
+
onKeyDown: handleKeyDown,
|
|
22214
|
+
onFocus: () => setFocused(true),
|
|
22215
|
+
onBlur: () => setFocused(false),
|
|
22216
|
+
placeholder,
|
|
22217
|
+
rows: 1,
|
|
22218
|
+
maxLength: 1500
|
|
22219
|
+
}
|
|
22220
|
+
),
|
|
22221
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-tools", children: [
|
|
22222
|
+
/* @__PURE__ */ jsx(
|
|
22223
|
+
"input",
|
|
22224
|
+
{
|
|
22225
|
+
ref: fileRef,
|
|
22226
|
+
type: "file",
|
|
22227
|
+
accept: "image/*",
|
|
22228
|
+
multiple: true,
|
|
22229
|
+
hidden: true,
|
|
22230
|
+
onChange: (event) => {
|
|
22231
|
+
const selected = Array.from(event.target.files ?? []);
|
|
22232
|
+
event.target.value = "";
|
|
22233
|
+
if (selected.length) onAttach(selected);
|
|
22234
|
+
}
|
|
22235
|
+
}
|
|
22236
|
+
),
|
|
22237
|
+
/* @__PURE__ */ jsx(
|
|
22238
|
+
"button",
|
|
22239
|
+
{
|
|
22240
|
+
type: "button",
|
|
22241
|
+
className: "setto-chat__attach",
|
|
22242
|
+
onClick: () => fileRef.current?.click(),
|
|
22243
|
+
disabled: busy,
|
|
22244
|
+
"aria-label": attachLabel,
|
|
22245
|
+
title: attachLabel,
|
|
22246
|
+
children: /* @__PURE__ */ jsx(PlusIcon, {})
|
|
22247
|
+
}
|
|
22248
|
+
),
|
|
22249
|
+
/* @__PURE__ */ jsx(
|
|
22250
|
+
"button",
|
|
22251
|
+
{
|
|
22252
|
+
type: "submit",
|
|
22253
|
+
className: `setto-chat__send${queued ? " is-queued" : ""}`,
|
|
22254
|
+
disabled: !canSend,
|
|
22255
|
+
"aria-busy": queued,
|
|
22256
|
+
"aria-label": sendLabel,
|
|
22257
|
+
children: /* @__PURE__ */ jsx(ArrowIcon, {})
|
|
22258
|
+
}
|
|
22259
|
+
)
|
|
22260
|
+
] })
|
|
22261
|
+
]
|
|
22262
|
+
}
|
|
22263
|
+
);
|
|
22264
|
+
}
|
|
22265
|
+
function useChatViewport(active, logRef) {
|
|
22266
|
+
useEffect(() => {
|
|
22267
|
+
if (!active) return;
|
|
22268
|
+
const root = document.documentElement;
|
|
22269
|
+
const body = document.body;
|
|
22270
|
+
const visualViewport = window.visualViewport;
|
|
22271
|
+
const savedScrollY = window.scrollY;
|
|
22272
|
+
const previous = {
|
|
22273
|
+
rootOverflow: root.style.overflow,
|
|
22274
|
+
rootOverscroll: root.style.overscrollBehavior,
|
|
22275
|
+
bodyPosition: body.style.position,
|
|
22276
|
+
bodyTop: body.style.top,
|
|
22277
|
+
bodyLeft: body.style.left,
|
|
22278
|
+
bodyRight: body.style.right,
|
|
22279
|
+
bodyWidth: body.style.width,
|
|
22280
|
+
bodyOverflow: body.style.overflow
|
|
22281
|
+
};
|
|
22282
|
+
root.style.overflow = "hidden";
|
|
22283
|
+
root.style.overscrollBehavior = "none";
|
|
22284
|
+
body.style.position = "fixed";
|
|
22285
|
+
body.style.top = `-${savedScrollY}px`;
|
|
22286
|
+
body.style.left = "0";
|
|
22287
|
+
body.style.right = "0";
|
|
22288
|
+
body.style.width = "100%";
|
|
22289
|
+
body.style.overflow = "hidden";
|
|
22290
|
+
const update = () => {
|
|
22291
|
+
const height = Math.round(visualViewport?.height ?? window.innerHeight);
|
|
22292
|
+
const top = Math.round(visualViewport?.offsetTop ?? 0);
|
|
22293
|
+
root.style.setProperty("--setto-chat-height", `${height}px`);
|
|
22294
|
+
root.style.setProperty("--setto-chat-top", `${top}px`);
|
|
22295
|
+
if (window.scrollY !== 0) window.scrollTo(0, 0);
|
|
22296
|
+
window.requestAnimationFrame(() => {
|
|
22297
|
+
const log = logRef.current;
|
|
22298
|
+
if (log) log.scrollTop = log.scrollHeight;
|
|
22299
|
+
});
|
|
22300
|
+
};
|
|
22301
|
+
update();
|
|
22302
|
+
visualViewport?.addEventListener("resize", update);
|
|
22303
|
+
visualViewport?.addEventListener("scroll", update);
|
|
22304
|
+
window.addEventListener("orientationchange", update);
|
|
22305
|
+
return () => {
|
|
22306
|
+
visualViewport?.removeEventListener("resize", update);
|
|
22307
|
+
visualViewport?.removeEventListener("scroll", update);
|
|
22308
|
+
window.removeEventListener("orientationchange", update);
|
|
22309
|
+
root.style.removeProperty("--setto-chat-height");
|
|
22310
|
+
root.style.removeProperty("--setto-chat-top");
|
|
22311
|
+
root.style.overflow = previous.rootOverflow;
|
|
22312
|
+
root.style.overscrollBehavior = previous.rootOverscroll;
|
|
22313
|
+
body.style.position = previous.bodyPosition;
|
|
22314
|
+
body.style.top = previous.bodyTop;
|
|
22315
|
+
body.style.left = previous.bodyLeft;
|
|
22316
|
+
body.style.right = previous.bodyRight;
|
|
22317
|
+
body.style.width = previous.bodyWidth;
|
|
22318
|
+
body.style.overflow = previous.bodyOverflow;
|
|
22319
|
+
window.scrollTo(0, savedScrollY);
|
|
22320
|
+
};
|
|
22321
|
+
}, [active, logRef]);
|
|
22322
|
+
}
|
|
22060
22323
|
function SettoChat() {
|
|
22061
22324
|
const { config } = useSetto();
|
|
22062
22325
|
const { i18n } = useTranslation();
|
|
22063
22326
|
const apiUrl = config.apiUrl.replace(/\/+$/, "");
|
|
22064
22327
|
const [settings, setSettings] = useState(null);
|
|
22065
|
-
const [
|
|
22328
|
+
const [mounted, setMounted] = useState(false);
|
|
22329
|
+
const [visible, setVisible] = useState(false);
|
|
22330
|
+
const [drawer, setDrawer] = useState(false);
|
|
22066
22331
|
const [messages, setMessages] = useState([]);
|
|
22332
|
+
const [conversations, setConversations] = useState([]);
|
|
22067
22333
|
const [draft, setDraft] = useState("");
|
|
22334
|
+
const [files, setFiles] = useState([]);
|
|
22068
22335
|
const [sending, setSending] = useState(false);
|
|
22336
|
+
const [queued, setQueued] = useState(false);
|
|
22337
|
+
const [loadingConversation, setLoadingConversation] = useState(false);
|
|
22338
|
+
const [availability, setAvailability] = useState({
|
|
22339
|
+
humanAvailable: false,
|
|
22340
|
+
avatarUrl: null
|
|
22341
|
+
});
|
|
22069
22342
|
const [leadOpen, setLeadOpen] = useState(false);
|
|
22070
22343
|
const [leadStatus, setLeadStatus] = useState("idle");
|
|
22071
|
-
const [lead, setLead] = useState({
|
|
22344
|
+
const [lead, setLead] = useState({
|
|
22345
|
+
name: "",
|
|
22346
|
+
email: "",
|
|
22347
|
+
phone: "",
|
|
22348
|
+
interest: "",
|
|
22349
|
+
consent: false
|
|
22350
|
+
});
|
|
22072
22351
|
const conversationId = useRef(null);
|
|
22073
22352
|
const visitor = useRef(null);
|
|
22074
22353
|
const logRef = useRef(null);
|
|
22354
|
+
const filesRef = useRef([]);
|
|
22075
22355
|
const formId = useId();
|
|
22356
|
+
const palette = useMemo(() => chatPalette(config.brandColors), [config.brandColors]);
|
|
22076
22357
|
useChatStyles();
|
|
22077
|
-
|
|
22078
|
-
if (window.location.pathname.startsWith("/setto")) return;
|
|
22079
|
-
let cancelled = false;
|
|
22080
|
-
fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/settings/public`, {
|
|
22081
|
-
headers: { Accept: "application/json" }
|
|
22082
|
-
}).then(async (response) => response.ok ? await response.json() : null).then((next) => {
|
|
22083
|
-
if (!cancelled && next?.enabled) setSettings(next);
|
|
22084
|
-
}).catch(() => {
|
|
22085
|
-
});
|
|
22086
|
-
return () => {
|
|
22087
|
-
cancelled = true;
|
|
22088
|
-
};
|
|
22089
|
-
}, [apiUrl, config.siteId]);
|
|
22090
|
-
useEffect(() => {
|
|
22091
|
-
if (!open || messages.length > 0 || !settings) return;
|
|
22092
|
-
setMessages([{ role: "assistant", content: intro(i18n.language, settings.displayName ?? void 0) }]);
|
|
22093
|
-
}, [open, messages.length, settings, i18n.language]);
|
|
22094
|
-
useEffect(() => {
|
|
22095
|
-
if (open) logRef.current?.scrollTo({ top: logRef.current.scrollHeight });
|
|
22096
|
-
}, [messages, open]);
|
|
22097
|
-
useEffect(() => {
|
|
22098
|
-
if (!open || !conversationId.current || !visitor.current || sending) return;
|
|
22099
|
-
let cancelled = false;
|
|
22100
|
-
const refresh = async () => {
|
|
22101
|
-
try {
|
|
22102
|
-
const query = new URLSearchParams({ visitorId: visitor.current });
|
|
22103
|
-
const response = await fetch(
|
|
22104
|
-
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(conversationId.current)}/public?${query}`,
|
|
22105
|
-
{ headers: { Accept: "application/json" } }
|
|
22106
|
-
);
|
|
22107
|
-
if (!response.ok) return;
|
|
22108
|
-
const next = await response.json();
|
|
22109
|
-
if (cancelled) return;
|
|
22110
|
-
const serverMessages = next.messages.map(({ role, content }) => ({ role, content }));
|
|
22111
|
-
setMessages((previous) => {
|
|
22112
|
-
const greeting = previous[0]?.role === "assistant" && !serverMessages.some((message) => message.content === previous[0]?.content) ? [previous[0]] : [];
|
|
22113
|
-
return [...greeting, ...serverMessages];
|
|
22114
|
-
});
|
|
22115
|
-
} catch {
|
|
22116
|
-
}
|
|
22117
|
-
};
|
|
22118
|
-
void refresh();
|
|
22119
|
-
const timer = window.setInterval(() => void refresh(), 5e3);
|
|
22120
|
-
return () => {
|
|
22121
|
-
cancelled = true;
|
|
22122
|
-
window.clearInterval(timer);
|
|
22123
|
-
};
|
|
22124
|
-
}, [apiUrl, config.siteId, open, sending]);
|
|
22125
|
-
if (!settings || window.location.pathname.startsWith("/setto")) return null;
|
|
22358
|
+
useChatViewport(mounted, logRef);
|
|
22126
22359
|
const locale = i18n.language || "no";
|
|
22127
|
-
const
|
|
22128
|
-
const
|
|
22129
|
-
|
|
22360
|
+
const isNo = norwegian(locale);
|
|
22361
|
+
const siteName = settings?.displayName?.trim() || config.displayName?.trim() || null;
|
|
22362
|
+
const labels = isNo ? {
|
|
22363
|
+
open: siteName ? `Åpne chat med ${siteName}` : "Åpne chat",
|
|
22130
22364
|
close: "Lukk chatten",
|
|
22131
22365
|
title: "Spør Barista",
|
|
22366
|
+
chats: "Chats",
|
|
22367
|
+
newChat: "Ny chat",
|
|
22132
22368
|
disclosure: "AI-assistent · svarer fra nettstedets godkjente informasjon",
|
|
22133
|
-
placeholder: "
|
|
22134
|
-
send: "Send",
|
|
22369
|
+
placeholder: "Hva vil du spørre om?",
|
|
22370
|
+
send: "Send melding",
|
|
22135
22371
|
sending: "Svarer …",
|
|
22372
|
+
attach: "Legg ved bilde",
|
|
22373
|
+
remove: "Fjern",
|
|
22136
22374
|
contact: "Kontakt teamet",
|
|
22137
22375
|
followUp: "Be om oppfølging",
|
|
22138
22376
|
followUpTitle: "La teamet følge opp",
|
|
@@ -22146,15 +22384,22 @@ function SettoChat() {
|
|
|
22146
22384
|
leadSent: "Takk — forespørselen er sendt til teamet.",
|
|
22147
22385
|
leadFailed: "Kunne ikke lagre forespørselen. Prøv igjen eller kontakt teamet direkte.",
|
|
22148
22386
|
leadConversation: "Still et spørsmål først, så kan vi knytte oppfølgingen til samtalen.",
|
|
22149
|
-
failed: "Jeg får ikke svart akkurat nå. Kontakt gjerne teamet direkte."
|
|
22387
|
+
failed: "Jeg får ikke svart akkurat nå. Kontakt gjerne teamet direkte.",
|
|
22388
|
+
image: "Bilde",
|
|
22389
|
+
team: "Teamet",
|
|
22390
|
+
emptyChats: "Nye samtaler du starter her vises i denne listen."
|
|
22150
22391
|
} : {
|
|
22151
|
-
open: siteName ? `
|
|
22392
|
+
open: siteName ? `Open chat with ${siteName}` : "Open chat",
|
|
22152
22393
|
close: "Close chat",
|
|
22153
22394
|
title: "Ask Barista",
|
|
22395
|
+
chats: "Chats",
|
|
22396
|
+
newChat: "New chat",
|
|
22154
22397
|
disclosure: "AI assistant · answers from this website’s approved information",
|
|
22155
|
-
placeholder: "
|
|
22156
|
-
send: "Send",
|
|
22398
|
+
placeholder: "What would you like to ask?",
|
|
22399
|
+
send: "Send message",
|
|
22157
22400
|
sending: "Replying …",
|
|
22401
|
+
attach: "Attach image",
|
|
22402
|
+
remove: "Remove",
|
|
22158
22403
|
contact: "Contact the team",
|
|
22159
22404
|
followUp: "Ask for follow-up",
|
|
22160
22405
|
followUpTitle: "Let the team follow up",
|
|
@@ -22168,17 +22413,224 @@ function SettoChat() {
|
|
|
22168
22413
|
leadSent: "Thanks — your request has been sent to the team.",
|
|
22169
22414
|
leadFailed: "We could not save your request. Please try again or contact the team directly.",
|
|
22170
22415
|
leadConversation: "Ask a question first so we can link the follow-up to this conversation.",
|
|
22171
|
-
failed: "I cannot reply right now. Please contact the team directly."
|
|
22416
|
+
failed: "I cannot reply right now. Please contact the team directly.",
|
|
22417
|
+
image: "Image",
|
|
22418
|
+
team: "The team",
|
|
22419
|
+
emptyChats: "New conversations you start here will appear in this list."
|
|
22172
22420
|
};
|
|
22173
|
-
|
|
22174
|
-
|
|
22175
|
-
|
|
22176
|
-
|
|
22421
|
+
function ensureVisitor() {
|
|
22422
|
+
return visitor.current ??= visitorId();
|
|
22423
|
+
}
|
|
22424
|
+
async function loadChats() {
|
|
22425
|
+
const id = visitor.current;
|
|
22426
|
+
if (!id) return;
|
|
22427
|
+
try {
|
|
22428
|
+
const query = new URLSearchParams({ visitorId: id });
|
|
22429
|
+
const response = await fetch(
|
|
22430
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/public?${query}`,
|
|
22431
|
+
{ headers: { Accept: "application/json" } }
|
|
22432
|
+
);
|
|
22433
|
+
if (response.ok) {
|
|
22434
|
+
const body = await response.json();
|
|
22435
|
+
setConversations(body.conversations);
|
|
22436
|
+
}
|
|
22437
|
+
} catch {
|
|
22438
|
+
}
|
|
22439
|
+
}
|
|
22440
|
+
async function loadConversation(id) {
|
|
22441
|
+
const visitorIdValue = ensureVisitor();
|
|
22442
|
+
setLoadingConversation(true);
|
|
22443
|
+
try {
|
|
22444
|
+
const query = new URLSearchParams({ visitorId: visitorIdValue });
|
|
22445
|
+
const response = await fetch(
|
|
22446
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(id)}/public?${query}`,
|
|
22447
|
+
{ headers: { Accept: "application/json" } }
|
|
22448
|
+
);
|
|
22449
|
+
if (!response.ok) return;
|
|
22450
|
+
const next = await response.json();
|
|
22451
|
+
conversationId.current = next.conversationId;
|
|
22452
|
+
setMessages(next.messages);
|
|
22453
|
+
setLeadOpen(false);
|
|
22454
|
+
setDrawer(false);
|
|
22455
|
+
} finally {
|
|
22456
|
+
setLoadingConversation(false);
|
|
22457
|
+
}
|
|
22458
|
+
}
|
|
22459
|
+
useEffect(() => {
|
|
22460
|
+
if (window.location.pathname.startsWith("/setto")) return;
|
|
22461
|
+
let cancelled = false;
|
|
22462
|
+
fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/settings/public`, {
|
|
22463
|
+
headers: { Accept: "application/json" }
|
|
22464
|
+
}).then(async (response) => response.ok ? await response.json() : null).then((next) => {
|
|
22465
|
+
if (!cancelled && next?.enabled) setSettings(next);
|
|
22466
|
+
}).catch(() => void 0);
|
|
22467
|
+
return () => {
|
|
22468
|
+
cancelled = true;
|
|
22469
|
+
};
|
|
22470
|
+
}, [apiUrl, config.siteId]);
|
|
22471
|
+
useEffect(() => {
|
|
22472
|
+
if (!settings) return;
|
|
22473
|
+
let cancelled = false;
|
|
22474
|
+
const refresh = () => fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/availability/public`, {
|
|
22475
|
+
headers: { Accept: "application/json" }
|
|
22476
|
+
}).then(async (response) => response.ok ? await response.json() : null).then((next) => {
|
|
22477
|
+
if (!cancelled && next) setAvailability(next);
|
|
22478
|
+
}).catch(() => void 0);
|
|
22479
|
+
void refresh();
|
|
22480
|
+
const timer = window.setInterval(() => void refresh(), 3e4);
|
|
22481
|
+
return () => {
|
|
22482
|
+
cancelled = true;
|
|
22483
|
+
window.clearInterval(timer);
|
|
22484
|
+
};
|
|
22485
|
+
}, [apiUrl, config.siteId, settings]);
|
|
22486
|
+
useEffect(() => {
|
|
22487
|
+
if (!mounted || !settings || conversationId.current) return;
|
|
22488
|
+
const welcome = intro(locale, settings.displayName ?? void 0);
|
|
22489
|
+
setMessages((current) => {
|
|
22490
|
+
if (current.length === 0) return [{ role: "assistant", content: welcome }];
|
|
22491
|
+
if (current.length === 1 && current[0]?.role === "assistant" && !current[0].id) {
|
|
22492
|
+
return current[0].content === welcome ? current : [{ role: "assistant", content: welcome }];
|
|
22493
|
+
}
|
|
22494
|
+
return current;
|
|
22495
|
+
});
|
|
22496
|
+
}, [mounted, settings, locale]);
|
|
22497
|
+
useEffect(() => {
|
|
22498
|
+
if (!mounted) return;
|
|
22499
|
+
const log = logRef.current;
|
|
22500
|
+
if (log) log.scrollTo({ top: log.scrollHeight, behavior: sending ? "auto" : "smooth" });
|
|
22501
|
+
}, [messages, mounted, sending, leadOpen]);
|
|
22502
|
+
useEffect(() => {
|
|
22503
|
+
filesRef.current = files;
|
|
22504
|
+
}, [files]);
|
|
22505
|
+
useEffect(
|
|
22506
|
+
() => () => {
|
|
22507
|
+
for (const image of filesRef.current) URL.revokeObjectURL(image.preview);
|
|
22508
|
+
},
|
|
22509
|
+
[]
|
|
22510
|
+
);
|
|
22511
|
+
useEffect(() => {
|
|
22512
|
+
if (!mounted || !conversationId.current || !visitor.current || sending) return;
|
|
22513
|
+
let cancelled = false;
|
|
22514
|
+
const refresh = async () => {
|
|
22515
|
+
try {
|
|
22516
|
+
const query = new URLSearchParams({ visitorId: visitor.current });
|
|
22517
|
+
const response = await fetch(
|
|
22518
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(conversationId.current)}/public?${query}`,
|
|
22519
|
+
{ headers: { Accept: "application/json" } }
|
|
22520
|
+
);
|
|
22521
|
+
if (!response.ok || cancelled) return;
|
|
22522
|
+
const next = await response.json();
|
|
22523
|
+
if (!cancelled) setMessages(next.messages);
|
|
22524
|
+
} catch {
|
|
22525
|
+
}
|
|
22526
|
+
};
|
|
22527
|
+
const timer = window.setInterval(() => void refresh(), 5e3);
|
|
22528
|
+
return () => {
|
|
22529
|
+
cancelled = true;
|
|
22530
|
+
window.clearInterval(timer);
|
|
22531
|
+
};
|
|
22532
|
+
}, [apiUrl, config.siteId, mounted, sending]);
|
|
22533
|
+
useEffect(() => {
|
|
22534
|
+
if (!sending && queued) {
|
|
22535
|
+
setQueued(false);
|
|
22536
|
+
void sendTurn();
|
|
22537
|
+
}
|
|
22538
|
+
}, [sending, queued]);
|
|
22539
|
+
useEffect(() => {
|
|
22540
|
+
if (!mounted) return;
|
|
22541
|
+
const closeOnEscape = (event) => {
|
|
22542
|
+
if (event.key === "Escape") hide();
|
|
22543
|
+
};
|
|
22544
|
+
window.addEventListener("keydown", closeOnEscape);
|
|
22545
|
+
return () => window.removeEventListener("keydown", closeOnEscape);
|
|
22546
|
+
}, [mounted]);
|
|
22547
|
+
if (!settings || window.location.pathname.startsWith("/setto")) return null;
|
|
22548
|
+
function show() {
|
|
22549
|
+
setMounted(true);
|
|
22550
|
+
window.requestAnimationFrame(() => setVisible(true));
|
|
22551
|
+
ensureVisitor();
|
|
22552
|
+
void loadChats();
|
|
22553
|
+
}
|
|
22554
|
+
function hide() {
|
|
22555
|
+
if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
|
|
22556
|
+
setVisible(false);
|
|
22557
|
+
window.setTimeout(() => {
|
|
22558
|
+
setMounted(false);
|
|
22559
|
+
setDrawer(false);
|
|
22560
|
+
}, CLOSE_MS);
|
|
22561
|
+
}
|
|
22562
|
+
function clearPendingFiles() {
|
|
22563
|
+
setFiles((current) => {
|
|
22564
|
+
for (const image of current) URL.revokeObjectURL(image.preview);
|
|
22565
|
+
return [];
|
|
22566
|
+
});
|
|
22567
|
+
}
|
|
22568
|
+
function startNew() {
|
|
22569
|
+
conversationId.current = null;
|
|
22570
|
+
setMessages([{ role: "assistant", content: intro(locale, settings?.displayName ?? void 0) }]);
|
|
22571
|
+
setDraft("");
|
|
22572
|
+
setQueued(false);
|
|
22573
|
+
setLeadOpen(false);
|
|
22574
|
+
setDrawer(false);
|
|
22575
|
+
clearPendingFiles();
|
|
22576
|
+
}
|
|
22577
|
+
function addFiles(selected) {
|
|
22578
|
+
setFiles((current) => {
|
|
22579
|
+
const available = Math.max(0, 4 - current.length);
|
|
22580
|
+
const accepted = selected.filter((file) => file.type.startsWith("image/")).slice(0, available).map((file) => ({ file, preview: URL.createObjectURL(file) }));
|
|
22581
|
+
return [...current, ...accepted];
|
|
22582
|
+
});
|
|
22583
|
+
}
|
|
22584
|
+
function removeFile(index) {
|
|
22585
|
+
setFiles((current) => {
|
|
22586
|
+
const removed = current[index];
|
|
22587
|
+
if (removed) URL.revokeObjectURL(removed.preview);
|
|
22588
|
+
return current.filter((_, itemIndex) => itemIndex !== index);
|
|
22589
|
+
});
|
|
22590
|
+
}
|
|
22591
|
+
function queueOrSend() {
|
|
22592
|
+
if (!draft.trim() && files.length === 0) return;
|
|
22593
|
+
if (sending) {
|
|
22594
|
+
setQueued(true);
|
|
22595
|
+
return;
|
|
22596
|
+
}
|
|
22597
|
+
void sendTurn();
|
|
22598
|
+
}
|
|
22599
|
+
async function sendTurn() {
|
|
22600
|
+
const content = draft.trim();
|
|
22601
|
+
const turnFiles = files;
|
|
22602
|
+
if (!content && turnFiles.length === 0) return;
|
|
22177
22603
|
setDraft("");
|
|
22604
|
+
setFiles([]);
|
|
22178
22605
|
setSending(true);
|
|
22179
|
-
|
|
22180
|
-
visitor.current ??= visitorId();
|
|
22606
|
+
const visitorIdValue = ensureVisitor();
|
|
22181
22607
|
try {
|
|
22608
|
+
const uploaded = [];
|
|
22609
|
+
for (const image of turnFiles) {
|
|
22610
|
+
const form = new FormData();
|
|
22611
|
+
form.set("visitorId", visitorIdValue);
|
|
22612
|
+
if (conversationId.current) form.set("conversationId", conversationId.current);
|
|
22613
|
+
form.set("locale", locale);
|
|
22614
|
+
form.set("page", window.location.href);
|
|
22615
|
+
form.set("file", image.file, image.file.name);
|
|
22616
|
+
const response2 = await fetch(
|
|
22617
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/attachments`,
|
|
22618
|
+
{ method: "POST", body: form }
|
|
22619
|
+
);
|
|
22620
|
+
if (!response2.ok) throw new Error("image_upload_failed");
|
|
22621
|
+
const body = await response2.json();
|
|
22622
|
+
conversationId.current = body.conversationId;
|
|
22623
|
+
uploaded.push(body.attachment);
|
|
22624
|
+
}
|
|
22625
|
+
setMessages((previous) => [
|
|
22626
|
+
...previous,
|
|
22627
|
+
{
|
|
22628
|
+
role: "visitor",
|
|
22629
|
+
content: content || `[${labels.image}]`,
|
|
22630
|
+
attachments: uploaded
|
|
22631
|
+
},
|
|
22632
|
+
{ role: "assistant", content: "" }
|
|
22633
|
+
]);
|
|
22182
22634
|
const response = await fetch(
|
|
22183
22635
|
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/messages`,
|
|
22184
22636
|
{
|
|
@@ -22186,157 +22638,338 @@ function SettoChat() {
|
|
|
22186
22638
|
headers: { "Content-Type": "application/json", Accept: "text/plain, application/json" },
|
|
22187
22639
|
body: JSON.stringify({
|
|
22188
22640
|
conversationId: conversationId.current ?? void 0,
|
|
22189
|
-
visitorId:
|
|
22641
|
+
visitorId: visitorIdValue,
|
|
22190
22642
|
locale,
|
|
22191
22643
|
page: window.location.href,
|
|
22192
|
-
message
|
|
22644
|
+
message: content || void 0,
|
|
22645
|
+
attachmentIds: uploaded.map((attachment) => attachment.id)
|
|
22193
22646
|
})
|
|
22194
22647
|
}
|
|
22195
22648
|
);
|
|
22196
22649
|
const headerConversation = response.headers.get("X-Setto-Conversation");
|
|
22197
22650
|
if (headerConversation) conversationId.current = headerConversation;
|
|
22198
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
22199
22651
|
if (!response.ok) throw new Error(`chat_failed_${response.status}`);
|
|
22200
|
-
if (
|
|
22652
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) {
|
|
22201
22653
|
const body = await response.json();
|
|
22202
22654
|
if (body.conversationId) conversationId.current = body.conversationId;
|
|
22203
22655
|
setMessages((previous) => replaceLastAssistant(previous, body.answer || labels.failed));
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
22656
|
+
} else {
|
|
22657
|
+
const reader = response.body?.getReader();
|
|
22658
|
+
if (!reader) throw new Error("chat_missing_stream");
|
|
22659
|
+
const decoder = new TextDecoder();
|
|
22660
|
+
let answer = "";
|
|
22661
|
+
while (true) {
|
|
22662
|
+
const { done, value } = await reader.read();
|
|
22663
|
+
if (done) break;
|
|
22664
|
+
answer += decoder.decode(value, { stream: true });
|
|
22665
|
+
setMessages((previous) => replaceLastAssistant(previous, answer));
|
|
22666
|
+
}
|
|
22667
|
+
answer += decoder.decode();
|
|
22668
|
+
if (!answer.trim()) {
|
|
22669
|
+
setMessages((previous) => replaceLastAssistant(previous, labels.failed));
|
|
22670
|
+
}
|
|
22215
22671
|
}
|
|
22216
|
-
|
|
22217
|
-
if (!answer.trim()) setMessages((previous) => replaceLastAssistant(previous, labels.failed));
|
|
22672
|
+
void loadChats();
|
|
22218
22673
|
} catch {
|
|
22219
22674
|
setMessages((previous) => replaceLastAssistant(previous, labels.failed));
|
|
22220
22675
|
} finally {
|
|
22676
|
+
for (const image of turnFiles) URL.revokeObjectURL(image.preview);
|
|
22221
22677
|
setSending(false);
|
|
22222
22678
|
}
|
|
22223
|
-
}
|
|
22224
|
-
|
|
22679
|
+
}
|
|
22680
|
+
async function submitLead(event) {
|
|
22225
22681
|
event.preventDefault();
|
|
22226
|
-
if (leadStatus === "sending")
|
|
22227
|
-
if (!conversationId.current || !visitor.current) {
|
|
22682
|
+
if (leadStatus === "sending" || !conversationId.current || !visitor.current) {
|
|
22228
22683
|
setLeadStatus("failed");
|
|
22229
22684
|
return;
|
|
22230
22685
|
}
|
|
22231
22686
|
setLeadStatus("sending");
|
|
22232
22687
|
try {
|
|
22233
|
-
const response = await fetch(
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22688
|
+
const response = await fetch(
|
|
22689
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/leads`,
|
|
22690
|
+
{
|
|
22691
|
+
method: "POST",
|
|
22692
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
22693
|
+
body: JSON.stringify({
|
|
22694
|
+
conversationId: conversationId.current,
|
|
22695
|
+
visitorId: visitor.current,
|
|
22696
|
+
consent: lead.consent,
|
|
22697
|
+
name: lead.name.trim() || void 0,
|
|
22698
|
+
email: lead.email.trim() || void 0,
|
|
22699
|
+
phone: lead.phone.trim() || void 0,
|
|
22700
|
+
interest: lead.interest.trim() || void 0
|
|
22701
|
+
})
|
|
22702
|
+
}
|
|
22703
|
+
);
|
|
22704
|
+
if (!response.ok) throw new Error("lead_failed");
|
|
22247
22705
|
setLeadStatus("sent");
|
|
22248
22706
|
} catch {
|
|
22249
22707
|
setLeadStatus("failed");
|
|
22250
22708
|
}
|
|
22251
|
-
}
|
|
22252
|
-
return /* @__PURE__ */ jsxs(
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
22256
|
-
|
|
22257
|
-
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
/* @__PURE__ */
|
|
22261
|
-
|
|
22262
|
-
/* @__PURE__ */ jsx("div", { className: "setto-chat__log", ref: logRef, "aria-live": "polite", children: messages.map((item, index) => /* @__PURE__ */ jsxs("div", { className: `setto-chat__message setto-chat__message--${item.role}`, children: [
|
|
22263
|
-
item.role === "human" ? /* @__PURE__ */ jsx("span", { className: "setto-chat__speaker", children: "Teamet" }) : null,
|
|
22264
|
-
item.content || /* @__PURE__ */ jsx("span", { className: "setto-chat__typing", children: "···" })
|
|
22265
|
-
] }, `${item.role}-${index}`)) }),
|
|
22266
|
-
/* @__PURE__ */ jsxs("div", { className: "setto-chat__contact", children: [
|
|
22267
|
-
/* @__PURE__ */ jsxs("button", { type: "button", onClick: contactTeam, children: [
|
|
22268
|
-
labels.contact,
|
|
22269
|
-
" ",
|
|
22270
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "↗" })
|
|
22271
|
-
] }),
|
|
22272
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
|
|
22273
|
-
setLeadOpen((value) => !value);
|
|
22274
|
-
setLeadStatus("idle");
|
|
22275
|
-
}, children: labels.followUp })
|
|
22276
|
-
] }),
|
|
22277
|
-
leadOpen ? /* @__PURE__ */ jsxs("form", { className: "setto-chat__lead", onSubmit: submitLead, children: [
|
|
22278
|
-
/* @__PURE__ */ jsx("strong", { children: labels.followUpTitle }),
|
|
22279
|
-
/* @__PURE__ */ jsx("p", { children: labels.followUpDetail }),
|
|
22280
|
-
/* @__PURE__ */ jsxs("div", { className: "setto-chat__lead-grid", children: [
|
|
22281
|
-
/* @__PURE__ */ jsxs("label", { children: [
|
|
22282
|
-
labels.name,
|
|
22283
|
-
/* @__PURE__ */ jsx("input", { value: lead.name, maxLength: 160, onChange: (event) => setLead((current) => ({ ...current, name: event.target.value })) })
|
|
22284
|
-
] }),
|
|
22285
|
-
/* @__PURE__ */ jsxs("label", { children: [
|
|
22286
|
-
labels.email,
|
|
22287
|
-
/* @__PURE__ */ jsx("input", { type: "email", value: lead.email, maxLength: 320, onChange: (event) => setLead((current) => ({ ...current, email: event.target.value })) })
|
|
22288
|
-
] }),
|
|
22289
|
-
/* @__PURE__ */ jsxs("label", { children: [
|
|
22290
|
-
labels.phone,
|
|
22291
|
-
/* @__PURE__ */ jsx("input", { type: "tel", value: lead.phone, maxLength: 64, onChange: (event) => setLead((current) => ({ ...current, phone: event.target.value })) })
|
|
22292
|
-
] }),
|
|
22293
|
-
/* @__PURE__ */ jsxs("label", { children: [
|
|
22294
|
-
labels.interest,
|
|
22295
|
-
/* @__PURE__ */ jsx("input", { value: lead.interest, maxLength: 500, onChange: (event) => setLead((current) => ({ ...current, interest: event.target.value })) })
|
|
22296
|
-
] })
|
|
22297
|
-
] }),
|
|
22298
|
-
/* @__PURE__ */ jsxs("label", { className: "setto-chat__consent", children: [
|
|
22299
|
-
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: lead.consent, onChange: (event) => setLead((current) => ({ ...current, consent: event.target.checked })) }),
|
|
22300
|
-
labels.consent
|
|
22301
|
-
] }),
|
|
22302
|
-
leadStatus === "sent" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-success", children: labels.leadSent }) : null,
|
|
22303
|
-
leadStatus === "failed" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-error", children: conversationId.current ? labels.leadFailed : labels.leadConversation }) : null,
|
|
22304
|
-
leadStatus !== "sent" ? /* @__PURE__ */ jsx("button", { type: "submit", disabled: leadStatus === "sending" || !lead.consent || !lead.email.trim() && !lead.phone.trim(), children: leadStatus === "sending" ? labels.sending : labels.leadSend }) : null
|
|
22305
|
-
] }) : null,
|
|
22306
|
-
/* @__PURE__ */ jsxs("form", { className: "setto-chat__composer", onSubmit: submit, children: [
|
|
22307
|
-
/* @__PURE__ */ jsx("label", { className: "setto-chat__sr", htmlFor: `${formId}-input`, children: labels.placeholder }),
|
|
22308
|
-
/* @__PURE__ */ jsx(
|
|
22309
|
-
"textarea",
|
|
22709
|
+
}
|
|
22710
|
+
return /* @__PURE__ */ jsxs(
|
|
22711
|
+
"aside",
|
|
22712
|
+
{
|
|
22713
|
+
className: `setto-chat${mounted ? " setto-chat--mounted" : ""}${visible ? " setto-chat--visible" : ""}`,
|
|
22714
|
+
style: palette,
|
|
22715
|
+
"aria-label": labels.title,
|
|
22716
|
+
"data-setto-ui": true,
|
|
22717
|
+
children: [
|
|
22718
|
+
mounted ? /* @__PURE__ */ jsxs(
|
|
22719
|
+
"section",
|
|
22310
22720
|
{
|
|
22311
|
-
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22317
|
-
|
|
22318
|
-
|
|
22319
|
-
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
22323
|
-
|
|
22721
|
+
className: "setto-chat__shell",
|
|
22722
|
+
role: "dialog",
|
|
22723
|
+
"aria-modal": "true",
|
|
22724
|
+
"aria-labelledby": `${formId}-title`,
|
|
22725
|
+
children: [
|
|
22726
|
+
/* @__PURE__ */ jsx(
|
|
22727
|
+
"button",
|
|
22728
|
+
{
|
|
22729
|
+
className: `setto-chat__scrim${drawer ? " is-open" : ""}`,
|
|
22730
|
+
type: "button",
|
|
22731
|
+
onClick: () => setDrawer(false),
|
|
22732
|
+
"aria-label": labels.close,
|
|
22733
|
+
tabIndex: drawer ? 0 : -1
|
|
22734
|
+
}
|
|
22735
|
+
),
|
|
22736
|
+
/* @__PURE__ */ jsxs(
|
|
22737
|
+
"nav",
|
|
22738
|
+
{
|
|
22739
|
+
className: `setto-chat__chats${drawer ? " setto-chat__chats--open" : ""}`,
|
|
22740
|
+
"aria-label": labels.chats,
|
|
22741
|
+
children: [
|
|
22742
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__chats-heading", children: [
|
|
22743
|
+
/* @__PURE__ */ jsx("span", { children: labels.chats }),
|
|
22744
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setDrawer(false), "aria-label": labels.close, children: /* @__PURE__ */ jsx(CloseIcon, { size: 18 }) })
|
|
22745
|
+
] }),
|
|
22746
|
+
/* @__PURE__ */ jsxs("button", { className: "setto-chat__new", type: "button", onClick: startNew, children: [
|
|
22747
|
+
/* @__PURE__ */ jsx(PlusIcon, {}),
|
|
22748
|
+
labels.newChat
|
|
22749
|
+
] }),
|
|
22750
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__chat-list", children: conversations.length ? conversations.map((chat) => /* @__PURE__ */ jsxs(
|
|
22751
|
+
"button",
|
|
22752
|
+
{
|
|
22753
|
+
className: `setto-chat__chat-row${conversationId.current === chat.id ? " is-active" : ""}`,
|
|
22754
|
+
type: "button",
|
|
22755
|
+
onClick: () => void loadConversation(chat.id),
|
|
22756
|
+
children: [
|
|
22757
|
+
/* @__PURE__ */ jsx("span", { children: chat.preview }),
|
|
22758
|
+
/* @__PURE__ */ jsx("time", { children: new Date(chat.lastActivityAt).toLocaleDateString(
|
|
22759
|
+
isNo ? "nb-NO" : "en-GB",
|
|
22760
|
+
{ day: "numeric", month: "short" }
|
|
22761
|
+
) })
|
|
22762
|
+
]
|
|
22763
|
+
},
|
|
22764
|
+
chat.id
|
|
22765
|
+
)) : /* @__PURE__ */ jsx("p", { children: labels.emptyChats }) })
|
|
22766
|
+
]
|
|
22767
|
+
}
|
|
22768
|
+
),
|
|
22769
|
+
/* @__PURE__ */ jsxs("main", { className: "setto-chat__main", children: [
|
|
22770
|
+
/* @__PURE__ */ jsxs("header", { className: "setto-chat__header", children: [
|
|
22771
|
+
/* @__PURE__ */ jsx(
|
|
22772
|
+
"button",
|
|
22773
|
+
{
|
|
22774
|
+
className: "setto-chat__round-button",
|
|
22775
|
+
type: "button",
|
|
22776
|
+
onClick: () => setDrawer((current) => !current),
|
|
22777
|
+
"aria-label": labels.chats,
|
|
22778
|
+
"aria-expanded": drawer,
|
|
22779
|
+
children: /* @__PURE__ */ jsx(MenuIcon, {})
|
|
22780
|
+
}
|
|
22781
|
+
),
|
|
22782
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__brand", children: [
|
|
22783
|
+
/* @__PURE__ */ jsx("h2", { id: `${formId}-title`, children: labels.title }),
|
|
22784
|
+
/* @__PURE__ */ jsx("p", { children: siteName ?? "Barista" })
|
|
22785
|
+
] }),
|
|
22786
|
+
/* @__PURE__ */ jsx(
|
|
22787
|
+
"button",
|
|
22788
|
+
{
|
|
22789
|
+
className: "setto-chat__round-button",
|
|
22790
|
+
type: "button",
|
|
22791
|
+
onClick: hide,
|
|
22792
|
+
"aria-label": labels.close,
|
|
22793
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
22794
|
+
}
|
|
22795
|
+
)
|
|
22796
|
+
] }),
|
|
22797
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__disclosure", children: [
|
|
22798
|
+
/* @__PURE__ */ jsx("span", { children: "BARISTA" }),
|
|
22799
|
+
/* @__PURE__ */ jsx("p", { children: labels.disclosure })
|
|
22800
|
+
] }),
|
|
22801
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__body", children: [
|
|
22802
|
+
/* @__PURE__ */ jsxs(
|
|
22803
|
+
"div",
|
|
22804
|
+
{
|
|
22805
|
+
className: "setto-chat__log",
|
|
22806
|
+
ref: logRef,
|
|
22807
|
+
"aria-live": "polite",
|
|
22808
|
+
"aria-busy": loadingConversation,
|
|
22809
|
+
children: [
|
|
22810
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__message-list", children: messages.map((item, index) => /* @__PURE__ */ jsxs(
|
|
22811
|
+
"div",
|
|
22812
|
+
{
|
|
22813
|
+
className: `setto-chat__message setto-chat__message--${item.role}`,
|
|
22814
|
+
children: [
|
|
22815
|
+
item.role === "human" ? /* @__PURE__ */ jsx("span", { className: "setto-chat__speaker", children: labels.team }) : null,
|
|
22816
|
+
/* @__PURE__ */ jsx("span", { className: "setto-chat__message-copy", children: item.content || /* @__PURE__ */ jsx("span", { className: "setto-chat__typing", children: "···" }) }),
|
|
22817
|
+
item.attachments?.map((attachment) => /* @__PURE__ */ jsx(
|
|
22818
|
+
"a",
|
|
22819
|
+
{
|
|
22820
|
+
className: "setto-chat__image",
|
|
22821
|
+
href: attachment.url,
|
|
22822
|
+
target: "_blank",
|
|
22823
|
+
rel: "noreferrer",
|
|
22824
|
+
children: /* @__PURE__ */ jsx("img", { src: attachment.url, alt: labels.image })
|
|
22825
|
+
},
|
|
22826
|
+
attachment.id
|
|
22827
|
+
))
|
|
22828
|
+
]
|
|
22829
|
+
},
|
|
22830
|
+
item.id ?? `${item.role}-${index}`
|
|
22831
|
+
)) }),
|
|
22832
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__actions", children: [
|
|
22833
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: contactTeam, children: [
|
|
22834
|
+
labels.contact,
|
|
22835
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "↗" })
|
|
22836
|
+
] }),
|
|
22837
|
+
/* @__PURE__ */ jsx(
|
|
22838
|
+
"button",
|
|
22839
|
+
{
|
|
22840
|
+
type: "button",
|
|
22841
|
+
onClick: () => {
|
|
22842
|
+
setLeadOpen((value) => !value);
|
|
22843
|
+
setLeadStatus("idle");
|
|
22844
|
+
},
|
|
22845
|
+
"aria-expanded": leadOpen,
|
|
22846
|
+
children: labels.followUp
|
|
22847
|
+
}
|
|
22848
|
+
)
|
|
22849
|
+
] }),
|
|
22850
|
+
leadOpen ? /* @__PURE__ */ jsxs("form", { className: "setto-chat__lead", onSubmit: submitLead, children: [
|
|
22851
|
+
/* @__PURE__ */ jsx("strong", { children: labels.followUpTitle }),
|
|
22852
|
+
/* @__PURE__ */ jsx("p", { children: labels.followUpDetail }),
|
|
22853
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__lead-grid", children: [
|
|
22854
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22855
|
+
labels.name,
|
|
22856
|
+
/* @__PURE__ */ jsx(
|
|
22857
|
+
"input",
|
|
22858
|
+
{
|
|
22859
|
+
value: lead.name,
|
|
22860
|
+
maxLength: 160,
|
|
22861
|
+
onChange: (event) => setLead((current) => ({ ...current, name: event.target.value }))
|
|
22862
|
+
}
|
|
22863
|
+
)
|
|
22864
|
+
] }),
|
|
22865
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22866
|
+
labels.email,
|
|
22867
|
+
/* @__PURE__ */ jsx(
|
|
22868
|
+
"input",
|
|
22869
|
+
{
|
|
22870
|
+
type: "email",
|
|
22871
|
+
value: lead.email,
|
|
22872
|
+
maxLength: 320,
|
|
22873
|
+
onChange: (event) => setLead((current) => ({ ...current, email: event.target.value }))
|
|
22874
|
+
}
|
|
22875
|
+
)
|
|
22876
|
+
] }),
|
|
22877
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22878
|
+
labels.phone,
|
|
22879
|
+
/* @__PURE__ */ jsx(
|
|
22880
|
+
"input",
|
|
22881
|
+
{
|
|
22882
|
+
type: "tel",
|
|
22883
|
+
value: lead.phone,
|
|
22884
|
+
maxLength: 64,
|
|
22885
|
+
onChange: (event) => setLead((current) => ({ ...current, phone: event.target.value }))
|
|
22886
|
+
}
|
|
22887
|
+
)
|
|
22888
|
+
] }),
|
|
22889
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22890
|
+
labels.interest,
|
|
22891
|
+
/* @__PURE__ */ jsx(
|
|
22892
|
+
"input",
|
|
22893
|
+
{
|
|
22894
|
+
value: lead.interest,
|
|
22895
|
+
maxLength: 500,
|
|
22896
|
+
onChange: (event) => setLead((current) => ({ ...current, interest: event.target.value }))
|
|
22897
|
+
}
|
|
22898
|
+
)
|
|
22899
|
+
] })
|
|
22900
|
+
] }),
|
|
22901
|
+
/* @__PURE__ */ jsxs("label", { className: "setto-chat__consent", children: [
|
|
22902
|
+
/* @__PURE__ */ jsx(
|
|
22903
|
+
"input",
|
|
22904
|
+
{
|
|
22905
|
+
type: "checkbox",
|
|
22906
|
+
checked: lead.consent,
|
|
22907
|
+
onChange: (event) => setLead((current) => ({ ...current, consent: event.target.checked }))
|
|
22908
|
+
}
|
|
22909
|
+
),
|
|
22910
|
+
labels.consent
|
|
22911
|
+
] }),
|
|
22912
|
+
leadStatus === "sent" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-success", children: labels.leadSent }) : null,
|
|
22913
|
+
leadStatus === "failed" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-error", children: conversationId.current ? labels.leadFailed : labels.leadConversation }) : null,
|
|
22914
|
+
leadStatus !== "sent" ? /* @__PURE__ */ jsx(
|
|
22915
|
+
"button",
|
|
22916
|
+
{
|
|
22917
|
+
type: "submit",
|
|
22918
|
+
disabled: leadStatus === "sending" || !lead.consent || !lead.email.trim() && !lead.phone.trim(),
|
|
22919
|
+
children: leadStatus === "sending" ? labels.sending : labels.leadSend
|
|
22920
|
+
}
|
|
22921
|
+
) : null
|
|
22922
|
+
] }) : null
|
|
22923
|
+
]
|
|
22924
|
+
}
|
|
22925
|
+
),
|
|
22926
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-shell", children: [
|
|
22927
|
+
/* @__PURE__ */ jsx(
|
|
22928
|
+
SiteChatComposer,
|
|
22929
|
+
{
|
|
22930
|
+
value: draft,
|
|
22931
|
+
onChange: setDraft,
|
|
22932
|
+
onSubmit: queueOrSend,
|
|
22933
|
+
busy: sending,
|
|
22934
|
+
queued,
|
|
22935
|
+
placeholder: labels.placeholder,
|
|
22936
|
+
attachLabel: labels.attach,
|
|
22937
|
+
sendLabel: labels.send,
|
|
22938
|
+
removeLabel: labels.remove,
|
|
22939
|
+
locale,
|
|
22940
|
+
attachments: files,
|
|
22941
|
+
onAttach: addFiles,
|
|
22942
|
+
onRemoveAttachment: removeFile
|
|
22943
|
+
}
|
|
22944
|
+
),
|
|
22945
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__composer-floor", "aria-hidden": "true" })
|
|
22946
|
+
] })
|
|
22947
|
+
] })
|
|
22948
|
+
] })
|
|
22949
|
+
]
|
|
22324
22950
|
}
|
|
22325
|
-
),
|
|
22326
|
-
/* @__PURE__ */ jsx(
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22330
|
-
|
|
22331
|
-
|
|
22332
|
-
|
|
22333
|
-
|
|
22951
|
+
) : null,
|
|
22952
|
+
!mounted ? /* @__PURE__ */ jsx(
|
|
22953
|
+
"button",
|
|
22954
|
+
{
|
|
22955
|
+
className: "setto-chat__launcher",
|
|
22956
|
+
type: "button",
|
|
22957
|
+
onClick: show,
|
|
22958
|
+
"aria-label": labels.open,
|
|
22959
|
+
title: labels.open,
|
|
22960
|
+
children: availability.humanAvailable && availability.avatarUrl ? /* @__PURE__ */ jsx("img", { src: availability.avatarUrl, alt: "" }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(BaristaIcon, {}) })
|
|
22961
|
+
}
|
|
22962
|
+
) : null
|
|
22963
|
+
]
|
|
22964
|
+
}
|
|
22965
|
+
);
|
|
22334
22966
|
}
|
|
22335
22967
|
function replaceLastAssistant(messages, content) {
|
|
22336
22968
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
22337
|
-
|
|
22969
|
+
const current = messages[index];
|
|
22970
|
+
if (current?.role === "assistant") {
|
|
22338
22971
|
const next = [...messages];
|
|
22339
|
-
next[index] = {
|
|
22972
|
+
next[index] = { ...current, content };
|
|
22340
22973
|
return next;
|
|
22341
22974
|
}
|
|
22342
22975
|
}
|
|
@@ -22347,51 +22980,15 @@ function useChatStyles() {
|
|
|
22347
22980
|
const style = document.createElement("style");
|
|
22348
22981
|
style.dataset.settoChat = "";
|
|
22349
22982
|
style.textContent = `
|
|
22350
|
-
|
|
22351
|
-
|
|
22352
|
-
|
|
22353
|
-
|
|
22354
|
-
|
|
22355
|
-
|
|
22356
|
-
|
|
22357
|
-
|
|
22358
|
-
|
|
22359
|
-
.setto-chat--open .setto-chat__header { padding-top: max(20px, env(safe-area-inset-top)); padding-right: max(20px, env(safe-area-inset-right)); padding-left: max(20px, env(safe-area-inset-left)); }
|
|
22360
|
-
.setto-chat__eyebrow { margin: 0 0 4px; color: #b54628; font-size: 10px; font-weight: 700; letter-spacing: .14em; }
|
|
22361
|
-
.setto-chat__header h2 { margin: 0; color: #211a14; font-family: "Instrument Serif", Georgia, serif; font-size: 29px; font-weight: 400; line-height: 1; }
|
|
22362
|
-
.setto-chat__header p:not(.setto-chat__eyebrow) { margin: 7px 0 0; max-width: 280px; color: #70665a; font-size: 12px; line-height: 1.35; }
|
|
22363
|
-
.setto-chat__close { width: 32px; height: 32px; border: 0; border-radius: 50%; background: rgba(64,47,31,.07); color: #4f453a; font: 300 28px/27px Georgia, serif; cursor: pointer; }
|
|
22364
|
-
.setto-chat__log { min-height: 0; overflow: auto; padding: 18px; display: flex; flex-direction: column; gap: 10px; scroll-behavior: smooth; }
|
|
22365
|
-
.setto-chat__message { max-width: 88%; padding: 11px 13px; border-radius: 14px; font-size: 14px; line-height: 1.48; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
22366
|
-
.setto-chat__message--assistant { align-self: flex-start; background: #efe7da; color: #302820; border-bottom-left-radius: 3px; }
|
|
22367
|
-
.setto-chat__message--visitor { align-self: flex-end; background: #2b221b; color: #fffaf1; border-bottom-right-radius: 3px; }
|
|
22368
|
-
.setto-chat__message--human { align-self: flex-start; background: #f5d3c7; color: #48261d; border-bottom-left-radius: 3px; }
|
|
22369
|
-
.setto-chat__message--system { align-self: center; max-width: 100%; padding: 2px 8px; background: transparent; color: #817568; font-size: 11px; text-align: center; }
|
|
22370
|
-
.setto-chat__speaker { display: block; margin-bottom: 3px; color: #a13e24; font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
|
|
22371
|
-
.setto-chat__typing { display: inline-block; min-width: 24px; animation: setto-chat-pulse 1.1s ease-in-out infinite; letter-spacing: 2px; }
|
|
22372
|
-
@keyframes setto-chat-pulse { 50% { opacity: .3; } }
|
|
22373
|
-
.setto-chat__contact { padding: 0 18px 12px; display: flex; gap: 14px; flex-wrap: wrap; }
|
|
22374
|
-
.setto-chat__contact button { border: 0; padding: 0; background: transparent; color: #b54628; font: 600 12px/1.4 Archivo, ui-sans-serif, sans-serif; cursor: pointer; }
|
|
22375
|
-
.setto-chat__lead { display: grid; gap: 8px; max-height: 255px; overflow: auto; padding: 12px 18px; border-top: 1px solid #eadfce; background: #fff7ed; font-size: 12px; line-height: 1.4; }
|
|
22376
|
-
.setto-chat__lead p { margin: 0; color: #70665a; }
|
|
22377
|
-
.setto-chat__lead-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
|
|
22378
|
-
.setto-chat__lead label { display: grid; gap: 3px; color: #5f5347; font-weight: 600; }
|
|
22379
|
-
.setto-chat__lead input:not([type="checkbox"]) { width: 100%; border: 1px solid #d8ccba; border-radius: 7px; background: #fffdf8; color: #231d17; padding: 7px; font: 13px Archivo, ui-sans-serif, sans-serif; }
|
|
22380
|
-
.setto-chat__lead .setto-chat__consent { grid-template-columns: auto 1fr; align-items: start; font-weight: 400; }
|
|
22381
|
-
.setto-chat__lead button { justify-self: start; min-height: 34px; border: 0; border-radius: 8px; padding: 0 12px; background: #b54628; color: #fffaf1; font: 600 12px Archivo, ui-sans-serif, sans-serif; cursor: pointer; }
|
|
22382
|
-
.setto-chat__lead button:disabled { opacity: .5; cursor: default; }
|
|
22383
|
-
.setto-chat__lead-success { color: #30683d !important; font-weight: 600; }
|
|
22384
|
-
.setto-chat__lead-error { color: #9b2f1e !important; font-weight: 600; }
|
|
22385
|
-
.setto-chat__composer { padding: 12px; display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 8px; border-top: 1px solid #eadfce; background: #fffdf8; }
|
|
22386
|
-
.setto-chat--open .setto-chat__composer { padding-right: max(12px, env(safe-area-inset-right)); padding-bottom: max(12px, env(safe-area-inset-bottom)); padding-left: max(12px, env(safe-area-inset-left)); }
|
|
22387
|
-
.setto-chat__composer textarea { width: 100%; max-height: 100px; resize: none; border: 1px solid #d8ccba; border-radius: 10px; background: #fffaf1; color: #231d17; padding: 10px; font: 14px/1.35 Archivo, ui-sans-serif, sans-serif; outline: none; }
|
|
22388
|
-
.setto-chat__composer textarea:focus { border-color: #b54628; box-shadow: 0 0 0 3px rgba(181,70,40,.12); }
|
|
22389
|
-
.setto-chat__composer button { min-height: 40px; border: 0; border-radius: 9px; padding: 0 13px; background: #b54628; color: #fffaf1; font: 600 13px Archivo, ui-sans-serif, sans-serif; cursor: pointer; }
|
|
22390
|
-
.setto-chat__composer button:disabled { opacity: .5; cursor: default; }
|
|
22391
|
-
.setto-chat__sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
|
|
22392
|
-
@media (max-width: 480px) { .setto-chat:not(.setto-chat--open) { right: 12px; bottom: max(12px, env(safe-area-inset-bottom)); } .setto-chat:not(.setto-chat--open) .setto-chat__panel { width: calc(100vw - 24px); height: min(610px, calc(100dvh - 88px)); } .setto-chat__launcher { min-height: 50px; } .setto-chat__lead-grid { grid-template-columns: 1fr; } }
|
|
22393
|
-
@media (prefers-reduced-motion: reduce) { .setto-chat *, .setto-chat *::before, .setto-chat *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } }
|
|
22394
|
-
`;
|
|
22983
|
+
.setto-chat{position:fixed;right:max(18px,env(safe-area-inset-right));bottom:max(18px,env(safe-area-inset-bottom));z-index:2147483000;color:var(--setto-chat-ink);font-family:"Archivo Variable",Archivo,system-ui,sans-serif;-webkit-font-smoothing:antialiased}.setto-chat *,.setto-chat *::before,.setto-chat *::after{box-sizing:border-box}.setto-chat button,.setto-chat input,.setto-chat textarea{font:inherit}.setto-chat--mounted{top:var(--setto-chat-top,0);right:auto;bottom:auto;left:0;width:100vw;height:var(--setto-chat-height,100svh);overflow:hidden;background:var(--setto-chat-canvas);opacity:0;transition:opacity ${CLOSE_MS}ms ease}.setto-chat--visible{opacity:1}.setto-chat__shell{position:relative;width:100%;height:100%;display:flex;overflow:hidden;background:var(--setto-chat-canvas);opacity:0;transform:translateY(12px) scale(.994);transform-origin:center bottom;transition:opacity ${CLOSE_MS}ms ease,transform ${CLOSE_MS}ms cubic-bezier(.16,1,.3,1)}.setto-chat--visible .setto-chat__shell{opacity:1;transform:none}
|
|
22984
|
+
.setto-chat__scrim{position:absolute;inset:0;z-index:45;border:0;background:rgba(33,23,17,.32);opacity:0;pointer-events:none;transition:opacity 220ms ease}.setto-chat__scrim.is-open{opacity:1;pointer-events:auto}.setto-chat__chats{position:relative;z-index:50;width:min(84vw,280px);height:100%;flex:0 0 280px;padding:1rem .85rem calc(1rem + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:.9rem;background:color-mix(in srgb,var(--setto-chat-crema) 32%,var(--setto-chat-canvas));border-right:1px solid color-mix(in srgb,var(--setto-chat-ink) 12%,transparent);overflow:hidden}.setto-chat__chats-heading{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:34px;font-size:.66rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:color-mix(in srgb,var(--setto-chat-ink) 58%,transparent)}.setto-chat__chats-heading button{width:34px;height:34px;padding:0;border:0;border-radius:8px;background:transparent;color:color-mix(in srgb,var(--setto-chat-ink) 58%,transparent);display:inline-flex;align-items:center;justify-content:center;cursor:pointer}.setto-chat__new{display:flex;align-items:center;gap:.55rem;width:100%;padding:.5rem .55rem;border:0;border-radius:8px;background:transparent;color:var(--setto-chat-ink);font-weight:600;font-size:.9rem;text-align:left;cursor:pointer;transition:background 140ms ease}.setto-chat__new:hover{background:color-mix(in srgb,var(--setto-chat-ink) 5%,transparent)}.setto-chat__chat-list{display:flex;flex-direction:column;gap:.1rem;flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;margin-right:-.85rem;padding-right:.3rem}.setto-chat__chat-list p{margin:.35rem .55rem;font-size:.85rem;line-height:1.5;color:color-mix(in srgb,var(--setto-chat-ink) 58%,transparent)}.setto-chat__chat-row{width:100%;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:.4rem;padding:.55rem;border:0;border-radius:8px;background:transparent;color:var(--setto-chat-ink);text-align:left;cursor:pointer}.setto-chat__chat-row:hover,.setto-chat__chat-row.is-active{background:color-mix(in srgb,var(--setto-chat-ink) 7.5%,transparent)}.setto-chat__chat-row span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.9rem;font-weight:600}.setto-chat__chat-row time{font-size:.72rem;color:color-mix(in srgb,var(--setto-chat-ink) 55%,transparent)}
|
|
22985
|
+
.setto-chat__main{position:relative;z-index:1;display:flex;flex:1;min-width:0;max-width:820px;height:100%;margin:0 auto;flex-direction:column;background:var(--setto-chat-canvas)}.setto-chat__header{position:relative;z-index:30;min-height:58px;padding:.35rem .75rem;display:flex;align-items:center;gap:.5rem;flex:0 0 auto;background:transparent;isolation:isolate}.setto-chat__header::before{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;background:color-mix(in srgb,var(--setto-chat-crema) 70%,transparent);backdrop-filter:blur(18px) saturate(150%);-webkit-backdrop-filter:blur(18px) saturate(150%);-webkit-mask-image:linear-gradient(to bottom,rgba(0,0,0,1) 45%,rgba(0,0,0,.45) 78%,rgba(0,0,0,0) 100%);mask-image:linear-gradient(to bottom,rgba(0,0,0,1) 45%,rgba(0,0,0,.45) 78%,rgba(0,0,0,0) 100%)}.setto-chat__round-button{flex:none;width:46px;height:46px;padding:0;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 12%,transparent);border-radius:50%;background:var(--setto-chat-raised);color:var(--setto-chat-ink);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background 160ms ease,border-color 160ms ease,color 160ms ease}.setto-chat__round-button:hover{background:var(--setto-chat-crema);border-color:var(--setto-chat-accent);color:var(--setto-chat-accent)}.setto-chat__round-button:active{transform:scale(.96)}.setto-chat__brand{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;line-height:1.15;text-align:center}.setto-chat__brand h2{margin:0;color:var(--setto-chat-ink);font-size:1.02rem;font-weight:600;line-height:1.15}.setto-chat__brand p{max-width:100%;margin:2px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:color-mix(in srgb,var(--setto-chat-ink) 56%,transparent);font-size:.78rem;line-height:1.15}.setto-chat__disclosure{display:flex;align-items:baseline;gap:.65rem;padding:.9rem .75rem .35rem;flex:0 0 auto}.setto-chat__disclosure span{flex:none;color:var(--setto-chat-accent);font-size:.68rem;font-weight:700;letter-spacing:.12em}.setto-chat__disclosure p{margin:0;color:color-mix(in srgb,var(--setto-chat-ink) 62%,transparent);font-size:.9rem;line-height:1.45}
|
|
22986
|
+
.setto-chat__body{position:relative;display:flex;flex:1 1 auto;min-height:0;flex-direction:column}.setto-chat__log{display:flex;flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;flex-direction:column;padding:1.3rem .75rem 8rem;scroll-behavior:smooth;-webkit-overflow-scrolling:touch}.setto-chat__message-list{display:flex;flex-direction:column}.setto-chat__message{display:block;margin-bottom:1.5rem;font-size:16px;line-height:1.65;overflow-wrap:anywhere;white-space:pre-wrap;color:color-mix(in srgb,var(--setto-chat-ink) 88%,transparent)}.setto-chat__message--assistant{align-self:stretch;max-width:100%;padding:0;background:transparent}.setto-chat__message--visitor{align-self:flex-end;max-width:85%;margin-bottom:1.25rem;padding:.65rem .95rem;background:var(--setto-chat-crema);color:var(--setto-chat-ink);border:1px solid color-mix(in srgb,var(--setto-chat-ink) 7%,transparent);border-radius:20px 20px 4px 20px;font-size:.95rem;line-height:1.55;box-shadow:0 1px 2px color-mix(in srgb,var(--setto-chat-ink) 6%,transparent)}.setto-chat__message--human{align-self:stretch;max-width:100%;padding-left:.8rem;border-left:2px solid var(--setto-chat-accent)}.setto-chat__message--system{align-self:center;max-width:85%;padding:.25rem .6rem;color:color-mix(in srgb,var(--setto-chat-ink) 55%,transparent);font-size:.78rem;line-height:1.45;text-align:center}.setto-chat__speaker{display:block;margin-bottom:.15rem;color:var(--setto-chat-accent);font-size:.68rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}.setto-chat__typing{display:inline-block;animation:setto-chat-pulse 1s ease-in-out infinite}.setto-chat__image{display:block;width:max-content;max-width:100%;margin-top:.65rem;overflow:hidden;border-radius:14px;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 10%,transparent)}.setto-chat__image img{display:block;width:min(280px,100%);max-height:260px;object-fit:cover}
|
|
22987
|
+
.setto-chat__actions{display:flex;flex-wrap:wrap;gap:1.35rem;margin-top:auto;padding:1.2rem 0 .65rem}.setto-chat__actions button{display:inline-flex;align-items:center;gap:.25rem;padding:.2rem 0;border:0;background:transparent;color:var(--setto-chat-accent);font-size:.9rem;font-weight:700;cursor:pointer}.setto-chat__lead{display:grid;gap:.6rem;margin:.5rem 0 1rem;padding:1rem;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 12%,transparent);border-radius:16px;background:color-mix(in srgb,var(--setto-chat-crema) 58%,var(--setto-chat-canvas))}.setto-chat__lead strong{font-size:.95rem}.setto-chat__lead p{margin:0;color:color-mix(in srgb,var(--setto-chat-ink) 65%,transparent);font-size:.8rem;line-height:1.45}.setto-chat__lead-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.55rem}.setto-chat__lead label{display:grid;gap:.25rem;font-size:.72rem;font-weight:700}.setto-chat__lead input{min-width:0;padding:.55rem .6rem;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 18%,transparent);border-radius:8px;background:var(--setto-chat-canvas);color:var(--setto-chat-ink);font-size:.85rem}.setto-chat__consent{display:flex!important;align-items:flex-start;gap:.45rem;font-weight:400!important;line-height:1.4}.setto-chat__lead>button{justify-self:start;padding:.55rem .8rem;border:0;border-radius:9px;background:var(--setto-chat-accent);color:var(--setto-chat-raised);font-size:.78rem;font-weight:700;cursor:pointer}.setto-chat__lead>button:disabled{opacity:.4;cursor:default}.setto-chat__lead-success{color:#28633d!important;font-weight:700}.setto-chat__lead-error{color:#a33024!important;font-weight:700}
|
|
22988
|
+
.setto-chat__composer-shell{position:absolute;left:0;right:0;bottom:0;z-index:25;display:grid;grid-template-columns:minmax(0,1fr);padding:1.5rem .75rem 0;background:transparent;isolation:isolate}.setto-chat__composer-shell::before{content:"";position:absolute;inset:0;pointer-events:none;backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);-webkit-mask-image:linear-gradient(to bottom,rgba(0,0,0,0) 0%,rgba(0,0,0,.4) 34%,rgba(0,0,0,1) 66%);mask-image:linear-gradient(to bottom,rgba(0,0,0,0) 0%,rgba(0,0,0,.4) 34%,rgba(0,0,0,1) 66%)}.setto-chat__composer{position:relative;z-index:1;display:flex;flex-direction:column;gap:.15rem;width:100%;min-width:0;margin:0;padding:.35rem .4rem;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 10%,transparent);border-radius:24px;background:var(--setto-chat-canvas);box-shadow:0 8px 24px -16px color-mix(in srgb,var(--setto-chat-ink) 28%,transparent);transition:padding 220ms cubic-bezier(.16,1,.3,1),border-color 200ms ease,box-shadow 360ms cubic-bezier(.16,1,.3,1)}.setto-chat__composer.is-open{padding:.55rem .5rem}.setto-chat__composer:focus-within{border-color:var(--setto-chat-accent)}.setto-chat__composer textarea{width:100%;min-height:36px;max-height:200px;padding:.45rem .25rem;resize:none;border:0;outline:none;background:transparent;color:var(--setto-chat-ink);font-family:"Archivo Variable",Archivo,system-ui,sans-serif;font-size:16px;line-height:1.35;transition:min-height 220ms cubic-bezier(.16,1,.3,1)}.setto-chat__composer.is-open textarea{min-height:40px}.setto-chat__composer textarea::placeholder{color:color-mix(in srgb,var(--setto-chat-ink) 52%,transparent)}.setto-chat__composer-tools{display:flex;align-items:center;gap:.4rem;width:100%;min-width:0}.setto-chat__attach{flex:none;width:34px;height:34px;padding:0;border:0;border-radius:50%;background:transparent;color:color-mix(in srgb,var(--setto-chat-ink) 58%,transparent);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background 160ms ease,color 160ms ease}.setto-chat__attach:hover:not(:disabled){background:color-mix(in srgb,var(--setto-chat-ink) 6%,transparent);color:var(--setto-chat-ink)}.setto-chat__attach:disabled{opacity:.4;cursor:default}.setto-chat__send{flex:none;width:36px;height:36px;margin-left:auto;padding:0;border:0;border-radius:50%;background:var(--setto-chat-accent);color:var(--setto-chat-raised);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:transform 360ms cubic-bezier(.16,1,.3,1),box-shadow 360ms cubic-bezier(.16,1,.3,1),background 200ms ease,opacity 200ms ease}.setto-chat__send:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 12px 24px -12px color-mix(in srgb,var(--setto-chat-ink) 45%,transparent)}.setto-chat__send:active:not(:disabled){transform:translateY(0);box-shadow:0 4px 10px -8px color-mix(in srgb,var(--setto-chat-ink) 40%,transparent);transition-duration:90ms}.setto-chat__send:disabled{opacity:.35;cursor:default}.setto-chat__send.is-queued{animation:setto-chat-queued 1.1s ease-in-out infinite}.setto-chat__attachments{display:flex;flex-wrap:wrap;gap:.4rem;list-style:none;margin:0;padding:.15rem .15rem 0}.setto-chat__attachments li{position:relative;width:56px;height:56px;border-radius:10px;overflow:hidden;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 12%,transparent);background:var(--setto-chat-crema)}.setto-chat__attachments img{display:block;width:100%;height:100%;object-fit:cover}.setto-chat__attachments button{position:absolute;top:2px;right:2px;width:18px;height:18px;padding:0;border:0;border-radius:50%;background:color-mix(in srgb,var(--setto-chat-ink) 72%,transparent);color:#fff;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}.setto-chat__composer-floor{position:relative;z-index:1;width:calc(100% + 1.5rem);height:max(.35rem,env(safe-area-inset-bottom));margin-left:-.75rem}
|
|
22989
|
+
.setto-chat__launcher{width:58px;height:58px;padding:3px;border:1px solid rgba(255,255,255,.54);border-radius:50%;background:color-mix(in srgb,var(--setto-chat-raised) 35%,transparent);box-shadow:0 10px 30px color-mix(in srgb,var(--setto-chat-ink) 24%,transparent),inset 0 0 0 1px color-mix(in srgb,var(--setto-chat-ink) 9%,transparent);backdrop-filter:blur(14px) saturate(115%);-webkit-backdrop-filter:blur(14px) saturate(115%);color:var(--setto-chat-ink);display:grid;place-items:center;cursor:pointer;transition:transform 180ms ease,box-shadow 180ms ease}.setto-chat__launcher:hover{transform:translateY(-2px);box-shadow:0 15px 35px color-mix(in srgb,var(--setto-chat-ink) 28%,transparent),inset 0 0 0 1px color-mix(in srgb,var(--setto-chat-ink) 9%,transparent)}.setto-chat__launcher span{width:46px;height:46px;border-radius:50%;display:grid;place-items:center;background:var(--setto-chat-accent);color:var(--setto-chat-raised)}.setto-chat__launcher img{display:block;width:50px;height:50px;border-radius:50%;object-fit:cover}.setto-chat__launcher:focus-visible,.setto-chat button:focus-visible,.setto-chat textarea:focus-visible,.setto-chat input:focus-visible{outline:2px solid var(--setto-chat-accent);outline-offset:2px}@keyframes setto-chat-pulse{50%{opacity:.35}}@keyframes setto-chat-queued{50%{opacity:.55}}
|
|
22990
|
+
@media(max-width:700px){.setto-chat__chats{position:absolute;inset:0 auto 0 0;transform:translateX(-100%);transition:transform 260ms cubic-bezier(.16,1,.3,1);box-shadow:14px 0 35px color-mix(in srgb,var(--setto-chat-ink) 16%,transparent)}.setto-chat__chats--open{transform:none}.setto-chat__main{width:100%;max-width:none;margin:0}.setto-chat__disclosure{padding-left:.75rem;padding-right:.75rem}.setto-chat__log{padding-left:.75rem;padding-right:.75rem}.setto-chat__lead-grid{grid-template-columns:1fr}.setto-chat__actions{gap:1.1rem}.setto-chat__actions button{font-size:.88rem}}@media(min-width:700px){.setto-chat__header,.setto-chat__disclosure,.setto-chat__log{padding-left:1.25rem;padding-right:1.25rem}.setto-chat__composer-shell{padding-left:1.25rem;padding-right:1.25rem}.setto-chat__composer-floor{width:calc(100% + 2.5rem);margin-left:-1.25rem}.setto-chat__message{font-size:16.5px}}@media(prefers-reduced-motion:reduce){.setto-chat,.setto-chat__shell,.setto-chat__chats,.setto-chat__launcher,.setto-chat__composer,.setto-chat__composer textarea,.setto-chat__send{transition:none!important}.setto-chat__typing,.setto-chat__send.is-queued{animation:none}}
|
|
22991
|
+
`;
|
|
22395
22992
|
document.head.appendChild(style);
|
|
22396
22993
|
return () => style.remove();
|
|
22397
22994
|
}, []);
|