@xpert-ai/chatkit-ui5 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/chatkit.cjs CHANGED
@@ -48,7 +48,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
48
48
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
49
49
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
50
50
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
51
- var _a, _b, _setTrainingOptOut_dec, _hideHistory_dec, _showHistory_dec, _sendCustomAction_dec, _shareThread_dec, _setThreadId_dec, _setComposerValue_dec, _sendUserMessage_dec, _fetchUpdates_dec, _focusComposer_dec, _c, _opts, _frameUrl, _frame, _wrapper, _launcherCloseButton, _launcherOpen, _framePetOptionsOverride, _shadow, _petOverlay, _resolveLoaded, _loaded, _messenger, _ChatKitElementBase_instances, emitAndThrow_fn, setOptionsDataAttributes_fn, getDisplayMode_fn, getConfiguredPetOptions_fn, getOverlayPetOptions_fn, resolvePetAssetUrl_fn, resolveOverlayPetOptions_fn, getFrameOptions_fn, setLauncherOpen_fn, handlePetActivate_fn, handlePetReply_fn, handlePetThreadSummaryActivate_fn, _handleLauncherClose, getFrameUrl_fn, setFrameUrl_fn, consumeFrameUrl_fn, _handleFrameLoad, _initialized, maybeInit_fn, _init;
51
+ var _a, _b, _setTrainingOptOut_dec, _hideHistory_dec, _showHistory_dec, _sendCustomAction_dec, _shareThread_dec, _setThreadId_dec, _setComposerValue_dec, _sendUserMessage_dec, _fetchUpdates_dec, _focusComposer_dec, _c, _opts, _frameUrl, _frame, _wrapper, _launcherCloseButton, _launcherOpen, _chatMinimizedToPet, _framePetOptionsOverride, _petClosedByContextMenu, _shadow, _petOverlay, _resolveLoaded, _loaded, _messenger, _ChatKitElementBase_instances, emitAndThrow_fn, setOptionsDataAttributes_fn, getDisplayMode_fn, getConfiguredPetOptions_fn, mergeConfiguredPetPositionDefaults_fn, getOverlayPetOptions_fn, resolvePetAssetUrl_fn, resolveOverlayPetOptions_fn, getFrameOptions_fn, setLauncherOpen_fn, setChatMinimizedToPet_fn, syncPetOverlayOptions_fn, handlePetActivate_fn, handlePetClose_fn, handlePetReply_fn, handlePetThreadSummaryActivate_fn, _handleLauncherClose, getFrameUrl_fn, setFrameUrl_fn, consumeFrameUrl_fn, _handleFrameLoad, _initialized, maybeInit_fn, _init;
52
52
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
53
53
  const chatkitTypes = require("@xpert-ai/chatkit-types");
