@sevenfold/setto-client 0.27.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/chat/SettoChat.d.ts +16 -0
- package/dist/modules/chat/realtime.d.ts +104 -0
- package/dist/setto-client.js +506 -72
- package/dist/setto-client.js.map +1 -1
- package/package.json +1 -1
package/dist/setto-client.js
CHANGED
|
@@ -5899,21 +5899,21 @@ var PostgresTypes;
|
|
|
5899
5899
|
PostgresTypes2["tsrange"] = "tsrange";
|
|
5900
5900
|
PostgresTypes2["tstzrange"] = "tstzrange";
|
|
5901
5901
|
})(PostgresTypes || (PostgresTypes = {}));
|
|
5902
|
-
const convertChangeData = (columns,
|
|
5902
|
+
const convertChangeData = (columns, record2, options = {}) => {
|
|
5903
5903
|
var _a;
|
|
5904
5904
|
const skipTypes = (_a = options.skipTypes) !== null && _a !== void 0 ? _a : [];
|
|
5905
|
-
if (!
|
|
5905
|
+
if (!record2) {
|
|
5906
5906
|
return {};
|
|
5907
5907
|
}
|
|
5908
|
-
return Object.keys(
|
|
5909
|
-
acc[rec_key] = convertColumn(rec_key, columns,
|
|
5908
|
+
return Object.keys(record2).reduce((acc, rec_key) => {
|
|
5909
|
+
acc[rec_key] = convertColumn(rec_key, columns, record2, skipTypes);
|
|
5910
5910
|
return acc;
|
|
5911
5911
|
}, {});
|
|
5912
5912
|
};
|
|
5913
|
-
const convertColumn = (columnName, columns,
|
|
5913
|
+
const convertColumn = (columnName, columns, record2, skipTypes) => {
|
|
5914
5914
|
const column = columns.find((x) => x.name === columnName);
|
|
5915
5915
|
const colType = column === null || column === void 0 ? void 0 : column.type;
|
|
5916
|
-
const value =
|
|
5916
|
+
const value = record2[columnName];
|
|
5917
5917
|
if (colType && !skipTypes.includes(colType)) {
|
|
5918
5918
|
return convertCell(colType, value);
|
|
5919
5919
|
}
|
|
@@ -34654,6 +34654,139 @@ function remarkGfm(options) {
|
|
|
34654
34654
|
fromMarkdownExtensions.push(gfmFromMarkdown());
|
|
34655
34655
|
toMarkdownExtensions.push(gfmToMarkdown(settings));
|
|
34656
34656
|
}
|
|
34657
|
+
const POLL_FALLBACK_MS = 5e3;
|
|
34658
|
+
const POLL_SUBSCRIBED_MS = 3e4;
|
|
34659
|
+
const CHANGED_DEBOUNCE_MS = 250;
|
|
34660
|
+
const TYPING_THROTTLE_MS = 2500;
|
|
34661
|
+
const TYPING_EXPIRY_MS = 6e3;
|
|
34662
|
+
function pollIntervalMs(subscribed) {
|
|
34663
|
+
return subscribed ? POLL_SUBSCRIBED_MS : POLL_FALLBACK_MS;
|
|
34664
|
+
}
|
|
34665
|
+
function conversationTopic(value) {
|
|
34666
|
+
return typeof value === "string" && value.length <= 200 && /^chat-conversation:\S+$/.test(value) ? value : null;
|
|
34667
|
+
}
|
|
34668
|
+
function isSide(value) {
|
|
34669
|
+
return value === "team" || value === "visitor";
|
|
34670
|
+
}
|
|
34671
|
+
function record(payload) {
|
|
34672
|
+
return payload && typeof payload === "object" ? payload : null;
|
|
34673
|
+
}
|
|
34674
|
+
function parseTypingSignal(payload) {
|
|
34675
|
+
const value = record(payload);
|
|
34676
|
+
if (!value || !isSide(value.side) || typeof value.typing !== "boolean") return null;
|
|
34677
|
+
return { side: value.side, typing: value.typing };
|
|
34678
|
+
}
|
|
34679
|
+
function parseReadSignal(payload) {
|
|
34680
|
+
const value = record(payload);
|
|
34681
|
+
if (!value || !isSide(value.side) || typeof value.at !== "string") return null;
|
|
34682
|
+
return timestampValue(value.at) === null ? null : { side: value.side, at: value.at };
|
|
34683
|
+
}
|
|
34684
|
+
const TIMESTAMP = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})(:\d{2})?(?:\.(\d+))?\s*(Z|[+-]\d{2}(?::?\d{2})?)?$/i;
|
|
34685
|
+
function timestampValue(value) {
|
|
34686
|
+
if (!value) return null;
|
|
34687
|
+
const match = TIMESTAMP.exec(value.trim());
|
|
34688
|
+
if (!match) {
|
|
34689
|
+
const parsed = Date.parse(value);
|
|
34690
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
34691
|
+
}
|
|
34692
|
+
const [, date, minutes, seconds = ":00", fraction, zone] = match;
|
|
34693
|
+
let offset = "Z";
|
|
34694
|
+
if (zone && zone.toUpperCase() !== "Z") {
|
|
34695
|
+
const digits = zone.slice(1).replace(":", "");
|
|
34696
|
+
offset = `${zone[0]}${digits.slice(0, 2)}:${digits.slice(2, 4) || "00"}`;
|
|
34697
|
+
}
|
|
34698
|
+
const whole = Date.parse(`${date}T${minutes}${seconds}${offset}`);
|
|
34699
|
+
if (!Number.isFinite(whole)) return null;
|
|
34700
|
+
return fraction ? whole + Number(`0.${fraction}`) * 1e3 : whole;
|
|
34701
|
+
}
|
|
34702
|
+
function laterTimestamp(current, next) {
|
|
34703
|
+
const nextValue = timestampValue(next);
|
|
34704
|
+
if (nextValue === null) return current;
|
|
34705
|
+
const currentValue = timestampValue(current);
|
|
34706
|
+
return currentValue === null || nextValue > currentValue ? next : current;
|
|
34707
|
+
}
|
|
34708
|
+
function createTypingSender(send, now = Date.now) {
|
|
34709
|
+
let typing = false;
|
|
34710
|
+
let lastSentAt = Number.NEGATIVE_INFINITY;
|
|
34711
|
+
function reset() {
|
|
34712
|
+
typing = false;
|
|
34713
|
+
lastSentAt = Number.NEGATIVE_INFINITY;
|
|
34714
|
+
}
|
|
34715
|
+
function stop() {
|
|
34716
|
+
if (!typing) return;
|
|
34717
|
+
reset();
|
|
34718
|
+
send(false);
|
|
34719
|
+
}
|
|
34720
|
+
function update(draft) {
|
|
34721
|
+
if (!draft.trim()) {
|
|
34722
|
+
stop();
|
|
34723
|
+
return;
|
|
34724
|
+
}
|
|
34725
|
+
const at = now();
|
|
34726
|
+
if (typing && at - lastSentAt < TYPING_THROTTLE_MS) return;
|
|
34727
|
+
if (send(true)) {
|
|
34728
|
+
typing = true;
|
|
34729
|
+
lastSentAt = at;
|
|
34730
|
+
}
|
|
34731
|
+
}
|
|
34732
|
+
return { update, stop, reset };
|
|
34733
|
+
}
|
|
34734
|
+
function nextTeamTypingDeadline(current, signal, now) {
|
|
34735
|
+
if (signal.side !== "team") return current;
|
|
34736
|
+
return signal.typing ? now + TYPING_EXPIRY_MS : null;
|
|
34737
|
+
}
|
|
34738
|
+
function latestHumanMessageKey(messages) {
|
|
34739
|
+
for (let index2 = messages.length - 1; index2 >= 0; index2 -= 1) {
|
|
34740
|
+
const message = messages[index2];
|
|
34741
|
+
if (message?.role === "human") return message.id ?? message.created_at ?? null;
|
|
34742
|
+
}
|
|
34743
|
+
return null;
|
|
34744
|
+
}
|
|
34745
|
+
function teamTypingIdentity(messages) {
|
|
34746
|
+
for (let index2 = messages.length - 1; index2 >= 0; index2 -= 1) {
|
|
34747
|
+
const message = messages[index2];
|
|
34748
|
+
if (message && (message.role === "human" || message.kind === "agent_joined")) {
|
|
34749
|
+
return { name: message.authorName?.trim() || null, avatarUrl: message.avatarUrl || null };
|
|
34750
|
+
}
|
|
34751
|
+
}
|
|
34752
|
+
return { name: null, avatarUrl: null };
|
|
34753
|
+
}
|
|
34754
|
+
function teamTypingLabel(norwegian2, name2) {
|
|
34755
|
+
if (norwegian2) return name2 ? `${name2} skriver …` : "Teamet skriver …";
|
|
34756
|
+
return name2 ? `${name2} is typing …` : "The team is typing …";
|
|
34757
|
+
}
|
|
34758
|
+
function seenVisitorMessageIndex(messages, humanJoined, teamReadAt) {
|
|
34759
|
+
const readValue = humanJoined ? timestampValue(teamReadAt) : null;
|
|
34760
|
+
if (readValue === null) return -1;
|
|
34761
|
+
for (let index2 = messages.length - 1; index2 >= 0; index2 -= 1) {
|
|
34762
|
+
if (messages[index2]?.role !== "visitor") continue;
|
|
34763
|
+
const created = timestampValue(messages[index2]?.created_at);
|
|
34764
|
+
return created !== null && created <= readValue ? index2 : -1;
|
|
34765
|
+
}
|
|
34766
|
+
return -1;
|
|
34767
|
+
}
|
|
34768
|
+
function readMarkerThrough(messages, context) {
|
|
34769
|
+
if (!context.humanJoined || !context.chatOpen || !context.pageVisible) return null;
|
|
34770
|
+
let newest = null;
|
|
34771
|
+
let newestValue = Number.NEGATIVE_INFINITY;
|
|
34772
|
+
for (const message of messages) {
|
|
34773
|
+
if (message.role === "visitor") continue;
|
|
34774
|
+
const value = timestampValue(message.created_at);
|
|
34775
|
+
if (value !== null && value > newestValue) {
|
|
34776
|
+
newest = message.created_at;
|
|
34777
|
+
newestValue = value;
|
|
34778
|
+
}
|
|
34779
|
+
}
|
|
34780
|
+
if (newest === null) return null;
|
|
34781
|
+
const sentValue = timestampValue(context.lastSent);
|
|
34782
|
+
return sentValue !== null && newestValue <= sentValue ? null : newest;
|
|
34783
|
+
}
|
|
34784
|
+
function refreshOutcome(started, current) {
|
|
34785
|
+
if (started.conversationId !== current.conversationId) return "drop";
|
|
34786
|
+
if (started.request !== current.request) return "drop";
|
|
34787
|
+
if (current.busy || started.turn !== current.turn) return "defer";
|
|
34788
|
+
return "apply";
|
|
34789
|
+
}
|
|
34657
34790
|
const VISITOR_KEY = "setto-chat-visitor";
|
|
34658
34791
|
const ACTIVE_CONVERSATION_KEY = "setto-chat-active";
|
|
34659
34792
|
const NAVIGATION_KEY = "setto-chat-navigation";
|
|
@@ -34697,6 +34830,9 @@ function sineOut(progress) {
|
|
|
34697
34830
|
function isAtChatBottom(scrollHeight, scrollTop, clientHeight) {
|
|
34698
34831
|
return scrollHeight - scrollTop - clientHeight <= 4;
|
|
34699
34832
|
}
|
|
34833
|
+
function chatComposerSpace(height) {
|
|
34834
|
+
return Math.max(0, Math.ceil(height)) + 16;
|
|
34835
|
+
}
|
|
34700
34836
|
function createWordRevealer(onReveal) {
|
|
34701
34837
|
let target = "";
|
|
34702
34838
|
let visibleLength = 0;
|
|
@@ -34843,6 +34979,25 @@ function AgentJoinedEvent({ message }) {
|
|
|
34843
34979
|
/* @__PURE__ */ jsx("div", { className: "setto-chat__message-stack", children: /* @__PURE__ */ jsx("div", { className: "setto-chat__message-bubble", children: /* @__PURE__ */ jsx("span", { className: "setto-chat__message-copy", children: message.content }) }) })
|
|
34844
34980
|
] });
|
|
34845
34981
|
}
|
|
34982
|
+
function TeamTypingRow({ label, avatarUrl }) {
|
|
34983
|
+
return /* @__PURE__ */ jsxs(
|
|
34984
|
+
"div",
|
|
34985
|
+
{
|
|
34986
|
+
className: "setto-chat__message setto-chat__message--human setto-chat__message--typing",
|
|
34987
|
+
"aria-live": "off",
|
|
34988
|
+
children: [
|
|
34989
|
+
/* @__PURE__ */ jsx("span", { className: "setto-chat__avatar setto-chat__avatar--human", "aria-hidden": "true", children: avatarUrl ? /* @__PURE__ */ jsx("img", { src: avatarUrl, alt: "" }) : /* @__PURE__ */ jsx(PersonIcon, {}) }),
|
|
34990
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__message-stack", children: [
|
|
34991
|
+
/* @__PURE__ */ jsx("span", { className: "setto-chat__speaker", children: label }),
|
|
34992
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__message-bubble", children: /* @__PURE__ */ jsx("div", { className: "setto-chat__message-copy", children: /* @__PURE__ */ jsx("span", { className: "setto-chat__typing", "aria-hidden": "true", children: "···" }) }) })
|
|
34993
|
+
] })
|
|
34994
|
+
]
|
|
34995
|
+
}
|
|
34996
|
+
);
|
|
34997
|
+
}
|
|
34998
|
+
function SeenMarker({ label }) {
|
|
34999
|
+
return /* @__PURE__ */ jsx("span", { className: "setto-chat__receipt", "aria-live": "off", children: label });
|
|
35000
|
+
}
|
|
34846
35001
|
function StaggeredText({ children }) {
|
|
34847
35002
|
return Children.map(children, (child) => {
|
|
34848
35003
|
if (typeof child !== "string") return child;
|
|
@@ -34884,6 +35039,7 @@ function SiteChatComposer({
|
|
|
34884
35039
|
value,
|
|
34885
35040
|
onChange,
|
|
34886
35041
|
onSubmit,
|
|
35042
|
+
onBlur,
|
|
34887
35043
|
busy,
|
|
34888
35044
|
queued,
|
|
34889
35045
|
placeholder,
|
|
@@ -34964,7 +35120,10 @@ function SiteChatComposer({
|
|
|
34964
35120
|
onChange: (event) => onChange(event.target.value),
|
|
34965
35121
|
onKeyDown: handleKeyDown,
|
|
34966
35122
|
onFocus: () => setFocused(true),
|
|
34967
|
-
onBlur: () =>
|
|
35123
|
+
onBlur: () => {
|
|
35124
|
+
setFocused(false);
|
|
35125
|
+
onBlur?.();
|
|
35126
|
+
},
|
|
34968
35127
|
placeholder,
|
|
34969
35128
|
rows: 1,
|
|
34970
35129
|
maxLength: 1500
|
|
@@ -35235,8 +35394,88 @@ function useChatAutoScroll(active, logRef, pinToBottomRef, programmaticScrollRef
|
|
|
35235
35394
|
};
|
|
35236
35395
|
}, [active, logRef, pinToBottomRef, programmaticScrollRef]);
|
|
35237
35396
|
}
|
|
35397
|
+
function usePageVisible() {
|
|
35398
|
+
const [pageVisible, setPageVisible] = useState(
|
|
35399
|
+
() => typeof document === "undefined" || document.visibilityState !== "hidden"
|
|
35400
|
+
);
|
|
35401
|
+
useEffect(() => {
|
|
35402
|
+
const update = () => setPageVisible(document.visibilityState !== "hidden");
|
|
35403
|
+
update();
|
|
35404
|
+
document.addEventListener("visibilitychange", update);
|
|
35405
|
+
return () => document.removeEventListener("visibilitychange", update);
|
|
35406
|
+
}, []);
|
|
35407
|
+
return pageVisible;
|
|
35408
|
+
}
|
|
35409
|
+
const channelRemovals = /* @__PURE__ */ new Map();
|
|
35410
|
+
function useConversationChannel(supabase, topic, handlers2) {
|
|
35411
|
+
const handlersRef = useRef(handlers2);
|
|
35412
|
+
const channelRef = useRef(null);
|
|
35413
|
+
useEffect(() => {
|
|
35414
|
+
handlersRef.current = handlers2;
|
|
35415
|
+
});
|
|
35416
|
+
useEffect(() => {
|
|
35417
|
+
if (!topic) return;
|
|
35418
|
+
let active = true;
|
|
35419
|
+
let channel = null;
|
|
35420
|
+
let changedTimer = 0;
|
|
35421
|
+
const join2 = () => {
|
|
35422
|
+
if (!active) return;
|
|
35423
|
+
channel = supabase.channel(topic).on("broadcast", { event: "changed" }, () => {
|
|
35424
|
+
window.clearTimeout(changedTimer);
|
|
35425
|
+
changedTimer = window.setTimeout(
|
|
35426
|
+
() => handlersRef.current.onChanged(),
|
|
35427
|
+
CHANGED_DEBOUNCE_MS
|
|
35428
|
+
);
|
|
35429
|
+
}).on("broadcast", { event: "read" }, (message) => {
|
|
35430
|
+
const signal = parseReadSignal(message.payload);
|
|
35431
|
+
if (signal) handlersRef.current.onRead(signal);
|
|
35432
|
+
}).on("broadcast", { event: "typing" }, (message) => {
|
|
35433
|
+
const signal = parseTypingSignal(message.payload);
|
|
35434
|
+
if (signal) handlersRef.current.onTyping(signal);
|
|
35435
|
+
});
|
|
35436
|
+
channelRef.current = channel;
|
|
35437
|
+
channel.subscribe((status) => {
|
|
35438
|
+
if (active) handlersRef.current.onStatus(status === "SUBSCRIBED");
|
|
35439
|
+
});
|
|
35440
|
+
};
|
|
35441
|
+
const previousRemoval = channelRemovals.get(topic);
|
|
35442
|
+
if (previousRemoval) void previousRemoval.then(join2);
|
|
35443
|
+
else join2();
|
|
35444
|
+
return () => {
|
|
35445
|
+
active = false;
|
|
35446
|
+
window.clearTimeout(changedTimer);
|
|
35447
|
+
if (!channel) return;
|
|
35448
|
+
handlersRef.current.onLeave();
|
|
35449
|
+
channelRef.current = null;
|
|
35450
|
+
handlersRef.current.onStatus(false);
|
|
35451
|
+
const removal = supabase.removeChannel(channel).catch(() => void 0).finally(() => {
|
|
35452
|
+
if (channelRemovals.get(topic) === removal) channelRemovals.delete(topic);
|
|
35453
|
+
});
|
|
35454
|
+
channelRemovals.set(topic, removal);
|
|
35455
|
+
};
|
|
35456
|
+
}, [supabase, topic]);
|
|
35457
|
+
return channelRef;
|
|
35458
|
+
}
|
|
35459
|
+
function useChatComposerSpace(active, composerRef, logRef) {
|
|
35460
|
+
useEffect(() => {
|
|
35461
|
+
if (!active) return;
|
|
35462
|
+
const composer = composerRef.current;
|
|
35463
|
+
const log = logRef.current;
|
|
35464
|
+
if (!composer || !log) return;
|
|
35465
|
+
const update = () => {
|
|
35466
|
+
log.style.setProperty("--setto-chat-composer-space", `${chatComposerSpace(composer.getBoundingClientRect().height)}px`);
|
|
35467
|
+
};
|
|
35468
|
+
const observer = new ResizeObserver(update);
|
|
35469
|
+
observer.observe(composer);
|
|
35470
|
+
update();
|
|
35471
|
+
return () => {
|
|
35472
|
+
observer.disconnect();
|
|
35473
|
+
log.style.removeProperty("--setto-chat-composer-space");
|
|
35474
|
+
};
|
|
35475
|
+
}, [active, composerRef, logRef]);
|
|
35476
|
+
}
|
|
35238
35477
|
function SettoChat() {
|
|
35239
|
-
const { config } = useSetto();
|
|
35478
|
+
const { config, supabase } = useSetto();
|
|
35240
35479
|
const { i18n } = useTranslation();
|
|
35241
35480
|
const apiUrl = config.apiUrl.replace(/\/+$/, "");
|
|
35242
35481
|
const [settings, setSettings] = useState(null);
|
|
@@ -35259,16 +35498,62 @@ function SettoChat() {
|
|
|
35259
35498
|
const visitor = useRef(null);
|
|
35260
35499
|
const chatRef = useRef(null);
|
|
35261
35500
|
const logRef = useRef(null);
|
|
35501
|
+
const composerShellRef = useRef(null);
|
|
35262
35502
|
const pinToBottomRef = useRef(true);
|
|
35263
35503
|
const programmaticScrollRef = useRef(false);
|
|
35264
35504
|
const filesRef = useRef([]);
|
|
35505
|
+
const [realtime, setRealtime] = useState(null);
|
|
35506
|
+
const [subscribed, setSubscribed] = useState(false);
|
|
35507
|
+
const [teamTypingUntil, setTeamTypingUntil] = useState(null);
|
|
35508
|
+
const subscribedRef = useRef(false);
|
|
35509
|
+
const turnRef = useRef({ busy: false, generation: 0 });
|
|
35510
|
+
const refreshRequestRef = useRef(0);
|
|
35511
|
+
const pendingRefreshRef = useRef(false);
|
|
35512
|
+
const loadedConversationRef = useRef(null);
|
|
35513
|
+
const latestHumanRef = useRef(null);
|
|
35514
|
+
const readMarkersRef = useRef(/* @__PURE__ */ new Map());
|
|
35515
|
+
const refreshRef = useRef(async () => void 0);
|
|
35516
|
+
const typingSenderRef = useRef(null);
|
|
35265
35517
|
const formId = useId();
|
|
35266
35518
|
const palette = useMemo(() => chatPalette(config.brandColors), [config.brandColors]);
|
|
35267
35519
|
const desktopPopup = useDesktopPopup();
|
|
35520
|
+
const pageVisible = usePageVisible();
|
|
35521
|
+
const channelRef = useConversationChannel(supabase, mounted ? realtime?.topic ?? null : null, {
|
|
35522
|
+
onStatus(next) {
|
|
35523
|
+
subscribedRef.current = next;
|
|
35524
|
+
setSubscribed(next);
|
|
35525
|
+
if (next) void refreshRef.current();
|
|
35526
|
+
else setTeamTypingUntil(null);
|
|
35527
|
+
},
|
|
35528
|
+
onChanged() {
|
|
35529
|
+
void refreshRef.current();
|
|
35530
|
+
},
|
|
35531
|
+
onRead(signal) {
|
|
35532
|
+
if (signal.side !== "team") return;
|
|
35533
|
+
setRealtime(
|
|
35534
|
+
(current) => current ? { ...current, teamReadAt: laterTimestamp(current.teamReadAt, signal.at) } : current
|
|
35535
|
+
);
|
|
35536
|
+
},
|
|
35537
|
+
onTyping(signal) {
|
|
35538
|
+
setTeamTypingUntil((current) => nextTeamTypingDeadline(current, signal, Date.now()));
|
|
35539
|
+
},
|
|
35540
|
+
onLeave() {
|
|
35541
|
+
typingSender().stop();
|
|
35542
|
+
}
|
|
35543
|
+
});
|
|
35544
|
+
function typingSender() {
|
|
35545
|
+
return typingSenderRef.current ??= createTypingSender((typing) => {
|
|
35546
|
+
const channel = channelRef.current;
|
|
35547
|
+
if (!channel || !subscribedRef.current || channel.state !== "joined") return false;
|
|
35548
|
+
void channel.send({ type: "broadcast", event: "typing", payload: { side: "visitor", typing } }).catch(() => void 0);
|
|
35549
|
+
return true;
|
|
35550
|
+
});
|
|
35551
|
+
}
|
|
35268
35552
|
useChatStyles();
|
|
35269
35553
|
useChatViewport(mounted && !desktopPopup, logRef, pinToBottomRef, programmaticScrollRef);
|
|
35270
35554
|
useChatPinTracking(mounted, logRef, pinToBottomRef, programmaticScrollRef);
|
|
35271
35555
|
useChatAutoScroll(mounted, logRef, pinToBottomRef, programmaticScrollRef);
|
|
35556
|
+
useChatComposerSpace(mounted, composerShellRef, logRef);
|
|
35272
35557
|
useChatInputIsolation(Boolean(settings?.enabled), mounted, chatRef, logRef);
|
|
35273
35558
|
const locale = i18n.language || "no";
|
|
35274
35559
|
const isNo = norwegian(locale);
|
|
@@ -35288,6 +35573,7 @@ function SettoChat() {
|
|
|
35288
35573
|
failed: "Jeg får ikke svart akkurat nå. Kontakt gjerne teamet direkte.",
|
|
35289
35574
|
image: "Bilde",
|
|
35290
35575
|
team: "Teamet",
|
|
35576
|
+
seen: "Sett",
|
|
35291
35577
|
emptyChats: "Nye samtaler du starter her vises i denne listen."
|
|
35292
35578
|
} : {
|
|
35293
35579
|
open: siteName ? `Open chat with ${siteName}` : "Open chat",
|
|
@@ -35304,6 +35590,7 @@ function SettoChat() {
|
|
|
35304
35590
|
failed: "I cannot reply right now. Please contact the team directly.",
|
|
35305
35591
|
image: "Image",
|
|
35306
35592
|
team: "The team",
|
|
35593
|
+
seen: "Seen",
|
|
35307
35594
|
emptyChats: "New conversations you start here will appear in this list."
|
|
35308
35595
|
};
|
|
35309
35596
|
function ensureVisitor() {
|
|
@@ -35318,9 +35605,24 @@ function SettoChat() {
|
|
|
35318
35605
|
}
|
|
35319
35606
|
}
|
|
35320
35607
|
function applyConversation(next) {
|
|
35608
|
+
const sameConversation = loadedConversationRef.current === next.conversationId;
|
|
35321
35609
|
rememberConversation(next.conversationId);
|
|
35610
|
+
loadedConversationRef.current = next.conversationId;
|
|
35322
35611
|
setHumanJoined(next.humanJoined);
|
|
35323
35612
|
setMessages(next.messages);
|
|
35613
|
+
const supported = "realtimeTopic" in next || "teamReadAt" in next || "visitorReadAt" in next;
|
|
35614
|
+
const serverTeamReadAt = typeof next.teamReadAt === "string" ? next.teamReadAt : null;
|
|
35615
|
+
setRealtime((current) => ({
|
|
35616
|
+
conversationId: next.conversationId,
|
|
35617
|
+
supported,
|
|
35618
|
+
topic: conversationTopic(next.realtimeTopic),
|
|
35619
|
+
teamReadAt: current?.conversationId === next.conversationId ? laterTimestamp(current.teamReadAt, serverTeamReadAt) : serverTeamReadAt
|
|
35620
|
+
}));
|
|
35621
|
+
const latestHuman = latestHumanMessageKey(next.messages);
|
|
35622
|
+
if (!sameConversation || latestHuman && latestHuman !== latestHumanRef.current) {
|
|
35623
|
+
setTeamTypingUntil(null);
|
|
35624
|
+
}
|
|
35625
|
+
latestHumanRef.current = latestHuman;
|
|
35324
35626
|
const navigation = [...next.messages].reverse().find((message) => message.kind === "navigation" && message.action?.type === "navigate");
|
|
35325
35627
|
if (!navigation?.id || !navigation.action?.url) return;
|
|
35326
35628
|
try {
|
|
@@ -35348,9 +35650,51 @@ function SettoChat() {
|
|
|
35348
35650
|
} catch {
|
|
35349
35651
|
}
|
|
35350
35652
|
}
|
|
35653
|
+
async function refreshConversation() {
|
|
35654
|
+
const id = conversationId.current;
|
|
35655
|
+
const visitorIdValue = visitor.current;
|
|
35656
|
+
if (!id || !visitorIdValue) return;
|
|
35657
|
+
if (turnRef.current.busy) {
|
|
35658
|
+
pendingRefreshRef.current = true;
|
|
35659
|
+
return;
|
|
35660
|
+
}
|
|
35661
|
+
pendingRefreshRef.current = false;
|
|
35662
|
+
refreshRequestRef.current += 1;
|
|
35663
|
+
const ticket = {
|
|
35664
|
+
conversationId: id,
|
|
35665
|
+
turn: turnRef.current.generation,
|
|
35666
|
+
request: refreshRequestRef.current
|
|
35667
|
+
};
|
|
35668
|
+
try {
|
|
35669
|
+
const query = new URLSearchParams({ visitorId: visitorIdValue });
|
|
35670
|
+
const response = await fetch(
|
|
35671
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(id)}/public?${query}`,
|
|
35672
|
+
{ headers: { Accept: "application/json" } }
|
|
35673
|
+
);
|
|
35674
|
+
if (!response.ok) return;
|
|
35675
|
+
const next = await response.json();
|
|
35676
|
+
const outcome = refreshOutcome(ticket, {
|
|
35677
|
+
conversationId: conversationId.current,
|
|
35678
|
+
turn: turnRef.current.generation,
|
|
35679
|
+
request: refreshRequestRef.current,
|
|
35680
|
+
busy: turnRef.current.busy
|
|
35681
|
+
});
|
|
35682
|
+
if (outcome === "apply") {
|
|
35683
|
+
applyConversation(next);
|
|
35684
|
+
} else if (outcome === "defer") {
|
|
35685
|
+
pendingRefreshRef.current = true;
|
|
35686
|
+
if (!turnRef.current.busy) void refreshConversation();
|
|
35687
|
+
}
|
|
35688
|
+
} catch {
|
|
35689
|
+
}
|
|
35690
|
+
}
|
|
35691
|
+
useEffect(() => {
|
|
35692
|
+
refreshRef.current = refreshConversation;
|
|
35693
|
+
});
|
|
35351
35694
|
async function loadConversation(id) {
|
|
35352
35695
|
const visitorIdValue = ensureVisitor();
|
|
35353
35696
|
setLoadingConversation(true);
|
|
35697
|
+
refreshRequestRef.current += 1;
|
|
35354
35698
|
try {
|
|
35355
35699
|
const query = new URLSearchParams({ visitorId: visitorIdValue });
|
|
35356
35700
|
const response = await fetch(
|
|
@@ -35413,34 +35757,71 @@ function SettoChat() {
|
|
|
35413
35757
|
},
|
|
35414
35758
|
[]
|
|
35415
35759
|
);
|
|
35760
|
+
const polledConversation = realtime?.conversationId ?? null;
|
|
35416
35761
|
useEffect(() => {
|
|
35417
35762
|
if (!mounted || !conversationId.current || !visitor.current || sending) return;
|
|
35418
|
-
|
|
35419
|
-
|
|
35420
|
-
|
|
35421
|
-
|
|
35422
|
-
|
|
35423
|
-
|
|
35424
|
-
|
|
35425
|
-
|
|
35426
|
-
|
|
35427
|
-
|
|
35428
|
-
|
|
35429
|
-
|
|
35763
|
+
const timer = window.setInterval(() => {
|
|
35764
|
+
if (document.visibilityState === "hidden") return;
|
|
35765
|
+
void refreshRef.current();
|
|
35766
|
+
}, pollIntervalMs(subscribed));
|
|
35767
|
+
return () => window.clearInterval(timer);
|
|
35768
|
+
}, [mounted, sending, subscribed, polledConversation]);
|
|
35769
|
+
const pageWasVisibleRef = useRef(pageVisible);
|
|
35770
|
+
useEffect(() => {
|
|
35771
|
+
const returned = pageVisible && !pageWasVisibleRef.current;
|
|
35772
|
+
pageWasVisibleRef.current = pageVisible;
|
|
35773
|
+
if (returned && mounted) void refreshRef.current();
|
|
35774
|
+
}, [pageVisible, mounted]);
|
|
35775
|
+
useEffect(() => {
|
|
35776
|
+
if (teamTypingUntil === null) return;
|
|
35777
|
+
const timer = window.setTimeout(
|
|
35778
|
+
() => setTeamTypingUntil(null),
|
|
35779
|
+
Math.max(0, teamTypingUntil - Date.now())
|
|
35780
|
+
);
|
|
35781
|
+
return () => window.clearTimeout(timer);
|
|
35782
|
+
}, [teamTypingUntil]);
|
|
35783
|
+
useEffect(() => {
|
|
35784
|
+
const id = conversationId.current;
|
|
35785
|
+
const visitorIdValue = visitor.current;
|
|
35786
|
+
if (!id || !visitorIdValue || realtime?.conversationId !== id || !realtime.supported) return;
|
|
35787
|
+
let marker = readMarkersRef.current.get(id);
|
|
35788
|
+
if (!marker) {
|
|
35789
|
+
marker = { sent: null, pending: null, unsupported: false };
|
|
35790
|
+
readMarkersRef.current.set(id, marker);
|
|
35791
|
+
}
|
|
35792
|
+
if (marker.unsupported) return;
|
|
35793
|
+
const through = readMarkerThrough(messages, {
|
|
35794
|
+
humanJoined,
|
|
35795
|
+
chatOpen: mounted && visible,
|
|
35796
|
+
pageVisible,
|
|
35797
|
+
lastSent: laterTimestamp(marker.sent, marker.pending)
|
|
35798
|
+
});
|
|
35799
|
+
if (!through) return;
|
|
35800
|
+
const state = marker;
|
|
35801
|
+
state.pending = through;
|
|
35802
|
+
fetch(
|
|
35803
|
+
`${apiUrl}/sites/${encodeURIComponent(config.siteId)}/chat/conversations/${encodeURIComponent(id)}/public/read`,
|
|
35804
|
+
{
|
|
35805
|
+
method: "POST",
|
|
35806
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
35807
|
+
body: JSON.stringify({ visitorId: visitorIdValue, through })
|
|
35430
35808
|
}
|
|
35431
|
-
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
|
|
35435
|
-
|
|
35436
|
-
};
|
|
35437
|
-
}, [apiUrl, config.siteId, mounted,
|
|
35809
|
+
).then((response) => {
|
|
35810
|
+
if (response.ok) state.sent = laterTimestamp(state.sent, through);
|
|
35811
|
+
else if (response.status === 404) state.unsupported = true;
|
|
35812
|
+
}).catch(() => void 0).finally(() => {
|
|
35813
|
+
if (state.pending === through) state.pending = null;
|
|
35814
|
+
});
|
|
35815
|
+
}, [apiUrl, config.siteId, messages, humanJoined, mounted, visible, pageVisible, realtime]);
|
|
35438
35816
|
useEffect(() => {
|
|
35439
35817
|
if (!sending && queued) {
|
|
35440
35818
|
setQueued(false);
|
|
35441
35819
|
void sendTurn();
|
|
35442
35820
|
}
|
|
35443
35821
|
}, [sending, queued]);
|
|
35822
|
+
useEffect(() => {
|
|
35823
|
+
if (!sending && pendingRefreshRef.current) void refreshRef.current();
|
|
35824
|
+
}, [sending]);
|
|
35444
35825
|
useEffect(() => {
|
|
35445
35826
|
if (!mounted) return;
|
|
35446
35827
|
const closeOnEscape = (event) => {
|
|
@@ -35449,6 +35830,29 @@ function SettoChat() {
|
|
|
35449
35830
|
window.addEventListener("keydown", closeOnEscape);
|
|
35450
35831
|
return () => window.removeEventListener("keydown", closeOnEscape);
|
|
35451
35832
|
}, [mounted]);
|
|
35833
|
+
const newestMessageKey = (() => {
|
|
35834
|
+
const newest = messages.at(-1);
|
|
35835
|
+
return newest ? `${messages.length}:${newest.id ?? newest.role}` : "empty";
|
|
35836
|
+
})();
|
|
35837
|
+
useEffect(() => {
|
|
35838
|
+
if (!mounted || newestMessageKey === "empty") return;
|
|
35839
|
+
pinToBottomRef.current = true;
|
|
35840
|
+
let releaseFrame = 0;
|
|
35841
|
+
const frame = window.requestAnimationFrame(() => {
|
|
35842
|
+
const log = logRef.current;
|
|
35843
|
+
if (!log) return;
|
|
35844
|
+
programmaticScrollRef.current = true;
|
|
35845
|
+
log.scrollTop = Math.max(0, log.scrollHeight - log.clientHeight);
|
|
35846
|
+
releaseFrame = window.requestAnimationFrame(() => {
|
|
35847
|
+
programmaticScrollRef.current = false;
|
|
35848
|
+
});
|
|
35849
|
+
});
|
|
35850
|
+
return () => {
|
|
35851
|
+
window.cancelAnimationFrame(frame);
|
|
35852
|
+
window.cancelAnimationFrame(releaseFrame);
|
|
35853
|
+
programmaticScrollRef.current = false;
|
|
35854
|
+
};
|
|
35855
|
+
}, [mounted, newestMessageKey]);
|
|
35452
35856
|
if (!settings || window.location.pathname.startsWith("/setto")) return null;
|
|
35453
35857
|
function show() {
|
|
35454
35858
|
pinToBottomRef.current = true;
|
|
@@ -35473,7 +35877,12 @@ function SettoChat() {
|
|
|
35473
35877
|
});
|
|
35474
35878
|
}
|
|
35475
35879
|
function startNew() {
|
|
35880
|
+
typingSender().stop();
|
|
35476
35881
|
rememberConversation(null);
|
|
35882
|
+
loadedConversationRef.current = null;
|
|
35883
|
+
latestHumanRef.current = null;
|
|
35884
|
+
setRealtime(null);
|
|
35885
|
+
setTeamTypingUntil(null);
|
|
35477
35886
|
setHumanJoined(false);
|
|
35478
35887
|
setMessages([{ role: "assistant", content: intro(locale, settings?.displayName ?? void 0) }]);
|
|
35479
35888
|
setDraft("");
|
|
@@ -35497,6 +35906,7 @@ function SettoChat() {
|
|
|
35497
35906
|
}
|
|
35498
35907
|
function queueOrSend() {
|
|
35499
35908
|
if (!draft.trim() && files.length === 0) return;
|
|
35909
|
+
typingSender().stop();
|
|
35500
35910
|
if (sending) {
|
|
35501
35911
|
setQueued(true);
|
|
35502
35912
|
return;
|
|
@@ -35507,6 +35917,9 @@ function SettoChat() {
|
|
|
35507
35917
|
const content2 = draft.trim();
|
|
35508
35918
|
const turnFiles = files;
|
|
35509
35919
|
if (!content2 && turnFiles.length === 0) return;
|
|
35920
|
+
turnRef.current.busy = true;
|
|
35921
|
+
turnRef.current.generation += 1;
|
|
35922
|
+
typingSender().stop();
|
|
35510
35923
|
pinToBottomRef.current = true;
|
|
35511
35924
|
setDraft("");
|
|
35512
35925
|
setFiles([]);
|
|
@@ -35591,9 +36004,15 @@ function SettoChat() {
|
|
|
35591
36004
|
setMessages((previous2) => replaceLastAssistant(previous2, labels.failed));
|
|
35592
36005
|
} finally {
|
|
35593
36006
|
for (const image2 of turnFiles) URL.revokeObjectURL(image2.preview);
|
|
36007
|
+
turnRef.current.busy = false;
|
|
36008
|
+
if (conversationId.current && loadedConversationRef.current !== conversationId.current) {
|
|
36009
|
+
pendingRefreshRef.current = true;
|
|
36010
|
+
}
|
|
35594
36011
|
setSending(false);
|
|
35595
36012
|
}
|
|
35596
36013
|
}
|
|
36014
|
+
const seenIndex = seenVisitorMessageIndex(messages, humanJoined, realtime?.teamReadAt ?? null);
|
|
36015
|
+
const typingIdentity = teamTypingUntil !== null ? teamTypingIdentity(messages) : null;
|
|
35597
36016
|
return /* @__PURE__ */ jsxs(
|
|
35598
36017
|
"aside",
|
|
35599
36018
|
{
|
|
@@ -35690,51 +36109,65 @@ function SettoChat() {
|
|
|
35690
36109
|
ref: logRef,
|
|
35691
36110
|
"aria-live": "polite",
|
|
35692
36111
|
"aria-busy": loadingConversation,
|
|
35693
|
-
children: /* @__PURE__ */
|
|
35694
|
-
|
|
35695
|
-
"
|
|
35696
|
-
|
|
35697
|
-
|
|
35698
|
-
|
|
35699
|
-
|
|
35700
|
-
"
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
|
|
35704
|
-
|
|
35705
|
-
|
|
35706
|
-
|
|
35707
|
-
|
|
35708
|
-
|
|
35709
|
-
|
|
35710
|
-
/* @__PURE__ */
|
|
35711
|
-
|
|
35712
|
-
|
|
35713
|
-
|
|
35714
|
-
|
|
35715
|
-
|
|
35716
|
-
|
|
35717
|
-
|
|
35718
|
-
|
|
35719
|
-
|
|
35720
|
-
|
|
35721
|
-
|
|
36112
|
+
children: /* @__PURE__ */ jsxs("div", { className: "setto-chat__message-list", children: [
|
|
36113
|
+
messages.map(
|
|
36114
|
+
(item, index2) => item.role === "system" && item.kind === "agent_joined" ? /* @__PURE__ */ jsx(AgentJoinedEvent, { message: item }, item.id ?? `agent-joined-${index2}`) : /* @__PURE__ */ jsxs(
|
|
36115
|
+
"div",
|
|
36116
|
+
{
|
|
36117
|
+
className: `setto-chat__message setto-chat__message--${item.role}`,
|
|
36118
|
+
children: [
|
|
36119
|
+
item.role === "assistant" || item.role === "human" ? /* @__PURE__ */ jsx(
|
|
36120
|
+
"span",
|
|
36121
|
+
{
|
|
36122
|
+
className: `setto-chat__avatar setto-chat__avatar--${item.role}`,
|
|
36123
|
+
"aria-hidden": "true",
|
|
36124
|
+
children: item.role === "human" && item.avatarUrl ? /* @__PURE__ */ jsx("img", { src: item.avatarUrl, alt: "" }) : item.role === "human" ? /* @__PURE__ */ jsx(PersonIcon, {}) : /* @__PURE__ */ jsx(BaristaIcon, {})
|
|
36125
|
+
}
|
|
36126
|
+
) : null,
|
|
36127
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__message-stack", children: [
|
|
36128
|
+
item.role === "assistant" || item.role === "human" ? /* @__PURE__ */ jsx("span", { className: "setto-chat__speaker", children: item.role === "human" ? item.authorName || labels.team : "Barista" }) : null,
|
|
36129
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__message-bubble", children: [
|
|
36130
|
+
/* @__PURE__ */ jsx("div", { className: "setto-chat__message-copy", children: item.content ? item.role === "assistant" ? /* @__PURE__ */ jsx(SiteChatMarkdown, { content: item.content, reveal: item.reveal }) : item.content : /* @__PURE__ */ jsx("span", { className: "setto-chat__typing", children: "···" }) }),
|
|
36131
|
+
item.attachments?.map((attachment) => /* @__PURE__ */ jsx(
|
|
36132
|
+
"a",
|
|
36133
|
+
{
|
|
36134
|
+
className: "setto-chat__image",
|
|
36135
|
+
href: attachment.url,
|
|
36136
|
+
target: "_blank",
|
|
36137
|
+
rel: "noreferrer",
|
|
36138
|
+
children: /* @__PURE__ */ jsx("img", { src: attachment.url, alt: labels.image })
|
|
36139
|
+
},
|
|
36140
|
+
attachment.id
|
|
36141
|
+
))
|
|
36142
|
+
] }),
|
|
36143
|
+
index2 === seenIndex ? /* @__PURE__ */ jsx(SeenMarker, { label: labels.seen }) : null
|
|
35722
36144
|
] })
|
|
35723
|
-
]
|
|
35724
|
-
|
|
35725
|
-
|
|
35726
|
-
|
|
35727
|
-
)
|
|
35728
|
-
|
|
36145
|
+
]
|
|
36146
|
+
},
|
|
36147
|
+
item.id ?? `${item.role}-${index2}`
|
|
36148
|
+
)
|
|
36149
|
+
),
|
|
36150
|
+
typingIdentity ? /* @__PURE__ */ jsx(
|
|
36151
|
+
TeamTypingRow,
|
|
36152
|
+
{
|
|
36153
|
+
label: teamTypingLabel(isNo, typingIdentity.name),
|
|
36154
|
+
avatarUrl: typingIdentity.avatarUrl
|
|
36155
|
+
}
|
|
36156
|
+
) : null
|
|
36157
|
+
] })
|
|
35729
36158
|
}
|
|
35730
36159
|
),
|
|
35731
|
-
/* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-shell", children: [
|
|
36160
|
+
/* @__PURE__ */ jsxs("div", { className: "setto-chat__composer-shell", ref: composerShellRef, children: [
|
|
35732
36161
|
/* @__PURE__ */ jsx(
|
|
35733
36162
|
SiteChatComposer,
|
|
35734
36163
|
{
|
|
35735
36164
|
value: draft,
|
|
35736
|
-
onChange:
|
|
36165
|
+
onChange: (value) => {
|
|
36166
|
+
setDraft(value);
|
|
36167
|
+
typingSender().update(value);
|
|
36168
|
+
},
|
|
35737
36169
|
onSubmit: queueOrSend,
|
|
36170
|
+
onBlur: () => typingSender().stop(),
|
|
35738
36171
|
busy: sending,
|
|
35739
36172
|
queued,
|
|
35740
36173
|
placeholder: humanJoined ? labels.humanPlaceholder : labels.placeholder,
|
|
@@ -35799,8 +36232,9 @@ function useChatStyles() {
|
|
|
35799
36232
|
.setto-chat{position:fixed;right:max(18px,env(safe-area-inset-right));bottom:max(18px,env(safe-area-inset-bottom));z-index:2147483000;overscroll-behavior:contain;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:0;right:auto;bottom:auto;left:0;width:100vw;height:var(--setto-chat-layout-height,100lvh);overflow:hidden;background:var(--setto-chat-canvas);opacity:0;transition:opacity ${CLOSE_MS}ms ease}.setto-chat--visible{opacity:1}.setto-chat__shell{position:absolute;top:var(--setto-chat-top,0);left:0;width:100%;height:var(--setto-chat-height,100svh);display:flex;overflow:hidden;overscroll-behavior:contain;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}
|
|
35800
36233
|
.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)}
|
|
35801
36234
|
.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:absolute;inset:0 0 auto;z-index:30;min-height:58px;padding:.35rem .75rem;display:flex;align-items:center;gap:.5rem;background:linear-gradient(to bottom,color-mix(in srgb,var(--setto-chat-crema) 88%,transparent),color-mix(in srgb,var(--setto-chat-crema) 72%,transparent));border-bottom:1px solid color-mix(in srgb,var(--setto-chat-ink) 12%,transparent);backdrop-filter:blur(20px) saturate(150%);-webkit-backdrop-filter:blur(20px) saturate(150%);isolation:isolate}.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}
|
|
35802
|
-
.setto-chat__body{position:relative;display:flex;flex:1 1 auto;min-height:0;height:100%;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:calc(58px + 1.3rem) .75rem 8rem;scroll-behavior:auto;-webkit-overflow-scrolling:touch}.setto-chat__message-list{display:flex;flex-direction:column}.setto-chat__message{--setto-chat-bubble:color-mix(in srgb,var(--setto-chat-ink) 7%,var(--setto-chat-canvas));display:flex;align-items:flex-end;gap:.55rem;width:100%;margin-bottom:1rem;color:color-mix(in srgb,var(--setto-chat-ink) 92%,transparent);font-size:16px;line-height:1.5;overflow-wrap:anywhere}.setto-chat__message-stack{display:flex;min-width:0;max-width:min(78%,36rem);flex-direction:column;align-items:flex-start}.setto-chat__message-bubble{position:relative;min-width:2.5rem;padding:.58rem .82rem;border-radius:19px 19px 19px 5px;background:var(--setto-chat-bubble);white-space:pre-wrap}.setto-chat__message--assistant .setto-chat__message-bubble::before,.setto-chat__message--human .setto-chat__message-bubble::before{content:"";position:absolute;z-index:0;left:-7px;bottom:0;width:16px;height:17px;border-bottom-right-radius:14px;background:var(--setto-chat-bubble)}.setto-chat__message--assistant .setto-chat__message-bubble::after,.setto-chat__message--human .setto-chat__message-bubble::after{content:"";position:absolute;z-index:1;left:-10px;bottom:0;width:10px;height:18px;border-bottom-right-radius:10px;background:var(--setto-chat-canvas)}.setto-chat__message-copy,.setto-chat__image{position:relative;z-index:2}.setto-chat__message--visitor{--setto-chat-bubble:#0a84ff;justify-content:flex-end;color:#fff}.setto-chat__message--visitor .setto-chat__message-stack{align-items:flex-end}.setto-chat__message--visitor .setto-chat__message-bubble{border-radius:19px 19px 5px 19px}.setto-chat__message--visitor .setto-chat__message-bubble::before{content:"";position:absolute;z-index:0;right:-7px;bottom:0;width:16px;height:17px;border-bottom-left-radius:14px;background:var(--setto-chat-bubble)}.setto-chat__message--visitor .setto-chat__message-bubble::after{content:"";position:absolute;z-index:1;right:-10px;bottom:0;width:10px;height:18px;border-bottom-left-radius:10px;background:var(--setto-chat-canvas)}.setto-chat__message--system{justify-content:center;color:color-mix(in srgb,var(--setto-chat-ink) 55%,transparent);font-size:.78rem;line-height:1.45;text-align:center}.setto-chat__message--system .setto-chat__message-stack{max-width:85%;align-items:center}.setto-chat__message--system .setto-chat__message-bubble{padding:.25rem .6rem;background:transparent}.setto-chat__avatar{position:relative;z-index:3;display:grid;place-items:center;flex:0 0 34px;width:34px;height:34px;margin-bottom:1px;overflow:hidden;border:2px solid var(--setto-chat-canvas);border-radius:50%;background:var(--setto-chat-crema);color:var(--setto-chat-ink);box-shadow:0 2px 8px color-mix(in srgb,var(--setto-chat-ink) 14%,transparent)}.setto-chat__avatar--assistant{background:var(--setto-chat-accent);color:var(--setto-chat-raised)}.setto-chat__avatar img{display:block;width:100%;height:100%;object-fit:cover}.setto-chat__speaker{display:block;margin:0 0 .2rem .4rem;color:color-mix(in srgb,var(--setto-chat-ink) 52%,transparent);font-size:.69rem;font-weight:500;line-height:1.2}.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:13px}.setto-chat__image img{display:block;width:min(280px,100%);max-height:260px;object-fit:cover}
|
|
36235
|
+
.setto-chat__body{position:relative;display:flex;flex:1 1 auto;min-height:0;height:100%;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:calc(58px + 1.3rem) .75rem var(--setto-chat-composer-space,8rem);scroll-padding-bottom:var(--setto-chat-composer-space,8rem);scroll-behavior:auto;-webkit-overflow-scrolling:touch}.setto-chat__message-list{display:flex;flex-direction:column}.setto-chat__message{--setto-chat-bubble:color-mix(in srgb,var(--setto-chat-ink) 7%,var(--setto-chat-canvas));display:flex;align-items:flex-end;gap:.55rem;width:100%;margin-bottom:1rem;color:color-mix(in srgb,var(--setto-chat-ink) 92%,transparent);font-size:16px;line-height:1.5;overflow-wrap:anywhere}.setto-chat__message-stack{display:flex;min-width:0;max-width:min(78%,36rem);flex-direction:column;align-items:flex-start}.setto-chat__message-bubble{position:relative;min-width:2.5rem;padding:.58rem .82rem;border-radius:19px 19px 19px 5px;background:var(--setto-chat-bubble);white-space:pre-wrap}.setto-chat__message--assistant .setto-chat__message-bubble::before,.setto-chat__message--human .setto-chat__message-bubble::before{content:"";position:absolute;z-index:0;left:-7px;bottom:0;width:16px;height:17px;border-bottom-right-radius:14px;background:var(--setto-chat-bubble)}.setto-chat__message--assistant .setto-chat__message-bubble::after,.setto-chat__message--human .setto-chat__message-bubble::after{content:"";position:absolute;z-index:1;left:-10px;bottom:0;width:10px;height:18px;border-bottom-right-radius:10px;background:var(--setto-chat-canvas)}.setto-chat__message-copy,.setto-chat__image{position:relative;z-index:2}.setto-chat__message--visitor{--setto-chat-bubble:#0a84ff;justify-content:flex-end;color:#fff}.setto-chat__message--visitor .setto-chat__message-stack{align-items:flex-end}.setto-chat__message--visitor .setto-chat__message-bubble{border-radius:19px 19px 5px 19px}.setto-chat__message--visitor .setto-chat__message-bubble::before{content:"";position:absolute;z-index:0;right:-7px;bottom:0;width:16px;height:17px;border-bottom-left-radius:14px;background:var(--setto-chat-bubble)}.setto-chat__message--visitor .setto-chat__message-bubble::after{content:"";position:absolute;z-index:1;right:-10px;bottom:0;width:10px;height:18px;border-bottom-left-radius:10px;background:var(--setto-chat-canvas)}.setto-chat__message--system{justify-content:center;color:color-mix(in srgb,var(--setto-chat-ink) 55%,transparent);font-size:.78rem;line-height:1.45;text-align:center}.setto-chat__message--system .setto-chat__message-stack{max-width:85%;align-items:center}.setto-chat__message--system .setto-chat__message-bubble{padding:.25rem .6rem;background:transparent}.setto-chat__avatar{position:relative;z-index:3;display:grid;place-items:center;flex:0 0 34px;width:34px;height:34px;margin-bottom:1px;overflow:hidden;border:2px solid var(--setto-chat-canvas);border-radius:50%;background:var(--setto-chat-crema);color:var(--setto-chat-ink);box-shadow:0 2px 8px color-mix(in srgb,var(--setto-chat-ink) 14%,transparent)}.setto-chat__avatar--assistant{background:var(--setto-chat-accent);color:var(--setto-chat-raised)}.setto-chat__avatar img{display:block;width:100%;height:100%;object-fit:cover}.setto-chat__speaker{display:block;margin:0 0 .2rem .4rem;color:color-mix(in srgb,var(--setto-chat-ink) 52%,transparent);font-size:.69rem;font-weight:500;line-height:1.2}.setto-chat__typing{display:inline-block;min-width:1.8rem;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:13px}.setto-chat__image img{display:block;width:min(280px,100%);max-height:260px;object-fit:cover}
|
|
35803
36236
|
.setto-chat__message--agent-joined{justify-content:flex-start;align-items:center;gap:.55rem;padding:.1rem .4rem;color:color-mix(in srgb,var(--setto-chat-ink) 62%,transparent);font-size:.8rem;line-height:1.4;text-align:left}.setto-chat__message--agent-joined .setto-chat__message-stack{max-width:calc(100% - 38px);align-items:flex-start}.setto-chat__message--agent-joined .setto-chat__message-bubble{min-width:0;padding:.2rem 0;background:transparent;white-space:normal}.setto-chat__avatar--joined{flex-basis:30px;width:30px;height:30px;margin:0;border-width:1px;box-shadow:0 2px 7px color-mix(in srgb,var(--setto-chat-ink) 12%,transparent)}
|
|
36237
|
+
.setto-chat__receipt{display:block;margin:.26rem .45rem 0 0;color:color-mix(in srgb,var(--setto-chat-ink) 52%,transparent);font-size:.69rem;font-weight:500;line-height:1.2}.setto-chat__message--typing{animation:setto-chat-typing-in 180ms cubic-bezier(.2,.8,.2,1) both}.setto-chat__message--typing .setto-chat__message-bubble{min-width:3.25rem;text-align:center}.setto-chat__message--typing .setto-chat__typing{font-weight:700;letter-spacing:.06em}@keyframes setto-chat-typing-in{from{opacity:0;transform:translateY(4px)}}@media(prefers-reduced-motion:reduce){.setto-chat__message--typing{animation:none}}
|
|
35804
36238
|
.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;background:linear-gradient(to bottom,transparent 0%,color-mix(in srgb,var(--setto-chat-canvas) 82%,transparent) 46%,var(--setto-chat-canvas) 66%);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:2px 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,.setto-chat__composer textarea:hover,.setto-chat__composer textarea:focus,.setto-chat__composer textarea:focus-visible{width:100%;min-height:36px;max-height:200px;padding:.45rem .25rem;resize:none;-webkit-appearance:none!important;appearance:none!important;border:0!important;border-radius:0!important;outline:0!important;box-shadow:none!important;background:transparent!important;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;background:var(--setto-chat-canvas)}
|
|
35805
36239
|
.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 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}}
|
|
35806
36240
|
@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__log{padding-left:.75rem;padding-right:.75rem}}@media(min-width:701px){.setto-chat--mounted{top:auto;right:max(18px,env(safe-area-inset-right));bottom:max(18px,env(safe-area-inset-bottom));left:auto;width:min(440px,calc(100vw - 36px));height:min(720px,calc(100dvh - 36px));overflow:visible;background:transparent}.setto-chat__shell{position:relative;top:auto;left:auto;width:100%;height:100%;border:1px solid color-mix(in srgb,var(--setto-chat-ink) 14%,transparent);border-radius:26px;background:var(--setto-chat-canvas);box-shadow:0 28px 80px -24px color-mix(in srgb,var(--setto-chat-ink) 42%,transparent),0 8px 24px -12px color-mix(in srgb,var(--setto-chat-ink) 24%,transparent);transform:translateY(16px) scale(.975);transform-origin:right bottom}.setto-chat--visible .setto-chat__shell{transform:none}.setto-chat__chats{position:absolute;inset:0 auto 0 0;width:min(82%,280px);height:100%;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__header,.setto-chat__log{padding-left:.9rem;padding-right:.9rem}.setto-chat__composer-shell{padding-left:.9rem;padding-right:.9rem}.setto-chat__composer-floor{width:calc(100% + 1.8rem);height:.45rem;margin-left:-.9rem}.setto-chat__message{font-size:15.5px}.setto-chat__message-stack{max-width:82%}}@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}}
|
|
@@ -36954,19 +37388,19 @@ function readLocaleFields(i18n, lng, formId, ns = "translation") {
|
|
|
36954
37388
|
for (const itemKey of collectKeysUnder(bundle, prefix)) {
|
|
36955
37389
|
const item = list2[itemKey];
|
|
36956
37390
|
if (!item || typeof item !== "object") continue;
|
|
36957
|
-
const
|
|
36958
|
-
const name2 = readString(
|
|
37391
|
+
const record2 = item;
|
|
37392
|
+
const name2 = readString(record2, "name").trim();
|
|
36959
37393
|
if (!name2) continue;
|
|
36960
|
-
const type = isFormFieldType(
|
|
37394
|
+
const type = isFormFieldType(record2.type) ? record2.type : "text";
|
|
36961
37395
|
fields.push({
|
|
36962
37396
|
itemKey,
|
|
36963
37397
|
name: name2,
|
|
36964
37398
|
type,
|
|
36965
37399
|
labelKey: `${prefix}.${itemKey}.label`,
|
|
36966
|
-
label: readString(
|
|
36967
|
-
placeholder: readString(
|
|
36968
|
-
required: readString(
|
|
36969
|
-
options: parseOptions(readString(
|
|
37400
|
+
label: readString(record2, "label"),
|
|
37401
|
+
placeholder: readString(record2, "placeholder"),
|
|
37402
|
+
required: readString(record2, "required") === "true",
|
|
37403
|
+
options: parseOptions(readString(record2, "options"))
|
|
36970
37404
|
});
|
|
36971
37405
|
}
|
|
36972
37406
|
return fields;
|