@sevenfold/setto-client 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/chat/SettoChat.d.ts +1 -4
- package/dist/setto-client.js +228 -241
- 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,7 @@ function EditModeShell({ children, themeStore, active }) {
|
|
|
22033
22034
|
] });
|
|
22034
22035
|
}
|
|
22035
22036
|
const VISITOR_KEY = "setto-chat-visitor";
|
|
22037
|
+
const CLOSE_MS = 210;
|
|
22036
22038
|
function visitorId() {
|
|
22037
22039
|
try {
|
|
22038
22040
|
const stored = window.localStorage.getItem(VISITOR_KEY);
|
|
@@ -22051,170 +22053,189 @@ function intro(locale, name) {
|
|
|
22051
22053
|
return norwegian(locale) ? `Hei! Jeg er Barista, ${name ? `${name}s` : "nettstedets"} AI-assistent. Jeg svarer ut fra det teamet har delt her, og hjelper deg videre til dem når det trengs.` : `Hello! I’m Barista, the AI assistant for ${name ?? "this website"}. I answer from what the team has shared here and help you reach them when needed.`;
|
|
22052
22054
|
}
|
|
22053
22055
|
function contactTeam() {
|
|
22054
|
-
const link = document.querySelector(
|
|
22055
|
-
'a[href*="kontakt" i], a[href*="contact" i], a[href^="mailto:"]'
|
|
22056
|
-
);
|
|
22056
|
+
const link = document.querySelector('a[href*="kontakt" i], a[href*="contact" i], a[href^="mailto:"]');
|
|
22057
22057
|
if (link) link.click();
|
|
22058
22058
|
else window.location.hash = "kontakt";
|
|
22059
22059
|
}
|
|
22060
|
+
function validColor(value, fallback) {
|
|
22061
|
+
return value && (/^#[0-9a-f]{3,8}$/i.test(value) || /^rgb/.test(value)) ? value : fallback;
|
|
22062
|
+
}
|
|
22063
|
+
function chatPalette(colors) {
|
|
22064
|
+
const values = colors?.map((color) => color.value) ?? [];
|
|
22065
|
+
const ink = values.find((value) => /^#(?:[0-2][0-9a-f]){3}$/i.test(value));
|
|
22066
|
+
return { "--setto-chat-pale": validColor(values[0], "#F2EBDF"), "--setto-chat-paper": validColor(values[1], "#FAF6EE"), "--setto-chat-ink": validColor(ink, "#211711"), "--setto-chat-accent": validColor(values[values.length - 2] ?? values.at(-1), "#C4502A") };
|
|
22067
|
+
}
|
|
22060
22068
|
function SettoChat() {
|
|
22061
22069
|
const { config } = useSetto();
|
|
22062
22070
|
const { i18n } = useTranslation();
|
|
22063
22071
|
const apiUrl = config.apiUrl.replace(/\/+$/, "");
|
|
22064
22072
|
const [settings, setSettings] = useState(null);
|
|
22065
|
-
const [
|
|
22073
|
+
const [mounted, setMounted] = useState(false);
|
|
22074
|
+
const [visible, setVisible] = useState(false);
|
|
22075
|
+
const [drawer, setDrawer] = useState(false);
|
|
22066
22076
|
const [messages, setMessages] = useState([]);
|
|
22077
|
+
const [conversations, setConversations] = useState([]);
|
|
22067
22078
|
const [draft, setDraft] = useState("");
|
|
22079
|
+
const [files, setFiles] = useState([]);
|
|
22068
22080
|
const [sending, setSending] = useState(false);
|
|
22081
|
+
const [loadingConversation, setLoadingConversation] = useState(false);
|
|
22082
|
+
const [availability, setAvailability] = useState({ humanAvailable: false, avatarUrl: null });
|
|
22069
22083
|
const [leadOpen, setLeadOpen] = useState(false);
|
|
22070
22084
|
const [leadStatus, setLeadStatus] = useState("idle");
|
|
22071
22085
|
const [lead, setLead] = useState({ name: "", email: "", phone: "", interest: "", consent: false });
|
|
22072
22086
|
const conversationId = useRef(null);
|
|
22073
22087
|
const visitor = useRef(null);
|
|
22074
22088
|
const logRef = useRef(null);
|
|
22089
|
+
const fileInput = useRef(null);
|
|
22075
22090
|
const formId = useId();
|
|
22091
|
+
const palette = useMemo(() => chatPalette(config.brandColors), [config.brandColors]);
|
|
22076
22092
|
useChatStyles();
|
|
22093
|
+
const locale = i18n.language || "no";
|
|
22094
|
+
const isNo = norwegian(locale);
|
|
22095
|
+
const siteName = settings?.displayName?.trim() || config.displayName?.trim() || null;
|
|
22096
|
+
const labels = isNo ? { open: siteName ? `Åpne chat med ${siteName}` : "Åpne chat", close: "Lukk chatten", title: "Spør Barista", chats: "Chats", newChat: "Ny chat", disclosure: "AI-assistent · svarer fra nettstedets godkjente informasjon", placeholder: "Hva vil du spørre om?", send: "Send", sending: "Svarer …", attach: "Legg ved bilde", contact: "Kontakt teamet", followUp: "Be om oppfølging", followUpTitle: "La teamet følge opp", followUpDetail: "Legg igjen kontaktinformasjon hvis du vil at teamet skal følge opp denne samtalen.", name: "Navn", email: "E-post", phone: "Telefon", interest: "Hva vil du ha hjelp med?", consent: "Ja, teamet kan bruke opplysningene mine til å følge opp denne henvendelsen.", leadSend: "Send forespørsel", leadSent: "Takk — forespørselen er sendt til teamet.", leadFailed: "Kunne ikke lagre forespørselen. Prøv igjen eller kontakt teamet direkte.", leadConversation: "Still et spørsmål først, så kan vi knytte oppfølgingen til samtalen.", failed: "Jeg får ikke svart akkurat nå. Kontakt gjerne teamet direkte.", image: "Bilde", team: "Teamet", emptyChats: "Nye samtaler du starter her vises i denne listen." } : { open: siteName ? `Open chat with ${siteName}` : "Open chat", close: "Close chat", title: "Ask Barista", chats: "Chats", newChat: "New chat", disclosure: "AI assistant · answers from this website’s approved information", placeholder: "What would you like to ask?", send: "Send", sending: "Replying …", attach: "Attach image", contact: "Contact the team", followUp: "Ask for follow-up", followUpTitle: "Let the team follow up", followUpDetail: "Leave contact details if you want the team to follow up on this conversation.", name: "Name", email: "Email", phone: "Phone", interest: "What would you like help with?", consent: "Yes, the team may use my details to follow up on this enquiry.", leadSend: "Send request", leadSent: "Thanks — your request has been sent to the team.", leadFailed: "We could not save your request. Please try again or contact the team directly.", leadConversation: "Ask a question first so we can link the follow-up to this conversation.", failed: "I cannot reply right now. Please contact the team directly.", image: "Image", team: "The team", emptyChats: "New conversations you start here will appear in this list." };
|
|
22097
|
+
const ensureVisitor = () => visitor.current ??= visitorId();
|
|
22098
|
+
const loadChats = async () => {
|
|
22099
|
+
const id = visitor.current;
|
|
22100
|
+
if (!id) return;
|
|
22101
|
+
try {
|
|
22102
|
+
const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/public?${new URLSearchParams({ visitorId: id })}`, { headers: { Accept: "application/json" } });
|
|
22103
|
+
if (response.ok) setConversations((await response.json()).conversations);
|
|
22104
|
+
} catch {
|
|
22105
|
+
}
|
|
22106
|
+
};
|
|
22107
|
+
const loadConversation = async (id) => {
|
|
22108
|
+
const visitorIdValue = ensureVisitor();
|
|
22109
|
+
setLoadingConversation(true);
|
|
22110
|
+
try {
|
|
22111
|
+
const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(id)}/public?${new URLSearchParams({ visitorId: visitorIdValue })}`, { headers: { Accept: "application/json" } });
|
|
22112
|
+
if (!response.ok) return;
|
|
22113
|
+
const next = await response.json();
|
|
22114
|
+
conversationId.current = next.conversationId;
|
|
22115
|
+
setMessages(next.messages);
|
|
22116
|
+
setLeadOpen(false);
|
|
22117
|
+
setDrawer(false);
|
|
22118
|
+
} finally {
|
|
22119
|
+
setLoadingConversation(false);
|
|
22120
|
+
}
|
|
22121
|
+
};
|
|
22077
22122
|
useEffect(() => {
|
|
22078
22123
|
if (window.location.pathname.startsWith("/setto")) return;
|
|
22079
22124
|
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) => {
|
|
22125
|
+
fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/settings/public`, { headers: { Accept: "application/json" } }).then(async (response) => response.ok ? await response.json() : null).then((next) => {
|
|
22083
22126
|
if (!cancelled && next?.enabled) setSettings(next);
|
|
22084
|
-
}).catch(() =>
|
|
22085
|
-
});
|
|
22127
|
+
}).catch(() => void 0);
|
|
22086
22128
|
return () => {
|
|
22087
22129
|
cancelled = true;
|
|
22088
22130
|
};
|
|
22089
22131
|
}, [apiUrl, config.siteId]);
|
|
22090
22132
|
useEffect(() => {
|
|
22091
|
-
if (!
|
|
22092
|
-
|
|
22093
|
-
|
|
22133
|
+
if (!settings) return;
|
|
22134
|
+
let cancelled = false;
|
|
22135
|
+
const refresh = () => fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/availability/public`, { headers: { Accept: "application/json" } }).then(async (response) => response.ok ? await response.json() : null).then((next) => {
|
|
22136
|
+
if (!cancelled && next) setAvailability(next);
|
|
22137
|
+
}).catch(() => void 0);
|
|
22138
|
+
void refresh();
|
|
22139
|
+
const timer = window.setInterval(() => void refresh(), 3e4);
|
|
22140
|
+
return () => {
|
|
22141
|
+
cancelled = true;
|
|
22142
|
+
window.clearInterval(timer);
|
|
22143
|
+
};
|
|
22144
|
+
}, [apiUrl, config.siteId, settings]);
|
|
22145
|
+
useEffect(() => {
|
|
22146
|
+
if (mounted && messages.length === 0 && settings) setMessages([{ role: "assistant", content: intro(locale, settings.displayName ?? void 0) }]);
|
|
22147
|
+
}, [mounted, messages.length, settings, locale]);
|
|
22094
22148
|
useEffect(() => {
|
|
22095
|
-
if (
|
|
22096
|
-
}, [messages,
|
|
22149
|
+
if (mounted) logRef.current?.scrollTo({ top: logRef.current.scrollHeight, behavior: "smooth" });
|
|
22150
|
+
}, [messages, mounted, sending]);
|
|
22151
|
+
useEffect(() => () => files.forEach((image) => URL.revokeObjectURL(image.preview)), [files]);
|
|
22097
22152
|
useEffect(() => {
|
|
22098
|
-
if (!
|
|
22153
|
+
if (!mounted || !conversationId.current || !visitor.current || sending) return;
|
|
22099
22154
|
let cancelled = false;
|
|
22100
22155
|
const refresh = async () => {
|
|
22101
22156
|
try {
|
|
22102
|
-
const
|
|
22103
|
-
|
|
22104
|
-
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(conversationId.current)}/public?${query}`,
|
|
22105
|
-
{ headers: { Accept: "application/json" } }
|
|
22106
|
-
);
|
|
22107
|
-
if (!response.ok) return;
|
|
22157
|
+
const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(conversationId.current)}/public?${new URLSearchParams({ visitorId: visitor.current })}`, { headers: { Accept: "application/json" } });
|
|
22158
|
+
if (!response.ok || cancelled) return;
|
|
22108
22159
|
const next = await response.json();
|
|
22109
|
-
if (cancelled)
|
|
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
|
-
});
|
|
22160
|
+
if (!cancelled) setMessages(next.messages);
|
|
22115
22161
|
} catch {
|
|
22116
22162
|
}
|
|
22117
22163
|
};
|
|
22118
|
-
void refresh();
|
|
22119
22164
|
const timer = window.setInterval(() => void refresh(), 5e3);
|
|
22120
22165
|
return () => {
|
|
22121
22166
|
cancelled = true;
|
|
22122
22167
|
window.clearInterval(timer);
|
|
22123
22168
|
};
|
|
22124
|
-
}, [apiUrl, config.siteId,
|
|
22169
|
+
}, [apiUrl, config.siteId, mounted, sending]);
|
|
22125
22170
|
if (!settings || window.location.pathname.startsWith("/setto")) return null;
|
|
22126
|
-
const
|
|
22127
|
-
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22133
|
-
|
|
22134
|
-
|
|
22135
|
-
|
|
22136
|
-
|
|
22137
|
-
|
|
22138
|
-
|
|
22139
|
-
|
|
22140
|
-
|
|
22141
|
-
|
|
22142
|
-
|
|
22143
|
-
|
|
22144
|
-
|
|
22145
|
-
leadSend: "Send forespørsel",
|
|
22146
|
-
leadSent: "Takk — forespørselen er sendt til teamet.",
|
|
22147
|
-
leadFailed: "Kunne ikke lagre forespørselen. Prøv igjen eller kontakt teamet direkte.",
|
|
22148
|
-
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."
|
|
22150
|
-
} : {
|
|
22151
|
-
open: siteName ? `Chat with ${siteName}` : "Chat with us",
|
|
22152
|
-
close: "Close chat",
|
|
22153
|
-
title: "Ask Barista",
|
|
22154
|
-
disclosure: "AI assistant · answers from this website’s approved information",
|
|
22155
|
-
placeholder: "Write your question …",
|
|
22156
|
-
send: "Send",
|
|
22157
|
-
sending: "Replying …",
|
|
22158
|
-
contact: "Contact the team",
|
|
22159
|
-
followUp: "Ask for follow-up",
|
|
22160
|
-
followUpTitle: "Let the team follow up",
|
|
22161
|
-
followUpDetail: "Leave contact details if you want the team to follow up on this conversation.",
|
|
22162
|
-
name: "Name",
|
|
22163
|
-
email: "Email",
|
|
22164
|
-
phone: "Phone",
|
|
22165
|
-
interest: "What would you like help with?",
|
|
22166
|
-
consent: "Yes, the team may use my details to follow up on this enquiry.",
|
|
22167
|
-
leadSend: "Send request",
|
|
22168
|
-
leadSent: "Thanks — your request has been sent to the team.",
|
|
22169
|
-
leadFailed: "We could not save your request. Please try again or contact the team directly.",
|
|
22170
|
-
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."
|
|
22171
|
+
const show = () => {
|
|
22172
|
+
setMounted(true);
|
|
22173
|
+
window.requestAnimationFrame(() => setVisible(true));
|
|
22174
|
+
ensureVisitor();
|
|
22175
|
+
void loadChats();
|
|
22176
|
+
};
|
|
22177
|
+
const hide = () => {
|
|
22178
|
+
setVisible(false);
|
|
22179
|
+
window.setTimeout(() => {
|
|
22180
|
+
setMounted(false);
|
|
22181
|
+
setDrawer(false);
|
|
22182
|
+
}, CLOSE_MS);
|
|
22183
|
+
};
|
|
22184
|
+
const startNew = () => {
|
|
22185
|
+
conversationId.current = null;
|
|
22186
|
+
setMessages([{ role: "assistant", content: intro(locale, settings.displayName ?? void 0) }]);
|
|
22187
|
+
setLeadOpen(false);
|
|
22188
|
+
setDrawer(false);
|
|
22189
|
+
setFiles([]);
|
|
22172
22190
|
};
|
|
22173
22191
|
const submit = async (event) => {
|
|
22174
22192
|
event.preventDefault();
|
|
22175
|
-
const
|
|
22176
|
-
if (!
|
|
22193
|
+
const content = draft.trim();
|
|
22194
|
+
if (!content && !files.length || sending) return;
|
|
22177
22195
|
setDraft("");
|
|
22178
22196
|
setSending(true);
|
|
22179
|
-
|
|
22180
|
-
visitor.current ??= visitorId();
|
|
22197
|
+
const visitorIdValue = ensureVisitor();
|
|
22181
22198
|
try {
|
|
22182
|
-
const
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
22187
|
-
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
|
|
22195
|
-
|
|
22199
|
+
const uploaded = [];
|
|
22200
|
+
for (const image of files) {
|
|
22201
|
+
const file = image.file;
|
|
22202
|
+
const form = new FormData();
|
|
22203
|
+
form.set("visitorId", visitorIdValue);
|
|
22204
|
+
if (conversationId.current) form.set("conversationId", conversationId.current);
|
|
22205
|
+
form.set("locale", locale);
|
|
22206
|
+
form.set("page", window.location.href);
|
|
22207
|
+
form.set("file", file, file.name);
|
|
22208
|
+
const response2 = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/attachments`, { method: "POST", body: form });
|
|
22209
|
+
if (!response2.ok) throw new Error("image_upload_failed");
|
|
22210
|
+
const body = await response2.json();
|
|
22211
|
+
conversationId.current = body.conversationId;
|
|
22212
|
+
uploaded.push(body.attachment);
|
|
22213
|
+
}
|
|
22214
|
+
setFiles([]);
|
|
22215
|
+
setMessages((previous) => [...previous, { role: "visitor", content: content || `[${labels.image}]`, attachments: uploaded }, { role: "assistant", content: "" }]);
|
|
22216
|
+
const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/messages`, { method: "POST", headers: { "Content-Type": "application/json", Accept: "text/plain, application/json" }, body: JSON.stringify({ conversationId: conversationId.current ?? void 0, visitorId: visitorIdValue, locale, page: window.location.href, message: content || void 0, attachmentIds: uploaded.map((attachment) => attachment.id) }) });
|
|
22196
22217
|
const headerConversation = response.headers.get("X-Setto-Conversation");
|
|
22197
22218
|
if (headerConversation) conversationId.current = headerConversation;
|
|
22198
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
22199
22219
|
if (!response.ok) throw new Error(`chat_failed_${response.status}`);
|
|
22200
|
-
if (
|
|
22220
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) {
|
|
22201
22221
|
const body = await response.json();
|
|
22202
22222
|
if (body.conversationId) conversationId.current = body.conversationId;
|
|
22203
22223
|
setMessages((previous) => replaceLastAssistant(previous, body.answer || labels.failed));
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
22209
|
-
|
|
22210
|
-
|
|
22211
|
-
|
|
22212
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
22224
|
+
} else {
|
|
22225
|
+
const reader = response.body?.getReader();
|
|
22226
|
+
if (!reader) throw new Error("chat_missing_stream");
|
|
22227
|
+
const decoder = new TextDecoder();
|
|
22228
|
+
let answer = "";
|
|
22229
|
+
while (true) {
|
|
22230
|
+
const { done, value } = await reader.read();
|
|
22231
|
+
if (done) break;
|
|
22232
|
+
answer += decoder.decode(value, { stream: true });
|
|
22233
|
+
setMessages((previous) => replaceLastAssistant(previous, answer));
|
|
22234
|
+
}
|
|
22235
|
+
answer += decoder.decode();
|
|
22236
|
+
if (!answer.trim()) setMessages((previous) => replaceLastAssistant(previous, labels.failed));
|
|
22215
22237
|
}
|
|
22216
|
-
|
|
22217
|
-
if (!answer.trim()) setMessages((previous) => replaceLastAssistant(previous, labels.failed));
|
|
22238
|
+
void loadChats();
|
|
22218
22239
|
} catch {
|
|
22219
22240
|
setMessages((previous) => replaceLastAssistant(previous, labels.failed));
|
|
22220
22241
|
} finally {
|
|
@@ -22223,120 +22244,122 @@ function SettoChat() {
|
|
|
22223
22244
|
};
|
|
22224
22245
|
const submitLead = async (event) => {
|
|
22225
22246
|
event.preventDefault();
|
|
22226
|
-
if (leadStatus === "sending")
|
|
22227
|
-
if (!conversationId.current || !visitor.current) {
|
|
22247
|
+
if (leadStatus === "sending" || !conversationId.current || !visitor.current) {
|
|
22228
22248
|
setLeadStatus("failed");
|
|
22229
22249
|
return;
|
|
22230
22250
|
}
|
|
22231
22251
|
setLeadStatus("sending");
|
|
22232
22252
|
try {
|
|
22233
|
-
const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/leads`, {
|
|
22234
|
-
|
|
22235
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
22236
|
-
body: JSON.stringify({
|
|
22237
|
-
conversationId: conversationId.current,
|
|
22238
|
-
visitorId: visitor.current,
|
|
22239
|
-
consent: lead.consent,
|
|
22240
|
-
name: lead.name.trim() || void 0,
|
|
22241
|
-
email: lead.email.trim() || void 0,
|
|
22242
|
-
phone: lead.phone.trim() || void 0,
|
|
22243
|
-
interest: lead.interest.trim() || void 0
|
|
22244
|
-
})
|
|
22245
|
-
});
|
|
22246
|
-
if (!response.ok) throw new Error(`lead_failed_${response.status}`);
|
|
22253
|
+
const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/leads`, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json" }, body: JSON.stringify({ conversationId: conversationId.current, visitorId: visitor.current, consent: lead.consent, name: lead.name.trim() || void 0, email: lead.email.trim() || void 0, phone: lead.phone.trim() || void 0, interest: lead.interest.trim() || void 0 }) });
|
|
22254
|
+
if (!response.ok) throw new Error("lead_failed");
|
|
22247
22255
|
setLeadStatus("sent");
|
|
22248
22256
|
} catch {
|
|
22249
22257
|
setLeadStatus("failed");
|
|
22250
22258
|
}
|
|
22251
22259
|
};
|
|
22252
|
-
return /* @__PURE__ */ jsxs("aside", { className: `setto-chat${
|
|
22253
|
-
|
|
22254
|
-
/* @__PURE__ */ jsxs("
|
|
22255
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
22256
|
-
/* @__PURE__ */ jsx("
|
|
22257
|
-
/* @__PURE__ */ jsx("
|
|
22258
|
-
/* @__PURE__ */ jsx("p", { children: labels.disclosure })
|
|
22260
|
+
return /* @__PURE__ */ jsxs("aside", { className: `setto-chat${mounted ? " setto-chat--mounted" : ""}${visible ? " setto-chat--visible" : ""}`, style: palette, "aria-label": labels.title, "data-setto-ui": true, children: [
|
|
22261
|
+
mounted ? /* @__PURE__ */ jsxs("section", { className: "setto-chat__shell", role: "dialog", "aria-modal": "true", "aria-labelledby": `${formId}-title`, children: [
|
|
22262
|
+
/* @__PURE__ */ jsxs("nav", { className: `setto-chat__chats${drawer ? " setto-chat__chats--open" : ""}`, "aria-label": labels.chats, children: [
|
|
22263
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__chats-heading", children: [
|
|
22264
|
+
/* @__PURE__ */ jsx("span", { children: labels.chats }),
|
|
22265
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: startNew, "aria-label": labels.newChat, children: "+" })
|
|
22259
22266
|
] }),
|
|
22260
|
-
/* @__PURE__ */
|
|
22261
|
-
|
|
22262
|
-
|
|
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: "↗" })
|
|
22267
|
+
/* @__PURE__ */ jsxs("button", { className: "setto-chat__new", type: "button", onClick: startNew, children: [
|
|
22268
|
+
"+ ",
|
|
22269
|
+
labels.newChat
|
|
22271
22270
|
] }),
|
|
22272
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () =>
|
|
22273
|
-
|
|
22274
|
-
|
|
22275
|
-
}, children: labels.
|
|
22271
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__chat-list", children: conversations.length ? conversations.map((chat) => /* @__PURE__ */ jsxs("button", { className: `setto-chat__chat-row${conversationId.current === chat.id ? " is-active" : ""}`, type: "button", onClick: () => void loadConversation(chat.id), children: [
|
|
22272
|
+
/* @__PURE__ */ jsx("span", { children: chat.preview }),
|
|
22273
|
+
/* @__PURE__ */ jsx("time", { children: new Date(chat.lastActivityAt).toLocaleDateString(isNo ? "nb-NO" : "en-GB", { day: "numeric", month: "short" }) })
|
|
22274
|
+
] }, chat.id)) : /* @__PURE__ */ jsx("p", { children: labels.emptyChats }) })
|
|
22276
22275
|
] }),
|
|
22277
|
-
|
|
22278
|
-
/* @__PURE__ */
|
|
22279
|
-
|
|
22280
|
-
|
|
22281
|
-
|
|
22282
|
-
|
|
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 })) })
|
|
22276
|
+
/* @__PURE__ */ jsxs("main", { className: "setto-chat__main", children: [
|
|
22277
|
+
/* @__PURE__ */ jsxs("header", { className: "setto-chat__header", children: [
|
|
22278
|
+
/* @__PURE__ */ jsx("button", { className: "setto-chat__round-button", type: "button", onClick: () => setDrawer((current) => !current), "aria-label": labels.chats, "aria-expanded": drawer, children: "☰" }),
|
|
22279
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__brand", children: [
|
|
22280
|
+
/* @__PURE__ */ jsx("h2", { id: `${formId}-title`, children: labels.title }),
|
|
22281
|
+
/* @__PURE__ */ jsx("p", { children: siteName ?? "Barista" })
|
|
22292
22282
|
] }),
|
|
22293
|
-
/* @__PURE__ */
|
|
22294
|
-
labels.interest,
|
|
22295
|
-
/* @__PURE__ */ jsx("input", { value: lead.interest, maxLength: 500, onChange: (event) => setLead((current) => ({ ...current, interest: event.target.value })) })
|
|
22296
|
-
] })
|
|
22283
|
+
/* @__PURE__ */ jsx("button", { className: "setto-chat__round-button", type: "button", onClick: hide, "aria-label": labels.close, children: "×" })
|
|
22297
22284
|
] }),
|
|
22298
|
-
/* @__PURE__ */ jsxs("
|
|
22299
|
-
/* @__PURE__ */ jsx("
|
|
22300
|
-
labels.
|
|
22285
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__disclosure", children: [
|
|
22286
|
+
/* @__PURE__ */ jsx("span", { children: "BARISTA" }),
|
|
22287
|
+
/* @__PURE__ */ jsx("p", { children: labels.disclosure })
|
|
22301
22288
|
] }),
|
|
22302
|
-
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
|
|
22306
|
-
|
|
22307
|
-
/* @__PURE__ */
|
|
22308
|
-
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22317
|
-
|
|
22318
|
-
|
|
22289
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__log", ref: logRef, "aria-live": "polite", "aria-busy": loadingConversation, children: messages.map((item, index) => /* @__PURE__ */ jsxs("div", { className: `setto-chat__message setto-chat__message--${item.role}`, children: [
|
|
22290
|
+
item.role === "human" ? /* @__PURE__ */ jsx("span", { className: "setto-chat__speaker", children: labels.team }) : null,
|
|
22291
|
+
item.content || /* @__PURE__ */ jsx("span", { className: "setto-chat__typing", children: "···" }),
|
|
22292
|
+
item.attachments?.map((attachment) => /* @__PURE__ */ jsx("a", { className: "setto-chat__image", href: attachment.url, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx("img", { src: attachment.url, alt: labels.image }) }, attachment.id))
|
|
22293
|
+
] }, item.id ?? `${item.role}-${index}`)) }),
|
|
22294
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__actions", children: [
|
|
22295
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: contactTeam, children: [
|
|
22296
|
+
labels.contact,
|
|
22297
|
+
" ",
|
|
22298
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "↗" })
|
|
22299
|
+
] }),
|
|
22300
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => {
|
|
22301
|
+
setLeadOpen((value) => !value);
|
|
22302
|
+
setLeadStatus("idle");
|
|
22303
|
+
}, children: labels.followUp })
|
|
22304
|
+
] }),
|
|
22305
|
+
leadOpen ? /* @__PURE__ */ jsxs("form", { className: "setto-chat__lead", onSubmit: submitLead, children: [
|
|
22306
|
+
/* @__PURE__ */ jsx("strong", { children: labels.followUpTitle }),
|
|
22307
|
+
/* @__PURE__ */ jsx("p", { children: labels.followUpDetail }),
|
|
22308
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__lead-grid", children: [
|
|
22309
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22310
|
+
labels.name,
|
|
22311
|
+
/* @__PURE__ */ jsx("input", { value: lead.name, maxLength: 160, onChange: (event) => setLead((current) => ({ ...current, name: event.target.value })) })
|
|
22312
|
+
] }),
|
|
22313
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22314
|
+
labels.email,
|
|
22315
|
+
/* @__PURE__ */ jsx("input", { type: "email", value: lead.email, maxLength: 320, onChange: (event) => setLead((current) => ({ ...current, email: event.target.value })) })
|
|
22316
|
+
] }),
|
|
22317
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22318
|
+
labels.phone,
|
|
22319
|
+
/* @__PURE__ */ jsx("input", { type: "tel", value: lead.phone, maxLength: 64, onChange: (event) => setLead((current) => ({ ...current, phone: event.target.value })) })
|
|
22320
|
+
] }),
|
|
22321
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
22322
|
+
labels.interest,
|
|
22323
|
+
/* @__PURE__ */ jsx("input", { value: lead.interest, maxLength: 500, onChange: (event) => setLead((current) => ({ ...current, interest: event.target.value })) })
|
|
22324
|
+
] })
|
|
22325
|
+
] }),
|
|
22326
|
+
/* @__PURE__ */ jsxs("label", { className: "setto-chat__consent", children: [
|
|
22327
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: lead.consent, onChange: (event) => setLead((current) => ({ ...current, consent: event.target.checked })) }),
|
|
22328
|
+
labels.consent
|
|
22329
|
+
] }),
|
|
22330
|
+
leadStatus === "sent" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-success", children: labels.leadSent }) : null,
|
|
22331
|
+
leadStatus === "failed" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-error", children: conversationId.current ? labels.leadFailed : labels.leadConversation }) : null,
|
|
22332
|
+
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
|
|
22333
|
+
] }) : null,
|
|
22334
|
+
/* @__PURE__ */ jsxs("form", { className: "setto-chat__composer", onSubmit: submit, children: [
|
|
22335
|
+
files.length ? /* @__PURE__ */ jsx("div", { className: "setto-chat__previews", children: files.map((image, index) => /* @__PURE__ */ jsxs("span", { children: [
|
|
22336
|
+
/* @__PURE__ */ jsx("img", { src: image.preview, alt: image.file.name }),
|
|
22337
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setFiles((current) => current.filter((_, itemIndex) => itemIndex !== index)), "aria-label": `Remove ${image.file.name}`, children: "×" })
|
|
22338
|
+
] }, `${image.file.name}-${index}`)) }) : null,
|
|
22339
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-row", children: [
|
|
22340
|
+
/* @__PURE__ */ jsx("input", { ref: fileInput, className: "setto-chat__file", type: "file", accept: "image/jpeg,image/png,image/webp,image/gif", multiple: true, onChange: (event) => setFiles(Array.from(event.target.files ?? []).slice(0, 4).map((file) => ({ file, preview: URL.createObjectURL(file) }))) }),
|
|
22341
|
+
/* @__PURE__ */ jsx("button", { className: "setto-chat__attach", type: "button", onClick: () => fileInput.current?.click(), "aria-label": labels.attach, children: "+" }),
|
|
22342
|
+
/* @__PURE__ */ jsx("label", { className: "setto-chat__sr", htmlFor: `${formId}-input`, children: labels.placeholder }),
|
|
22343
|
+
/* @__PURE__ */ jsx("textarea", { id: `${formId}-input`, value: draft, rows: 1, maxLength: 1500, onChange: (event) => setDraft(event.target.value), placeholder: labels.placeholder, disabled: sending, onKeyDown: (event) => {
|
|
22319
22344
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
22320
22345
|
event.preventDefault();
|
|
22321
22346
|
event.currentTarget.form?.requestSubmit();
|
|
22322
22347
|
}
|
|
22323
|
-
}
|
|
22324
|
-
|
|
22325
|
-
|
|
22326
|
-
|
|
22348
|
+
} }),
|
|
22349
|
+
/* @__PURE__ */ jsx("button", { className: "setto-chat__send", type: "submit", disabled: !draft.trim() && !files.length || sending, "aria-label": labels.send, children: sending ? "…" : "↑" })
|
|
22350
|
+
] })
|
|
22351
|
+
] })
|
|
22327
22352
|
] })
|
|
22328
22353
|
] }) : null,
|
|
22329
|
-
!
|
|
22330
|
-
/* @__PURE__ */ jsx("span", { className: "setto-chat__launcher-mark", "aria-hidden": "true", children: "✦" }),
|
|
22331
|
-
/* @__PURE__ */ jsx("span", { children: labels.open })
|
|
22332
|
-
] }) : null
|
|
22354
|
+
!mounted ? /* @__PURE__ */ jsx("button", { className: "setto-chat__launcher", type: "button", onClick: show, "aria-label": labels.open, title: labels.open, children: availability.humanAvailable && availability.avatarUrl ? /* @__PURE__ */ jsx("img", { src: availability.avatarUrl, alt: "" }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "✦" }) }) : null
|
|
22333
22355
|
] });
|
|
22334
22356
|
}
|
|
22335
22357
|
function replaceLastAssistant(messages, content) {
|
|
22336
22358
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
22337
|
-
|
|
22359
|
+
const current = messages[index];
|
|
22360
|
+
if (current?.role === "assistant") {
|
|
22338
22361
|
const next = [...messages];
|
|
22339
|
-
next[index] = {
|
|
22362
|
+
next[index] = { ...current, content };
|
|
22340
22363
|
return next;
|
|
22341
22364
|
}
|
|
22342
22365
|
}
|
|
@@ -22347,51 +22370,15 @@ function useChatStyles() {
|
|
|
22347
22370
|
const style = document.createElement("style");
|
|
22348
22371
|
style.dataset.settoChat = "";
|
|
22349
22372
|
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
|
-
`;
|
|
22373
|
+
.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,ui-sans-serif,sans-serif}.setto-chat *,.setto-chat *::before,.setto-chat *::after{box-sizing:border-box}.setto-chat--mounted{inset:0;right:0;bottom:0;width:100vw;height:100dvh;background:color-mix(in srgb,var(--setto-chat-paper) 88%,transparent);backdrop-filter:blur(8px);opacity:0;transition:opacity ${CLOSE_MS}ms ease}.setto-chat--visible{opacity:1}.setto-chat__shell{width:100%;height:100%;display:flex;overflow:hidden;opacity:0;transform:translateY(14px) scale(.992);transition:opacity ${CLOSE_MS}ms ease,transform ${CLOSE_MS}ms cubic-bezier(.2,.8,.2,1);background:var(--setto-chat-paper)}.setto-chat--visible .setto-chat__shell{opacity:1;transform:none}
|
|
22374
|
+
.setto-chat__chats{width:274px;flex:0 0 274px;padding:calc(25px + env(safe-area-inset-top)) 14px 22px;background:color-mix(in srgb,var(--setto-chat-pale) 72%,white);border-right:1px solid color-mix(in srgb,var(--setto-chat-ink) 12%,transparent);display:flex;flex-direction:column;gap:18px}.setto-chat__chats-heading{padding:0 7px;display:flex;align-items:center;justify-content:space-between;font-size:12px;font-weight:800;letter-spacing:.13em;text-transform:uppercase}.setto-chat__chats-heading button{border:0;background:transparent;font-size:22px;cursor:pointer;color:var(--setto-chat-ink)}.setto-chat__new{border:1px solid color-mix(in srgb,var(--setto-chat-ink) 18%,transparent);border-radius:14px;padding:11px 13px;background:rgba(255,255,255,.45);color:var(--setto-chat-ink);font:600 13px Archivo,sans-serif;text-align:left;cursor:pointer}.setto-chat__chat-list{display:grid;gap:5px;overflow:auto}.setto-chat__chat-list p{margin:7px;padding:0;color:color-mix(in srgb,var(--setto-chat-ink) 57%,transparent);font-size:12px;line-height:1.45}.setto-chat__chat-row{border:0;border-radius:11px;padding:10px;background:transparent;color:var(--setto-chat-ink);display:grid;gap:4px;text-align:left;cursor:pointer}.setto-chat__chat-row:hover,.setto-chat__chat-row.is-active{background:color-mix(in srgb,var(--setto-chat-paper) 75%,transparent)}.setto-chat__chat-row span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font:600 13px Archivo,sans-serif}.setto-chat__chat-row time{font-size:11px;color:color-mix(in srgb,var(--setto-chat-ink) 55%,transparent)}
|
|
22375
|
+
.setto-chat__main{min-width:0;flex:1;display:grid;grid-template-rows:auto auto minmax(0,1fr) auto auto auto;background:var(--setto-chat-paper)}.setto-chat__header{min-height:86px;padding:calc(14px + env(safe-area-inset-top)) 26px 12px;display:grid;grid-template-columns:46px 1fr 46px;align-items:center;border-bottom:1px solid color-mix(in srgb,var(--setto-chat-ink) 10%,transparent)}.setto-chat__round-button{width:46px;height:46px;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 11%,transparent);border-radius:50%;background:color-mix(in srgb,var(--setto-chat-paper) 72%,white);color:var(--setto-chat-ink);font:500 23px/1 Archivo,sans-serif;cursor:pointer}.setto-chat__brand{text-align:center}.setto-chat__brand h2{margin:0;font:400 26px/1.05 "Instrument Serif",Georgia,serif;letter-spacing:-.025em}.setto-chat__brand p{margin:3px 0 0;color:color-mix(in srgb,var(--setto-chat-ink) 57%,transparent);font-size:12px}.setto-chat__disclosure{padding:14px max(32px,6vw) 6px;display:flex;align-items:baseline;gap:10px}.setto-chat__disclosure span{font-size:10px;font-weight:800;letter-spacing:.16em;color:var(--setto-chat-accent)}.setto-chat__disclosure p{margin:0;color:color-mix(in srgb,var(--setto-chat-ink) 60%,transparent);font-size:12px}
|
|
22376
|
+
.setto-chat__log{min-height:0;overflow:auto;padding:28px max(32px,10vw);display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth}.setto-chat__message{max-width:min(620px,82%);padding:14px 16px;border-radius:18px;font-size:15px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.setto-chat__message--assistant{align-self:flex-start;background:color-mix(in srgb,var(--setto-chat-pale) 82%,white);border-bottom-left-radius:4px}.setto-chat__message--visitor{align-self:flex-end;background:var(--setto-chat-ink);color:var(--setto-chat-paper);border-bottom-right-radius:4px}.setto-chat__message--human{align-self:flex-start;background:color-mix(in srgb,var(--setto-chat-accent) 20%,var(--setto-chat-paper));border-bottom-left-radius:4px}.setto-chat__message--system{align-self:center;padding:3px 10px;background:transparent;color:color-mix(in srgb,var(--setto-chat-ink) 55%,transparent);font-size:12px;text-align:center}.setto-chat__speaker{display:block;margin-bottom:4px;color:var(--setto-chat-accent);font-size:10px;font-weight:800;letter-spacing:.11em;text-transform:uppercase}.setto-chat__typing{animation:setto-chat-pulse 1s ease-in-out infinite}.setto-chat__image{display:block;margin-top:9px;overflow:hidden;border-radius:11px}.setto-chat__image img{display:block;width:min(280px,100%);max-height:260px;object-fit:cover}
|
|
22377
|
+
.setto-chat__actions{padding:8px max(30px,6vw) 11px;display:flex;gap:20px}.setto-chat__actions button{border:0;background:transparent;color:var(--setto-chat-accent);font:700 13px Archivo,sans-serif;cursor:pointer}.setto-chat__lead{margin:0 max(30px,6vw) 10px;padding:16px;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 13%,transparent);border-radius:16px;background:color-mix(in srgb,var(--setto-chat-pale) 55%,white);display:grid;gap:9px}.setto-chat__lead strong{font-size:15px}.setto-chat__lead p{margin:0;color:color-mix(in srgb,var(--setto-chat-ink) 65%,transparent);font-size:12px;line-height:1.4}.setto-chat__lead-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.setto-chat__lead label{display:grid;gap:4px;font-size:11px;font-weight:700}.setto-chat__lead input{min-width:0;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 18%,transparent);border-radius:8px;padding:8px;background:var(--setto-chat-paper);color:var(--setto-chat-ink);font:13px Archivo,sans-serif}.setto-chat__consent{display:flex!important;grid-template-columns:auto 1fr;align-items:start;font-weight:400!important;line-height:1.35}.setto-chat__lead>button{justify-self:start;border:0;border-radius:9px;padding:9px 12px;background:var(--setto-chat-accent);color:white;font:700 12px Archivo,sans-serif;cursor:pointer}.setto-chat__lead-success{color:#28633d!important;font-weight:700}.setto-chat__lead-error{color:#a33024!important;font-weight:700}
|
|
22378
|
+
.setto-chat__composer{padding:0 max(24px,6vw) max(17px,env(safe-area-inset-bottom));background:var(--setto-chat-paper)}.setto-chat__composer-row{min-height:78px;padding:12px 14px;display:grid;grid-template-columns:36px 1fr 44px;gap:8px;align-items:end;border:1.5px solid color-mix(in srgb,var(--setto-chat-accent) 80%,var(--setto-chat-ink));border-radius:25px;background:color-mix(in srgb,var(--setto-chat-paper) 82%,white);box-shadow:0 9px 26px color-mix(in srgb,var(--setto-chat-ink) 8%,transparent)}.setto-chat__composer textarea{width:100%;min-height:38px;max-height:100px;resize:none;border:0;background:transparent;color:var(--setto-chat-ink);padding:8px 0;font:15px/1.4 Archivo,sans-serif;outline:none}.setto-chat__composer textarea::placeholder{color:color-mix(in srgb,var(--setto-chat-ink) 45%,transparent)}.setto-chat__file,.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}.setto-chat__attach{width:32px;height:32px;align-self:end;border:0;border-radius:50%;background:transparent;color:color-mix(in srgb,var(--setto-chat-ink) 70%,transparent);font-size:28px;line-height:1;cursor:pointer}.setto-chat__send{width:44px;height:44px;border:0;border-radius:50%;background:color-mix(in srgb,var(--setto-chat-accent) 36%,var(--setto-chat-paper));color:var(--setto-chat-accent);font:400 27px/1 Georgia,serif;cursor:pointer}.setto-chat__send:disabled{opacity:.45;cursor:default}.setto-chat__previews{display:flex;gap:8px;padding:0 10px 8px}.setto-chat__previews span{position:relative;width:42px;height:42px}.setto-chat__previews img{display:block;width:42px;height:42px;object-fit:cover;border-radius:9px}.setto-chat__previews button{position:absolute;right:-5px;top:-5px;width:17px;height:17px;border:0;border-radius:50%;background:var(--setto-chat-ink);color:white;line-height:15px;cursor:pointer}
|
|
22379
|
+
.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-paper) 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(1.15);color:var(--setto-chat-ink);display:grid;place-items:center;cursor:pointer;transition:transform .18s ease,box-shadow .18s 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-paper);font-size:19px}.setto-chat__launcher img{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:3px solid color-mix(in srgb,var(--setto-chat-accent) 45%,white);outline-offset:3px}@keyframes setto-chat-pulse{50%{opacity:.35}}
|
|
22380
|
+
@media(max-width:700px){.setto-chat__chats{position:absolute;z-index:2;top:0;bottom:0;left:0;transform:translateX(-104%);transition:transform ${CLOSE_MS}ms ease;box-shadow:14px 0 35px color-mix(in srgb,var(--setto-chat-ink) 16%,transparent)}.setto-chat__chats--open{transform:none}.setto-chat__header{padding-left:16px;padding-right:16px}.setto-chat__disclosure{padding:13px 20px 5px}.setto-chat__log{padding:24px 20px}.setto-chat__message{max-width:91%;font-size:14px}.setto-chat__actions{padding-left:20px;padding-right:20px;gap:15px}.setto-chat__composer{padding-left:14px;padding-right:14px}.setto-chat__lead{margin-left:20px;margin-right:20px}.setto-chat__lead-grid{grid-template-columns:1fr}.setto-chat__round-button{width:42px;height:42px}.setto-chat__brand h2{font-size:24px}}@media(prefers-reduced-motion:reduce){.setto-chat,.setto-chat__shell,.setto-chat__chats,.setto-chat__launcher{transition:none!important}.setto-chat__typing{animation:none}}
|
|
22381
|
+
`;
|
|
22395
22382
|
document.head.appendChild(style);
|
|
22396
22383
|
return () => style.remove();
|
|
22397
22384
|
}, []);
|