@webless/agent 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-SVWXFDV3.js → chunk-E4JSTYPX.js} +881 -212
- package/dist/chunk-E4JSTYPX.js.map +1 -0
- package/dist/embed.cjs +891 -226
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +244 -64
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +2 -2
- package/dist/embed.d.ts +2 -2
- package/dist/embed.js +5 -2
- package/dist/embed.js.map +1 -1
- package/dist/index.cjs +39 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -2
- package/dist/index.js.map +1 -1
- package/dist/{manifest-C8l7WUf6.d.cts → manifest-Zln88OPl.d.cts} +4 -1
- package/dist/{manifest-C8l7WUf6.d.ts → manifest-Zln88OPl.d.ts} +4 -1
- package/dist/react.cjs +883 -214
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +244 -64
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +37 -4
- package/dist/react.d.ts +37 -4
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-SVWXFDV3.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -34,7 +34,7 @@ __export(react_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(react_exports);
|
|
35
35
|
|
|
36
36
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
37
|
-
var
|
|
37
|
+
var import_react7 = require("react");
|
|
38
38
|
|
|
39
39
|
// src/react/page-shift.ts
|
|
40
40
|
var import_react = require("react");
|
|
@@ -303,6 +303,9 @@ function runtimeSessionIdKey(visitorSessionId, prefix) {
|
|
|
303
303
|
function runtimeStreamIndexKey(visitorSessionId, prefix) {
|
|
304
304
|
return `${prefix}:eve:${visitorSessionId}:streamIndex`;
|
|
305
305
|
}
|
|
306
|
+
function runtimeLastMessageKey(visitorSessionId, prefix) {
|
|
307
|
+
return `${prefix}:eve:${visitorSessionId}:lastMessage`;
|
|
308
|
+
}
|
|
306
309
|
function loadPersistedAgentSession(visitorSessionId, options) {
|
|
307
310
|
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
|
|
308
311
|
const prefix = resolvePrefix(options);
|
|
@@ -310,9 +313,11 @@ function loadPersistedAgentSession(visitorSessionId, options) {
|
|
|
310
313
|
if (!sessionId) return null;
|
|
311
314
|
const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
312
315
|
const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
|
|
316
|
+
const lastMessage = sessionStorage.getItem(runtimeLastMessageKey(visitorSessionId, prefix))?.trim();
|
|
313
317
|
return {
|
|
314
318
|
sessionId,
|
|
315
|
-
streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0
|
|
319
|
+
streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0,
|
|
320
|
+
...lastMessage ? { lastMessage } : {}
|
|
316
321
|
};
|
|
317
322
|
}
|
|
318
323
|
function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
|
|
@@ -326,11 +331,19 @@ function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, opt
|
|
|
326
331
|
String(Math.max(0, streamIndex))
|
|
327
332
|
);
|
|
328
333
|
}
|
|
334
|
+
function savePersistedAgentTurnMessage(visitorSessionId, message, options) {
|
|
335
|
+
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !message) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const prefix = resolvePrefix(options);
|
|
339
|
+
sessionStorage.setItem(runtimeLastMessageKey(visitorSessionId, prefix), message);
|
|
340
|
+
}
|
|
329
341
|
function clearPersistedAgentSession(visitorSessionId, options) {
|
|
330
342
|
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
|
|
331
343
|
const prefix = resolvePrefix(options);
|
|
332
344
|
sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
|
|
333
345
|
sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
346
|
+
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
334
347
|
}
|
|
335
348
|
|
|
336
349
|
// src/runtime/client.ts
|
|
@@ -345,6 +358,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
345
358
|
if (event.type === "message.completed") {
|
|
346
359
|
handlers.onComplete?.();
|
|
347
360
|
}
|
|
361
|
+
if (event.type === "action.result") {
|
|
362
|
+
const result = event.data.result;
|
|
363
|
+
if (result && typeof result === "object" && "output" in result) {
|
|
364
|
+
handlers.onActionResult?.(result.output);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
348
367
|
if (event.type !== "message.appended") return rendered;
|
|
349
368
|
const { messageDelta, messageSoFar } = event.data;
|
|
350
369
|
let delta = messageDelta;
|
|
@@ -358,6 +377,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
358
377
|
if (delta) handlers.onDelta(delta);
|
|
359
378
|
return next;
|
|
360
379
|
}
|
|
380
|
+
function isResumeTurnMessage(received, candidate) {
|
|
381
|
+
if (received === candidate) return true;
|
|
382
|
+
return Boolean(candidate) && received.endsWith(`
|
|
383
|
+
|
|
384
|
+
${candidate}`);
|
|
385
|
+
}
|
|
361
386
|
function latestTurnEvents(events) {
|
|
362
387
|
let startIndex = -1;
|
|
363
388
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
@@ -589,6 +614,11 @@ var AgentSession = class {
|
|
|
589
614
|
this.session = session;
|
|
590
615
|
try {
|
|
591
616
|
const activeSession = session;
|
|
617
|
+
savePersistedAgentTurnMessage(
|
|
618
|
+
this.visitorSessionId,
|
|
619
|
+
message,
|
|
620
|
+
this.storeOptions
|
|
621
|
+
);
|
|
592
622
|
response = await withCapabilityRefresh(
|
|
593
623
|
this.capability,
|
|
594
624
|
() => activeSession.send(message, { signal })
|
|
@@ -604,6 +634,11 @@ var AgentSession = class {
|
|
|
604
634
|
}
|
|
605
635
|
}
|
|
606
636
|
if (!response) {
|
|
637
|
+
savePersistedAgentTurnMessage(
|
|
638
|
+
this.visitorSessionId,
|
|
639
|
+
message,
|
|
640
|
+
this.storeOptions
|
|
641
|
+
);
|
|
607
642
|
const created = await withCapabilityRefresh(
|
|
608
643
|
this.capability,
|
|
609
644
|
() => client.sessions.create({ message, signal })
|
|
@@ -658,7 +693,9 @@ var AgentSession = class {
|
|
|
658
693
|
);
|
|
659
694
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
660
695
|
const received = turnEvents[0];
|
|
661
|
-
|
|
696
|
+
const lastSent = persisted.lastMessage;
|
|
697
|
+
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
698
|
+
if (received?.type !== "message.received" || !(isResumeTurnMessage(received.data.message, message) || inFlight && lastSent !== void 0 && received.data.message === lastSent)) {
|
|
662
699
|
return null;
|
|
663
700
|
}
|
|
664
701
|
let rendered = renderTurn(turnEvents);
|
|
@@ -822,6 +859,246 @@ function formatAgentError(error) {
|
|
|
822
859
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
823
860
|
}
|
|
824
861
|
|
|
862
|
+
// src/react/lib/tool-card.ts
|
|
863
|
+
function bookingOfferIdentityKey(offer) {
|
|
864
|
+
const eventTypes = offer.eventTypes.map(
|
|
865
|
+
(item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
|
|
866
|
+
).join("|");
|
|
867
|
+
const slots = offer.slots.map((slot) => `${slot.eventTypeUri ?? ""}:${slot.startTime}`).join("|");
|
|
868
|
+
return `${eventTypes}::${slots}` || "offer";
|
|
869
|
+
}
|
|
870
|
+
var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
|
|
871
|
+
function asRecord(value) {
|
|
872
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
873
|
+
}
|
|
874
|
+
function asString(value) {
|
|
875
|
+
return typeof value === "string" ? value.trim() : "";
|
|
876
|
+
}
|
|
877
|
+
function isEventUri(value) {
|
|
878
|
+
return /^https:\/\/api\.calendly\.com\/scheduled_events\/[^/]+$/i.test(value);
|
|
879
|
+
}
|
|
880
|
+
function isEventTypeUri(value) {
|
|
881
|
+
return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
|
|
882
|
+
}
|
|
883
|
+
function parseToolCard(value) {
|
|
884
|
+
const record = asRecord(value);
|
|
885
|
+
if (!record) return null;
|
|
886
|
+
if (record.booking_offer && asString(record.type) !== "booking_offer") {
|
|
887
|
+
const nested = parseToolCard(record.booking_offer);
|
|
888
|
+
if (nested) return nested;
|
|
889
|
+
}
|
|
890
|
+
const type = asString(record.type);
|
|
891
|
+
if (type === "booking_offer") {
|
|
892
|
+
const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
|
|
893
|
+
const entry = asRecord(item);
|
|
894
|
+
const uri = asString(entry?.uri);
|
|
895
|
+
if (!entry || !isEventTypeUri(uri)) return [];
|
|
896
|
+
const duration = entry.duration;
|
|
897
|
+
const locationKind = asString(entry.locationKind);
|
|
898
|
+
const location = asString(entry.location);
|
|
899
|
+
return [
|
|
900
|
+
{
|
|
901
|
+
name: asString(entry.name) || "Meeting",
|
|
902
|
+
uri,
|
|
903
|
+
...typeof duration === "number" ? { duration } : {},
|
|
904
|
+
...locationKind ? { locationKind } : {},
|
|
905
|
+
...location ? { location } : {}
|
|
906
|
+
}
|
|
907
|
+
];
|
|
908
|
+
}) : [];
|
|
909
|
+
const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
|
|
910
|
+
const entry = asRecord(item);
|
|
911
|
+
const startTime = asString(entry?.startTime);
|
|
912
|
+
if (!entry || !startTime) return [];
|
|
913
|
+
const eventTypeUri = asString(entry.eventTypeUri);
|
|
914
|
+
return [
|
|
915
|
+
{
|
|
916
|
+
startTime,
|
|
917
|
+
...isEventTypeUri(eventTypeUri) ? { eventTypeUri } : {}
|
|
918
|
+
}
|
|
919
|
+
];
|
|
920
|
+
}) : [];
|
|
921
|
+
if (slots.length === 0) return null;
|
|
922
|
+
return { type: "booking_offer", eventTypes, slots };
|
|
923
|
+
}
|
|
924
|
+
if (type === "booking_confirmed") {
|
|
925
|
+
const eventUri = asString(record.eventUri);
|
|
926
|
+
if (!isEventUri(eventUri)) return null;
|
|
927
|
+
const inviteeUri = asString(record.inviteeUri);
|
|
928
|
+
const inviteeEmail = asString(record.inviteeEmail);
|
|
929
|
+
const startTime = asString(record.startTime);
|
|
930
|
+
return {
|
|
931
|
+
type: "booking_confirmed",
|
|
932
|
+
eventUri,
|
|
933
|
+
...inviteeUri ? { inviteeUri } : {},
|
|
934
|
+
...inviteeEmail ? { inviteeEmail } : {},
|
|
935
|
+
...startTime ? { startTime } : {}
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
if (type === "booking_canceled") {
|
|
939
|
+
const eventUri = asString(record.eventUri);
|
|
940
|
+
if (!isEventUri(eventUri)) return null;
|
|
941
|
+
return { type: "booking_canceled", eventUri };
|
|
942
|
+
}
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
function formatBookingOfferFence(offer) {
|
|
946
|
+
return [
|
|
947
|
+
"```webless-tool-card",
|
|
948
|
+
JSON.stringify({
|
|
949
|
+
type: "booking_offer",
|
|
950
|
+
eventTypes: offer.eventTypes,
|
|
951
|
+
slots: offer.slots
|
|
952
|
+
}),
|
|
953
|
+
"```"
|
|
954
|
+
].join("\n");
|
|
955
|
+
}
|
|
956
|
+
function bookingOfferFromActionOutput(output) {
|
|
957
|
+
const record = asRecord(output);
|
|
958
|
+
const data = asRecord(record?.data) ?? record;
|
|
959
|
+
const card = parseToolCard(data);
|
|
960
|
+
return card?.type === "booking_offer" ? card : null;
|
|
961
|
+
}
|
|
962
|
+
function ensureBookingOfferText(text, offer) {
|
|
963
|
+
if (!offer) return text;
|
|
964
|
+
if (extractToolCards(text).some((card) => card.type === "booking_offer")) {
|
|
965
|
+
return text;
|
|
966
|
+
}
|
|
967
|
+
const visible = stripToolCards(text).trim() || text.trim();
|
|
968
|
+
return `${visible}
|
|
969
|
+
|
|
970
|
+
${formatBookingOfferFence(offer)}`;
|
|
971
|
+
}
|
|
972
|
+
function hideToolCardFences(text) {
|
|
973
|
+
return text.replace(/```(?:webless-tool-card|json)\s*[\s\S]*?```/gi, "").replace(/```(?:webless-tool-card|json)[\s\S]*$/i, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
974
|
+
}
|
|
975
|
+
function visitorTimeZone() {
|
|
976
|
+
try {
|
|
977
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
978
|
+
} catch {
|
|
979
|
+
return "UTC";
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function extractToolCards(text) {
|
|
983
|
+
const cards = [];
|
|
984
|
+
for (const match of text.matchAll(FENCE_PATTERN)) {
|
|
985
|
+
try {
|
|
986
|
+
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
987
|
+
if (card) cards.push(card);
|
|
988
|
+
} catch {
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return cards;
|
|
992
|
+
}
|
|
993
|
+
function stripToolCards(text) {
|
|
994
|
+
return text.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
995
|
+
}
|
|
996
|
+
function localDateKey(date) {
|
|
997
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
998
|
+
return [
|
|
999
|
+
date.getFullYear(),
|
|
1000
|
+
String(date.getMonth() + 1).padStart(2, "0"),
|
|
1001
|
+
String(date.getDate()).padStart(2, "0")
|
|
1002
|
+
].join("-");
|
|
1003
|
+
}
|
|
1004
|
+
function slotDateKey(startTime) {
|
|
1005
|
+
return localDateKey(new Date(startTime)) || startTime;
|
|
1006
|
+
}
|
|
1007
|
+
function bookingSlotsForEventType(slots, eventTypeUri) {
|
|
1008
|
+
return slots.filter(
|
|
1009
|
+
(slot) => !eventTypeUri || !slot.eventTypeUri || slot.eventTypeUri === eventTypeUri
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
function firstAvailableBookingMonth(slots) {
|
|
1013
|
+
let earliest;
|
|
1014
|
+
for (const slot of slots) {
|
|
1015
|
+
const key = slotDateKey(slot.startTime);
|
|
1016
|
+
if (!earliest || key < earliest) earliest = key;
|
|
1017
|
+
}
|
|
1018
|
+
const [year, month] = (earliest ?? slotDateKey((/* @__PURE__ */ new Date()).toISOString())).split("-").map(Number);
|
|
1019
|
+
if (!year || !month) {
|
|
1020
|
+
const now = /* @__PURE__ */ new Date();
|
|
1021
|
+
return { year: now.getFullYear(), month: now.getMonth() };
|
|
1022
|
+
}
|
|
1023
|
+
return { year, month: month - 1 };
|
|
1024
|
+
}
|
|
1025
|
+
function formatMonthTitle(year, month) {
|
|
1026
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1027
|
+
month: "long",
|
|
1028
|
+
year: "numeric"
|
|
1029
|
+
}).format(new Date(year, month, 1));
|
|
1030
|
+
}
|
|
1031
|
+
function formatLongDate(startTime) {
|
|
1032
|
+
const date = new Date(startTime);
|
|
1033
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
1034
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1035
|
+
weekday: "long",
|
|
1036
|
+
month: "long",
|
|
1037
|
+
day: "numeric"
|
|
1038
|
+
}).format(date);
|
|
1039
|
+
}
|
|
1040
|
+
function weekdayLabels() {
|
|
1041
|
+
return Array.from(
|
|
1042
|
+
{ length: 7 },
|
|
1043
|
+
(_, index) => new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(
|
|
1044
|
+
new Date(2026, 7, 3 + index)
|
|
1045
|
+
)
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
function formatTimeChip(startTime) {
|
|
1049
|
+
const date = new Date(startTime);
|
|
1050
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
1051
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1052
|
+
hour: "numeric",
|
|
1053
|
+
minute: "2-digit"
|
|
1054
|
+
}).format(date);
|
|
1055
|
+
}
|
|
1056
|
+
function formatSlotTimeZone(startTime) {
|
|
1057
|
+
const date = new Date(startTime);
|
|
1058
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
1059
|
+
return new Intl.DateTimeFormat(void 0, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
1060
|
+
}
|
|
1061
|
+
function formatSlotLabel(startTime) {
|
|
1062
|
+
const date = new Date(startTime);
|
|
1063
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
1064
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1065
|
+
weekday: "short",
|
|
1066
|
+
month: "short",
|
|
1067
|
+
day: "numeric",
|
|
1068
|
+
hour: "numeric",
|
|
1069
|
+
minute: "2-digit",
|
|
1070
|
+
timeZoneName: "short"
|
|
1071
|
+
}).format(date);
|
|
1072
|
+
}
|
|
1073
|
+
function formatBookingRequest(input) {
|
|
1074
|
+
return [
|
|
1075
|
+
"Book this meeting now with CALENDLY_POST_INVITEE.",
|
|
1076
|
+
"Do not open a Calendly URL and do not list other scheduled events.",
|
|
1077
|
+
"Do not invent a location kind. Use only the location fields below.",
|
|
1078
|
+
`event_type: ${input.eventTypeUri}`,
|
|
1079
|
+
`start_time: ${input.startTime}`,
|
|
1080
|
+
`invitee.name: ${input.inviteeName}`,
|
|
1081
|
+
`invitee.email: ${input.inviteeEmail}`,
|
|
1082
|
+
`invitee.timezone: ${input.timezone}`,
|
|
1083
|
+
...input.locationKind ? [
|
|
1084
|
+
`location.kind: ${input.locationKind}`,
|
|
1085
|
+
...input.location ? [`location.location: ${input.location}`] : []
|
|
1086
|
+
] : ["Do not send a location field."],
|
|
1087
|
+
"After it succeeds, reply with one short confirmation and a webless-tool-card booking_confirmed block using visitor_booking."
|
|
1088
|
+
].join("\n");
|
|
1089
|
+
}
|
|
1090
|
+
function visitorBookingPrefix(booking) {
|
|
1091
|
+
return [
|
|
1092
|
+
"This visitor already booked a meeting. Use only this meeting:",
|
|
1093
|
+
`- scheduled event URI: ${booking.eventUri}`,
|
|
1094
|
+
...booking.inviteeUri ? [`- invitee URI: ${booking.inviteeUri}`] : [],
|
|
1095
|
+
...booking.inviteeEmail ? [`- invitee email: ${booking.inviteeEmail}`] : [],
|
|
1096
|
+
"For details call CALENDLY_GET_EVENT or CALENDLY_GET_EVENT_INVITEE with those URIs.",
|
|
1097
|
+
"If you must list events, pass this invitee_email. Never describe any other scheduled event.",
|
|
1098
|
+
"start_time values from Calendly are UTC."
|
|
1099
|
+
].join("\n");
|
|
1100
|
+
}
|
|
1101
|
+
|
|
825
1102
|
// src/react/persisted-conversation.ts
|
|
826
1103
|
var CONVERSATION_VERSION = 1;
|
|
827
1104
|
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
@@ -833,13 +1110,25 @@ function parseMessage(value) {
|
|
|
833
1110
|
if (typeof record.id !== "string" || record.role !== "agent" && record.role !== "visitor" || typeof record.text !== "string" || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt)) {
|
|
834
1111
|
return null;
|
|
835
1112
|
}
|
|
1113
|
+
if (record.role === "visitor") {
|
|
1114
|
+
return {
|
|
1115
|
+
id: record.id,
|
|
1116
|
+
role: "visitor",
|
|
1117
|
+
text: record.text,
|
|
1118
|
+
createdAt: record.createdAt,
|
|
1119
|
+
...typeof record.runtimeText === "string" && record.runtimeText ? { runtimeText: record.runtimeText } : {}
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
836
1122
|
return {
|
|
837
1123
|
id: record.id,
|
|
838
|
-
role:
|
|
1124
|
+
role: "agent",
|
|
839
1125
|
text: record.text,
|
|
840
1126
|
createdAt: record.createdAt
|
|
841
1127
|
};
|
|
842
1128
|
}
|
|
1129
|
+
function visitorTurnText(message) {
|
|
1130
|
+
return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
|
|
1131
|
+
}
|
|
843
1132
|
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
844
1133
|
if (typeof sessionStorage === "undefined") return null;
|
|
845
1134
|
const raw = sessionStorage.getItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
@@ -872,6 +1161,42 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
|
|
|
872
1161
|
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
873
1162
|
if (typeof sessionStorage === "undefined") return;
|
|
874
1163
|
sessionStorage.removeItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
1164
|
+
clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
|
|
1165
|
+
}
|
|
1166
|
+
function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
|
|
1167
|
+
return `${storageKeyPrefix}:pending-booking:${visitorSessionId}`;
|
|
1168
|
+
}
|
|
1169
|
+
function loadPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1170
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1171
|
+
const raw = sessionStorage.getItem(
|
|
1172
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId)
|
|
1173
|
+
);
|
|
1174
|
+
if (!raw) return null;
|
|
1175
|
+
try {
|
|
1176
|
+
const value = JSON.parse(raw);
|
|
1177
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1178
|
+
const record = value;
|
|
1179
|
+
if (typeof record.eventUri !== "string" || !record.eventUri) return null;
|
|
1180
|
+
return {
|
|
1181
|
+
eventUri: record.eventUri,
|
|
1182
|
+
...typeof record.inviteeUri === "string" && record.inviteeUri ? { inviteeUri: record.inviteeUri } : {},
|
|
1183
|
+
...typeof record.inviteeEmail === "string" && record.inviteeEmail ? { inviteeEmail: record.inviteeEmail } : {},
|
|
1184
|
+
...typeof record.startTime === "string" && record.startTime ? { startTime: record.startTime } : {}
|
|
1185
|
+
};
|
|
1186
|
+
} catch {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
|
|
1191
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1192
|
+
sessionStorage.setItem(
|
|
1193
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId),
|
|
1194
|
+
JSON.stringify(booking)
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1198
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1199
|
+
sessionStorage.removeItem(pendingBookingKey(storageKeyPrefix, visitorSessionId));
|
|
875
1200
|
}
|
|
876
1201
|
|
|
877
1202
|
// src/react/hooks/useAgentChat.ts
|
|
@@ -891,6 +1216,7 @@ function createInitialState(greeting = DEFAULT_GREETING) {
|
|
|
891
1216
|
journey: null,
|
|
892
1217
|
followUps: [],
|
|
893
1218
|
streamingText: "",
|
|
1219
|
+
pendingOffer: null,
|
|
894
1220
|
error: null
|
|
895
1221
|
};
|
|
896
1222
|
}
|
|
@@ -973,6 +1299,9 @@ function useAgentChat({
|
|
|
973
1299
|
initialState
|
|
974
1300
|
)
|
|
975
1301
|
);
|
|
1302
|
+
const pendingBookingRef = (0, import_react2.useRef)(
|
|
1303
|
+
loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
|
|
1304
|
+
);
|
|
976
1305
|
const runRef = (0, import_react2.useRef)(null);
|
|
977
1306
|
const clientRef = (0, import_react2.useRef)(
|
|
978
1307
|
createAgentClient({
|
|
@@ -1005,6 +1334,10 @@ function useAgentChat({
|
|
|
1005
1334
|
visitorSessionId: visitorId,
|
|
1006
1335
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
1007
1336
|
});
|
|
1337
|
+
pendingBookingRef.current = loadPendingWidgetBooking(
|
|
1338
|
+
resolvedStorageKeyPrefix,
|
|
1339
|
+
visitorId
|
|
1340
|
+
);
|
|
1008
1341
|
setState(
|
|
1009
1342
|
stateFromConversation(
|
|
1010
1343
|
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
|
|
@@ -1040,6 +1373,7 @@ function useAgentChat({
|
|
|
1040
1373
|
runRef.current?.abort();
|
|
1041
1374
|
runRef.current = null;
|
|
1042
1375
|
clientRef.current.reset();
|
|
1376
|
+
pendingBookingRef.current = null;
|
|
1043
1377
|
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
1044
1378
|
setState(initialState);
|
|
1045
1379
|
}, [initialState, resolvedStorageKeyPrefix, visitorId]);
|
|
@@ -1051,6 +1385,7 @@ function useAgentChat({
|
|
|
1051
1385
|
try {
|
|
1052
1386
|
let streamStarted = Boolean(initialText);
|
|
1053
1387
|
let streamed = initialText;
|
|
1388
|
+
const capturedOffers = [];
|
|
1054
1389
|
const handlers = {
|
|
1055
1390
|
onWork: (item) => {
|
|
1056
1391
|
if (!isActiveRun()) return;
|
|
@@ -1060,6 +1395,12 @@ function useAgentChat({
|
|
|
1060
1395
|
toolSteps: upsertToolStep(prev.toolSteps, item)
|
|
1061
1396
|
}));
|
|
1062
1397
|
},
|
|
1398
|
+
onActionResult: (output) => {
|
|
1399
|
+
const offer = bookingOfferFromActionOutput(output);
|
|
1400
|
+
if (!offer) return;
|
|
1401
|
+
capturedOffers.push(offer);
|
|
1402
|
+
setState((prev) => ({ ...prev, pendingOffer: offer }));
|
|
1403
|
+
},
|
|
1063
1404
|
onDelta: (delta) => {
|
|
1064
1405
|
if (!isActiveRun()) return;
|
|
1065
1406
|
if (!streamStarted) {
|
|
@@ -1075,7 +1416,8 @@ function useAgentChat({
|
|
|
1075
1416
|
setState((prev) => ({
|
|
1076
1417
|
...prev,
|
|
1077
1418
|
phase: "streaming",
|
|
1078
|
-
streamingText: streamed
|
|
1419
|
+
streamingText: hideToolCardFences(streamed),
|
|
1420
|
+
pendingOffer: prev.pendingOffer ?? capturedOffers.at(-1) ?? null
|
|
1079
1421
|
}));
|
|
1080
1422
|
},
|
|
1081
1423
|
onComplete: () => {
|
|
@@ -1096,18 +1438,34 @@ function useAgentChat({
|
|
|
1096
1438
|
});
|
|
1097
1439
|
}
|
|
1098
1440
|
if (!isActiveRun() || finalText === null) return;
|
|
1441
|
+
const displayText = ensureBookingOfferText(
|
|
1442
|
+
finalText,
|
|
1443
|
+
capturedOffers.at(-1) ?? null
|
|
1444
|
+
);
|
|
1099
1445
|
const agentMessage = {
|
|
1100
1446
|
id: `agent-${Date.now()}`,
|
|
1101
1447
|
role: "agent",
|
|
1102
|
-
text:
|
|
1448
|
+
text: displayText,
|
|
1103
1449
|
createdAt: Date.now()
|
|
1104
1450
|
};
|
|
1451
|
+
const parsedCards = extractToolCards(displayText);
|
|
1452
|
+
for (const card of parsedCards) {
|
|
1453
|
+
if (card.type === "booking_confirmed") {
|
|
1454
|
+
pendingBookingRef.current = card;
|
|
1455
|
+
savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, card);
|
|
1456
|
+
}
|
|
1457
|
+
if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
|
|
1458
|
+
pendingBookingRef.current = null;
|
|
1459
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1105
1462
|
setState((prev) => ({
|
|
1106
1463
|
...prev,
|
|
1107
1464
|
phase: "complete",
|
|
1108
1465
|
messages: [...prev.messages, agentMessage],
|
|
1109
1466
|
toolSteps: completeActivePlanning(prev.toolSteps),
|
|
1110
1467
|
streamingText: "",
|
|
1468
|
+
pendingOffer: null,
|
|
1111
1469
|
followUps: [],
|
|
1112
1470
|
journey: null
|
|
1113
1471
|
}));
|
|
@@ -1129,26 +1487,54 @@ function useAgentChat({
|
|
|
1129
1487
|
} : step
|
|
1130
1488
|
),
|
|
1131
1489
|
streamingText: "",
|
|
1490
|
+
pendingOffer: null,
|
|
1132
1491
|
error: message
|
|
1133
1492
|
}));
|
|
1134
1493
|
runRef.current = null;
|
|
1135
1494
|
}
|
|
1136
1495
|
},
|
|
1137
|
-
[]
|
|
1496
|
+
[resolvedStorageKeyPrefix, visitorId]
|
|
1497
|
+
);
|
|
1498
|
+
const rememberBooking = (0, import_react2.useCallback)(
|
|
1499
|
+
(booking) => {
|
|
1500
|
+
const current = pendingBookingRef.current;
|
|
1501
|
+
if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
pendingBookingRef.current = booking;
|
|
1505
|
+
savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, booking);
|
|
1506
|
+
},
|
|
1507
|
+
[resolvedStorageKeyPrefix, visitorId]
|
|
1508
|
+
);
|
|
1509
|
+
const forgetBooking = (0, import_react2.useCallback)(
|
|
1510
|
+
(eventUri) => {
|
|
1511
|
+
const current = pendingBookingRef.current;
|
|
1512
|
+
if (!current) return;
|
|
1513
|
+
if (eventUri && current.eventUri !== eventUri) return;
|
|
1514
|
+
pendingBookingRef.current = null;
|
|
1515
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
1516
|
+
},
|
|
1517
|
+
[resolvedStorageKeyPrefix, visitorId]
|
|
1138
1518
|
);
|
|
1139
1519
|
const submit = (0, import_react2.useCallback)(
|
|
1140
|
-
async (visitorText) => {
|
|
1520
|
+
async (visitorText, options) => {
|
|
1141
1521
|
if (runRef.current) {
|
|
1142
1522
|
runRef.current.abort();
|
|
1143
1523
|
clientRef.current.cancelActive();
|
|
1144
1524
|
}
|
|
1145
1525
|
const controller = new AbortController();
|
|
1146
1526
|
runRef.current = controller;
|
|
1527
|
+
const booking = pendingBookingRef.current;
|
|
1528
|
+
const outgoing = options?.runtimeText ?? visitorText;
|
|
1529
|
+
const runtimeText = booking ? `${visitorBookingPrefix(booking)}
|
|
1530
|
+
|
|
1531
|
+
${outgoing}` : outgoing;
|
|
1147
1532
|
const visitorMessage = {
|
|
1148
1533
|
id: `visitor-${Date.now()}`,
|
|
1149
1534
|
role: "visitor",
|
|
1150
1535
|
text: visitorText,
|
|
1151
|
-
createdAt: Date.now()
|
|
1536
|
+
createdAt: Date.now(),
|
|
1537
|
+
...runtimeText !== visitorText ? { runtimeText } : {}
|
|
1152
1538
|
};
|
|
1153
1539
|
setState((prev) => ({
|
|
1154
1540
|
...prev,
|
|
@@ -1165,9 +1551,10 @@ function useAgentChat({
|
|
|
1165
1551
|
journey: null,
|
|
1166
1552
|
followUps: [],
|
|
1167
1553
|
streamingText: "",
|
|
1554
|
+
pendingOffer: null,
|
|
1168
1555
|
error: null
|
|
1169
1556
|
}));
|
|
1170
|
-
await runTurn({ controller, resume: false, visitorText });
|
|
1557
|
+
await runTurn({ controller, resume: false, visitorText: runtimeText });
|
|
1171
1558
|
},
|
|
1172
1559
|
[runTurn]
|
|
1173
1560
|
);
|
|
@@ -1194,12 +1581,13 @@ function useAgentChat({
|
|
|
1194
1581
|
journey: null,
|
|
1195
1582
|
followUps: [],
|
|
1196
1583
|
streamingText: "",
|
|
1584
|
+
pendingOffer: null,
|
|
1197
1585
|
error: null
|
|
1198
1586
|
}));
|
|
1199
1587
|
await runTurn({
|
|
1200
1588
|
controller,
|
|
1201
1589
|
resume: false,
|
|
1202
|
-
visitorText: visitorMessage
|
|
1590
|
+
visitorText: visitorTurnText(visitorMessage)
|
|
1203
1591
|
});
|
|
1204
1592
|
}, [runTurn, state.messages]);
|
|
1205
1593
|
(0, import_react2.useEffect)(() => {
|
|
@@ -1216,7 +1604,7 @@ function useAgentChat({
|
|
|
1216
1604
|
controller,
|
|
1217
1605
|
initialText: conversation.streamingText,
|
|
1218
1606
|
resume: true,
|
|
1219
|
-
visitorText: visitorMessage
|
|
1607
|
+
visitorText: visitorTurnText(visitorMessage)
|
|
1220
1608
|
});
|
|
1221
1609
|
return () => {
|
|
1222
1610
|
if (runRef.current === controller) {
|
|
@@ -1236,6 +1624,8 @@ function useAgentChat({
|
|
|
1236
1624
|
reset,
|
|
1237
1625
|
retry,
|
|
1238
1626
|
submit,
|
|
1627
|
+
rememberBooking,
|
|
1628
|
+
forgetBooking,
|
|
1239
1629
|
visitorSessionId: visitorId,
|
|
1240
1630
|
sessionId: clientRef.current.getActiveSessionId()
|
|
1241
1631
|
};
|
|
@@ -1297,7 +1687,7 @@ function unregisterAgentPanelController(customerId) {
|
|
|
1297
1687
|
}
|
|
1298
1688
|
|
|
1299
1689
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1300
|
-
var
|
|
1690
|
+
var import_react6 = require("react");
|
|
1301
1691
|
|
|
1302
1692
|
// src/react/types/conversation.ts
|
|
1303
1693
|
var defaultAgentRailTheme = {
|
|
@@ -1337,10 +1727,9 @@ function workSummary(steps, failed, brandLabel) {
|
|
|
1337
1727
|
);
|
|
1338
1728
|
if (hasError) return "Answered with available information";
|
|
1339
1729
|
if (specialists.length > 1)
|
|
1340
|
-
return `
|
|
1341
|
-
if (specialists.length === 1)
|
|
1342
|
-
|
|
1343
|
-
if (searched) return "Answer prepared from this site";
|
|
1730
|
+
return `Consulted ${specialists.length} specialists`;
|
|
1731
|
+
if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
|
|
1732
|
+
if (searched) return "Searched this site";
|
|
1344
1733
|
return "Answer ready";
|
|
1345
1734
|
}
|
|
1346
1735
|
function stepLabel(step, brandLabel) {
|
|
@@ -1390,7 +1779,6 @@ function AgentActivityBubble({
|
|
|
1390
1779
|
steps
|
|
1391
1780
|
}) {
|
|
1392
1781
|
const active = steps.some((step) => step.state === "active");
|
|
1393
|
-
const delegated = steps.some((step) => step.kind === "specialist");
|
|
1394
1782
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
|
|
1395
1783
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
|
|
1396
1784
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1402,57 +1790,50 @@ function AgentActivityBubble({
|
|
|
1402
1790
|
),
|
|
1403
1791
|
workSummary(steps, failed, brandLabel)
|
|
1404
1792
|
] }),
|
|
1405
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1406
|
-
"details",
|
|
1407
|
-
{
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
},
|
|
1450
|
-
step.id
|
|
1451
|
-
);
|
|
1452
|
-
}) })
|
|
1453
|
-
]
|
|
1454
|
-
}
|
|
1455
|
-
)
|
|
1793
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
|
|
1795
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
|
|
1796
|
+
const detail = stepDetail(step, steps);
|
|
1797
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1798
|
+
"li",
|
|
1799
|
+
{
|
|
1800
|
+
className: "agent-activity-bubble__step",
|
|
1801
|
+
"data-kind": step.kind,
|
|
1802
|
+
"data-state": step.state,
|
|
1803
|
+
children: [
|
|
1804
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1805
|
+
"span",
|
|
1806
|
+
{
|
|
1807
|
+
className: "agent-activity-bubble__step-icon",
|
|
1808
|
+
"aria-hidden": "true",
|
|
1809
|
+
children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
|
|
1811
|
+
brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1812
|
+
"img",
|
|
1813
|
+
{
|
|
1814
|
+
src: brandLogoUrl,
|
|
1815
|
+
alt: "",
|
|
1816
|
+
onError: (event) => {
|
|
1817
|
+
event.currentTarget.hidden = true;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
) : null
|
|
1821
|
+
] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
|
|
1822
|
+
}
|
|
1823
|
+
),
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
|
|
1825
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
|
|
1826
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }),
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
|
|
1828
|
+
] }),
|
|
1829
|
+
detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
|
|
1830
|
+
] })
|
|
1831
|
+
]
|
|
1832
|
+
},
|
|
1833
|
+
step.id
|
|
1834
|
+
);
|
|
1835
|
+
}) })
|
|
1836
|
+
] })
|
|
1456
1837
|
] });
|
|
1457
1838
|
}
|
|
1458
1839
|
|
|
@@ -1554,34 +1935,318 @@ function FollowUpChips({
|
|
|
1554
1935
|
] });
|
|
1555
1936
|
}
|
|
1556
1937
|
|
|
1938
|
+
// src/react/components/BookingCard/BookingCard.tsx
|
|
1939
|
+
var import_react5 = require("react");
|
|
1940
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1941
|
+
function monthFromKey(key) {
|
|
1942
|
+
const [year, month] = key.split("-").map(Number);
|
|
1943
|
+
if (!year || !month) return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
|
|
1944
|
+
return { year, month: month - 1 };
|
|
1945
|
+
}
|
|
1946
|
+
function dateKeyFromParts(year, month, day) {
|
|
1947
|
+
return [
|
|
1948
|
+
year,
|
|
1949
|
+
String(month + 1).padStart(2, "0"),
|
|
1950
|
+
String(day).padStart(2, "0")
|
|
1951
|
+
].join("-");
|
|
1952
|
+
}
|
|
1953
|
+
function calendarCells(year, month) {
|
|
1954
|
+
const firstWeekday = (new Date(year, month, 1).getDay() + 6) % 7;
|
|
1955
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
1956
|
+
const cells = [];
|
|
1957
|
+
for (let index = 0; index < firstWeekday; index += 1) cells.push(null);
|
|
1958
|
+
for (let day = 1; day <= daysInMonth; day += 1) {
|
|
1959
|
+
cells.push({ day, key: dateKeyFromParts(year, month, day) });
|
|
1960
|
+
}
|
|
1961
|
+
while (cells.length < 42) cells.push(null);
|
|
1962
|
+
return cells;
|
|
1963
|
+
}
|
|
1964
|
+
function BookingCard({
|
|
1965
|
+
offer,
|
|
1966
|
+
onBook
|
|
1967
|
+
}) {
|
|
1968
|
+
const fieldId = (0, import_react5.useId)();
|
|
1969
|
+
const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
|
|
1970
|
+
const [step, setStep] = (0, import_react5.useState)("date");
|
|
1971
|
+
const [eventTypeUri, setEventTypeUri] = (0, import_react5.useState)(defaultType);
|
|
1972
|
+
const [selectedDate, setSelectedDate] = (0, import_react5.useState)("");
|
|
1973
|
+
const [startTime, setStartTime] = (0, import_react5.useState)("");
|
|
1974
|
+
const [name, setName] = (0, import_react5.useState)("");
|
|
1975
|
+
const [email, setEmail] = (0, import_react5.useState)("");
|
|
1976
|
+
const slots = (0, import_react5.useMemo)(
|
|
1977
|
+
() => bookingSlotsForEventType(offer.slots, eventTypeUri),
|
|
1978
|
+
[eventTypeUri, offer.slots]
|
|
1979
|
+
);
|
|
1980
|
+
const availableByDate = (0, import_react5.useMemo)(() => {
|
|
1981
|
+
const next = /* @__PURE__ */ new Map();
|
|
1982
|
+
for (const slot of slots) {
|
|
1983
|
+
const key = slotDateKey(slot.startTime);
|
|
1984
|
+
if (!next.has(key)) next.set(key, slot.startTime);
|
|
1985
|
+
}
|
|
1986
|
+
return next;
|
|
1987
|
+
}, [slots]);
|
|
1988
|
+
const [visibleMonth, setVisibleMonth] = (0, import_react5.useState)(
|
|
1989
|
+
() => firstAvailableBookingMonth(slots)
|
|
1990
|
+
);
|
|
1991
|
+
function selectEventType(nextType) {
|
|
1992
|
+
setEventTypeUri(nextType);
|
|
1993
|
+
setSelectedDate("");
|
|
1994
|
+
setStartTime("");
|
|
1995
|
+
setVisibleMonth(
|
|
1996
|
+
firstAvailableBookingMonth(bookingSlotsForEventType(offer.slots, nextType))
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
const daySlots = (0, import_react5.useMemo)(
|
|
2000
|
+
() => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
|
|
2001
|
+
[selectedDate, slots]
|
|
2002
|
+
);
|
|
2003
|
+
const selectedType = offer.eventTypes.find((item) => item.uri === eventTypeUri);
|
|
2004
|
+
const selectedSample = availableByDate.get(selectedDate) ?? startTime;
|
|
2005
|
+
const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
|
|
2006
|
+
const weekdays = (0, import_react5.useMemo)(() => weekdayLabels(), []);
|
|
2007
|
+
const cells = calendarCells(visibleMonth.year, visibleMonth.month);
|
|
2008
|
+
const canPrevMonth = [...availableByDate.keys()].some((key) => {
|
|
2009
|
+
const month = monthFromKey(key);
|
|
2010
|
+
return month.year < visibleMonth.year || month.year === visibleMonth.year && month.month < visibleMonth.month;
|
|
2011
|
+
});
|
|
2012
|
+
const canNextMonth = [...availableByDate.keys()].some((key) => {
|
|
2013
|
+
const month = monthFromKey(key);
|
|
2014
|
+
return month.year > visibleMonth.year || month.year === visibleMonth.year && month.month > visibleMonth.month;
|
|
2015
|
+
});
|
|
2016
|
+
function goToMonth(offset) {
|
|
2017
|
+
setVisibleMonth((current) => {
|
|
2018
|
+
const next = new Date(current.year, current.month + offset, 1);
|
|
2019
|
+
return { year: next.getFullYear(), month: next.getMonth() };
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
function selectDate(key) {
|
|
2023
|
+
if (!availableByDate.has(key)) return;
|
|
2024
|
+
setSelectedDate(key);
|
|
2025
|
+
setStartTime("");
|
|
2026
|
+
setStep("time");
|
|
2027
|
+
}
|
|
2028
|
+
function selectTime(value) {
|
|
2029
|
+
setStartTime(value);
|
|
2030
|
+
setStep("details");
|
|
2031
|
+
}
|
|
2032
|
+
function handleSubmit(event) {
|
|
2033
|
+
event.preventDefault();
|
|
2034
|
+
if (!eventTypeUri || !startTime || !name.trim() || !email.trim()) return;
|
|
2035
|
+
onBook?.({
|
|
2036
|
+
displayText: `Book the ${formatSlotLabel(startTime)} demo`,
|
|
2037
|
+
runtimeText: formatBookingRequest({
|
|
2038
|
+
eventTypeUri,
|
|
2039
|
+
inviteeEmail: email.trim(),
|
|
2040
|
+
inviteeName: name.trim(),
|
|
2041
|
+
startTime,
|
|
2042
|
+
timezone: visitorTimeZone(),
|
|
2043
|
+
locationKind: selectedType?.locationKind,
|
|
2044
|
+
location: selectedType?.location
|
|
2045
|
+
})
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { className: "booking-card", "aria-label": "Book a meeting", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "booking-card__form", onSubmit: handleSubmit, children: [
|
|
2049
|
+
step === "date" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2050
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
|
|
2051
|
+
timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
|
|
2052
|
+
"Times in ",
|
|
2053
|
+
timeZone
|
|
2054
|
+
] }) : null,
|
|
2055
|
+
offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "booking-card__field", htmlFor: `${fieldId}-type`, children: [
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Meeting" }),
|
|
2057
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2058
|
+
"select",
|
|
2059
|
+
{
|
|
2060
|
+
id: `${fieldId}-type`,
|
|
2061
|
+
value: eventTypeUri,
|
|
2062
|
+
onChange: (event) => selectEventType(event.target.value),
|
|
2063
|
+
children: offer.eventTypes.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: item.uri, children: item.name }, item.uri))
|
|
2064
|
+
}
|
|
2065
|
+
)
|
|
2066
|
+
] }) : null,
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__month", children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2069
|
+
"button",
|
|
2070
|
+
{
|
|
2071
|
+
type: "button",
|
|
2072
|
+
className: "booking-card__nav",
|
|
2073
|
+
"aria-label": "Previous month",
|
|
2074
|
+
disabled: !canPrevMonth,
|
|
2075
|
+
onClick: () => goToMonth(-1),
|
|
2076
|
+
children: "\u2039"
|
|
2077
|
+
}
|
|
2078
|
+
),
|
|
2079
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__month-title", children: formatMonthTitle(visibleMonth.year, visibleMonth.month) }),
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2081
|
+
"button",
|
|
2082
|
+
{
|
|
2083
|
+
type: "button",
|
|
2084
|
+
className: "booking-card__nav",
|
|
2085
|
+
"aria-label": "Next month",
|
|
2086
|
+
disabled: !canNextMonth,
|
|
2087
|
+
onClick: () => goToMonth(1),
|
|
2088
|
+
children: "\u203A"
|
|
2089
|
+
}
|
|
2090
|
+
)
|
|
2091
|
+
] }),
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__weekdays", children: weekdays.map((label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: label }, label)) }),
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__calendar", role: "grid", "aria-label": "Available dates", children: cells.map((cell, index) => {
|
|
2094
|
+
if (!cell) {
|
|
2095
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "booking-card__day" }, `empty-${index}`);
|
|
2096
|
+
}
|
|
2097
|
+
const available = availableByDate.has(cell.key);
|
|
2098
|
+
const selected = cell.key === selectedDate;
|
|
2099
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2100
|
+
"button",
|
|
2101
|
+
{
|
|
2102
|
+
type: "button",
|
|
2103
|
+
className: [
|
|
2104
|
+
"booking-card__day",
|
|
2105
|
+
available ? "booking-card__day--available" : "",
|
|
2106
|
+
selected ? "booking-card__day--selected" : ""
|
|
2107
|
+
].filter(Boolean).join(" "),
|
|
2108
|
+
disabled: !available,
|
|
2109
|
+
"aria-pressed": selected,
|
|
2110
|
+
onClick: () => selectDate(cell.key),
|
|
2111
|
+
children: cell.day
|
|
2112
|
+
},
|
|
2113
|
+
cell.key
|
|
2114
|
+
);
|
|
2115
|
+
}) })
|
|
2116
|
+
] }, "date") : null,
|
|
2117
|
+
step === "time" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
|
|
2119
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2120
|
+
"button",
|
|
2121
|
+
{
|
|
2122
|
+
type: "button",
|
|
2123
|
+
className: "booking-card__nav",
|
|
2124
|
+
"aria-label": "Back to dates",
|
|
2125
|
+
onClick: () => setStep("date"),
|
|
2126
|
+
children: "\u2039"
|
|
2127
|
+
}
|
|
2128
|
+
),
|
|
2129
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
2130
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedSample ? formatLongDate(selectedSample) : "Pick a time" }),
|
|
2131
|
+
timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
|
|
2132
|
+
"Times in ",
|
|
2133
|
+
timeZone
|
|
2134
|
+
] }) : null
|
|
2135
|
+
] })
|
|
2136
|
+
] }),
|
|
2137
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__times", children: daySlots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2138
|
+
"button",
|
|
2139
|
+
{
|
|
2140
|
+
type: "button",
|
|
2141
|
+
className: startTime === slot.startTime ? "booking-card__time booking-card__time--selected" : "booking-card__time",
|
|
2142
|
+
onClick: () => selectTime(slot.startTime),
|
|
2143
|
+
children: formatTimeChip(slot.startTime)
|
|
2144
|
+
},
|
|
2145
|
+
slot.startTime
|
|
2146
|
+
)) })
|
|
2147
|
+
] }, "time") : null,
|
|
2148
|
+
step === "details" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2151
|
+
"button",
|
|
2152
|
+
{
|
|
2153
|
+
type: "button",
|
|
2154
|
+
className: "booking-card__nav",
|
|
2155
|
+
"aria-label": "Back to times",
|
|
2156
|
+
onClick: () => setStep("time"),
|
|
2157
|
+
children: "\u2039"
|
|
2158
|
+
}
|
|
2159
|
+
),
|
|
2160
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
2161
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: "Enter details" }),
|
|
2162
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__tz", children: formatSlotLabel(startTime) }),
|
|
2163
|
+
selectedType?.location ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__tz", children: selectedType.location }) : null
|
|
2164
|
+
] })
|
|
2165
|
+
] }),
|
|
2166
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__identity", children: [
|
|
2167
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "booking-card__field", htmlFor: `${fieldId}-name`, children: [
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Name" }),
|
|
2169
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2170
|
+
"input",
|
|
2171
|
+
{
|
|
2172
|
+
id: `${fieldId}-name`,
|
|
2173
|
+
autoComplete: "name",
|
|
2174
|
+
value: name,
|
|
2175
|
+
onChange: (event) => setName(event.target.value),
|
|
2176
|
+
required: true
|
|
2177
|
+
}
|
|
2178
|
+
)
|
|
2179
|
+
] }),
|
|
2180
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "booking-card__field", htmlFor: `${fieldId}-email`, children: [
|
|
2181
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Email" }),
|
|
2182
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2183
|
+
"input",
|
|
2184
|
+
{
|
|
2185
|
+
id: `${fieldId}-email`,
|
|
2186
|
+
type: "email",
|
|
2187
|
+
autoComplete: "email",
|
|
2188
|
+
value: email,
|
|
2189
|
+
onChange: (event) => setEmail(event.target.value),
|
|
2190
|
+
required: true
|
|
2191
|
+
}
|
|
2192
|
+
)
|
|
2193
|
+
] })
|
|
2194
|
+
] }),
|
|
2195
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "submit", className: "booking-card__submit", children: "Book this time" })
|
|
2196
|
+
] }, "details") : null
|
|
2197
|
+
] }) });
|
|
2198
|
+
}
|
|
2199
|
+
|
|
1557
2200
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
1558
2201
|
var import_streamdown = require("streamdown");
|
|
1559
2202
|
var import_styles = require("streamdown/styles.css");
|
|
1560
|
-
var
|
|
1561
|
-
function MessageBubble({
|
|
2203
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2204
|
+
function MessageBubble({
|
|
2205
|
+
message,
|
|
2206
|
+
offer,
|
|
2207
|
+
onBook
|
|
2208
|
+
}) {
|
|
2209
|
+
const cards = message.role === "agent" ? extractToolCards(message.text) : [];
|
|
2210
|
+
const extractedOffers = cards.filter(
|
|
2211
|
+
(card) => card.type === "booking_offer"
|
|
2212
|
+
);
|
|
2213
|
+
const offers = offer ? [offer] : extractedOffers;
|
|
2214
|
+
const visibleText = hideToolCardFences(message.text);
|
|
2215
|
+
const isStreaming = message.role === "agent" && Boolean(message.streaming);
|
|
2216
|
+
const displayText = offers.length > 0 ? visibleText || "Pick a date and time that works for you." : visibleText || (isStreaming ? "" : message.text);
|
|
1562
2217
|
if (message.role === "visitor") {
|
|
1563
|
-
return /* @__PURE__ */ (0,
|
|
2218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "message-bubble__text", children: message.text }) });
|
|
1564
2219
|
}
|
|
1565
|
-
return /* @__PURE__ */ (0,
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
2220
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
|
|
2221
|
+
displayText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2222
|
+
import_streamdown.Streamdown,
|
|
2223
|
+
{
|
|
2224
|
+
animated: true,
|
|
2225
|
+
caret: "circle",
|
|
2226
|
+
className: "message-bubble__markdown",
|
|
2227
|
+
controls: false,
|
|
2228
|
+
isAnimating: isStreaming,
|
|
2229
|
+
linkSafety: { enabled: false },
|
|
2230
|
+
mode: isStreaming ? "streaming" : "static",
|
|
2231
|
+
skipHtml: true,
|
|
2232
|
+
children: displayText
|
|
2233
|
+
}
|
|
2234
|
+
) }) : null,
|
|
2235
|
+
offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2236
|
+
BookingCard,
|
|
2237
|
+
{
|
|
2238
|
+
offer: nextOffer,
|
|
2239
|
+
onBook
|
|
2240
|
+
},
|
|
2241
|
+
`${bookingOfferIdentityKey(nextOffer)}-${index}`
|
|
2242
|
+
))
|
|
2243
|
+
] });
|
|
1579
2244
|
}
|
|
1580
2245
|
|
|
1581
2246
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1582
|
-
var
|
|
2247
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1583
2248
|
function MinimizeIcon() {
|
|
1584
|
-
return /* @__PURE__ */ (0,
|
|
2249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1585
2250
|
"path",
|
|
1586
2251
|
{
|
|
1587
2252
|
d: "M3.5 8h9",
|
|
@@ -1592,7 +2257,7 @@ function MinimizeIcon() {
|
|
|
1592
2257
|
) });
|
|
1593
2258
|
}
|
|
1594
2259
|
function CloseIcon() {
|
|
1595
|
-
return /* @__PURE__ */ (0,
|
|
2260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1596
2261
|
"path",
|
|
1597
2262
|
{
|
|
1598
2263
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -1603,7 +2268,7 @@ function CloseIcon() {
|
|
|
1603
2268
|
) });
|
|
1604
2269
|
}
|
|
1605
2270
|
function NewChatIcon() {
|
|
1606
|
-
return /* @__PURE__ */ (0,
|
|
2271
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1607
2272
|
"path",
|
|
1608
2273
|
{
|
|
1609
2274
|
d: "M9.5 3.5h3v3M12.25 3.75 8 8M7 4H4.5A1.5 1.5 0 0 0 3 5.5v6A1.5 1.5 0 0 0 4.5 13h6a1.5 1.5 0 0 0 1.5-1.5V9",
|
|
@@ -1615,7 +2280,7 @@ function NewChatIcon() {
|
|
|
1615
2280
|
) });
|
|
1616
2281
|
}
|
|
1617
2282
|
function ExpandIcon() {
|
|
1618
|
-
return /* @__PURE__ */ (0,
|
|
2283
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1619
2284
|
"path",
|
|
1620
2285
|
{
|
|
1621
2286
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -1627,7 +2292,7 @@ function ExpandIcon() {
|
|
|
1627
2292
|
) });
|
|
1628
2293
|
}
|
|
1629
2294
|
function RestoreIcon() {
|
|
1630
|
-
return /* @__PURE__ */ (0,
|
|
2295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1631
2296
|
"path",
|
|
1632
2297
|
{
|
|
1633
2298
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -1653,10 +2318,10 @@ function AgentRail({
|
|
|
1653
2318
|
onReset,
|
|
1654
2319
|
onRetry,
|
|
1655
2320
|
onSubmit,
|
|
1656
|
-
onFollowUpSelect
|
|
2321
|
+
onFollowUpSelect,
|
|
2322
|
+
onBook
|
|
1657
2323
|
}) {
|
|
1658
|
-
const transcriptRef = (0,
|
|
1659
|
-
const welcomeTitleId = (0, import_react5.useId)();
|
|
2324
|
+
const transcriptRef = (0, import_react6.useRef)(null);
|
|
1660
2325
|
const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
1661
2326
|
const railStyle = {
|
|
1662
2327
|
"--rail-width": resolvedTheme.railMaxWidth,
|
|
@@ -1696,8 +2361,14 @@ function AgentRail({
|
|
|
1696
2361
|
role: "agent",
|
|
1697
2362
|
streaming: true,
|
|
1698
2363
|
text: state.streamingText
|
|
2364
|
+
} : state.pendingOffer ? {
|
|
2365
|
+
createdAt: 0,
|
|
2366
|
+
id: "pending-booking",
|
|
2367
|
+
role: "agent",
|
|
2368
|
+
streaming: false,
|
|
2369
|
+
text: "Pick a date and time that works for you."
|
|
1699
2370
|
} : null;
|
|
1700
|
-
(0,
|
|
2371
|
+
(0, import_react6.useEffect)(() => {
|
|
1701
2372
|
const node = transcriptRef.current;
|
|
1702
2373
|
if (!node) return;
|
|
1703
2374
|
node.scrollTop = node.scrollHeight;
|
|
@@ -1708,7 +2379,7 @@ function AgentRail({
|
|
|
1708
2379
|
state.followUps,
|
|
1709
2380
|
state.journey
|
|
1710
2381
|
]);
|
|
1711
|
-
return /* @__PURE__ */ (0,
|
|
2382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1712
2383
|
"aside",
|
|
1713
2384
|
{
|
|
1714
2385
|
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
@@ -1719,30 +2390,30 @@ function AgentRail({
|
|
|
1719
2390
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
1720
2391
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
1721
2392
|
children: [
|
|
1722
|
-
/* @__PURE__ */ (0,
|
|
1723
|
-
onCollapse ? /* @__PURE__ */ (0,
|
|
2393
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__brand-row", children: [
|
|
2394
|
+
onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1724
2395
|
"button",
|
|
1725
2396
|
{
|
|
1726
2397
|
type: "button",
|
|
1727
2398
|
className: "agent-rail__collapse",
|
|
1728
2399
|
"aria-label": "Collapse assist",
|
|
1729
2400
|
onClick: onCollapse,
|
|
1730
|
-
children: /* @__PURE__ */ (0,
|
|
2401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
|
|
1731
2402
|
}
|
|
1732
|
-
) : onClose ? /* @__PURE__ */ (0,
|
|
2403
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1733
2404
|
"button",
|
|
1734
2405
|
{
|
|
1735
2406
|
type: "button",
|
|
1736
2407
|
className: "agent-rail__close",
|
|
1737
2408
|
"aria-label": "Close agent",
|
|
1738
2409
|
onClick: onClose,
|
|
1739
|
-
children: /* @__PURE__ */ (0,
|
|
2410
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, {})
|
|
1740
2411
|
}
|
|
1741
|
-
) : /* @__PURE__ */ (0,
|
|
1742
|
-
/* @__PURE__ */ (0,
|
|
1743
|
-
/* @__PURE__ */ (0,
|
|
2412
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
|
|
2413
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__identity", children: [
|
|
2414
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: [
|
|
1744
2415
|
brandLabel.slice(0, 1).toUpperCase(),
|
|
1745
|
-
brandLogoUrl ? /* @__PURE__ */ (0,
|
|
2416
|
+
brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1746
2417
|
"img",
|
|
1747
2418
|
{
|
|
1748
2419
|
className: "agent-rail__brand-logo",
|
|
@@ -1754,10 +2425,10 @@ function AgentRail({
|
|
|
1754
2425
|
}
|
|
1755
2426
|
) : null
|
|
1756
2427
|
] }),
|
|
1757
|
-
/* @__PURE__ */ (0,
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
|
|
1758
2429
|
] }),
|
|
1759
|
-
/* @__PURE__ */ (0,
|
|
1760
|
-
onReset ? /* @__PURE__ */ (0,
|
|
2430
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
2431
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1761
2432
|
"button",
|
|
1762
2433
|
{
|
|
1763
2434
|
type: "button",
|
|
@@ -1765,60 +2436,37 @@ function AgentRail({
|
|
|
1765
2436
|
"aria-label": "Start a new conversation",
|
|
1766
2437
|
disabled: !hasVisitorMessages2,
|
|
1767
2438
|
onClick: onReset,
|
|
1768
|
-
children: /* @__PURE__ */ (0,
|
|
2439
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NewChatIcon, {})
|
|
1769
2440
|
}
|
|
1770
2441
|
) : null,
|
|
1771
|
-
onExpandToggle ? /* @__PURE__ */ (0,
|
|
2442
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1772
2443
|
"button",
|
|
1773
2444
|
{
|
|
1774
2445
|
type: "button",
|
|
1775
2446
|
className: "agent-rail__expand",
|
|
1776
2447
|
"aria-label": expanded ? "Exit focus view" : "Open focus view",
|
|
1777
2448
|
onClick: onExpandToggle,
|
|
1778
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
2449
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
|
|
1779
2450
|
}
|
|
1780
2451
|
) : null
|
|
1781
2452
|
] })
|
|
1782
2453
|
] }) }),
|
|
1783
|
-
/* @__PURE__ */ (0,
|
|
1784
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0,
|
|
1785
|
-
"
|
|
1786
|
-
{
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
event.currentTarget.hidden = true;
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
) : null
|
|
1803
|
-
] }),
|
|
1804
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__welcome-copy", children: [
|
|
1805
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: welcomeTitleId, children: "What can I help you find?" }),
|
|
1806
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: greeting.text }) : null
|
|
1807
|
-
] }),
|
|
1808
|
-
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1809
|
-
FollowUpChips,
|
|
1810
|
-
{
|
|
1811
|
-
suggestions: state.followUps,
|
|
1812
|
-
disabled: isBusy,
|
|
1813
|
-
label: "Start here",
|
|
1814
|
-
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
1815
|
-
}
|
|
1816
|
-
) }) : null
|
|
1817
|
-
]
|
|
1818
|
-
}
|
|
1819
|
-
) : null,
|
|
1820
|
-
transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
|
|
1821
|
-
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2454
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__thread", children: [
|
|
2455
|
+
!hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
|
|
2456
|
+
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message: greeting, onBook }) : null,
|
|
2457
|
+
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2458
|
+
FollowUpChips,
|
|
2459
|
+
{
|
|
2460
|
+
suggestions: state.followUps,
|
|
2461
|
+
disabled: isBusy,
|
|
2462
|
+
label: "Start here",
|
|
2463
|
+
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
2464
|
+
}
|
|
2465
|
+
) }) : null
|
|
2466
|
+
] }) : null,
|
|
2467
|
+
transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message, onBook }, message.id)),
|
|
2468
|
+
completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message: completedAnswer, onBook }) : null,
|
|
2469
|
+
showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1822
2470
|
AgentActivityBubble,
|
|
1823
2471
|
{
|
|
1824
2472
|
brandLabel,
|
|
@@ -1827,18 +2475,24 @@ function AgentRail({
|
|
|
1827
2475
|
steps: state.toolSteps
|
|
1828
2476
|
}
|
|
1829
2477
|
) : null,
|
|
1830
|
-
streamingMessage ? /* @__PURE__ */ (0,
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
2478
|
+
streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2479
|
+
MessageBubble,
|
|
2480
|
+
{
|
|
2481
|
+
message: streamingMessage,
|
|
2482
|
+
offer: state.pendingOffer,
|
|
2483
|
+
onBook
|
|
2484
|
+
}
|
|
2485
|
+
) : null,
|
|
2486
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
|
|
2487
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Something went wrong" }),
|
|
2489
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: state.error })
|
|
1836
2490
|
] }),
|
|
1837
|
-
onRetry ? /* @__PURE__ */ (0,
|
|
2491
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
1838
2492
|
] }) : null
|
|
1839
2493
|
] }) }),
|
|
1840
|
-
/* @__PURE__ */ (0,
|
|
1841
|
-
/* @__PURE__ */ (0,
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1842
2496
|
Composer,
|
|
1843
2497
|
{
|
|
1844
2498
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
@@ -1847,10 +2501,9 @@ function AgentRail({
|
|
|
1847
2501
|
onSubmit
|
|
1848
2502
|
}
|
|
1849
2503
|
),
|
|
1850
|
-
/* @__PURE__ */ (0,
|
|
1851
|
-
/* @__PURE__ */ (0,
|
|
1852
|
-
/* @__PURE__ */ (0,
|
|
1853
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: poweredByLabel })
|
|
2504
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
|
|
2505
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "AI can make mistakes. Check important info." }),
|
|
2506
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: poweredByLabel })
|
|
1854
2507
|
] }) })
|
|
1855
2508
|
] })
|
|
1856
2509
|
]
|
|
@@ -1859,9 +2512,9 @@ function AgentRail({
|
|
|
1859
2512
|
}
|
|
1860
2513
|
|
|
1861
2514
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
1862
|
-
var
|
|
2515
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1863
2516
|
function SparklesIcon() {
|
|
1864
|
-
return /* @__PURE__ */ (0,
|
|
2517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1865
2518
|
"svg",
|
|
1866
2519
|
{
|
|
1867
2520
|
className: "assist-edge-tab__sparkles",
|
|
@@ -1869,21 +2522,21 @@ function SparklesIcon() {
|
|
|
1869
2522
|
fill: "none",
|
|
1870
2523
|
"aria-hidden": "true",
|
|
1871
2524
|
children: [
|
|
1872
|
-
/* @__PURE__ */ (0,
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1873
2526
|
"path",
|
|
1874
2527
|
{
|
|
1875
2528
|
d: "M8 1.2l.95 2.7 2.85.05-2.25 1.75.8 2.75L8 6.7 5.65 8.45l.8-2.75L4.2 3.95l2.85-.05L8 1.2z",
|
|
1876
2529
|
fill: "currentColor"
|
|
1877
2530
|
}
|
|
1878
2531
|
),
|
|
1879
|
-
/* @__PURE__ */ (0,
|
|
2532
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1880
2533
|
"path",
|
|
1881
2534
|
{
|
|
1882
2535
|
d: "M14.2 6.4l.55 1.55 1.65.03-1.3 1 .46 1.58-1.36-1-1.36 1 .46-1.58-1.3-1 1.65-.03.55-1.55z",
|
|
1883
2536
|
fill: "currentColor"
|
|
1884
2537
|
}
|
|
1885
2538
|
),
|
|
1886
|
-
/* @__PURE__ */ (0,
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1887
2540
|
"path",
|
|
1888
2541
|
{
|
|
1889
2542
|
d: "M3.1 9.1l.4 1.15 1.22.02-.96.74.34 1.17-1-.74-1 .74.34-1.17-.96-.74 1.22-.02.4-1.15z",
|
|
@@ -1894,8 +2547,23 @@ function SparklesIcon() {
|
|
|
1894
2547
|
}
|
|
1895
2548
|
);
|
|
1896
2549
|
}
|
|
2550
|
+
function TabMarkIcon({ customIconUrl }) {
|
|
2551
|
+
const url = customIconUrl?.trim();
|
|
2552
|
+
if (url) {
|
|
2553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2554
|
+
"img",
|
|
2555
|
+
{
|
|
2556
|
+
alt: "",
|
|
2557
|
+
"aria-hidden": true,
|
|
2558
|
+
className: "assist-edge-tab__custom-icon",
|
|
2559
|
+
src: url
|
|
2560
|
+
}
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2563
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {});
|
|
2564
|
+
}
|
|
1897
2565
|
function ChevronLeftIcon() {
|
|
1898
|
-
return /* @__PURE__ */ (0,
|
|
2566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1899
2567
|
"path",
|
|
1900
2568
|
{
|
|
1901
2569
|
d: "M10 4L6 8l4 4",
|
|
@@ -1907,7 +2575,7 @@ function ChevronLeftIcon() {
|
|
|
1907
2575
|
) });
|
|
1908
2576
|
}
|
|
1909
2577
|
function ChevronDownIcon() {
|
|
1910
|
-
return /* @__PURE__ */ (0,
|
|
2578
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1911
2579
|
"path",
|
|
1912
2580
|
{
|
|
1913
2581
|
d: "M4 6l4 4 4-4",
|
|
@@ -1919,7 +2587,7 @@ function ChevronDownIcon() {
|
|
|
1919
2587
|
) });
|
|
1920
2588
|
}
|
|
1921
2589
|
function DragDots() {
|
|
1922
|
-
return /* @__PURE__ */ (0,
|
|
2590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", {}, index)) });
|
|
1923
2591
|
}
|
|
1924
2592
|
var VARIANT_COPY = {
|
|
1925
2593
|
outline: { label: "Assist", aria: "Open Assist" },
|
|
@@ -1933,6 +2601,7 @@ function AssistEdgeTab({
|
|
|
1933
2601
|
inset,
|
|
1934
2602
|
visible,
|
|
1935
2603
|
label,
|
|
2604
|
+
customIconUrl,
|
|
1936
2605
|
logoUrl,
|
|
1937
2606
|
brandColor,
|
|
1938
2607
|
brandForeground,
|
|
@@ -1945,6 +2614,7 @@ function AssistEdgeTab({
|
|
|
1945
2614
|
}) {
|
|
1946
2615
|
const copy = VARIANT_COPY[variant];
|
|
1947
2616
|
const visibleLabel = label?.trim() || copy.label;
|
|
2617
|
+
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
1948
2618
|
const style = {
|
|
1949
2619
|
"--tab-along": `${along}%`,
|
|
1950
2620
|
"--tab-inset": `${inset}px`,
|
|
@@ -1955,7 +2625,7 @@ function AssistEdgeTab({
|
|
|
1955
2625
|
...surfaceColor ? { "--as-surface": surfaceColor } : {},
|
|
1956
2626
|
...textColor ? { "--as-text": textColor } : {}
|
|
1957
2627
|
};
|
|
1958
|
-
return /* @__PURE__ */ (0,
|
|
2628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1959
2629
|
"button",
|
|
1960
2630
|
{
|
|
1961
2631
|
type: "button",
|
|
@@ -1966,15 +2636,15 @@ function AssistEdgeTab({
|
|
|
1966
2636
|
tabIndex: visible ? 0 : -1,
|
|
1967
2637
|
onClick: onOpen,
|
|
1968
2638
|
children: [
|
|
1969
|
-
mobile ? /* @__PURE__ */ (0,
|
|
1970
|
-
/* @__PURE__ */ (0,
|
|
2639
|
+
mobile ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1971
2641
|
"span",
|
|
1972
2642
|
{
|
|
1973
2643
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
1974
2644
|
"aria-hidden": "true",
|
|
1975
2645
|
children: [
|
|
1976
|
-
visibleLabel.slice(0, 1).toUpperCase(),
|
|
1977
|
-
|
|
2646
|
+
customIconUrl?.trim() ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }) : visibleLabel.slice(0, 1).toUpperCase(),
|
|
2647
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1978
2648
|
"img",
|
|
1979
2649
|
{
|
|
1980
2650
|
className: "assist-edge-tab__logo",
|
|
@@ -1988,14 +2658,14 @@ function AssistEdgeTab({
|
|
|
1988
2658
|
]
|
|
1989
2659
|
}
|
|
1990
2660
|
),
|
|
1991
|
-
/* @__PURE__ */ (0,
|
|
2661
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__label", children: [
|
|
1992
2662
|
"Ask ",
|
|
1993
2663
|
visibleLabel
|
|
1994
2664
|
] })
|
|
1995
|
-
] }) : variant === "outline" ? /* @__PURE__ */ (0,
|
|
1996
|
-
/* @__PURE__ */ (0,
|
|
1997
|
-
/* @__PURE__ */ (0,
|
|
1998
|
-
|
|
2665
|
+
] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2666
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
2667
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
|
|
2668
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1999
2669
|
"img",
|
|
2000
2670
|
{
|
|
2001
2671
|
className: "assist-edge-tab__logo",
|
|
@@ -2007,18 +2677,18 @@ function AssistEdgeTab({
|
|
|
2007
2677
|
}
|
|
2008
2678
|
) : null
|
|
2009
2679
|
] }),
|
|
2010
|
-
/* @__PURE__ */ (0,
|
|
2011
|
-
/* @__PURE__ */ (0,
|
|
2680
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
2681
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDownIcon, {})
|
|
2012
2682
|
] }) : null,
|
|
2013
|
-
variant === "ask" ? /* @__PURE__ */ (0,
|
|
2014
|
-
/* @__PURE__ */ (0,
|
|
2015
|
-
/* @__PURE__ */ (0,
|
|
2016
|
-
/* @__PURE__ */ (0,
|
|
2683
|
+
variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2684
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}),
|
|
2685
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
2686
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DragDots, {})
|
|
2017
2687
|
] }) : null,
|
|
2018
|
-
variant === "fill" ? /* @__PURE__ */ (0,
|
|
2019
|
-
/* @__PURE__ */ (0,
|
|
2020
|
-
/* @__PURE__ */ (0,
|
|
2021
|
-
|
|
2688
|
+
variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2689
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
|
|
2690
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
|
|
2691
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2022
2692
|
"img",
|
|
2023
2693
|
{
|
|
2024
2694
|
className: "assist-edge-tab__logo",
|
|
@@ -2030,8 +2700,8 @@ function AssistEdgeTab({
|
|
|
2030
2700
|
}
|
|
2031
2701
|
) : null
|
|
2032
2702
|
] }),
|
|
2033
|
-
/* @__PURE__ */ (0,
|
|
2034
|
-
/* @__PURE__ */ (0,
|
|
2703
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
|
|
2704
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {})
|
|
2035
2705
|
] }) : null
|
|
2036
2706
|
]
|
|
2037
2707
|
}
|
|
@@ -2039,7 +2709,7 @@ function AssistEdgeTab({
|
|
|
2039
2709
|
}
|
|
2040
2710
|
|
|
2041
2711
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2042
|
-
var
|
|
2712
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2043
2713
|
function AgentWidget({
|
|
2044
2714
|
indexId,
|
|
2045
2715
|
customerId,
|
|
@@ -2055,9 +2725,9 @@ function AgentWidget({
|
|
|
2055
2725
|
}) {
|
|
2056
2726
|
const isMobile = useIsMobile();
|
|
2057
2727
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2058
|
-
const railSlotRef = (0,
|
|
2059
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
2060
|
-
const [railExpanded, setRailExpanded] = (0,
|
|
2728
|
+
const railSlotRef = (0, import_react7.useRef)(null);
|
|
2729
|
+
const [railCollapsed, setRailCollapsed] = (0, import_react7.useState)(defaultCollapsed);
|
|
2730
|
+
const [railExpanded, setRailExpanded] = (0, import_react7.useState)(false);
|
|
2061
2731
|
const pageShiftActive = shouldApplyPageShift({
|
|
2062
2732
|
pageShift,
|
|
2063
2733
|
isMobile,
|
|
@@ -2078,6 +2748,7 @@ function AgentWidget({
|
|
|
2078
2748
|
greeting: branding?.greeting
|
|
2079
2749
|
});
|
|
2080
2750
|
const agentName = branding?.agentName ?? "Webless Guide";
|
|
2751
|
+
const tabLabel = branding?.tabLabel ?? agentName;
|
|
2081
2752
|
const theme = {
|
|
2082
2753
|
...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
|
|
2083
2754
|
...branding?.colors?.primary ? {
|
|
@@ -2095,8 +2766,7 @@ function AgentWidget({
|
|
|
2095
2766
|
} : {},
|
|
2096
2767
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2097
2768
|
};
|
|
2098
|
-
|
|
2099
|
-
(0, import_react6.useEffect)(() => {
|
|
2769
|
+
(0, import_react7.useEffect)(() => {
|
|
2100
2770
|
if (!registerPanelController) return;
|
|
2101
2771
|
registerAgentPanelController(customerId, {
|
|
2102
2772
|
open: () => setRailCollapsed(false),
|
|
@@ -2111,7 +2781,7 @@ function AgentWidget({
|
|
|
2111
2781
|
if (isMobile) setRailCollapsed(false);
|
|
2112
2782
|
await submit(message);
|
|
2113
2783
|
}
|
|
2114
|
-
(0,
|
|
2784
|
+
(0, import_react7.useEffect)(() => {
|
|
2115
2785
|
if (railCollapsed) return;
|
|
2116
2786
|
const handleKeyDown = (event) => {
|
|
2117
2787
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2145,31 +2815,28 @@ function AgentWidget({
|
|
|
2145
2815
|
window.addEventListener("keydown", handleKeyDown);
|
|
2146
2816
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2147
2817
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2148
|
-
return /* @__PURE__ */ (0,
|
|
2149
|
-
/* @__PURE__ */ (0,
|
|
2818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
|
|
2819
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2150
2820
|
"div",
|
|
2151
2821
|
{
|
|
2152
2822
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2153
2823
|
children: [
|
|
2154
|
-
/* @__PURE__ */ (0,
|
|
2824
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2155
2825
|
"div",
|
|
2156
2826
|
{
|
|
2157
2827
|
ref: railSlotRef,
|
|
2158
2828
|
className: "webless-agent-root__rail-slot",
|
|
2159
2829
|
inert: railCollapsed || void 0,
|
|
2160
2830
|
"aria-hidden": railCollapsed,
|
|
2161
|
-
children: /* @__PURE__ */ (0,
|
|
2831
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2162
2832
|
AgentRail,
|
|
2163
2833
|
{
|
|
2164
2834
|
theme,
|
|
2165
2835
|
brandLabel: agentName,
|
|
2166
2836
|
brandLogoUrl: branding?.logoUrl,
|
|
2167
|
-
composerPlaceholder: branding?.composerPlaceholder ??
|
|
2837
|
+
composerPlaceholder: branding?.composerPlaceholder ?? "Ask as a visitor\u2026",
|
|
2168
2838
|
poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
|
|
2169
|
-
state
|
|
2170
|
-
...state,
|
|
2171
|
-
followUps: createIdleSuggestions()
|
|
2172
|
-
} : state,
|
|
2839
|
+
state,
|
|
2173
2840
|
mobileFullscreen: isMobile && !railCollapsed,
|
|
2174
2841
|
expanded: railExpanded,
|
|
2175
2842
|
onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
|
|
@@ -2178,12 +2845,13 @@ function AgentWidget({
|
|
|
2178
2845
|
onSubmit: handleSubmit,
|
|
2179
2846
|
onReset: reset,
|
|
2180
2847
|
onRetry: () => void retry(),
|
|
2181
|
-
onFollowUpSelect: (label) => void handleSubmit(label)
|
|
2848
|
+
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
2849
|
+
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
2182
2850
|
}
|
|
2183
2851
|
)
|
|
2184
2852
|
}
|
|
2185
2853
|
),
|
|
2186
|
-
!railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0,
|
|
2854
|
+
!railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2187
2855
|
"button",
|
|
2188
2856
|
{
|
|
2189
2857
|
type: "button",
|
|
@@ -2202,7 +2870,7 @@ function AgentWidget({
|
|
|
2202
2870
|
]
|
|
2203
2871
|
}
|
|
2204
2872
|
),
|
|
2205
|
-
railCollapsed ? /* @__PURE__ */ (0,
|
|
2873
|
+
railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2206
2874
|
AssistEdgeTab,
|
|
2207
2875
|
{
|
|
2208
2876
|
variant: placement.variant,
|
|
@@ -2210,7 +2878,8 @@ function AgentWidget({
|
|
|
2210
2878
|
along: placement.along,
|
|
2211
2879
|
inset: placement.inset,
|
|
2212
2880
|
visible: true,
|
|
2213
|
-
label:
|
|
2881
|
+
label: tabLabel,
|
|
2882
|
+
customIconUrl: branding?.tabIconUrl,
|
|
2214
2883
|
logoUrl: branding?.logoUrl,
|
|
2215
2884
|
brandColor: branding?.colors?.primary,
|
|
2216
2885
|
brandForeground: branding?.colors?.primaryForeground,
|