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