@webless/agent 0.4.0 → 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 +245 -65
- 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 +245 -65
- 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/embed.cjs
CHANGED
|
@@ -50,7 +50,7 @@ function closeAgentPanel(customerId) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
53
|
-
var
|
|
53
|
+
var import_react7 = require("react");
|
|
54
54
|
|
|
55
55
|
// src/react/page-shift.ts
|
|
56
56
|
var import_react = require("react");
|
|
@@ -319,6 +319,9 @@ function runtimeSessionIdKey(visitorSessionId, prefix) {
|
|
|
319
319
|
function runtimeStreamIndexKey(visitorSessionId, prefix) {
|
|
320
320
|
return `${prefix}:eve:${visitorSessionId}:streamIndex`;
|
|
321
321
|
}
|
|
322
|
+
function runtimeLastMessageKey(visitorSessionId, prefix) {
|
|
323
|
+
return `${prefix}:eve:${visitorSessionId}:lastMessage`;
|
|
324
|
+
}
|
|
322
325
|
function loadPersistedAgentSession(visitorSessionId, options) {
|
|
323
326
|
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
|
|
324
327
|
const prefix = resolvePrefix(options);
|
|
@@ -326,9 +329,11 @@ function loadPersistedAgentSession(visitorSessionId, options) {
|
|
|
326
329
|
if (!sessionId) return null;
|
|
327
330
|
const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
328
331
|
const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
|
|
332
|
+
const lastMessage = sessionStorage.getItem(runtimeLastMessageKey(visitorSessionId, prefix))?.trim();
|
|
329
333
|
return {
|
|
330
334
|
sessionId,
|
|
331
|
-
streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0
|
|
335
|
+
streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0,
|
|
336
|
+
...lastMessage ? { lastMessage } : {}
|
|
332
337
|
};
|
|
333
338
|
}
|
|
334
339
|
function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
|
|
@@ -342,11 +347,19 @@ function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, opt
|
|
|
342
347
|
String(Math.max(0, streamIndex))
|
|
343
348
|
);
|
|
344
349
|
}
|
|
350
|
+
function savePersistedAgentTurnMessage(visitorSessionId, message, options) {
|
|
351
|
+
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !message) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const prefix = resolvePrefix(options);
|
|
355
|
+
sessionStorage.setItem(runtimeLastMessageKey(visitorSessionId, prefix), message);
|
|
356
|
+
}
|
|
345
357
|
function clearPersistedAgentSession(visitorSessionId, options) {
|
|
346
358
|
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
|
|
347
359
|
const prefix = resolvePrefix(options);
|
|
348
360
|
sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
|
|
349
361
|
sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
362
|
+
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
350
363
|
}
|
|
351
364
|
|
|
352
365
|
// src/runtime/client.ts
|
|
@@ -361,6 +374,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
361
374
|
if (event.type === "message.completed") {
|
|
362
375
|
handlers.onComplete?.();
|
|
363
376
|
}
|
|
377
|
+
if (event.type === "action.result") {
|
|
378
|
+
const result = event.data.result;
|
|
379
|
+
if (result && typeof result === "object" && "output" in result) {
|
|
380
|
+
handlers.onActionResult?.(result.output);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
364
383
|
if (event.type !== "message.appended") return rendered;
|
|
365
384
|
const { messageDelta, messageSoFar } = event.data;
|
|
366
385
|
let delta = messageDelta;
|
|
@@ -374,6 +393,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
374
393
|
if (delta) handlers.onDelta(delta);
|
|
375
394
|
return next;
|
|
376
395
|
}
|
|
396
|
+
function isResumeTurnMessage(received, candidate) {
|
|
397
|
+
if (received === candidate) return true;
|
|
398
|
+
return Boolean(candidate) && received.endsWith(`
|
|
399
|
+
|
|
400
|
+
${candidate}`);
|
|
401
|
+
}
|
|
377
402
|
function latestTurnEvents(events) {
|
|
378
403
|
let startIndex = -1;
|
|
379
404
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
@@ -605,6 +630,11 @@ var AgentSession = class {
|
|
|
605
630
|
this.session = session;
|
|
606
631
|
try {
|
|
607
632
|
const activeSession = session;
|
|
633
|
+
savePersistedAgentTurnMessage(
|
|
634
|
+
this.visitorSessionId,
|
|
635
|
+
message,
|
|
636
|
+
this.storeOptions
|
|
637
|
+
);
|
|
608
638
|
response = await withCapabilityRefresh(
|
|
609
639
|
this.capability,
|
|
610
640
|
() => activeSession.send(message, { signal })
|
|
@@ -620,6 +650,11 @@ var AgentSession = class {
|
|
|
620
650
|
}
|
|
621
651
|
}
|
|
622
652
|
if (!response) {
|
|
653
|
+
savePersistedAgentTurnMessage(
|
|
654
|
+
this.visitorSessionId,
|
|
655
|
+
message,
|
|
656
|
+
this.storeOptions
|
|
657
|
+
);
|
|
623
658
|
const created = await withCapabilityRefresh(
|
|
624
659
|
this.capability,
|
|
625
660
|
() => client.sessions.create({ message, signal })
|
|
@@ -674,7 +709,9 @@ var AgentSession = class {
|
|
|
674
709
|
);
|
|
675
710
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
676
711
|
const received = turnEvents[0];
|
|
677
|
-
|
|
712
|
+
const lastSent = persisted.lastMessage;
|
|
713
|
+
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
714
|
+
if (received?.type !== "message.received" || !(isResumeTurnMessage(received.data.message, message) || inFlight && lastSent !== void 0 && received.data.message === lastSent)) {
|
|
678
715
|
return null;
|
|
679
716
|
}
|
|
680
717
|
let rendered = renderTurn(turnEvents);
|
|
@@ -838,6 +875,246 @@ function formatAgentError(error) {
|
|
|
838
875
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
839
876
|
}
|
|
840
877
|
|
|
878
|
+
// src/react/lib/tool-card.ts
|
|
879
|
+
function bookingOfferIdentityKey(offer) {
|
|
880
|
+
const eventTypes = offer.eventTypes.map(
|
|
881
|
+
(item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
|
|
882
|
+
).join("|");
|
|
883
|
+
const slots = offer.slots.map((slot) => `${slot.eventTypeUri ?? ""}:${slot.startTime}`).join("|");
|
|
884
|
+
return `${eventTypes}::${slots}` || "offer";
|
|
885
|
+
}
|
|
886
|
+
var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
|
|
887
|
+
function asRecord(value) {
|
|
888
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
889
|
+
}
|
|
890
|
+
function asString(value) {
|
|
891
|
+
return typeof value === "string" ? value.trim() : "";
|
|
892
|
+
}
|
|
893
|
+
function isEventUri(value) {
|
|
894
|
+
return /^https:\/\/api\.calendly\.com\/scheduled_events\/[^/]+$/i.test(value);
|
|
895
|
+
}
|
|
896
|
+
function isEventTypeUri(value) {
|
|
897
|
+
return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
|
|
898
|
+
}
|
|
899
|
+
function parseToolCard(value) {
|
|
900
|
+
const record = asRecord(value);
|
|
901
|
+
if (!record) return null;
|
|
902
|
+
if (record.booking_offer && asString(record.type) !== "booking_offer") {
|
|
903
|
+
const nested = parseToolCard(record.booking_offer);
|
|
904
|
+
if (nested) return nested;
|
|
905
|
+
}
|
|
906
|
+
const type = asString(record.type);
|
|
907
|
+
if (type === "booking_offer") {
|
|
908
|
+
const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
|
|
909
|
+
const entry = asRecord(item);
|
|
910
|
+
const uri = asString(entry?.uri);
|
|
911
|
+
if (!entry || !isEventTypeUri(uri)) return [];
|
|
912
|
+
const duration = entry.duration;
|
|
913
|
+
const locationKind = asString(entry.locationKind);
|
|
914
|
+
const location = asString(entry.location);
|
|
915
|
+
return [
|
|
916
|
+
{
|
|
917
|
+
name: asString(entry.name) || "Meeting",
|
|
918
|
+
uri,
|
|
919
|
+
...typeof duration === "number" ? { duration } : {},
|
|
920
|
+
...locationKind ? { locationKind } : {},
|
|
921
|
+
...location ? { location } : {}
|
|
922
|
+
}
|
|
923
|
+
];
|
|
924
|
+
}) : [];
|
|
925
|
+
const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
|
|
926
|
+
const entry = asRecord(item);
|
|
927
|
+
const startTime = asString(entry?.startTime);
|
|
928
|
+
if (!entry || !startTime) return [];
|
|
929
|
+
const eventTypeUri = asString(entry.eventTypeUri);
|
|
930
|
+
return [
|
|
931
|
+
{
|
|
932
|
+
startTime,
|
|
933
|
+
...isEventTypeUri(eventTypeUri) ? { eventTypeUri } : {}
|
|
934
|
+
}
|
|
935
|
+
];
|
|
936
|
+
}) : [];
|
|
937
|
+
if (slots.length === 0) return null;
|
|
938
|
+
return { type: "booking_offer", eventTypes, slots };
|
|
939
|
+
}
|
|
940
|
+
if (type === "booking_confirmed") {
|
|
941
|
+
const eventUri = asString(record.eventUri);
|
|
942
|
+
if (!isEventUri(eventUri)) return null;
|
|
943
|
+
const inviteeUri = asString(record.inviteeUri);
|
|
944
|
+
const inviteeEmail = asString(record.inviteeEmail);
|
|
945
|
+
const startTime = asString(record.startTime);
|
|
946
|
+
return {
|
|
947
|
+
type: "booking_confirmed",
|
|
948
|
+
eventUri,
|
|
949
|
+
...inviteeUri ? { inviteeUri } : {},
|
|
950
|
+
...inviteeEmail ? { inviteeEmail } : {},
|
|
951
|
+
...startTime ? { startTime } : {}
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (type === "booking_canceled") {
|
|
955
|
+
const eventUri = asString(record.eventUri);
|
|
956
|
+
if (!isEventUri(eventUri)) return null;
|
|
957
|
+
return { type: "booking_canceled", eventUri };
|
|
958
|
+
}
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
961
|
+
function formatBookingOfferFence(offer) {
|
|
962
|
+
return [
|
|
963
|
+
"```webless-tool-card",
|
|
964
|
+
JSON.stringify({
|
|
965
|
+
type: "booking_offer",
|
|
966
|
+
eventTypes: offer.eventTypes,
|
|
967
|
+
slots: offer.slots
|
|
968
|
+
}),
|
|
969
|
+
"```"
|
|
970
|
+
].join("\n");
|
|
971
|
+
}
|
|
972
|
+
function bookingOfferFromActionOutput(output) {
|
|
973
|
+
const record = asRecord(output);
|
|
974
|
+
const data = asRecord(record?.data) ?? record;
|
|
975
|
+
const card = parseToolCard(data);
|
|
976
|
+
return card?.type === "booking_offer" ? card : null;
|
|
977
|
+
}
|
|
978
|
+
function ensureBookingOfferText(text, offer) {
|
|
979
|
+
if (!offer) return text;
|
|
980
|
+
if (extractToolCards(text).some((card) => card.type === "booking_offer")) {
|
|
981
|
+
return text;
|
|
982
|
+
}
|
|
983
|
+
const visible = stripToolCards(text).trim() || text.trim();
|
|
984
|
+
return `${visible}
|
|
985
|
+
|
|
986
|
+
${formatBookingOfferFence(offer)}`;
|
|
987
|
+
}
|
|
988
|
+
function hideToolCardFences(text) {
|
|
989
|
+
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();
|
|
990
|
+
}
|
|
991
|
+
function visitorTimeZone() {
|
|
992
|
+
try {
|
|
993
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
994
|
+
} catch {
|
|
995
|
+
return "UTC";
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
function extractToolCards(text) {
|
|
999
|
+
const cards = [];
|
|
1000
|
+
for (const match of text.matchAll(FENCE_PATTERN)) {
|
|
1001
|
+
try {
|
|
1002
|
+
const card = parseToolCard(JSON.parse(match[1] ?? ""));
|
|
1003
|
+
if (card) cards.push(card);
|
|
1004
|
+
} catch {
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return cards;
|
|
1008
|
+
}
|
|
1009
|
+
function stripToolCards(text) {
|
|
1010
|
+
return text.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
1011
|
+
}
|
|
1012
|
+
function localDateKey(date) {
|
|
1013
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
1014
|
+
return [
|
|
1015
|
+
date.getFullYear(),
|
|
1016
|
+
String(date.getMonth() + 1).padStart(2, "0"),
|
|
1017
|
+
String(date.getDate()).padStart(2, "0")
|
|
1018
|
+
].join("-");
|
|
1019
|
+
}
|
|
1020
|
+
function slotDateKey(startTime) {
|
|
1021
|
+
return localDateKey(new Date(startTime)) || startTime;
|
|
1022
|
+
}
|
|
1023
|
+
function bookingSlotsForEventType(slots, eventTypeUri) {
|
|
1024
|
+
return slots.filter(
|
|
1025
|
+
(slot) => !eventTypeUri || !slot.eventTypeUri || slot.eventTypeUri === eventTypeUri
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
function firstAvailableBookingMonth(slots) {
|
|
1029
|
+
let earliest;
|
|
1030
|
+
for (const slot of slots) {
|
|
1031
|
+
const key = slotDateKey(slot.startTime);
|
|
1032
|
+
if (!earliest || key < earliest) earliest = key;
|
|
1033
|
+
}
|
|
1034
|
+
const [year, month] = (earliest ?? slotDateKey((/* @__PURE__ */ new Date()).toISOString())).split("-").map(Number);
|
|
1035
|
+
if (!year || !month) {
|
|
1036
|
+
const now = /* @__PURE__ */ new Date();
|
|
1037
|
+
return { year: now.getFullYear(), month: now.getMonth() };
|
|
1038
|
+
}
|
|
1039
|
+
return { year, month: month - 1 };
|
|
1040
|
+
}
|
|
1041
|
+
function formatMonthTitle(year, month) {
|
|
1042
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1043
|
+
month: "long",
|
|
1044
|
+
year: "numeric"
|
|
1045
|
+
}).format(new Date(year, month, 1));
|
|
1046
|
+
}
|
|
1047
|
+
function formatLongDate(startTime) {
|
|
1048
|
+
const date = new Date(startTime);
|
|
1049
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
1050
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1051
|
+
weekday: "long",
|
|
1052
|
+
month: "long",
|
|
1053
|
+
day: "numeric"
|
|
1054
|
+
}).format(date);
|
|
1055
|
+
}
|
|
1056
|
+
function weekdayLabels() {
|
|
1057
|
+
return Array.from(
|
|
1058
|
+
{ length: 7 },
|
|
1059
|
+
(_, index) => new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(
|
|
1060
|
+
new Date(2026, 7, 3 + index)
|
|
1061
|
+
)
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
function formatTimeChip(startTime) {
|
|
1065
|
+
const date = new Date(startTime);
|
|
1066
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
1067
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1068
|
+
hour: "numeric",
|
|
1069
|
+
minute: "2-digit"
|
|
1070
|
+
}).format(date);
|
|
1071
|
+
}
|
|
1072
|
+
function formatSlotTimeZone(startTime) {
|
|
1073
|
+
const date = new Date(startTime);
|
|
1074
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
1075
|
+
return new Intl.DateTimeFormat(void 0, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
|
|
1076
|
+
}
|
|
1077
|
+
function formatSlotLabel(startTime) {
|
|
1078
|
+
const date = new Date(startTime);
|
|
1079
|
+
if (Number.isNaN(date.getTime())) return startTime;
|
|
1080
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
1081
|
+
weekday: "short",
|
|
1082
|
+
month: "short",
|
|
1083
|
+
day: "numeric",
|
|
1084
|
+
hour: "numeric",
|
|
1085
|
+
minute: "2-digit",
|
|
1086
|
+
timeZoneName: "short"
|
|
1087
|
+
}).format(date);
|
|
1088
|
+
}
|
|
1089
|
+
function formatBookingRequest(input) {
|
|
1090
|
+
return [
|
|
1091
|
+
"Book this meeting now with CALENDLY_POST_INVITEE.",
|
|
1092
|
+
"Do not open a Calendly URL and do not list other scheduled events.",
|
|
1093
|
+
"Do not invent a location kind. Use only the location fields below.",
|
|
1094
|
+
`event_type: ${input.eventTypeUri}`,
|
|
1095
|
+
`start_time: ${input.startTime}`,
|
|
1096
|
+
`invitee.name: ${input.inviteeName}`,
|
|
1097
|
+
`invitee.email: ${input.inviteeEmail}`,
|
|
1098
|
+
`invitee.timezone: ${input.timezone}`,
|
|
1099
|
+
...input.locationKind ? [
|
|
1100
|
+
`location.kind: ${input.locationKind}`,
|
|
1101
|
+
...input.location ? [`location.location: ${input.location}`] : []
|
|
1102
|
+
] : ["Do not send a location field."],
|
|
1103
|
+
"After it succeeds, reply with one short confirmation and a webless-tool-card booking_confirmed block using visitor_booking."
|
|
1104
|
+
].join("\n");
|
|
1105
|
+
}
|
|
1106
|
+
function visitorBookingPrefix(booking) {
|
|
1107
|
+
return [
|
|
1108
|
+
"This visitor already booked a meeting. Use only this meeting:",
|
|
1109
|
+
`- scheduled event URI: ${booking.eventUri}`,
|
|
1110
|
+
...booking.inviteeUri ? [`- invitee URI: ${booking.inviteeUri}`] : [],
|
|
1111
|
+
...booking.inviteeEmail ? [`- invitee email: ${booking.inviteeEmail}`] : [],
|
|
1112
|
+
"For details call CALENDLY_GET_EVENT or CALENDLY_GET_EVENT_INVITEE with those URIs.",
|
|
1113
|
+
"If you must list events, pass this invitee_email. Never describe any other scheduled event.",
|
|
1114
|
+
"start_time values from Calendly are UTC."
|
|
1115
|
+
].join("\n");
|
|
1116
|
+
}
|
|
1117
|
+
|
|
841
1118
|
// src/react/persisted-conversation.ts
|
|
842
1119
|
var CONVERSATION_VERSION = 1;
|
|
843
1120
|
function conversationKey(storageKeyPrefix, visitorSessionId) {
|
|
@@ -849,13 +1126,25 @@ function parseMessage(value) {
|
|
|
849
1126
|
if (typeof record.id !== "string" || record.role !== "agent" && record.role !== "visitor" || typeof record.text !== "string" || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt)) {
|
|
850
1127
|
return null;
|
|
851
1128
|
}
|
|
1129
|
+
if (record.role === "visitor") {
|
|
1130
|
+
return {
|
|
1131
|
+
id: record.id,
|
|
1132
|
+
role: "visitor",
|
|
1133
|
+
text: record.text,
|
|
1134
|
+
createdAt: record.createdAt,
|
|
1135
|
+
...typeof record.runtimeText === "string" && record.runtimeText ? { runtimeText: record.runtimeText } : {}
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
852
1138
|
return {
|
|
853
1139
|
id: record.id,
|
|
854
|
-
role:
|
|
1140
|
+
role: "agent",
|
|
855
1141
|
text: record.text,
|
|
856
1142
|
createdAt: record.createdAt
|
|
857
1143
|
};
|
|
858
1144
|
}
|
|
1145
|
+
function visitorTurnText(message) {
|
|
1146
|
+
return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
|
|
1147
|
+
}
|
|
859
1148
|
function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
860
1149
|
if (typeof sessionStorage === "undefined") return null;
|
|
861
1150
|
const raw = sessionStorage.getItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
@@ -888,6 +1177,42 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
|
|
|
888
1177
|
function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
|
|
889
1178
|
if (typeof sessionStorage === "undefined") return;
|
|
890
1179
|
sessionStorage.removeItem(conversationKey(storageKeyPrefix, visitorSessionId));
|
|
1180
|
+
clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
|
|
1181
|
+
}
|
|
1182
|
+
function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
|
|
1183
|
+
return `${storageKeyPrefix}:pending-booking:${visitorSessionId}`;
|
|
1184
|
+
}
|
|
1185
|
+
function loadPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1186
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1187
|
+
const raw = sessionStorage.getItem(
|
|
1188
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId)
|
|
1189
|
+
);
|
|
1190
|
+
if (!raw) return null;
|
|
1191
|
+
try {
|
|
1192
|
+
const value = JSON.parse(raw);
|
|
1193
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1194
|
+
const record = value;
|
|
1195
|
+
if (typeof record.eventUri !== "string" || !record.eventUri) return null;
|
|
1196
|
+
return {
|
|
1197
|
+
eventUri: record.eventUri,
|
|
1198
|
+
...typeof record.inviteeUri === "string" && record.inviteeUri ? { inviteeUri: record.inviteeUri } : {},
|
|
1199
|
+
...typeof record.inviteeEmail === "string" && record.inviteeEmail ? { inviteeEmail: record.inviteeEmail } : {},
|
|
1200
|
+
...typeof record.startTime === "string" && record.startTime ? { startTime: record.startTime } : {}
|
|
1201
|
+
};
|
|
1202
|
+
} catch {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
|
|
1207
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1208
|
+
sessionStorage.setItem(
|
|
1209
|
+
pendingBookingKey(storageKeyPrefix, visitorSessionId),
|
|
1210
|
+
JSON.stringify(booking)
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
|
|
1214
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1215
|
+
sessionStorage.removeItem(pendingBookingKey(storageKeyPrefix, visitorSessionId));
|
|
891
1216
|
}
|
|
892
1217
|
|
|
893
1218
|
// src/react/hooks/useAgentChat.ts
|
|
@@ -907,6 +1232,7 @@ function createInitialState(greeting = DEFAULT_GREETING) {
|
|
|
907
1232
|
journey: null,
|
|
908
1233
|
followUps: [],
|
|
909
1234
|
streamingText: "",
|
|
1235
|
+
pendingOffer: null,
|
|
910
1236
|
error: null
|
|
911
1237
|
};
|
|
912
1238
|
}
|
|
@@ -989,6 +1315,9 @@ function useAgentChat({
|
|
|
989
1315
|
initialState
|
|
990
1316
|
)
|
|
991
1317
|
);
|
|
1318
|
+
const pendingBookingRef = (0, import_react2.useRef)(
|
|
1319
|
+
loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
|
|
1320
|
+
);
|
|
992
1321
|
const runRef = (0, import_react2.useRef)(null);
|
|
993
1322
|
const clientRef = (0, import_react2.useRef)(
|
|
994
1323
|
createAgentClient({
|
|
@@ -1021,6 +1350,10 @@ function useAgentChat({
|
|
|
1021
1350
|
visitorSessionId: visitorId,
|
|
1022
1351
|
storageKeyPrefix: resolvedStorageKeyPrefix
|
|
1023
1352
|
});
|
|
1353
|
+
pendingBookingRef.current = loadPendingWidgetBooking(
|
|
1354
|
+
resolvedStorageKeyPrefix,
|
|
1355
|
+
visitorId
|
|
1356
|
+
);
|
|
1024
1357
|
setState(
|
|
1025
1358
|
stateFromConversation(
|
|
1026
1359
|
loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
|
|
@@ -1056,6 +1389,7 @@ function useAgentChat({
|
|
|
1056
1389
|
runRef.current?.abort();
|
|
1057
1390
|
runRef.current = null;
|
|
1058
1391
|
clientRef.current.reset();
|
|
1392
|
+
pendingBookingRef.current = null;
|
|
1059
1393
|
clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
|
|
1060
1394
|
setState(initialState);
|
|
1061
1395
|
}, [initialState, resolvedStorageKeyPrefix, visitorId]);
|
|
@@ -1067,6 +1401,7 @@ function useAgentChat({
|
|
|
1067
1401
|
try {
|
|
1068
1402
|
let streamStarted = Boolean(initialText);
|
|
1069
1403
|
let streamed = initialText;
|
|
1404
|
+
const capturedOffers = [];
|
|
1070
1405
|
const handlers = {
|
|
1071
1406
|
onWork: (item) => {
|
|
1072
1407
|
if (!isActiveRun()) return;
|
|
@@ -1076,6 +1411,12 @@ function useAgentChat({
|
|
|
1076
1411
|
toolSteps: upsertToolStep(prev.toolSteps, item)
|
|
1077
1412
|
}));
|
|
1078
1413
|
},
|
|
1414
|
+
onActionResult: (output) => {
|
|
1415
|
+
const offer = bookingOfferFromActionOutput(output);
|
|
1416
|
+
if (!offer) return;
|
|
1417
|
+
capturedOffers.push(offer);
|
|
1418
|
+
setState((prev) => ({ ...prev, pendingOffer: offer }));
|
|
1419
|
+
},
|
|
1079
1420
|
onDelta: (delta) => {
|
|
1080
1421
|
if (!isActiveRun()) return;
|
|
1081
1422
|
if (!streamStarted) {
|
|
@@ -1091,7 +1432,8 @@ function useAgentChat({
|
|
|
1091
1432
|
setState((prev) => ({
|
|
1092
1433
|
...prev,
|
|
1093
1434
|
phase: "streaming",
|
|
1094
|
-
streamingText: streamed
|
|
1435
|
+
streamingText: hideToolCardFences(streamed),
|
|
1436
|
+
pendingOffer: prev.pendingOffer ?? capturedOffers.at(-1) ?? null
|
|
1095
1437
|
}));
|
|
1096
1438
|
},
|
|
1097
1439
|
onComplete: () => {
|
|
@@ -1112,18 +1454,34 @@ function useAgentChat({
|
|
|
1112
1454
|
});
|
|
1113
1455
|
}
|
|
1114
1456
|
if (!isActiveRun() || finalText === null) return;
|
|
1457
|
+
const displayText = ensureBookingOfferText(
|
|
1458
|
+
finalText,
|
|
1459
|
+
capturedOffers.at(-1) ?? null
|
|
1460
|
+
);
|
|
1115
1461
|
const agentMessage = {
|
|
1116
1462
|
id: `agent-${Date.now()}`,
|
|
1117
1463
|
role: "agent",
|
|
1118
|
-
text:
|
|
1464
|
+
text: displayText,
|
|
1119
1465
|
createdAt: Date.now()
|
|
1120
1466
|
};
|
|
1467
|
+
const parsedCards = extractToolCards(displayText);
|
|
1468
|
+
for (const card of parsedCards) {
|
|
1469
|
+
if (card.type === "booking_confirmed") {
|
|
1470
|
+
pendingBookingRef.current = card;
|
|
1471
|
+
savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, card);
|
|
1472
|
+
}
|
|
1473
|
+
if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
|
|
1474
|
+
pendingBookingRef.current = null;
|
|
1475
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1121
1478
|
setState((prev) => ({
|
|
1122
1479
|
...prev,
|
|
1123
1480
|
phase: "complete",
|
|
1124
1481
|
messages: [...prev.messages, agentMessage],
|
|
1125
1482
|
toolSteps: completeActivePlanning(prev.toolSteps),
|
|
1126
1483
|
streamingText: "",
|
|
1484
|
+
pendingOffer: null,
|
|
1127
1485
|
followUps: [],
|
|
1128
1486
|
journey: null
|
|
1129
1487
|
}));
|
|
@@ -1145,26 +1503,54 @@ function useAgentChat({
|
|
|
1145
1503
|
} : step
|
|
1146
1504
|
),
|
|
1147
1505
|
streamingText: "",
|
|
1506
|
+
pendingOffer: null,
|
|
1148
1507
|
error: message
|
|
1149
1508
|
}));
|
|
1150
1509
|
runRef.current = null;
|
|
1151
1510
|
}
|
|
1152
1511
|
},
|
|
1153
|
-
[]
|
|
1512
|
+
[resolvedStorageKeyPrefix, visitorId]
|
|
1513
|
+
);
|
|
1514
|
+
const rememberBooking = (0, import_react2.useCallback)(
|
|
1515
|
+
(booking) => {
|
|
1516
|
+
const current = pendingBookingRef.current;
|
|
1517
|
+
if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
pendingBookingRef.current = booking;
|
|
1521
|
+
savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, booking);
|
|
1522
|
+
},
|
|
1523
|
+
[resolvedStorageKeyPrefix, visitorId]
|
|
1524
|
+
);
|
|
1525
|
+
const forgetBooking = (0, import_react2.useCallback)(
|
|
1526
|
+
(eventUri) => {
|
|
1527
|
+
const current = pendingBookingRef.current;
|
|
1528
|
+
if (!current) return;
|
|
1529
|
+
if (eventUri && current.eventUri !== eventUri) return;
|
|
1530
|
+
pendingBookingRef.current = null;
|
|
1531
|
+
clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
|
|
1532
|
+
},
|
|
1533
|
+
[resolvedStorageKeyPrefix, visitorId]
|
|
1154
1534
|
);
|
|
1155
1535
|
const submit = (0, import_react2.useCallback)(
|
|
1156
|
-
async (visitorText) => {
|
|
1536
|
+
async (visitorText, options) => {
|
|
1157
1537
|
if (runRef.current) {
|
|
1158
1538
|
runRef.current.abort();
|
|
1159
1539
|
clientRef.current.cancelActive();
|
|
1160
1540
|
}
|
|
1161
1541
|
const controller = new AbortController();
|
|
1162
1542
|
runRef.current = controller;
|
|
1543
|
+
const booking = pendingBookingRef.current;
|
|
1544
|
+
const outgoing = options?.runtimeText ?? visitorText;
|
|
1545
|
+
const runtimeText = booking ? `${visitorBookingPrefix(booking)}
|
|
1546
|
+
|
|
1547
|
+
${outgoing}` : outgoing;
|
|
1163
1548
|
const visitorMessage = {
|
|
1164
1549
|
id: `visitor-${Date.now()}`,
|
|
1165
1550
|
role: "visitor",
|
|
1166
1551
|
text: visitorText,
|
|
1167
|
-
createdAt: Date.now()
|
|
1552
|
+
createdAt: Date.now(),
|
|
1553
|
+
...runtimeText !== visitorText ? { runtimeText } : {}
|
|
1168
1554
|
};
|
|
1169
1555
|
setState((prev) => ({
|
|
1170
1556
|
...prev,
|
|
@@ -1181,9 +1567,10 @@ function useAgentChat({
|
|
|
1181
1567
|
journey: null,
|
|
1182
1568
|
followUps: [],
|
|
1183
1569
|
streamingText: "",
|
|
1570
|
+
pendingOffer: null,
|
|
1184
1571
|
error: null
|
|
1185
1572
|
}));
|
|
1186
|
-
await runTurn({ controller, resume: false, visitorText });
|
|
1573
|
+
await runTurn({ controller, resume: false, visitorText: runtimeText });
|
|
1187
1574
|
},
|
|
1188
1575
|
[runTurn]
|
|
1189
1576
|
);
|
|
@@ -1210,12 +1597,13 @@ function useAgentChat({
|
|
|
1210
1597
|
journey: null,
|
|
1211
1598
|
followUps: [],
|
|
1212
1599
|
streamingText: "",
|
|
1600
|
+
pendingOffer: null,
|
|
1213
1601
|
error: null
|
|
1214
1602
|
}));
|
|
1215
1603
|
await runTurn({
|
|
1216
1604
|
controller,
|
|
1217
1605
|
resume: false,
|
|
1218
|
-
visitorText: visitorMessage
|
|
1606
|
+
visitorText: visitorTurnText(visitorMessage)
|
|
1219
1607
|
});
|
|
1220
1608
|
}, [runTurn, state.messages]);
|
|
1221
1609
|
(0, import_react2.useEffect)(() => {
|
|
@@ -1232,7 +1620,7 @@ function useAgentChat({
|
|
|
1232
1620
|
controller,
|
|
1233
1621
|
initialText: conversation.streamingText,
|
|
1234
1622
|
resume: true,
|
|
1235
|
-
visitorText: visitorMessage
|
|
1623
|
+
visitorText: visitorTurnText(visitorMessage)
|
|
1236
1624
|
});
|
|
1237
1625
|
return () => {
|
|
1238
1626
|
if (runRef.current === controller) {
|
|
@@ -1252,6 +1640,8 @@ function useAgentChat({
|
|
|
1252
1640
|
reset,
|
|
1253
1641
|
retry,
|
|
1254
1642
|
submit,
|
|
1643
|
+
rememberBooking,
|
|
1644
|
+
forgetBooking,
|
|
1255
1645
|
visitorSessionId: visitorId,
|
|
1256
1646
|
sessionId: clientRef.current.getActiveSessionId()
|
|
1257
1647
|
};
|
|
@@ -1259,13 +1649,6 @@ function useAgentChat({
|
|
|
1259
1649
|
function hasVisitorMessages(messages) {
|
|
1260
1650
|
return messages.some((message) => message.role === "visitor");
|
|
1261
1651
|
}
|
|
1262
|
-
function createIdleSuggestions() {
|
|
1263
|
-
return [
|
|
1264
|
-
{ id: "idle-1", label: "Hello \u2014 what can you do?" },
|
|
1265
|
-
{ id: "idle-2", label: "Help me evaluate this product" },
|
|
1266
|
-
{ id: "idle-3", label: "What should I ask you?" }
|
|
1267
|
-
];
|
|
1268
|
-
}
|
|
1269
1652
|
function isAgentBusy(phase) {
|
|
1270
1653
|
return phase === "thinking" || phase === "running-tools" || phase === "streaming";
|
|
1271
1654
|
}
|
|
@@ -1304,7 +1687,7 @@ function normalizeAgentPlacement(placement) {
|
|
|
1304
1687
|
}
|
|
1305
1688
|
|
|
1306
1689
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1307
|
-
var
|
|
1690
|
+
var import_react6 = require("react");
|
|
1308
1691
|
|
|
1309
1692
|
// src/react/types/conversation.ts
|
|
1310
1693
|
var defaultAgentRailTheme = {
|
|
@@ -1344,10 +1727,9 @@ function workSummary(steps, failed, brandLabel) {
|
|
|
1344
1727
|
);
|
|
1345
1728
|
if (hasError) return "Answered with available information";
|
|
1346
1729
|
if (specialists.length > 1)
|
|
1347
|
-
return `
|
|
1348
|
-
if (specialists.length === 1)
|
|
1349
|
-
|
|
1350
|
-
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";
|
|
1351
1733
|
return "Answer ready";
|
|
1352
1734
|
}
|
|
1353
1735
|
function stepLabel(step, brandLabel) {
|
|
@@ -1397,7 +1779,6 @@ function AgentActivityBubble({
|
|
|
1397
1779
|
steps
|
|
1398
1780
|
}) {
|
|
1399
1781
|
const active = steps.some((step) => step.state === "active");
|
|
1400
|
-
const delegated = steps.some((step) => step.kind === "specialist");
|
|
1401
1782
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
|
|
1402
1783
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
|
|
1403
1784
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -1409,57 +1790,50 @@ function AgentActivityBubble({
|
|
|
1409
1790
|
),
|
|
1410
1791
|
workSummary(steps, failed, brandLabel)
|
|
1411
1792
|
] }),
|
|
1412
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1413
|
-
"details",
|
|
1414
|
-
{
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
},
|
|
1457
|
-
step.id
|
|
1458
|
-
);
|
|
1459
|
-
}) })
|
|
1460
|
-
]
|
|
1461
|
-
}
|
|
1462
|
-
)
|
|
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
|
+
] })
|
|
1463
1837
|
] });
|
|
1464
1838
|
}
|
|
1465
1839
|
|
|
@@ -1561,34 +1935,318 @@ function FollowUpChips({
|
|
|
1561
1935
|
] });
|
|
1562
1936
|
}
|
|
1563
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
|
+
|
|
1564
2200
|
// src/react/components/MessageBubble/MessageBubble.tsx
|
|
1565
2201
|
var import_streamdown = require("streamdown");
|
|
1566
2202
|
var import_styles = require("streamdown/styles.css");
|
|
1567
|
-
var
|
|
1568
|
-
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);
|
|
1569
2217
|
if (message.role === "visitor") {
|
|
1570
|
-
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 }) });
|
|
1571
2219
|
}
|
|
1572
|
-
return /* @__PURE__ */ (0,
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
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
|
+
] });
|
|
1586
2244
|
}
|
|
1587
2245
|
|
|
1588
2246
|
// src/react/components/AgentRail/AgentRail.tsx
|
|
1589
|
-
var
|
|
2247
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1590
2248
|
function MinimizeIcon() {
|
|
1591
|
-
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)(
|
|
1592
2250
|
"path",
|
|
1593
2251
|
{
|
|
1594
2252
|
d: "M3.5 8h9",
|
|
@@ -1599,7 +2257,7 @@ function MinimizeIcon() {
|
|
|
1599
2257
|
) });
|
|
1600
2258
|
}
|
|
1601
2259
|
function CloseIcon() {
|
|
1602
|
-
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)(
|
|
1603
2261
|
"path",
|
|
1604
2262
|
{
|
|
1605
2263
|
d: "M4 4l8 8M12 4l-8 8",
|
|
@@ -1610,7 +2268,7 @@ function CloseIcon() {
|
|
|
1610
2268
|
) });
|
|
1611
2269
|
}
|
|
1612
2270
|
function NewChatIcon() {
|
|
1613
|
-
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)(
|
|
1614
2272
|
"path",
|
|
1615
2273
|
{
|
|
1616
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",
|
|
@@ -1622,7 +2280,7 @@ function NewChatIcon() {
|
|
|
1622
2280
|
) });
|
|
1623
2281
|
}
|
|
1624
2282
|
function ExpandIcon() {
|
|
1625
|
-
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)(
|
|
1626
2284
|
"path",
|
|
1627
2285
|
{
|
|
1628
2286
|
d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
|
|
@@ -1634,7 +2292,7 @@ function ExpandIcon() {
|
|
|
1634
2292
|
) });
|
|
1635
2293
|
}
|
|
1636
2294
|
function RestoreIcon() {
|
|
1637
|
-
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)(
|
|
1638
2296
|
"path",
|
|
1639
2297
|
{
|
|
1640
2298
|
d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
|
|
@@ -1660,10 +2318,10 @@ function AgentRail({
|
|
|
1660
2318
|
onReset,
|
|
1661
2319
|
onRetry,
|
|
1662
2320
|
onSubmit,
|
|
1663
|
-
onFollowUpSelect
|
|
2321
|
+
onFollowUpSelect,
|
|
2322
|
+
onBook
|
|
1664
2323
|
}) {
|
|
1665
|
-
const transcriptRef = (0,
|
|
1666
|
-
const welcomeTitleId = (0, import_react5.useId)();
|
|
2324
|
+
const transcriptRef = (0, import_react6.useRef)(null);
|
|
1667
2325
|
const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
|
|
1668
2326
|
const railStyle = {
|
|
1669
2327
|
"--rail-width": resolvedTheme.railMaxWidth,
|
|
@@ -1703,8 +2361,14 @@ function AgentRail({
|
|
|
1703
2361
|
role: "agent",
|
|
1704
2362
|
streaming: true,
|
|
1705
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."
|
|
1706
2370
|
} : null;
|
|
1707
|
-
(0,
|
|
2371
|
+
(0, import_react6.useEffect)(() => {
|
|
1708
2372
|
const node = transcriptRef.current;
|
|
1709
2373
|
if (!node) return;
|
|
1710
2374
|
node.scrollTop = node.scrollHeight;
|
|
@@ -1715,7 +2379,7 @@ function AgentRail({
|
|
|
1715
2379
|
state.followUps,
|
|
1716
2380
|
state.journey
|
|
1717
2381
|
]);
|
|
1718
|
-
return /* @__PURE__ */ (0,
|
|
2382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1719
2383
|
"aside",
|
|
1720
2384
|
{
|
|
1721
2385
|
className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
|
|
@@ -1726,30 +2390,30 @@ function AgentRail({
|
|
|
1726
2390
|
role: mobileFullscreen || expanded ? "dialog" : void 0,
|
|
1727
2391
|
tabIndex: mobileFullscreen || expanded ? -1 : void 0,
|
|
1728
2392
|
children: [
|
|
1729
|
-
/* @__PURE__ */ (0,
|
|
1730
|
-
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)(
|
|
1731
2395
|
"button",
|
|
1732
2396
|
{
|
|
1733
2397
|
type: "button",
|
|
1734
2398
|
className: "agent-rail__collapse",
|
|
1735
2399
|
"aria-label": "Collapse assist",
|
|
1736
2400
|
onClick: onCollapse,
|
|
1737
|
-
children: /* @__PURE__ */ (0,
|
|
2401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
|
|
1738
2402
|
}
|
|
1739
|
-
) : onClose ? /* @__PURE__ */ (0,
|
|
2403
|
+
) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1740
2404
|
"button",
|
|
1741
2405
|
{
|
|
1742
2406
|
type: "button",
|
|
1743
2407
|
className: "agent-rail__close",
|
|
1744
2408
|
"aria-label": "Close agent",
|
|
1745
2409
|
onClick: onClose,
|
|
1746
|
-
children: /* @__PURE__ */ (0,
|
|
2410
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, {})
|
|
1747
2411
|
}
|
|
1748
|
-
) : /* @__PURE__ */ (0,
|
|
1749
|
-
/* @__PURE__ */ (0,
|
|
1750
|
-
/* @__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: [
|
|
1751
2415
|
brandLabel.slice(0, 1).toUpperCase(),
|
|
1752
|
-
brandLogoUrl ? /* @__PURE__ */ (0,
|
|
2416
|
+
brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1753
2417
|
"img",
|
|
1754
2418
|
{
|
|
1755
2419
|
className: "agent-rail__brand-logo",
|
|
@@ -1761,10 +2425,10 @@ function AgentRail({
|
|
|
1761
2425
|
}
|
|
1762
2426
|
) : null
|
|
1763
2427
|
] }),
|
|
1764
|
-
/* @__PURE__ */ (0,
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
|
|
1765
2429
|
] }),
|
|
1766
|
-
/* @__PURE__ */ (0,
|
|
1767
|
-
onReset ? /* @__PURE__ */ (0,
|
|
2430
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__actions", children: [
|
|
2431
|
+
onReset ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1768
2432
|
"button",
|
|
1769
2433
|
{
|
|
1770
2434
|
type: "button",
|
|
@@ -1772,60 +2436,37 @@ function AgentRail({
|
|
|
1772
2436
|
"aria-label": "Start a new conversation",
|
|
1773
2437
|
disabled: !hasVisitorMessages2,
|
|
1774
2438
|
onClick: onReset,
|
|
1775
|
-
children: /* @__PURE__ */ (0,
|
|
2439
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NewChatIcon, {})
|
|
1776
2440
|
}
|
|
1777
2441
|
) : null,
|
|
1778
|
-
onExpandToggle ? /* @__PURE__ */ (0,
|
|
2442
|
+
onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1779
2443
|
"button",
|
|
1780
2444
|
{
|
|
1781
2445
|
type: "button",
|
|
1782
2446
|
className: "agent-rail__expand",
|
|
1783
2447
|
"aria-label": expanded ? "Exit focus view" : "Open focus view",
|
|
1784
2448
|
onClick: onExpandToggle,
|
|
1785
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
2449
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
|
|
1786
2450
|
}
|
|
1787
2451
|
) : null
|
|
1788
2452
|
] })
|
|
1789
2453
|
] }) }),
|
|
1790
|
-
/* @__PURE__ */ (0,
|
|
1791
|
-
!hasVisitorMessages2 ? /* @__PURE__ */ (0,
|
|
1792
|
-
"
|
|
1793
|
-
{
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
event.currentTarget.hidden = true;
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
) : null
|
|
1810
|
-
] }),
|
|
1811
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__welcome-copy", children: [
|
|
1812
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: welcomeTitleId, children: "What can I help you find?" }),
|
|
1813
|
-
greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: greeting.text }) : null
|
|
1814
|
-
] }),
|
|
1815
|
-
showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1816
|
-
FollowUpChips,
|
|
1817
|
-
{
|
|
1818
|
-
suggestions: state.followUps,
|
|
1819
|
-
disabled: isBusy,
|
|
1820
|
-
label: "Start here",
|
|
1821
|
-
onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
|
|
1822
|
-
}
|
|
1823
|
-
) }) : null
|
|
1824
|
-
]
|
|
1825
|
-
}
|
|
1826
|
-
) : null,
|
|
1827
|
-
transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
|
|
1828
|
-
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)(
|
|
1829
2470
|
AgentActivityBubble,
|
|
1830
2471
|
{
|
|
1831
2472
|
brandLabel,
|
|
@@ -1834,18 +2475,24 @@ function AgentRail({
|
|
|
1834
2475
|
steps: state.toolSteps
|
|
1835
2476
|
}
|
|
1836
2477
|
) : null,
|
|
1837
|
-
streamingMessage ? /* @__PURE__ */ (0,
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
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 })
|
|
1843
2490
|
] }),
|
|
1844
|
-
onRetry ? /* @__PURE__ */ (0,
|
|
2491
|
+
onRetry ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
|
|
1845
2492
|
] }) : null
|
|
1846
2493
|
] }) }),
|
|
1847
|
-
/* @__PURE__ */ (0,
|
|
1848
|
-
/* @__PURE__ */ (0,
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1849
2496
|
Composer,
|
|
1850
2497
|
{
|
|
1851
2498
|
variant: expanded || mobileFullscreen ? "dock" : "default",
|
|
@@ -1854,10 +2501,9 @@ function AgentRail({
|
|
|
1854
2501
|
onSubmit
|
|
1855
2502
|
}
|
|
1856
2503
|
),
|
|
1857
|
-
/* @__PURE__ */ (0,
|
|
1858
|
-
/* @__PURE__ */ (0,
|
|
1859
|
-
/* @__PURE__ */ (0,
|
|
1860
|
-
/* @__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 })
|
|
1861
2507
|
] }) })
|
|
1862
2508
|
] })
|
|
1863
2509
|
]
|
|
@@ -1866,9 +2512,9 @@ function AgentRail({
|
|
|
1866
2512
|
}
|
|
1867
2513
|
|
|
1868
2514
|
// src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
|
|
1869
|
-
var
|
|
2515
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1870
2516
|
function SparklesIcon() {
|
|
1871
|
-
return /* @__PURE__ */ (0,
|
|
2517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1872
2518
|
"svg",
|
|
1873
2519
|
{
|
|
1874
2520
|
className: "assist-edge-tab__sparkles",
|
|
@@ -1876,21 +2522,21 @@ function SparklesIcon() {
|
|
|
1876
2522
|
fill: "none",
|
|
1877
2523
|
"aria-hidden": "true",
|
|
1878
2524
|
children: [
|
|
1879
|
-
/* @__PURE__ */ (0,
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1880
2526
|
"path",
|
|
1881
2527
|
{
|
|
1882
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",
|
|
1883
2529
|
fill: "currentColor"
|
|
1884
2530
|
}
|
|
1885
2531
|
),
|
|
1886
|
-
/* @__PURE__ */ (0,
|
|
2532
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1887
2533
|
"path",
|
|
1888
2534
|
{
|
|
1889
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",
|
|
1890
2536
|
fill: "currentColor"
|
|
1891
2537
|
}
|
|
1892
2538
|
),
|
|
1893
|
-
/* @__PURE__ */ (0,
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1894
2540
|
"path",
|
|
1895
2541
|
{
|
|
1896
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",
|
|
@@ -1901,8 +2547,23 @@ function SparklesIcon() {
|
|
|
1901
2547
|
}
|
|
1902
2548
|
);
|
|
1903
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
|
+
}
|
|
1904
2565
|
function ChevronLeftIcon() {
|
|
1905
|
-
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)(
|
|
1906
2567
|
"path",
|
|
1907
2568
|
{
|
|
1908
2569
|
d: "M10 4L6 8l4 4",
|
|
@@ -1914,7 +2575,7 @@ function ChevronLeftIcon() {
|
|
|
1914
2575
|
) });
|
|
1915
2576
|
}
|
|
1916
2577
|
function ChevronDownIcon() {
|
|
1917
|
-
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)(
|
|
1918
2579
|
"path",
|
|
1919
2580
|
{
|
|
1920
2581
|
d: "M4 6l4 4 4-4",
|
|
@@ -1926,7 +2587,7 @@ function ChevronDownIcon() {
|
|
|
1926
2587
|
) });
|
|
1927
2588
|
}
|
|
1928
2589
|
function DragDots() {
|
|
1929
|
-
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)) });
|
|
1930
2591
|
}
|
|
1931
2592
|
var VARIANT_COPY = {
|
|
1932
2593
|
outline: { label: "Assist", aria: "Open Assist" },
|
|
@@ -1940,6 +2601,7 @@ function AssistEdgeTab({
|
|
|
1940
2601
|
inset,
|
|
1941
2602
|
visible,
|
|
1942
2603
|
label,
|
|
2604
|
+
customIconUrl,
|
|
1943
2605
|
logoUrl,
|
|
1944
2606
|
brandColor,
|
|
1945
2607
|
brandForeground,
|
|
@@ -1952,6 +2614,7 @@ function AssistEdgeTab({
|
|
|
1952
2614
|
}) {
|
|
1953
2615
|
const copy = VARIANT_COPY[variant];
|
|
1954
2616
|
const visibleLabel = label?.trim() || copy.label;
|
|
2617
|
+
const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
|
|
1955
2618
|
const style = {
|
|
1956
2619
|
"--tab-along": `${along}%`,
|
|
1957
2620
|
"--tab-inset": `${inset}px`,
|
|
@@ -1962,7 +2625,7 @@ function AssistEdgeTab({
|
|
|
1962
2625
|
...surfaceColor ? { "--as-surface": surfaceColor } : {},
|
|
1963
2626
|
...textColor ? { "--as-text": textColor } : {}
|
|
1964
2627
|
};
|
|
1965
|
-
return /* @__PURE__ */ (0,
|
|
2628
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1966
2629
|
"button",
|
|
1967
2630
|
{
|
|
1968
2631
|
type: "button",
|
|
@@ -1973,15 +2636,15 @@ function AssistEdgeTab({
|
|
|
1973
2636
|
tabIndex: visible ? 0 : -1,
|
|
1974
2637
|
onClick: onOpen,
|
|
1975
2638
|
children: [
|
|
1976
|
-
mobile ? /* @__PURE__ */ (0,
|
|
1977
|
-
/* @__PURE__ */ (0,
|
|
2639
|
+
mobile ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1978
2641
|
"span",
|
|
1979
2642
|
{
|
|
1980
2643
|
className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
|
|
1981
2644
|
"aria-hidden": "true",
|
|
1982
2645
|
children: [
|
|
1983
|
-
visibleLabel.slice(0, 1).toUpperCase(),
|
|
1984
|
-
|
|
2646
|
+
customIconUrl?.trim() ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }) : visibleLabel.slice(0, 1).toUpperCase(),
|
|
2647
|
+
showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1985
2648
|
"img",
|
|
1986
2649
|
{
|
|
1987
2650
|
className: "assist-edge-tab__logo",
|
|
@@ -1995,14 +2658,14 @@ function AssistEdgeTab({
|
|
|
1995
2658
|
]
|
|
1996
2659
|
}
|
|
1997
2660
|
),
|
|
1998
|
-
/* @__PURE__ */ (0,
|
|
2661
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__label", children: [
|
|
1999
2662
|
"Ask ",
|
|
2000
2663
|
visibleLabel
|
|
2001
2664
|
] })
|
|
2002
|
-
] }) : variant === "outline" ? /* @__PURE__ */ (0,
|
|
2003
|
-
/* @__PURE__ */ (0,
|
|
2004
|
-
/* @__PURE__ */ (0,
|
|
2005
|
-
|
|
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)(
|
|
2006
2669
|
"img",
|
|
2007
2670
|
{
|
|
2008
2671
|
className: "assist-edge-tab__logo",
|
|
@@ -2014,18 +2677,18 @@ function AssistEdgeTab({
|
|
|
2014
2677
|
}
|
|
2015
2678
|
) : null
|
|
2016
2679
|
] }),
|
|
2017
|
-
/* @__PURE__ */ (0,
|
|
2018
|
-
/* @__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, {})
|
|
2019
2682
|
] }) : null,
|
|
2020
|
-
variant === "ask" ? /* @__PURE__ */ (0,
|
|
2021
|
-
/* @__PURE__ */ (0,
|
|
2022
|
-
/* @__PURE__ */ (0,
|
|
2023
|
-
/* @__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, {})
|
|
2024
2687
|
] }) : null,
|
|
2025
|
-
variant === "fill" ? /* @__PURE__ */ (0,
|
|
2026
|
-
/* @__PURE__ */ (0,
|
|
2027
|
-
/* @__PURE__ */ (0,
|
|
2028
|
-
|
|
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)(
|
|
2029
2692
|
"img",
|
|
2030
2693
|
{
|
|
2031
2694
|
className: "assist-edge-tab__logo",
|
|
@@ -2037,8 +2700,8 @@ function AssistEdgeTab({
|
|
|
2037
2700
|
}
|
|
2038
2701
|
) : null
|
|
2039
2702
|
] }),
|
|
2040
|
-
/* @__PURE__ */ (0,
|
|
2041
|
-
/* @__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, {})
|
|
2042
2705
|
] }) : null
|
|
2043
2706
|
]
|
|
2044
2707
|
}
|
|
@@ -2046,7 +2709,7 @@ function AssistEdgeTab({
|
|
|
2046
2709
|
}
|
|
2047
2710
|
|
|
2048
2711
|
// src/react/components/AgentWidget/AgentWidget.tsx
|
|
2049
|
-
var
|
|
2712
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2050
2713
|
function AgentWidget({
|
|
2051
2714
|
indexId,
|
|
2052
2715
|
customerId,
|
|
@@ -2062,9 +2725,9 @@ function AgentWidget({
|
|
|
2062
2725
|
}) {
|
|
2063
2726
|
const isMobile = useIsMobile();
|
|
2064
2727
|
const placement = normalizeAgentPlacement(placementInput);
|
|
2065
|
-
const railSlotRef = (0,
|
|
2066
|
-
const [railCollapsed, setRailCollapsed] = (0,
|
|
2067
|
-
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);
|
|
2068
2731
|
const pageShiftActive = shouldApplyPageShift({
|
|
2069
2732
|
pageShift,
|
|
2070
2733
|
isMobile,
|
|
@@ -2085,6 +2748,7 @@ function AgentWidget({
|
|
|
2085
2748
|
greeting: branding?.greeting
|
|
2086
2749
|
});
|
|
2087
2750
|
const agentName = branding?.agentName ?? "Webless Guide";
|
|
2751
|
+
const tabLabel = branding?.tabLabel ?? agentName;
|
|
2088
2752
|
const theme = {
|
|
2089
2753
|
...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
|
|
2090
2754
|
...branding?.colors?.primary ? {
|
|
@@ -2102,8 +2766,7 @@ function AgentWidget({
|
|
|
2102
2766
|
} : {},
|
|
2103
2767
|
...branding?.colors?.border ? { border: branding.colors.border } : {}
|
|
2104
2768
|
};
|
|
2105
|
-
|
|
2106
|
-
(0, import_react6.useEffect)(() => {
|
|
2769
|
+
(0, import_react7.useEffect)(() => {
|
|
2107
2770
|
if (!registerPanelController) return;
|
|
2108
2771
|
registerAgentPanelController(customerId, {
|
|
2109
2772
|
open: () => setRailCollapsed(false),
|
|
@@ -2118,7 +2781,7 @@ function AgentWidget({
|
|
|
2118
2781
|
if (isMobile) setRailCollapsed(false);
|
|
2119
2782
|
await submit(message);
|
|
2120
2783
|
}
|
|
2121
|
-
(0,
|
|
2784
|
+
(0, import_react7.useEffect)(() => {
|
|
2122
2785
|
if (railCollapsed) return;
|
|
2123
2786
|
const handleKeyDown = (event) => {
|
|
2124
2787
|
if (event.key === "Tab" && (isMobile || railExpanded)) {
|
|
@@ -2152,31 +2815,28 @@ function AgentWidget({
|
|
|
2152
2815
|
window.addEventListener("keydown", handleKeyDown);
|
|
2153
2816
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2154
2817
|
}, [isMobile, railCollapsed, railExpanded]);
|
|
2155
|
-
return /* @__PURE__ */ (0,
|
|
2156
|
-
/* @__PURE__ */ (0,
|
|
2818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
|
|
2819
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2157
2820
|
"div",
|
|
2158
2821
|
{
|
|
2159
2822
|
className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
|
|
2160
2823
|
children: [
|
|
2161
|
-
/* @__PURE__ */ (0,
|
|
2824
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2162
2825
|
"div",
|
|
2163
2826
|
{
|
|
2164
2827
|
ref: railSlotRef,
|
|
2165
2828
|
className: "webless-agent-root__rail-slot",
|
|
2166
2829
|
inert: railCollapsed || void 0,
|
|
2167
2830
|
"aria-hidden": railCollapsed,
|
|
2168
|
-
children: /* @__PURE__ */ (0,
|
|
2831
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2169
2832
|
AgentRail,
|
|
2170
2833
|
{
|
|
2171
2834
|
theme,
|
|
2172
2835
|
brandLabel: agentName,
|
|
2173
2836
|
brandLogoUrl: branding?.logoUrl,
|
|
2174
|
-
composerPlaceholder: branding?.composerPlaceholder ??
|
|
2837
|
+
composerPlaceholder: branding?.composerPlaceholder ?? "Ask as a visitor\u2026",
|
|
2175
2838
|
poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
|
|
2176
|
-
state
|
|
2177
|
-
...state,
|
|
2178
|
-
followUps: createIdleSuggestions()
|
|
2179
|
-
} : state,
|
|
2839
|
+
state,
|
|
2180
2840
|
mobileFullscreen: isMobile && !railCollapsed,
|
|
2181
2841
|
expanded: railExpanded,
|
|
2182
2842
|
onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
|
|
@@ -2185,12 +2845,13 @@ function AgentWidget({
|
|
|
2185
2845
|
onSubmit: handleSubmit,
|
|
2186
2846
|
onReset: reset,
|
|
2187
2847
|
onRetry: () => void retry(),
|
|
2188
|
-
onFollowUpSelect: (label) => void handleSubmit(label)
|
|
2848
|
+
onFollowUpSelect: (label) => void handleSubmit(label),
|
|
2849
|
+
onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
|
|
2189
2850
|
}
|
|
2190
2851
|
)
|
|
2191
2852
|
}
|
|
2192
2853
|
),
|
|
2193
|
-
!railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0,
|
|
2854
|
+
!railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2194
2855
|
"button",
|
|
2195
2856
|
{
|
|
2196
2857
|
type: "button",
|
|
@@ -2209,7 +2870,7 @@ function AgentWidget({
|
|
|
2209
2870
|
]
|
|
2210
2871
|
}
|
|
2211
2872
|
),
|
|
2212
|
-
railCollapsed ? /* @__PURE__ */ (0,
|
|
2873
|
+
railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2213
2874
|
AssistEdgeTab,
|
|
2214
2875
|
{
|
|
2215
2876
|
variant: placement.variant,
|
|
@@ -2217,7 +2878,8 @@ function AgentWidget({
|
|
|
2217
2878
|
along: placement.along,
|
|
2218
2879
|
inset: placement.inset,
|
|
2219
2880
|
visible: true,
|
|
2220
|
-
label:
|
|
2881
|
+
label: tabLabel,
|
|
2882
|
+
customIconUrl: branding?.tabIconUrl,
|
|
2221
2883
|
logoUrl: branding?.logoUrl,
|
|
2222
2884
|
brandColor: branding?.colors?.primary,
|
|
2223
2885
|
brandForeground: branding?.colors?.primaryForeground,
|
|
@@ -2246,11 +2908,12 @@ function readUnpublishedPreviewBuildId(href) {
|
|
|
2246
2908
|
}
|
|
2247
2909
|
|
|
2248
2910
|
// src/embed/AgentWidget.tsx
|
|
2249
|
-
var
|
|
2911
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2250
2912
|
function AgentWidget2({
|
|
2251
2913
|
manifest
|
|
2252
2914
|
}) {
|
|
2253
|
-
|
|
2915
|
+
const defaultCollapsed = manifest.version !== "unpublished";
|
|
2916
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2254
2917
|
AgentWidget,
|
|
2255
2918
|
{
|
|
2256
2919
|
indexId: manifest.indexId,
|
|
@@ -2261,7 +2924,7 @@ function AgentWidget2({
|
|
|
2261
2924
|
placement: manifest.placement,
|
|
2262
2925
|
pageShift: manifest.pageShift,
|
|
2263
2926
|
branding: manifest.branding,
|
|
2264
|
-
defaultCollapsed
|
|
2927
|
+
defaultCollapsed,
|
|
2265
2928
|
registerPanelController: true
|
|
2266
2929
|
}
|
|
2267
2930
|
);
|
|
@@ -2315,6 +2978,8 @@ function normalizeAgentBranding(branding) {
|
|
|
2315
2978
|
) : void 0;
|
|
2316
2979
|
const normalized = {
|
|
2317
2980
|
agentName: normalizeOptionalValue(branding.agentName),
|
|
2981
|
+
tabLabel: normalizeOptionalValue(branding.tabLabel),
|
|
2982
|
+
tabIconUrl: normalizeOptionalValue(branding.tabIconUrl),
|
|
2318
2983
|
logoUrl: normalizeOptionalValue(branding.logoUrl),
|
|
2319
2984
|
greeting: normalizeOptionalValue(branding.greeting),
|
|
2320
2985
|
composerPlaceholder: normalizeOptionalValue(branding.composerPlaceholder),
|
|
@@ -2326,7 +2991,7 @@ function normalizeAgentBranding(branding) {
|
|
|
2326
2991
|
}
|
|
2327
2992
|
|
|
2328
2993
|
// src/embed/mount.tsx
|
|
2329
|
-
var
|
|
2994
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2330
2995
|
var mountedHandles = /* @__PURE__ */ new Map();
|
|
2331
2996
|
var latestCustomerId = null;
|
|
2332
2997
|
function resolveMountHost(manifest, script) {
|
|
@@ -2356,7 +3021,7 @@ function mountAgent(input) {
|
|
|
2356
3021
|
const host = createHost(manifest.customerId);
|
|
2357
3022
|
mountTarget.append(host);
|
|
2358
3023
|
const root = (0, import_client5.createRoot)(host);
|
|
2359
|
-
root.render(/* @__PURE__ */ (0,
|
|
3024
|
+
root.render(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentWidget2, { manifest }));
|
|
2360
3025
|
const handle = {
|
|
2361
3026
|
customerId: manifest.customerId,
|
|
2362
3027
|
manifest,
|