@sevenfold/setto-client 0.24.0 → 0.24.2

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.
@@ -22035,6 +22035,8 @@ function EditModeShell({ children, themeStore, active }) {
22035
22035
  }
22036
22036
  const VISITOR_KEY = "setto-chat-visitor";
22037
22037
  const CLOSE_MS = 210;
22038
+ const MIN_COMPOSER_HEIGHT = 36;
22039
+ const MAX_COMPOSER_HEIGHT = 200;
22038
22040
  function visitorId() {
22039
22041
  try {
22040
22042
  const stored = window.localStorage.getItem(VISITOR_KEY);
@@ -22053,17 +22055,268 @@ function intro(locale, name) {
22053
22055
  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.`;
22054
22056
  }
22055
22057
  function contactTeam() {
22056
- const link = document.querySelector('a[href*="kontakt" i], a[href*="contact" i], a[href^="mailto:"]');
22058
+ const link = document.querySelector(
22059
+ 'a[href*="kontakt" i], a[href*="contact" i], a[href^="mailto:"]'
22060
+ );
22057
22061
  if (link) link.click();
22058
22062
  else window.location.hash = "kontakt";
22059
22063
  }
22060
22064
  function validColor(value, fallback) {
22061
- return value && (/^#[0-9a-f]{3,8}$/i.test(value) || /^rgb/.test(value)) ? value : fallback;
22065
+ return value && (/^#[0-9a-f]{3,8}$/i.test(value) || /^rgba?\(/i.test(value)) ? value : fallback;
22062
22066
  }
22063
22067
  function chatPalette(colors) {
22064
22068
  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") };
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 [focused, setFocused] = useState(false);
22153
+ useEffect(() => {
22154
+ const textarea = textareaRef.current;
22155
+ if (!textarea) return;
22156
+ textarea.style.height = "auto";
22157
+ const height = Math.min(
22158
+ Math.max(textarea.scrollHeight, MIN_COMPOSER_HEIGHT),
22159
+ MAX_COMPOSER_HEIGHT
22160
+ );
22161
+ textarea.style.height = `${height}px`;
22162
+ }, [value]);
22163
+ useEffect(() => {
22164
+ if (!busy) textareaRef.current?.focus();
22165
+ }, [busy]);
22166
+ function send() {
22167
+ if (!value.trim() && attachments.length === 0) return;
22168
+ onSubmit();
22169
+ }
22170
+ function handleSubmit(event) {
22171
+ event.preventDefault();
22172
+ send();
22173
+ }
22174
+ function handleKeyDown(event) {
22175
+ if (event.key === "Enter" && !event.shiftKey) {
22176
+ event.preventDefault();
22177
+ send();
22178
+ }
22179
+ }
22180
+ const canSend = value.trim().length > 0 || attachments.length > 0;
22181
+ const open = focused || value.length > 0 || attachments.length > 0;
22182
+ return /* @__PURE__ */ jsxs(
22183
+ "form",
22184
+ {
22185
+ className: `setto-chat__composer${open ? " is-open" : ""}`,
22186
+ onSubmit: handleSubmit,
22187
+ onPointerDown: (event) => {
22188
+ if (event.target === event.currentTarget) textareaRef.current?.focus();
22189
+ },
22190
+ children: [
22191
+ attachments.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "setto-chat__attachments", children: attachments.map((attachment, index) => /* @__PURE__ */ jsxs("li", { children: [
22192
+ /* @__PURE__ */ jsx("img", { src: attachment.preview, alt: attachment.file.name }),
22193
+ /* @__PURE__ */ jsx(
22194
+ "button",
22195
+ {
22196
+ type: "button",
22197
+ onClick: () => onRemoveAttachment(index),
22198
+ "aria-label": `${removeLabel} ${attachment.file.name}`,
22199
+ children: /* @__PURE__ */ jsx(CloseIcon, { size: 12 })
22200
+ }
22201
+ )
22202
+ ] }, `${attachment.file.name}-${attachment.preview}`)) }) : null,
22203
+ /* @__PURE__ */ jsx(
22204
+ "textarea",
22205
+ {
22206
+ ref: textareaRef,
22207
+ lang: norwegian(locale) ? "nb" : "en",
22208
+ spellCheck: true,
22209
+ value,
22210
+ onChange: (event) => onChange(event.target.value),
22211
+ onKeyDown: handleKeyDown,
22212
+ onFocus: () => setFocused(true),
22213
+ onBlur: () => setFocused(false),
22214
+ placeholder,
22215
+ rows: 1,
22216
+ maxLength: 1500
22217
+ }
22218
+ ),
22219
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-tools", children: [
22220
+ /* @__PURE__ */ jsx(
22221
+ "input",
22222
+ {
22223
+ ref: fileRef,
22224
+ type: "file",
22225
+ accept: "image/*",
22226
+ multiple: true,
22227
+ hidden: true,
22228
+ onChange: (event) => {
22229
+ const selected = Array.from(event.target.files ?? []);
22230
+ event.target.value = "";
22231
+ if (selected.length) onAttach(selected);
22232
+ }
22233
+ }
22234
+ ),
22235
+ /* @__PURE__ */ jsx(
22236
+ "button",
22237
+ {
22238
+ type: "button",
22239
+ className: "setto-chat__attach",
22240
+ onClick: () => fileRef.current?.click(),
22241
+ disabled: busy,
22242
+ "aria-label": attachLabel,
22243
+ title: attachLabel,
22244
+ children: /* @__PURE__ */ jsx(PlusIcon, {})
22245
+ }
22246
+ ),
22247
+ /* @__PURE__ */ jsx(
22248
+ "button",
22249
+ {
22250
+ type: "submit",
22251
+ className: `setto-chat__send${queued ? " is-queued" : ""}`,
22252
+ disabled: !canSend,
22253
+ "aria-busy": queued,
22254
+ "aria-label": sendLabel,
22255
+ children: /* @__PURE__ */ jsx(ArrowIcon, {})
22256
+ }
22257
+ )
22258
+ ] })
22259
+ ]
22260
+ }
22261
+ );
22262
+ }
22263
+ function useChatViewport(active, logRef) {
22264
+ useEffect(() => {
22265
+ if (!active) return;
22266
+ const root = document.documentElement;
22267
+ const body = document.body;
22268
+ const visualViewport = window.visualViewport;
22269
+ const savedScrollY = window.scrollY;
22270
+ const previous = {
22271
+ rootOverflow: root.style.overflow,
22272
+ rootOverscroll: root.style.overscrollBehavior,
22273
+ bodyPosition: body.style.position,
22274
+ bodyTop: body.style.top,
22275
+ bodyLeft: body.style.left,
22276
+ bodyRight: body.style.right,
22277
+ bodyWidth: body.style.width,
22278
+ bodyOverflow: body.style.overflow
22279
+ };
22280
+ root.style.overflow = "hidden";
22281
+ root.style.overscrollBehavior = "none";
22282
+ body.style.position = "fixed";
22283
+ body.style.top = `-${savedScrollY}px`;
22284
+ body.style.left = "0";
22285
+ body.style.right = "0";
22286
+ body.style.width = "100%";
22287
+ body.style.overflow = "hidden";
22288
+ const update = () => {
22289
+ const height = Math.round(visualViewport?.height ?? window.innerHeight);
22290
+ const top = Math.round(visualViewport?.offsetTop ?? 0);
22291
+ root.style.setProperty("--setto-chat-height", `${height}px`);
22292
+ root.style.setProperty("--setto-chat-top", `${top}px`);
22293
+ if (window.scrollY !== 0) window.scrollTo(0, 0);
22294
+ window.requestAnimationFrame(() => {
22295
+ const log = logRef.current;
22296
+ if (log) log.scrollTop = log.scrollHeight;
22297
+ });
22298
+ };
22299
+ update();
22300
+ visualViewport?.addEventListener("resize", update);
22301
+ visualViewport?.addEventListener("scroll", update);
22302
+ window.addEventListener("orientationchange", update);
22303
+ return () => {
22304
+ visualViewport?.removeEventListener("resize", update);
22305
+ visualViewport?.removeEventListener("scroll", update);
22306
+ window.removeEventListener("orientationchange", update);
22307
+ root.style.removeProperty("--setto-chat-height");
22308
+ root.style.removeProperty("--setto-chat-top");
22309
+ root.style.overflow = previous.rootOverflow;
22310
+ root.style.overscrollBehavior = previous.rootOverscroll;
22311
+ body.style.position = previous.bodyPosition;
22312
+ body.style.top = previous.bodyTop;
22313
+ body.style.left = previous.bodyLeft;
22314
+ body.style.right = previous.bodyRight;
22315
+ body.style.width = previous.bodyWidth;
22316
+ body.style.overflow = previous.bodyOverflow;
22317
+ window.scrollTo(0, savedScrollY);
22318
+ };
22319
+ }, [active, logRef]);
22067
22320
  }
22068
22321
  function SettoChat() {
22069
22322
  const { config } = useSetto();
@@ -22078,37 +22331,119 @@ function SettoChat() {
22078
22331
  const [draft, setDraft] = useState("");
22079
22332
  const [files, setFiles] = useState([]);
22080
22333
  const [sending, setSending] = useState(false);
22334
+ const [queued, setQueued] = useState(false);
22081
22335
  const [loadingConversation, setLoadingConversation] = useState(false);
22082
- const [availability, setAvailability] = useState({ humanAvailable: false, avatarUrl: null });
22336
+ const [availability, setAvailability] = useState({
22337
+ humanAvailable: false,
22338
+ avatarUrl: null
22339
+ });
22083
22340
  const [leadOpen, setLeadOpen] = useState(false);
22084
22341
  const [leadStatus, setLeadStatus] = useState("idle");
22085
- const [lead, setLead] = useState({ name: "", email: "", phone: "", interest: "", consent: false });
22342
+ const [lead, setLead] = useState({
22343
+ name: "",
22344
+ email: "",
22345
+ phone: "",
22346
+ interest: "",
22347
+ consent: false
22348
+ });
22086
22349
  const conversationId = useRef(null);
22087
22350
  const visitor = useRef(null);
22088
22351
  const logRef = useRef(null);
22089
- const fileInput = useRef(null);
22352
+ const filesRef = useRef([]);
22090
22353
  const formId = useId();
22091
22354
  const palette = useMemo(() => chatPalette(config.brandColors), [config.brandColors]);
22092
22355
  useChatStyles();
22356
+ useChatViewport(mounted, logRef);
22093
22357
  const locale = i18n.language || "no";
22094
22358
  const isNo = norwegian(locale);
22095
22359
  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 () => {
22360
+ const labels = isNo ? {
22361
+ open: siteName ? `Åpne chat med ${siteName}` : "Åpne chat",
22362
+ close: "Lukk chatten",
22363
+ title: "Spør Barista",
22364
+ chats: "Chats",
22365
+ newChat: "Ny chat",
22366
+ disclosure: "AI-assistent · svarer fra nettstedets godkjente informasjon",
22367
+ placeholder: "Hva vil du spørre om?",
22368
+ send: "Send melding",
22369
+ sending: "Svarer …",
22370
+ attach: "Legg ved bilde",
22371
+ remove: "Fjern",
22372
+ contact: "Kontakt teamet",
22373
+ followUp: "Be om oppfølging",
22374
+ followUpTitle: "La teamet følge opp",
22375
+ followUpDetail: "Legg igjen kontaktinformasjon hvis du vil at teamet skal følge opp denne samtalen.",
22376
+ name: "Navn",
22377
+ email: "E-post",
22378
+ phone: "Telefon",
22379
+ interest: "Hva vil du ha hjelp med?",
22380
+ consent: "Ja, teamet kan bruke opplysningene mine til å følge opp denne henvendelsen.",
22381
+ leadSend: "Send forespørsel",
22382
+ leadSent: "Takk — forespørselen er sendt til teamet.",
22383
+ leadFailed: "Kunne ikke lagre forespørselen. Prøv igjen eller kontakt teamet direkte.",
22384
+ leadConversation: "Still et spørsmål først, så kan vi knytte oppfølgingen til samtalen.",
22385
+ failed: "Jeg får ikke svart akkurat nå. Kontakt gjerne teamet direkte.",
22386
+ image: "Bilde",
22387
+ team: "Teamet",
22388
+ emptyChats: "Nye samtaler du starter her vises i denne listen."
22389
+ } : {
22390
+ open: siteName ? `Open chat with ${siteName}` : "Open chat",
22391
+ close: "Close chat",
22392
+ title: "Ask Barista",
22393
+ chats: "Chats",
22394
+ newChat: "New chat",
22395
+ disclosure: "AI assistant · answers from this website’s approved information",
22396
+ placeholder: "What would you like to ask?",
22397
+ send: "Send message",
22398
+ sending: "Replying …",
22399
+ attach: "Attach image",
22400
+ remove: "Remove",
22401
+ contact: "Contact the team",
22402
+ followUp: "Ask for follow-up",
22403
+ followUpTitle: "Let the team follow up",
22404
+ followUpDetail: "Leave contact details if you want the team to follow up on this conversation.",
22405
+ name: "Name",
22406
+ email: "Email",
22407
+ phone: "Phone",
22408
+ interest: "What would you like help with?",
22409
+ consent: "Yes, the team may use my details to follow up on this enquiry.",
22410
+ leadSend: "Send request",
22411
+ leadSent: "Thanks — your request has been sent to the team.",
22412
+ leadFailed: "We could not save your request. Please try again or contact the team directly.",
22413
+ leadConversation: "Ask a question first so we can link the follow-up to this conversation.",
22414
+ failed: "I cannot reply right now. Please contact the team directly.",
22415
+ image: "Image",
22416
+ team: "The team",
22417
+ emptyChats: "New conversations you start here will appear in this list."
22418
+ };
22419
+ function ensureVisitor() {
22420
+ return visitor.current ??= visitorId();
22421
+ }
22422
+ async function loadChats() {
22099
22423
  const id = visitor.current;
22100
22424
  if (!id) return;
22101
22425
  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);
22426
+ const query = new URLSearchParams({ visitorId: id });
22427
+ const response = await fetch(
22428
+ `${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/public?${query}`,
22429
+ { headers: { Accept: "application/json" } }
22430
+ );
22431
+ if (response.ok) {
22432
+ const body = await response.json();
22433
+ setConversations(body.conversations);
22434
+ }
22104
22435
  } catch {
22105
22436
  }
22106
- };
22107
- const loadConversation = async (id) => {
22437
+ }
22438
+ async function loadConversation(id) {
22108
22439
  const visitorIdValue = ensureVisitor();
22109
22440
  setLoadingConversation(true);
22110
22441
  try {
22111
- const response = await fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(id)}/public?${new URLSearchParams({ visitorId: visitorIdValue })}`, { headers: { Accept: "application/json" } });
22442
+ const query = new URLSearchParams({ visitorId: visitorIdValue });
22443
+ const response = await fetch(
22444
+ `${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(id)}/public?${query}`,
22445
+ { headers: { Accept: "application/json" } }
22446
+ );
22112
22447
  if (!response.ok) return;
22113
22448
  const next = await response.json();
22114
22449
  conversationId.current = next.conversationId;
@@ -22118,11 +22453,13 @@ function SettoChat() {
22118
22453
  } finally {
22119
22454
  setLoadingConversation(false);
22120
22455
  }
22121
- };
22456
+ }
22122
22457
  useEffect(() => {
22123
22458
  if (window.location.pathname.startsWith("/setto")) return;
22124
22459
  let cancelled = false;
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) => {
22460
+ fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/settings/public`, {
22461
+ headers: { Accept: "application/json" }
22462
+ }).then(async (response) => response.ok ? await response.json() : null).then((next) => {
22126
22463
  if (!cancelled && next?.enabled) setSettings(next);
22127
22464
  }).catch(() => void 0);
22128
22465
  return () => {
@@ -22132,7 +22469,9 @@ function SettoChat() {
22132
22469
  useEffect(() => {
22133
22470
  if (!settings) return;
22134
22471
  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) => {
22472
+ const refresh = () => fetch(`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/availability/public`, {
22473
+ headers: { Accept: "application/json" }
22474
+ }).then(async (response) => response.ok ? await response.json() : null).then((next) => {
22136
22475
  if (!cancelled && next) setAvailability(next);
22137
22476
  }).catch(() => void 0);
22138
22477
  void refresh();
@@ -22143,18 +22482,40 @@ function SettoChat() {
22143
22482
  };
22144
22483
  }, [apiUrl, config.siteId, settings]);
22145
22484
  useEffect(() => {
22146
- if (mounted && messages.length === 0 && settings) setMessages([{ role: "assistant", content: intro(locale, settings.displayName ?? void 0) }]);
22147
- }, [mounted, messages.length, settings, locale]);
22485
+ if (!mounted || !settings || conversationId.current) return;
22486
+ const welcome = intro(locale, settings.displayName ?? void 0);
22487
+ setMessages((current) => {
22488
+ if (current.length === 0) return [{ role: "assistant", content: welcome }];
22489
+ if (current.length === 1 && current[0]?.role === "assistant" && !current[0].id) {
22490
+ return current[0].content === welcome ? current : [{ role: "assistant", content: welcome }];
22491
+ }
22492
+ return current;
22493
+ });
22494
+ }, [mounted, settings, locale]);
22148
22495
  useEffect(() => {
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]);
22496
+ if (!mounted) return;
22497
+ const log = logRef.current;
22498
+ if (log) log.scrollTo({ top: log.scrollHeight, behavior: sending ? "auto" : "smooth" });
22499
+ }, [messages, mounted, sending, leadOpen]);
22500
+ useEffect(() => {
22501
+ filesRef.current = files;
22502
+ }, [files]);
22503
+ useEffect(
22504
+ () => () => {
22505
+ for (const image of filesRef.current) URL.revokeObjectURL(image.preview);
22506
+ },
22507
+ []
22508
+ );
22152
22509
  useEffect(() => {
22153
22510
  if (!mounted || !conversationId.current || !visitor.current || sending) return;
22154
22511
  let cancelled = false;
22155
22512
  const refresh = async () => {
22156
22513
  try {
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" } });
22514
+ const query = new URLSearchParams({ visitorId: visitor.current });
22515
+ const response = await fetch(
22516
+ `${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(conversationId.current)}/public?${query}`,
22517
+ { headers: { Accept: "application/json" } }
22518
+ );
22158
22519
  if (!response.ok || cancelled) return;
22159
22520
  const next = await response.json();
22160
22521
  if (!cancelled) setMessages(next.messages);
@@ -22167,53 +22528,122 @@ function SettoChat() {
22167
22528
  window.clearInterval(timer);
22168
22529
  };
22169
22530
  }, [apiUrl, config.siteId, mounted, sending]);
22531
+ useEffect(() => {
22532
+ if (!sending && queued) {
22533
+ setQueued(false);
22534
+ void sendTurn();
22535
+ }
22536
+ }, [sending, queued]);
22537
+ useEffect(() => {
22538
+ if (!mounted) return;
22539
+ const closeOnEscape = (event) => {
22540
+ if (event.key === "Escape") hide();
22541
+ };
22542
+ window.addEventListener("keydown", closeOnEscape);
22543
+ return () => window.removeEventListener("keydown", closeOnEscape);
22544
+ }, [mounted]);
22170
22545
  if (!settings || window.location.pathname.startsWith("/setto")) return null;
22171
- const show = () => {
22546
+ function show() {
22172
22547
  setMounted(true);
22173
22548
  window.requestAnimationFrame(() => setVisible(true));
22174
22549
  ensureVisitor();
22175
22550
  void loadChats();
22176
- };
22177
- const hide = () => {
22551
+ }
22552
+ function hide() {
22553
+ if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
22178
22554
  setVisible(false);
22179
22555
  window.setTimeout(() => {
22180
22556
  setMounted(false);
22181
22557
  setDrawer(false);
22182
22558
  }, CLOSE_MS);
22183
- };
22184
- const startNew = () => {
22559
+ }
22560
+ function clearPendingFiles() {
22561
+ setFiles((current) => {
22562
+ for (const image of current) URL.revokeObjectURL(image.preview);
22563
+ return [];
22564
+ });
22565
+ }
22566
+ function startNew() {
22185
22567
  conversationId.current = null;
22186
- setMessages([{ role: "assistant", content: intro(locale, settings.displayName ?? void 0) }]);
22568
+ setMessages([{ role: "assistant", content: intro(locale, settings?.displayName ?? void 0) }]);
22569
+ setDraft("");
22570
+ setQueued(false);
22187
22571
  setLeadOpen(false);
22188
22572
  setDrawer(false);
22189
- setFiles([]);
22190
- };
22191
- const submit = async (event) => {
22192
- event.preventDefault();
22573
+ clearPendingFiles();
22574
+ }
22575
+ function addFiles(selected) {
22576
+ setFiles((current) => {
22577
+ const available = Math.max(0, 4 - current.length);
22578
+ const accepted = selected.filter((file) => file.type.startsWith("image/")).slice(0, available).map((file) => ({ file, preview: URL.createObjectURL(file) }));
22579
+ return [...current, ...accepted];
22580
+ });
22581
+ }
22582
+ function removeFile(index) {
22583
+ setFiles((current) => {
22584
+ const removed = current[index];
22585
+ if (removed) URL.revokeObjectURL(removed.preview);
22586
+ return current.filter((_, itemIndex) => itemIndex !== index);
22587
+ });
22588
+ }
22589
+ function queueOrSend() {
22590
+ if (!draft.trim() && files.length === 0) return;
22591
+ if (sending) {
22592
+ setQueued(true);
22593
+ return;
22594
+ }
22595
+ void sendTurn();
22596
+ }
22597
+ async function sendTurn() {
22193
22598
  const content = draft.trim();
22194
- if (!content && !files.length || sending) return;
22599
+ const turnFiles = files;
22600
+ if (!content && turnFiles.length === 0) return;
22195
22601
  setDraft("");
22602
+ setFiles([]);
22196
22603
  setSending(true);
22197
22604
  const visitorIdValue = ensureVisitor();
22198
22605
  try {
22199
22606
  const uploaded = [];
22200
- for (const image of files) {
22201
- const file = image.file;
22607
+ for (const image of turnFiles) {
22202
22608
  const form = new FormData();
22203
22609
  form.set("visitorId", visitorIdValue);
22204
22610
  if (conversationId.current) form.set("conversationId", conversationId.current);
22205
22611
  form.set("locale", locale);
22206
22612
  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 });
22613
+ form.set("file", image.file, image.file.name);
22614
+ const response2 = await fetch(
22615
+ `${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/attachments`,
22616
+ { method: "POST", body: form }
22617
+ );
22209
22618
  if (!response2.ok) throw new Error("image_upload_failed");
22210
22619
  const body = await response2.json();
22211
22620
  conversationId.current = body.conversationId;
22212
22621
  uploaded.push(body.attachment);
22213
22622
  }
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) }) });
22623
+ setMessages((previous) => [
22624
+ ...previous,
22625
+ {
22626
+ role: "visitor",
22627
+ content: content || `[${labels.image}]`,
22628
+ attachments: uploaded
22629
+ },
22630
+ { role: "assistant", content: "" }
22631
+ ]);
22632
+ const response = await fetch(
22633
+ `${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/messages`,
22634
+ {
22635
+ method: "POST",
22636
+ headers: { "Content-Type": "application/json", Accept: "text/plain, application/json" },
22637
+ body: JSON.stringify({
22638
+ conversationId: conversationId.current ?? void 0,
22639
+ visitorId: visitorIdValue,
22640
+ locale,
22641
+ page: window.location.href,
22642
+ message: content || void 0,
22643
+ attachmentIds: uploaded.map((attachment) => attachment.id)
22644
+ })
22645
+ }
22646
+ );
22217
22647
  const headerConversation = response.headers.get("X-Setto-Conversation");
22218
22648
  if (headerConversation) conversationId.current = headerConversation;
22219
22649
  if (!response.ok) throw new Error(`chat_failed_${response.status}`);
@@ -22233,16 +22663,19 @@ function SettoChat() {
22233
22663
  setMessages((previous) => replaceLastAssistant(previous, answer));
22234
22664
  }
22235
22665
  answer += decoder.decode();
22236
- if (!answer.trim()) setMessages((previous) => replaceLastAssistant(previous, labels.failed));
22666
+ if (!answer.trim()) {
22667
+ setMessages((previous) => replaceLastAssistant(previous, labels.failed));
22668
+ }
22237
22669
  }
22238
22670
  void loadChats();
22239
22671
  } catch {
22240
22672
  setMessages((previous) => replaceLastAssistant(previous, labels.failed));
22241
22673
  } finally {
22674
+ for (const image of turnFiles) URL.revokeObjectURL(image.preview);
22242
22675
  setSending(false);
22243
22676
  }
22244
- };
22245
- const submitLead = async (event) => {
22677
+ }
22678
+ async function submitLead(event) {
22246
22679
  event.preventDefault();
22247
22680
  if (leadStatus === "sending" || !conversationId.current || !visitor.current) {
22248
22681
  setLeadStatus("failed");
@@ -22250,109 +22683,284 @@ function SettoChat() {
22250
22683
  }
22251
22684
  setLeadStatus("sending");
22252
22685
  try {
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 }) });
22686
+ const response = await fetch(
22687
+ `${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/leads`,
22688
+ {
22689
+ method: "POST",
22690
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
22691
+ body: JSON.stringify({
22692
+ conversationId: conversationId.current,
22693
+ visitorId: visitor.current,
22694
+ consent: lead.consent,
22695
+ name: lead.name.trim() || void 0,
22696
+ email: lead.email.trim() || void 0,
22697
+ phone: lead.phone.trim() || void 0,
22698
+ interest: lead.interest.trim() || void 0
22699
+ })
22700
+ }
22701
+ );
22254
22702
  if (!response.ok) throw new Error("lead_failed");
22255
22703
  setLeadStatus("sent");
22256
22704
  } catch {
22257
22705
  setLeadStatus("failed");
22258
22706
  }
22259
- };
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: "+" })
22266
- ] }),
22267
- /* @__PURE__ */ jsxs("button", { className: "setto-chat__new", type: "button", onClick: startNew, children: [
22268
- "+ ",
22269
- labels.newChat
22270
- ] }),
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 }) })
22275
- ] }),
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" })
22282
- ] }),
22283
- /* @__PURE__ */ jsx("button", { className: "setto-chat__round-button", type: "button", onClick: hide, "aria-label": labels.close, children: "×" })
22284
- ] }),
22285
- /* @__PURE__ */ jsxs("div", { className: "setto-chat__disclosure", children: [
22286
- /* @__PURE__ */ jsx("span", { children: "BARISTA" }),
22287
- /* @__PURE__ */ jsx("p", { children: labels.disclosure })
22288
- ] }),
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) => {
22344
- if (event.key === "Enter" && !event.shiftKey) {
22345
- event.preventDefault();
22346
- event.currentTarget.form?.requestSubmit();
22347
- }
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
- ] })
22352
- ] })
22353
- ] }) : 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
22355
- ] });
22707
+ }
22708
+ return /* @__PURE__ */ jsxs(
22709
+ "aside",
22710
+ {
22711
+ className: `setto-chat${mounted ? " setto-chat--mounted" : ""}${visible ? " setto-chat--visible" : ""}`,
22712
+ style: palette,
22713
+ "aria-label": labels.title,
22714
+ "data-setto-ui": true,
22715
+ children: [
22716
+ mounted ? /* @__PURE__ */ jsxs(
22717
+ "section",
22718
+ {
22719
+ className: "setto-chat__shell",
22720
+ role: "dialog",
22721
+ "aria-modal": "true",
22722
+ "aria-labelledby": `${formId}-title`,
22723
+ children: [
22724
+ /* @__PURE__ */ jsx(
22725
+ "button",
22726
+ {
22727
+ className: `setto-chat__scrim${drawer ? " is-open" : ""}`,
22728
+ type: "button",
22729
+ onClick: () => setDrawer(false),
22730
+ "aria-label": labels.close,
22731
+ tabIndex: drawer ? 0 : -1
22732
+ }
22733
+ ),
22734
+ /* @__PURE__ */ jsxs(
22735
+ "nav",
22736
+ {
22737
+ className: `setto-chat__chats${drawer ? " setto-chat__chats--open" : ""}`,
22738
+ "aria-label": labels.chats,
22739
+ children: [
22740
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__chats-heading", children: [
22741
+ /* @__PURE__ */ jsx("span", { children: labels.chats }),
22742
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setDrawer(false), "aria-label": labels.close, children: /* @__PURE__ */ jsx(CloseIcon, { size: 18 }) })
22743
+ ] }),
22744
+ /* @__PURE__ */ jsxs("button", { className: "setto-chat__new", type: "button", onClick: startNew, children: [
22745
+ /* @__PURE__ */ jsx(PlusIcon, {}),
22746
+ labels.newChat
22747
+ ] }),
22748
+ /* @__PURE__ */ jsx("div", { className: "setto-chat__chat-list", children: conversations.length ? conversations.map((chat) => /* @__PURE__ */ jsxs(
22749
+ "button",
22750
+ {
22751
+ className: `setto-chat__chat-row${conversationId.current === chat.id ? " is-active" : ""}`,
22752
+ type: "button",
22753
+ onClick: () => void loadConversation(chat.id),
22754
+ children: [
22755
+ /* @__PURE__ */ jsx("span", { children: chat.preview }),
22756
+ /* @__PURE__ */ jsx("time", { children: new Date(chat.lastActivityAt).toLocaleDateString(
22757
+ isNo ? "nb-NO" : "en-GB",
22758
+ { day: "numeric", month: "short" }
22759
+ ) })
22760
+ ]
22761
+ },
22762
+ chat.id
22763
+ )) : /* @__PURE__ */ jsx("p", { children: labels.emptyChats }) })
22764
+ ]
22765
+ }
22766
+ ),
22767
+ /* @__PURE__ */ jsxs("main", { className: "setto-chat__main", children: [
22768
+ /* @__PURE__ */ jsxs("header", { className: "setto-chat__header", children: [
22769
+ /* @__PURE__ */ jsx(
22770
+ "button",
22771
+ {
22772
+ className: "setto-chat__round-button",
22773
+ type: "button",
22774
+ onClick: () => setDrawer((current) => !current),
22775
+ "aria-label": labels.chats,
22776
+ "aria-expanded": drawer,
22777
+ children: /* @__PURE__ */ jsx(MenuIcon, {})
22778
+ }
22779
+ ),
22780
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__brand", children: [
22781
+ /* @__PURE__ */ jsx("h2", { id: `${formId}-title`, children: labels.title }),
22782
+ /* @__PURE__ */ jsx("p", { children: siteName ?? "Barista" })
22783
+ ] }),
22784
+ /* @__PURE__ */ jsx(
22785
+ "button",
22786
+ {
22787
+ className: "setto-chat__round-button",
22788
+ type: "button",
22789
+ onClick: hide,
22790
+ "aria-label": labels.close,
22791
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
22792
+ }
22793
+ )
22794
+ ] }),
22795
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__disclosure", children: [
22796
+ /* @__PURE__ */ jsx("span", { children: "BARISTA" }),
22797
+ /* @__PURE__ */ jsx("p", { children: labels.disclosure })
22798
+ ] }),
22799
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__body", children: [
22800
+ /* @__PURE__ */ jsxs(
22801
+ "div",
22802
+ {
22803
+ className: "setto-chat__log",
22804
+ ref: logRef,
22805
+ "aria-live": "polite",
22806
+ "aria-busy": loadingConversation,
22807
+ children: [
22808
+ /* @__PURE__ */ jsx("div", { className: "setto-chat__message-list", children: messages.map((item, index) => /* @__PURE__ */ jsxs(
22809
+ "div",
22810
+ {
22811
+ className: `setto-chat__message setto-chat__message--${item.role}`,
22812
+ children: [
22813
+ item.role === "human" ? /* @__PURE__ */ jsx("span", { className: "setto-chat__speaker", children: labels.team }) : null,
22814
+ /* @__PURE__ */ jsx("span", { className: "setto-chat__message-copy", children: item.content || /* @__PURE__ */ jsx("span", { className: "setto-chat__typing", children: "···" }) }),
22815
+ item.attachments?.map((attachment) => /* @__PURE__ */ jsx(
22816
+ "a",
22817
+ {
22818
+ className: "setto-chat__image",
22819
+ href: attachment.url,
22820
+ target: "_blank",
22821
+ rel: "noreferrer",
22822
+ children: /* @__PURE__ */ jsx("img", { src: attachment.url, alt: labels.image })
22823
+ },
22824
+ attachment.id
22825
+ ))
22826
+ ]
22827
+ },
22828
+ item.id ?? `${item.role}-${index}`
22829
+ )) }),
22830
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__actions", children: [
22831
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: contactTeam, children: [
22832
+ labels.contact,
22833
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "↗" })
22834
+ ] }),
22835
+ /* @__PURE__ */ jsx(
22836
+ "button",
22837
+ {
22838
+ type: "button",
22839
+ onClick: () => {
22840
+ setLeadOpen((value) => !value);
22841
+ setLeadStatus("idle");
22842
+ },
22843
+ "aria-expanded": leadOpen,
22844
+ children: labels.followUp
22845
+ }
22846
+ )
22847
+ ] }),
22848
+ leadOpen ? /* @__PURE__ */ jsxs("form", { className: "setto-chat__lead", onSubmit: submitLead, children: [
22849
+ /* @__PURE__ */ jsx("strong", { children: labels.followUpTitle }),
22850
+ /* @__PURE__ */ jsx("p", { children: labels.followUpDetail }),
22851
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__lead-grid", children: [
22852
+ /* @__PURE__ */ jsxs("label", { children: [
22853
+ labels.name,
22854
+ /* @__PURE__ */ jsx(
22855
+ "input",
22856
+ {
22857
+ value: lead.name,
22858
+ maxLength: 160,
22859
+ onChange: (event) => setLead((current) => ({ ...current, name: event.target.value }))
22860
+ }
22861
+ )
22862
+ ] }),
22863
+ /* @__PURE__ */ jsxs("label", { children: [
22864
+ labels.email,
22865
+ /* @__PURE__ */ jsx(
22866
+ "input",
22867
+ {
22868
+ type: "email",
22869
+ value: lead.email,
22870
+ maxLength: 320,
22871
+ onChange: (event) => setLead((current) => ({ ...current, email: event.target.value }))
22872
+ }
22873
+ )
22874
+ ] }),
22875
+ /* @__PURE__ */ jsxs("label", { children: [
22876
+ labels.phone,
22877
+ /* @__PURE__ */ jsx(
22878
+ "input",
22879
+ {
22880
+ type: "tel",
22881
+ value: lead.phone,
22882
+ maxLength: 64,
22883
+ onChange: (event) => setLead((current) => ({ ...current, phone: event.target.value }))
22884
+ }
22885
+ )
22886
+ ] }),
22887
+ /* @__PURE__ */ jsxs("label", { children: [
22888
+ labels.interest,
22889
+ /* @__PURE__ */ jsx(
22890
+ "input",
22891
+ {
22892
+ value: lead.interest,
22893
+ maxLength: 500,
22894
+ onChange: (event) => setLead((current) => ({ ...current, interest: event.target.value }))
22895
+ }
22896
+ )
22897
+ ] })
22898
+ ] }),
22899
+ /* @__PURE__ */ jsxs("label", { className: "setto-chat__consent", children: [
22900
+ /* @__PURE__ */ jsx(
22901
+ "input",
22902
+ {
22903
+ type: "checkbox",
22904
+ checked: lead.consent,
22905
+ onChange: (event) => setLead((current) => ({ ...current, consent: event.target.checked }))
22906
+ }
22907
+ ),
22908
+ labels.consent
22909
+ ] }),
22910
+ leadStatus === "sent" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-success", children: labels.leadSent }) : null,
22911
+ leadStatus === "failed" ? /* @__PURE__ */ jsx("p", { className: "setto-chat__lead-error", children: conversationId.current ? labels.leadFailed : labels.leadConversation }) : null,
22912
+ leadStatus !== "sent" ? /* @__PURE__ */ jsx(
22913
+ "button",
22914
+ {
22915
+ type: "submit",
22916
+ disabled: leadStatus === "sending" || !lead.consent || !lead.email.trim() && !lead.phone.trim(),
22917
+ children: leadStatus === "sending" ? labels.sending : labels.leadSend
22918
+ }
22919
+ ) : null
22920
+ ] }) : null
22921
+ ]
22922
+ }
22923
+ ),
22924
+ /* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-shell", children: [
22925
+ /* @__PURE__ */ jsx(
22926
+ SiteChatComposer,
22927
+ {
22928
+ value: draft,
22929
+ onChange: setDraft,
22930
+ onSubmit: queueOrSend,
22931
+ busy: sending,
22932
+ queued,
22933
+ placeholder: labels.placeholder,
22934
+ attachLabel: labels.attach,
22935
+ sendLabel: labels.send,
22936
+ removeLabel: labels.remove,
22937
+ locale,
22938
+ attachments: files,
22939
+ onAttach: addFiles,
22940
+ onRemoveAttachment: removeFile
22941
+ }
22942
+ ),
22943
+ /* @__PURE__ */ jsx("div", { className: "setto-chat__composer-floor", "aria-hidden": "true" })
22944
+ ] })
22945
+ ] })
22946
+ ] })
22947
+ ]
22948
+ }
22949
+ ) : null,
22950
+ !mounted ? /* @__PURE__ */ jsx(
22951
+ "button",
22952
+ {
22953
+ className: "setto-chat__launcher",
22954
+ type: "button",
22955
+ onClick: show,
22956
+ "aria-label": labels.open,
22957
+ title: labels.open,
22958
+ children: availability.humanAvailable && availability.avatarUrl ? /* @__PURE__ */ jsx("img", { src: availability.avatarUrl, alt: "" }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(BaristaIcon, {}) })
22959
+ }
22960
+ ) : null
22961
+ ]
22962
+ }
22963
+ );
22356
22964
  }
22357
22965
  function replaceLastAssistant(messages, content) {
22358
22966
  for (let index = messages.length - 1; index >= 0; index -= 1) {
@@ -22370,14 +22978,14 @@ function useChatStyles() {
22370
22978
  const style = document.createElement("style");
22371
22979
  style.dataset.settoChat = "";
22372
22980
  style.textContent = `
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}}
22981
+ .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}
22982
+ .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)}
22983
+ .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}
22984
+ .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}
22985
+ .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}
22986
+ .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}
22987
+ .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}}
22988
+ @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}}
22381
22989
  `;
22382
22990
  document.head.appendChild(style);
22383
22991
  return () => style.remove();