54
54
  class EventEmitter {
@@ -894,8 +894,11 @@ const PET_RESTING_DELAY_MS = 2e3;
894
894
  const PET_SUMMARY_GAP = 12;
895
895
  const PET_SUMMARY_WIDTH = 320;
896
896
  const PET_SUMMARY_MARGIN = 12;
897
+ const PET_CONTEXT_MENU_MARGIN = 8;
898
+ const PET_SUMMARY_FONT_FAMILY = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
897
899
  const PET_OVERLAY_COPY = {
898
900
  "en-US": {
901
+ closePetMenuItem: "Close pet",
899
902
  hideMessage: "Hide message",
900
903
  expandMessage: "Expand message",
901
904
  collapseMessage: "Collapse message",
@@ -907,6 +910,7 @@ const PET_OVERLAY_COPY = {
907
910
  sendFailed: "Failed to send"
908
911
  },
909
912
  "zh-CN": {
913
+ closePetMenuItem: "关闭宠物",
910
914
  hideMessage: "隐藏消息",
911
915
  expandMessage: "展开消息",
912
916
  collapseMessage: "收起消息",
@@ -983,6 +987,65 @@ function resolveCopyLocale(locale) {
983
987
  }
984
988
  return "en-US";
985
989
  }
990
+ function getSummaryDensityMetrics(density) {
991
+ switch (density) {
992
+ case "compact":
993
+ return {
994
+ padding: "8px 10px",
995
+ gap: 6,
996
+ marginTop: 3,
997
+ toggleSize: 30,
998
+ actionSize: 22
999
+ };
1000
+ case "spacious":
1001
+ return {
1002
+ padding: "14px 16px",
1003
+ gap: 10,
1004
+ marginTop: 6,
1005
+ toggleSize: 38,
1006
+ actionSize: 28
1007
+ };
1008
+ case "normal":
1009
+ default:
1010
+ return {
1011
+ padding: "12px 14px",
1012
+ gap: 8,
1013
+ marginTop: 4,
1014
+ toggleSize: 34,
1015
+ actionSize: 26
1016
+ };
1017
+ }
1018
+ }
1019
+ function getSummaryRadius(radius) {
1020
+ switch (radius) {
1021
+ case "pill":
1022
+ return 24;
1023
+ case "round":
1024
+ return 16;
1025
+ case "sharp":
1026
+ return 0;
1027
+ case "soft":
1028
+ default:
1029
+ return 12;
1030
+ }
1031
+ }
1032
+ function getSummaryThemeMetrics(theme) {
1033
+ var _a2, _b2;
1034
+ const themeObject = typeof theme === "string" ? null : theme;
1035
+ const baseSize = ((_a2 = themeObject == null ? void 0 : themeObject.typography) == null ? void 0 : _a2.baseSize) ?? 16;
1036
+ const density = (themeObject == null ? void 0 : themeObject.density) ?? "normal";
1037
+ const densityOffset = density === "compact" ? -1 : density === "spacious" ? 1 : 0;
1038
+ const bodySize = clampNumber(baseSize - 2 + densityOffset, 11, 17);
1039
+ const densityMetrics = getSummaryDensityMetrics(density);
1040
+ return {
1041
+ ...densityMetrics,
1042
+ bodySize,
1043
+ titleSize: bodySize + 1,
1044
+ iconSize: clampNumber(bodySize + 2, 13, 19),
1045
+ radius: getSummaryRadius(themeObject == null ? void 0 : themeObject.radius),
1046
+ fontFamily: ((_b2 = themeObject == null ? void 0 : themeObject.typography) == null ? void 0 : _b2.fontFamily) ?? PET_SUMMARY_FONT_FAMILY
1047
+ };
1048
+ }
986
1049
  function isPetState(value) {
987
1050
  return value === "idle" || value === "running-right" || value === "running-left" || value === "waving" || value === "jumping" || value === "failed" || value === "waiting" || value === "running" || value === "review";
988
1051
  }
@@ -1018,6 +1081,12 @@ function getThreadSummaryKey(summary) {
1018
1081
  summary.messageId || summary.updatedAt || summary.message
1019
1082
  ].join(":");
1020
1083
  }
1084
+ function getThreadSummaryInteractionKey(summary) {
1085
+ if (!summary) {
1086
+ return null;
1087
+ }
1088
+ return [summary.threadId, summary.messageId ?? ""].join(":");
1089
+ }
1021
1090
  function parseThreadSummaryPayload(value) {
1022
1091
  if (value === null) {
1023
1092
  return { summary: null };
@@ -1054,10 +1123,12 @@ class PetOverlay {
1054
1123
  constructor(root, options) {
1055
1124
  this.overlayElement = null;
1056
1125
  this.petElement = null;
1126
+ this.contextMenuElement = null;
1057
1127
  this.summaryElement = null;
1058
1128
  this.summaryToggleElement = null;
1059
1129
  this.mediaElement = null;
1060
1130
  this.options = null;
1131
+ this.theme = null;
1061
1132
  this.resolved = null;
1062
1133
  this.summary = null;
1063
1134
  this.summaryRenderKey = null;
@@ -1094,6 +1165,7 @@ class PetOverlay {
1094
1165
  this.mediaQuery = null;
1095
1166
  this.connected = false;
1096
1167
  this.handleViewportChange = () => {
1168
+ this.closeContextMenu();
1097
1169
  this.render();
1098
1170
  };
1099
1171
  this.handleReducedMotionChange = () => {
@@ -1114,6 +1186,36 @@ class PetOverlay {
1114
1186
  this.enterRestingAfterDelay();
1115
1187
  }
1116
1188
  };
1189
+ this.handleContextMenu = (event) => {
1190
+ event.preventDefault();
1191
+ event.stopPropagation();
1192
+ this.openContextMenu({ x: event.clientX, y: event.clientY });
1193
+ };
1194
+ this.handleContextMenuEvent = (event) => {
1195
+ event.preventDefault();
1196
+ event.stopPropagation();
1197
+ };
1198
+ this.handleContextMenuOutsidePointerDown = (event) => {
1199
+ const menu = this.contextMenuElement;
1200
+ if (menu && event.composedPath().includes(menu)) {
1201
+ return;
1202
+ }
1203
+ this.closeContextMenu();
1204
+ };
1205
+ this.handleContextMenuKeyDown = (event) => {
1206
+ if (event.key !== "Escape") {
1207
+ return;
1208
+ }
1209
+ event.preventDefault();
1210
+ this.closeContextMenu();
1211
+ };
1212
+ this.handleClosePetMenuItemClick = (event) => {
1213
+ var _a2;
1214
+ event.preventDefault();
1215
+ event.stopPropagation();
1216
+ this.closeContextMenu();
1217
+ (_a2 = this.onClose) == null ? void 0 : _a2.call(this);
1218
+ };
1117
1219
  this.handleSummaryPointerEnter = () => {
1118
1220
  this.isSummaryHovering = true;
1119
1221
  this.render();
@@ -1181,7 +1283,11 @@ class PetOverlay {
1181
1283
  };
1182
1284
  this.handlePointerDown = (event) => {
1183
1285
  var _a2, _b2;
1184
- if (!((_a2 = this.options) == null ? void 0 : _a2.position.draggable) || event.button !== 0) {
1286
+ if (event.button !== 0 || event.ctrlKey) {
1287
+ return;
1288
+ }
1289
+ this.closeContextMenu();
1290
+ if (!((_a2 = this.options) == null ? void 0 : _a2.position.draggable)) {
1185
1291
  return;
1186
1292
  }
1187
1293
  event.preventDefault();
@@ -1257,6 +1363,7 @@ class PetOverlay {
1257
1363
  };
1258
1364
  this.root = root;
1259
1365
  this.onActivate = options == null ? void 0 : options.onActivate;
1366
+ this.onClose = options == null ? void 0 : options.onClose;
1260
1367
  this.onReply = options == null ? void 0 : options.onReply;
1261
1368
  this.onThreadSummaryActivate = options == null ? void 0 : options.onThreadSummaryActivate;
1262
1369
  this.connect();
@@ -1270,9 +1377,11 @@ class PetOverlay {
1270
1377
  this.installReducedMotionListener();
1271
1378
  this.render();
1272
1379
  }
1273
- setOptions(pet) {
1380
+ setOptions(pet, theme) {
1381
+ this.closeContextMenu();
1274
1382
  const nextOptions = chatkitTypes.normalizePetOptions(pet);
1275
1383
  this.options = nextOptions;
1384
+ this.theme = theme ?? null;
1276
1385
  if (!nextOptions) {
1277
1386
  this.isDragging = false;
1278
1387
  this.isHovering = false;
@@ -1318,11 +1427,12 @@ class PetOverlay {
1318
1427
  this.copyLocale = nextLocale;
1319
1428
  this.copy = PET_OVERLAY_COPY[nextLocale];
1320
1429
  this.summaryRenderKey = null;
1430
+ this.closeContextMenu();
1321
1431
  this.render();
1322
1432
  }
1323
1433
  setThreadSummary(summary) {
1324
- const previousKey = getThreadSummaryKey(this.summary);
1325
- const nextKey = getThreadSummaryKey(summary);
1434
+ const previousKey = getThreadSummaryInteractionKey(this.summary);
1435
+ const nextKey = getThreadSummaryInteractionKey(summary);
1326
1436
  this.summary = summary;
1327
1437
  if (!summary) {
1328
1438
  this.isSummaryCollapsed = false;
@@ -1383,8 +1493,14 @@ class PetOverlay {
1383
1493
  installViewportListeners() {
1384
1494
  var _a2, _b2;
1385
1495
  window.addEventListener("resize", this.handleViewportChange);
1386
- (_a2 = window.visualViewport) == null ? void 0 : _a2.addEventListener("resize", this.handleViewportChange);
1387
- (_b2 = window.visualViewport) == null ? void 0 : _b2.addEventListener("scroll", this.handleViewportChange);
1496
+ (_a2 = window.visualViewport) == null ? void 0 : _a2.addEventListener(
1497
+ "resize",
1498
+ this.handleViewportChange
1499
+ );
1500
+ (_b2 = window.visualViewport) == null ? void 0 : _b2.addEventListener(
1501
+ "scroll",
1502
+ this.handleViewportChange
1503
+ );
1388
1504
  }
1389
1505
  installReducedMotionListener() {
1390
1506
  var _a2, _b2;
@@ -1435,6 +1551,7 @@ class PetOverlay {
1435
1551
  pet.addEventListener("pointerenter", this.handlePointerEnter);
1436
1552
  pet.addEventListener("pointerleave", this.handlePointerLeave);
1437
1553
  pet.addEventListener("pointerdown", this.handlePointerDown);
1554
+ pet.addEventListener("contextmenu", this.handleContextMenu);
1438
1555
  pet.addEventListener("click", this.handleClick);
1439
1556
  overlay.append(style, pet);
1440
1557
  this.root.append(overlay);
@@ -1445,6 +1562,7 @@ class PetOverlay {
1445
1562
  var _a2;
1446
1563
  this.clearRestingDelayTimer();
1447
1564
  this.clearTimers();
1565
+ this.closeContextMenu();
1448
1566
  if (this.petElement) {
1449
1567
  this.petElement.removeEventListener(
1450
1568
  "pointerenter",
@@ -1454,7 +1572,14 @@ class PetOverlay {
1454
1572
  "pointerleave",
1455
1573
  this.handlePointerLeave
1456
1574
  );
1457
- this.petElement.removeEventListener("pointerdown", this.handlePointerDown);
1575
+ this.petElement.removeEventListener(
1576
+ "pointerdown",
1577
+ this.handlePointerDown
1578
+ );
1579
+ this.petElement.removeEventListener(
1580
+ "contextmenu",
1581
+ this.handleContextMenu
1582
+ );
1458
1583
  this.petElement.removeEventListener("click", this.handleClick);
1459
1584
  }
1460
1585
  this.isHovering = false;
@@ -1602,10 +1727,6 @@ class PetOverlay {
1602
1727
  return;
1603
1728
  }
1604
1729
  const nextRenderKey = [
1605
- summaryKey,
1606
- summary.title,
1607
- summary.message,
1608
- summary.status,
1609
1730
  this.copyLocale,
1610
1731
  this.isSummaryExpanded ? "expanded" : "compact",
1611
1732
  this.isSummaryHovering ? "hover" : "rest",
@@ -1618,7 +1739,10 @@ class PetOverlay {
1618
1739
  (_b2 = this.summaryToggleElement) == null ? void 0 : _b2.remove();
1619
1740
  this.summaryElement = this.createSummaryBubble(summary);
1620
1741
  this.summaryToggleElement = this.createSummaryToggleButton("v");
1621
- (_c2 = this.overlayElement) == null ? void 0 : _c2.append(this.summaryElement, this.summaryToggleElement);
1742
+ (_c2 = this.overlayElement) == null ? void 0 : _c2.append(
1743
+ this.summaryElement,
1744
+ this.summaryToggleElement
1745
+ );
1622
1746
  this.summaryRenderKey = nextRenderKey;
1623
1747
  if (this.isReplyOpen && !this.isReplySubmitting) {
1624
1748
  window.setTimeout(() => {
@@ -1627,6 +1751,7 @@ class PetOverlay {
1627
1751
  }, 0);
1628
1752
  }
1629
1753
  }
1754
+ this.updateSummaryBubbleContent(summary);
1630
1755
  this.positionSummaryBubble(position, size);
1631
1756
  }
1632
1757
  renderCollapsedSummary(position, size) {
@@ -1647,7 +1772,7 @@ class PetOverlay {
1647
1772
  return;
1648
1773
  }
1649
1774
  const viewport = getViewportSize();
1650
- const badgeSize = 34;
1775
+ const badgeSize = getSummaryThemeMetrics(this.theme).toggleSize;
1651
1776
  const x = clampNumber(
1652
1777
  position.x + size.width - badgeSize * 0.35,
1653
1778
  PET_SUMMARY_MARGIN,
@@ -1670,7 +1795,10 @@ class PetOverlay {
1670
1795
  return;
1671
1796
  }
1672
1797
  const viewport = getViewportSize();
1673
- const width = Math.min(PET_SUMMARY_WIDTH, viewport.width - PET_SUMMARY_MARGIN * 2);
1798
+ const width = Math.min(
1799
+ PET_SUMMARY_WIDTH,
1800
+ viewport.width - PET_SUMMARY_MARGIN * 2
1801
+ );
1674
1802
  bubble.style.width = `${width}px`;
1675
1803
  const bubbleHeight = bubble.offsetHeight || 96;
1676
1804
  const aboveY = position.y - bubbleHeight - PET_SUMMARY_GAP;
@@ -1690,6 +1818,7 @@ class PetOverlay {
1690
1818
  }
1691
1819
  createSummaryBubble(summary) {
1692
1820
  const shouldShowReplyButton = this.isSummaryHovering && !this.isReplyOpen;
1821
+ const metrics = getSummaryThemeMetrics(this.theme);
1693
1822
  const bubble = document.createElement("div");
1694
1823
  bubble.setAttribute("data-chatkit-pet-summary", "");
1695
1824
  bubble.addEventListener("pointerenter", this.handleSummaryPointerEnter);
@@ -1700,19 +1829,18 @@ class PetOverlay {
1700
1829
  bubble.style.left = "0";
1701
1830
  bubble.style.boxSizing = "border-box";
1702
1831
  bubble.style.pointerEvents = "auto";
1703
- bubble.style.padding = "12px 14px";
1704
- bubble.style.paddingBottom = "12px";
1832
+ bubble.style.padding = metrics.padding;
1705
1833
  bubble.style.border = "1px solid rgba(148, 163, 184, 0.22)";
1706
- bubble.style.borderRadius = "22px";
1834
+ bubble.style.borderRadius = `${metrics.radius}px`;
1707
1835
  bubble.style.background = "rgba(255, 255, 255, 0.94)";
1708
1836
  bubble.style.color = "#1f2937";
1709
1837
  bubble.style.boxShadow = "0 8px 24px rgba(15, 23, 42, 0.1)";
1710
- bubble.style.font = '500 14px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
1838
+ bubble.style.font = `500 ${metrics.bodySize}px/1.35 ${metrics.fontFamily}`;
1711
1839
  bubble.style.backdropFilter = "blur(10px)";
1712
1840
  const header = document.createElement("div");
1713
1841
  header.style.display = "flex";
1714
1842
  header.style.alignItems = "center";
1715
- header.style.gap = "8px";
1843
+ header.style.gap = `${metrics.gap}px`;
1716
1844
  if (this.isSummaryHovering || this.isReplyOpen) {
1717
1845
  header.append(
1718
1846
  this.createSummaryActionButton(
@@ -1730,7 +1858,8 @@ class PetOverlay {
1730
1858
  title.style.textOverflow = "ellipsis";
1731
1859
  title.style.whiteSpace = "nowrap";
1732
1860
  title.style.fontWeight = "700";
1733
- title.style.fontSize = "15px";
1861
+ title.style.fontSize = `${metrics.titleSize}px`;
1862
+ title.dataset.chatkitPetSummaryTitle = "";
1734
1863
  header.append(title);
1735
1864
  if (this.isSummaryHovering || this.isReplyOpen) {
1736
1865
  header.append(
@@ -1745,7 +1874,7 @@ class PetOverlay {
1745
1874
  }
1746
1875
  const message = document.createElement("div");
1747
1876
  message.textContent = summary.message;
1748
- message.style.marginTop = "4px";
1877
+ message.style.marginTop = `${metrics.marginTop}px`;
1749
1878
  message.style.fontWeight = "400";
1750
1879
  message.style.color = "#374151";
1751
1880
  message.style.overflow = "hidden";
@@ -1757,6 +1886,7 @@ class PetOverlay {
1757
1886
  message.style.setProperty("-webkit-line-clamp", "2");
1758
1887
  message.style.setProperty("-webkit-box-orient", "vertical");
1759
1888
  }
1889
+ message.dataset.chatkitPetSummaryMessage = "";
1760
1890
  bubble.append(header, message);
1761
1891
  if (shouldShowReplyButton) {
1762
1892
  const reply = this.createTextButton(
@@ -1773,7 +1903,30 @@ class PetOverlay {
1773
1903
  }
1774
1904
  return bubble;
1775
1905
  }
1906
+ updateSummaryBubbleContent(summary) {
1907
+ var _a2, _b2, _c2;
1908
+ const title = (_a2 = this.summaryElement) == null ? void 0 : _a2.querySelector(
1909
+ "[data-chatkit-pet-summary-title]"
1910
+ );
1911
+ if (title && title.textContent !== summary.title) {
1912
+ title.textContent = summary.title;
1913
+ }
1914
+ const message = (_b2 = this.summaryElement) == null ? void 0 : _b2.querySelector(
1915
+ "[data-chatkit-pet-summary-message]"
1916
+ );
1917
+ if (message && message.textContent !== summary.message) {
1918
+ message.textContent = summary.message;
1919
+ }
1920
+ const statusIcon = (_c2 = this.summaryElement) == null ? void 0 : _c2.querySelector(
1921
+ "[data-chatkit-pet-summary-status]"
1922
+ );
1923
+ if (statusIcon && statusIcon.dataset.chatkitPetSummaryStatus !== summary.status) {
1924
+ const nextIcon = this.createStatusIcon(summary.status);
1925
+ statusIcon.replaceWith(nextIcon);
1926
+ }
1927
+ }
1776
1928
  createSummaryToggleButton(label) {
1929
+ const metrics = getSummaryThemeMetrics(this.theme);
1777
1930
  const button = document.createElement("button");
1778
1931
  button.type = "button";
1779
1932
  button.textContent = label;
@@ -1789,18 +1942,21 @@ class PetOverlay {
1789
1942
  button.style.color = "#475569";
1790
1943
  button.style.boxShadow = "0 6px 18px rgba(15, 23, 42, 0.1)";
1791
1944
  button.style.cursor = "pointer";
1792
- button.style.font = '600 15px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
1945
+ button.style.font = `600 ${metrics.titleSize}px/1 ${metrics.fontFamily}`;
1793
1946
  button.style.display = "flex";
1794
1947
  button.style.alignItems = "center";
1795
1948
  button.style.justifyContent = "center";
1796
1949
  return button;
1797
1950
  }
1798
1951
  createStatusIcon(status) {
1952
+ const metrics = getSummaryThemeMetrics(this.theme);
1953
+ const iconSize = metrics.iconSize;
1799
1954
  const icon = document.createElement("span");
1800
1955
  icon.setAttribute("aria-hidden", "true");
1956
+ icon.dataset.chatkitPetSummaryStatus = status;
1801
1957
  icon.style.display = "inline-flex";
1802
- icon.style.width = "16px";
1803
- icon.style.height = "16px";
1958
+ icon.style.width = `${iconSize}px`;
1959
+ icon.style.height = `${iconSize}px`;
1804
1960
  icon.style.alignItems = "center";
1805
1961
  icon.style.justifyContent = "center";
1806
1962
  icon.style.flex = "0 0 auto";
@@ -1812,7 +1968,7 @@ class PetOverlay {
1812
1968
  return icon;
1813
1969
  }
1814
1970
  icon.style.borderRadius = "999px";
1815
- icon.style.fontSize = "12px";
1971
+ icon.style.fontSize = `${Math.max(10, iconSize - 4)}px`;
1816
1972
  icon.style.fontWeight = "700";
1817
1973
  if (status === "failed") {
1818
1974
  icon.textContent = "!";
@@ -1824,8 +1980,8 @@ class PetOverlay {
1824
1980
  icon.style.border = "2px solid #22c55e";
1825
1981
  icon.style.position = "relative";
1826
1982
  const check = document.createElement("span");
1827
- check.style.width = "7px";
1828
- check.style.height = "4px";
1983
+ check.style.width = `${Math.round(iconSize * 0.44)}px`;
1984
+ check.style.height = `${Math.round(iconSize * 0.25)}px`;
1829
1985
  check.style.borderLeft = "2px solid #22c55e";
1830
1986
  check.style.borderBottom = "2px solid #22c55e";
1831
1987
  check.style.transform = "rotate(-45deg) translate(1px, -1px)";
@@ -1833,6 +1989,7 @@ class PetOverlay {
1833
1989
  return icon;
1834
1990
  }
1835
1991
  createSummaryActionButton(label, title, onClick) {
1992
+ const metrics = getSummaryThemeMetrics(this.theme);
1836
1993
  const button = document.createElement("button");
1837
1994
  button.type = "button";
1838
1995
  button.textContent = label;
@@ -1842,14 +1999,14 @@ class PetOverlay {
1842
1999
  event.stopPropagation();
1843
2000
  onClick();
1844
2001
  });
1845
- button.style.width = "26px";
1846
- button.style.height = "26px";
2002
+ button.style.width = `${metrics.actionSize}px`;
2003
+ button.style.height = `${metrics.actionSize}px`;
1847
2004
  button.style.border = "0";
1848
2005
  button.style.borderRadius = "999px";
1849
2006
  button.style.background = "rgba(241, 245, 249, 0.96)";
1850
2007
  button.style.color = "#475569";
1851
2008
  button.style.cursor = "pointer";
1852
- button.style.font = '600 15px/1 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
2009
+ button.style.font = `600 ${metrics.titleSize}px/1 ${metrics.fontFamily}`;
1853
2010
  return button;
1854
2011
  }
1855
2012
  createTextButton(label, onClick) {
@@ -1915,6 +2072,95 @@ class PetOverlay {
1915
2072
  }
1916
2073
  return form;
1917
2074
  }
2075
+ createContextMenu() {
2076
+ const menu = document.createElement("div");
2077
+ menu.setAttribute("data-chatkit-pet-context-menu", "");
2078
+ menu.setAttribute("role", "menu");
2079
+ menu.addEventListener("contextmenu", this.handleContextMenuEvent);
2080
+ menu.addEventListener("pointerdown", stopEventPropagation);
2081
+ menu.addEventListener("click", stopEventPropagation);
2082
+ menu.style.position = "absolute";
2083
+ menu.style.top = "0";
2084
+ menu.style.left = "0";
2085
+ menu.style.boxSizing = "border-box";
2086
+ menu.style.minWidth = "132px";
2087
+ menu.style.padding = "4px";
2088
+ menu.style.pointerEvents = "auto";
2089
+ menu.style.border = "1px solid rgba(148, 163, 184, 0.28)";
2090
+ menu.style.borderRadius = "10px";
2091
+ menu.style.background = "rgba(255, 255, 255, 0.96)";
2092
+ menu.style.color = "#1f2937";
2093
+ menu.style.boxShadow = "0 12px 28px rgba(15, 23, 42, 0.16)";
2094
+ menu.style.font = '500 13px/1.3 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
2095
+ menu.style.backdropFilter = "blur(10px)";
2096
+ const closeItem = document.createElement("button");
2097
+ closeItem.type = "button";
2098
+ closeItem.textContent = this.copy.closePetMenuItem;
2099
+ closeItem.setAttribute("role", "menuitem");
2100
+ closeItem.addEventListener("click", this.handleClosePetMenuItemClick);
2101
+ closeItem.style.display = "block";
2102
+ closeItem.style.width = "100%";
2103
+ closeItem.style.border = "0";
2104
+ closeItem.style.borderRadius = "7px";
2105
+ closeItem.style.background = "transparent";
2106
+ closeItem.style.color = "inherit";
2107
+ closeItem.style.cursor = "pointer";
2108
+ closeItem.style.padding = "7px 10px";
2109
+ closeItem.style.textAlign = "left";
2110
+ closeItem.style.font = "inherit";
2111
+ menu.append(closeItem);
2112
+ return menu;
2113
+ }
2114
+ openContextMenu(position) {
2115
+ var _a2;
2116
+ const overlay = this.overlayElement;
2117
+ if (!overlay) {
2118
+ return;
2119
+ }
2120
+ this.closeContextMenu();
2121
+ const menu = this.createContextMenu();
2122
+ overlay.append(menu);
2123
+ this.contextMenuElement = menu;
2124
+ this.positionContextMenu(position);
2125
+ (_a2 = menu.querySelector("button")) == null ? void 0 : _a2.focus({ preventScroll: true });
2126
+ window.addEventListener(
2127
+ "pointerdown",
2128
+ this.handleContextMenuOutsidePointerDown,
2129
+ true
2130
+ );
2131
+ window.addEventListener("keydown", this.handleContextMenuKeyDown);
2132
+ }
2133
+ closeContextMenu() {
2134
+ var _a2;
2135
+ (_a2 = this.contextMenuElement) == null ? void 0 : _a2.remove();
2136
+ this.contextMenuElement = null;
2137
+ window.removeEventListener(
2138
+ "pointerdown",
2139
+ this.handleContextMenuOutsidePointerDown,
2140
+ true
2141
+ );
2142
+ window.removeEventListener("keydown", this.handleContextMenuKeyDown);
2143
+ }
2144
+ positionContextMenu(position) {
2145
+ const menu = this.contextMenuElement;
2146
+ if (!menu) {
2147
+ return;
2148
+ }
2149
+ const viewport = getViewportSize();
2150
+ const width = menu.offsetWidth || 132;
2151
+ const height = menu.offsetHeight || 40;
2152
+ const maxX = Math.max(
2153
+ PET_CONTEXT_MENU_MARGIN,
2154
+ viewport.width - width - PET_CONTEXT_MENU_MARGIN
2155
+ );
2156
+ const maxY = Math.max(
2157
+ PET_CONTEXT_MENU_MARGIN,
2158
+ viewport.height - height - PET_CONTEXT_MENU_MARGIN
2159
+ );
2160
+ const x = clampNumber(position.x, PET_CONTEXT_MENU_MARGIN, maxX);
2161
+ const y = clampNumber(position.y, PET_CONTEXT_MENU_MARGIN, maxY);
2162
+ menu.style.transform = `translate3d(${x}px, ${y}px, 0)`;
2163
+ }
1918
2164
  renderAtlas(animationName, animationMode) {
1919
2165
  var _a2;
1920
2166
  if (!this.petElement || ((_a2 = this.resolved) == null ? void 0 : _a2.kind) !== "atlas" || !this.options) {
@@ -2015,13 +2261,16 @@ class PetOverlay {
2015
2261
  this.rescheduleAnimation();
2016
2262
  }, PET_RESTING_DELAY_MS);
2017
2263
  }
2018
- isPointerOverPet(event) {
2264
+ isPointOverPet(clientX, clientY) {
2019
2265
  var _a2;
2020
2266
  const rect = (_a2 = this.petElement) == null ? void 0 : _a2.getBoundingClientRect();
2021
2267
  if (!rect) {
2022
2268
  return false;
2023
2269
  }
2024
- return event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom;
2270
+ return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
2271
+ }
2272
+ isPointerOverPet(event) {
2273
+ return this.isPointOverPet(event.clientX, event.clientY);
2025
2274
  }
2026
2275
  async submitReply() {
2027
2276
  var _a2;
@@ -2096,10 +2345,13 @@ class ChatKitElementBase extends (_c = HTMLElement, _focusComposer_dec = [requir
2096
2345
  __privateAdd(this, _wrapper);
2097
2346
  __privateAdd(this, _launcherCloseButton);
2098
2347
  __privateAdd(this, _launcherOpen, false);
2348
+ __privateAdd(this, _chatMinimizedToPet, false);
2099
2349
  __privateAdd(this, _framePetOptionsOverride);
2350
+ __privateAdd(this, _petClosedByContextMenu, false);
2100
2351
  __privateAdd(this, _shadow, this.attachShadow({ mode: "open" }));
2101
2352
  __privateAdd(this, _petOverlay, new PetOverlay(__privateGet(this, _shadow), {
2102
2353
  onActivate: () => __privateMethod(this, _ChatKitElementBase_instances, handlePetActivate_fn).call(this),
2354
+ onClose: () => __privateMethod(this, _ChatKitElementBase_instances, handlePetClose_fn).call(this),
2103
2355
  onReply: (text) => __privateMethod(this, _ChatKitElementBase_instances, handlePetReply_fn).call(this, text),
2104
2356
  onThreadSummaryActivate: (threadId) => void __privateMethod(this, _ChatKitElementBase_instances, handlePetThreadSummaryActivate_fn).call(this, threadId)
2105
2357
  }));
@@ -2230,6 +2482,9 @@ class ChatKitElementBase extends (_c = HTMLElement, _focusComposer_dec = [requir
2230
2482
  :host([data-display-mode="pet"]) {
2231
2483
  display: contents;
2232
2484
  }
2485
+ :host([data-chat-minimized-to-pet="true"]) {
2486
+ display: contents;
2487
+ }
2233
2488
  .ck-iframe {
2234
2489
  border: none;
2235
2490
  position: absolute;
@@ -2340,6 +2595,11 @@ class ChatKitElementBase extends (_c = HTMLElement, _focusComposer_dec = [requir
2340
2595
  :host([data-loaded="true"]) .ck-wrapper {
2341
2596
  opacity: 1;
2342
2597
  }
2598
+ :host([data-chat-minimized-to-pet="true"]) .ck-wrapper {
2599
+ opacity: 0;
2600
+ pointer-events: none;
2601
+ visibility: hidden;
2602
+ }
2343
2603
  :host([data-display-mode="pet"]:not([data-chat-open="true"])) .ck-wrapper {
2344
2604
  opacity: 0;
2345
2605
  }
@@ -2422,8 +2682,21 @@ class ChatKitElementBase extends (_c = HTMLElement, _focusComposer_dec = [requir
2422
2682
  __privateGet(this, _messenger).on("pet_options_change", (data) => {
2423
2683
  const payload = parsePetOptionsChangePayload(data);
2424
2684
  if (payload) {
2685
+ if (!__privateGet(this, _petClosedByContextMenu) || payload.pet === null) {
2686
+ __privateSet(this, _petClosedByContextMenu, false);
2687
+ }
2425
2688
  __privateSet(this, _framePetOptionsOverride, payload.pet);
2426
- __privateGet(this, _petOverlay).setOptions(__privateMethod(this, _ChatKitElementBase_instances, getOverlayPetOptions_fn).call(this));
2689
+ __privateMethod(this, _ChatKitElementBase_instances, syncPetOverlayOptions_fn).call(this);
2690
+ }
2691
+ });
2692
+ __privateGet(this, _messenger).on("chat_minimize_change", (data) => {
2693
+ const minimized = typeof data === "object" && data !== null && "minimized" in data && data.minimized === true;
2694
+ if (minimized && !__privateMethod(this, _ChatKitElementBase_instances, getOverlayPetOptions_fn).call(this)) {
2695
+ return;
2696
+ }
2697
+ __privateMethod(this, _ChatKitElementBase_instances, setChatMinimizedToPet_fn).call(this, minimized);
2698
+ if (minimized) {
2699
+ __privateMethod(this, _ChatKitElementBase_instances, setLauncherOpen_fn).call(this, false);
2427
2700
  }
2428
2701
  });
2429
2702
  __privateGet(this, _messenger).on("unmount", () => {
@@ -2459,8 +2732,9 @@ class ChatKitElementBase extends (_c = HTMLElement, _focusComposer_dec = [requir
2459
2732
  }
2460
2733
  applySanitizedOptions(newOptions) {
2461
2734
  __privateSet(this, _opts, newOptions);
2735
+ __privateSet(this, _petClosedByContextMenu, false);
2462
2736
  __privateGet(this, _petOverlay).setLocale(newOptions.locale);
2463
- __privateGet(this, _petOverlay).setOptions(__privateMethod(this, _ChatKitElementBase_instances, getOverlayPetOptions_fn).call(this));
2737
+ __privateMethod(this, _ChatKitElementBase_instances, syncPetOverlayOptions_fn).call(this);
2464
2738
  if (__privateGet(this, _initialized)) {
2465
2739
  __privateMethod(this, _ChatKitElementBase_instances, setOptionsDataAttributes_fn).call(this, __privateGet(this, _opts));
2466
2740
  __privateGet(this, _loaded).then(() => {
@@ -2540,7 +2814,9 @@ _frame = new WeakMap();
2540
2814
  _wrapper = new WeakMap();
2541
2815
  _launcherCloseButton = new WeakMap();
2542
2816
  _launcherOpen = new WeakMap();
2817
+ _chatMinimizedToPet = new WeakMap();
2543
2818
  _framePetOptionsOverride = new WeakMap();
2819
+ _petClosedByContextMenu = new WeakMap();
2544
2820
  _shadow = new WeakMap();
2545
2821
  _petOverlay = new WeakMap();
2546
2822
  _resolveLoaded = new WeakMap();
@@ -2571,8 +2847,42 @@ getConfiguredPetOptions_fn = function(options = __privateGet(this, _opts)) {
2571
2847
  }
2572
2848
  return options.pet ?? null;
2573
2849
  };
2850
+ mergeConfiguredPetPositionDefaults_fn = function(pet) {
2851
+ if (!pet) {
2852
+ return pet;
2853
+ }
2854
+ const configured = chatkitTypes.normalizePetOptions(
2855
+ __privateMethod(this, _ChatKitElementBase_instances, getConfiguredPetOptions_fn).call(this) ?? null
2856
+ );
2857
+ if (!configured) {
2858
+ return pet;
2859
+ }
2860
+ if (pet === true) {
2861
+ return { position: configured.position };
2862
+ }
2863
+ if (!pet.position) {
2864
+ return { ...pet, position: configured.position };
2865
+ }
2866
+ return {
2867
+ ...pet,
2868
+ position: {
2869
+ ...configured.position,
2870
+ ...pet.position,
2871
+ boundsPadding: pet.position.boundsPadding ?? configured.position.boundsPadding,
2872
+ pin: "pin" in pet.position ? pet.position.pin : configured.position.pin
2873
+ }
2874
+ };
2875
+ };
2574
2876
  getOverlayPetOptions_fn = function() {
2575
- let pet = __privateGet(this, _framePetOptionsOverride) !== void 0 ? __privateGet(this, _framePetOptionsOverride) : __privateMethod(this, _ChatKitElementBase_instances, getConfiguredPetOptions_fn).call(this);
2877
+ if (__privateGet(this, _petClosedByContextMenu)) {
2878
+ return null;
2879
+ }
2880
+ let pet;
2881
+ if (__privateGet(this, _framePetOptionsOverride) !== void 0) {
2882
+ pet = __privateMethod(this, _ChatKitElementBase_instances, mergeConfiguredPetPositionDefaults_fn).call(this, __privateGet(this, _framePetOptionsOverride));
2883
+ } else {
2884
+ pet = __privateMethod(this, _ChatKitElementBase_instances, getConfiguredPetOptions_fn).call(this);
2885
+ }
2576
2886
  if (__privateMethod(this, _ChatKitElementBase_instances, getDisplayMode_fn).call(this) === "pet" && !chatkitTypes.normalizePetOptions(pet ?? null)) {
2577
2887
  pet = true;
2578
2888
  }
@@ -2626,13 +2936,49 @@ setLauncherOpen_fn = function(open) {
2626
2936
  delete this.dataset.chatOpen;
2627
2937
  }
2628
2938
  };
2939
+ setChatMinimizedToPet_fn = function(minimized) {
2940
+ const next = minimized && Boolean(__privateMethod(this, _ChatKitElementBase_instances, getOverlayPetOptions_fn).call(this));
2941
+ __privateSet(this, _chatMinimizedToPet, next);
2942
+ if (next) {
2943
+ this.dataset.chatMinimizedToPet = "true";
2944
+ } else {
2945
+ delete this.dataset.chatMinimizedToPet;
2946
+ }
2947
+ };
2948
+ syncPetOverlayOptions_fn = function() {
2949
+ var _a2;
2950
+ const overlayPetOptions = __privateMethod(this, _ChatKitElementBase_instances, getOverlayPetOptions_fn).call(this);
2951
+ __privateGet(this, _petOverlay).setOptions(overlayPetOptions, (_a2 = __privateGet(this, _opts)) == null ? void 0 : _a2.theme);
2952
+ if (!overlayPetOptions) {
2953
+ __privateMethod(this, _ChatKitElementBase_instances, setChatMinimizedToPet_fn).call(this, false);
2954
+ }
2955
+ };
2629
2956
  handlePetActivate_fn = function() {
2957
+ if (__privateGet(this, _chatMinimizedToPet)) {
2958
+ __privateMethod(this, _ChatKitElementBase_instances, setChatMinimizedToPet_fn).call(this, false);
2959
+ if (__privateMethod(this, _ChatKitElementBase_instances, getDisplayMode_fn).call(this) === "pet") {
2960
+ __privateMethod(this, _ChatKitElementBase_instances, setLauncherOpen_fn).call(this, true);
2961
+ }
2962
+ __privateGet(this, _loaded).then(() => this.focusComposer()).catch(() => void 0);
2963
+ return;
2964
+ }
2630
2965
  if (__privateMethod(this, _ChatKitElementBase_instances, getDisplayMode_fn).call(this) !== "pet") {
2631
2966
  return;
2632
2967
  }
2633
2968
  __privateMethod(this, _ChatKitElementBase_instances, setLauncherOpen_fn).call(this, true);
2634
2969
  __privateGet(this, _loaded).then(() => this.focusComposer()).catch(() => void 0);
2635
2970
  };
2971
+ handlePetClose_fn = function() {
2972
+ __privateSet(this, _petClosedByContextMenu, true);
2973
+ __privateSet(this, _framePetOptionsOverride, null);
2974
+ __privateMethod(this, _ChatKitElementBase_instances, setChatMinimizedToPet_fn).call(this, false);
2975
+ __privateMethod(this, _ChatKitElementBase_instances, setLauncherOpen_fn).call(this, false);
2976
+ __privateGet(this, _petOverlay).setOptions(null);
2977
+ if (__privateMethod(this, _ChatKitElementBase_instances, getDisplayMode_fn).call(this) === "pet") {
2978
+ return;
2979
+ }
2980
+ __privateGet(this, _loaded).then(() => __privateGet(this, _messenger).commands.setPetEnabled({ enabled: false })).catch(() => void 0);
2981
+ };
2636
2982
  handlePetReply_fn = async function(text) {
2637
2983
  await this.sendUserMessage({ text });
2638
2984
  };