@xapp/chat-widget 1.98.4 → 1.99.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/index.js CHANGED
@@ -4491,23 +4491,48 @@ var MCPChat = /** @class */ (function () {
4491
4491
  log("SERVER: account_status: ".concat(JSON.stringify(status)));
4492
4492
  this.dispatch(setAccountStatus(status));
4493
4493
  };
4494
- MCPChat.prototype.getEndpoint = function () {
4494
+ /**
4495
+ * The paths this mode hangs off its base URL: [stream, close].
4496
+ */
4497
+ MCPChat.prototype.getEndpointPaths = function () {
4498
+ return (this.config.mode || "homeowner") === "business_owner"
4499
+ ? [BUSINESS_OWNER_ENDPOINT_PATH, BUSINESS_OWNER_CLOSE_ENDPOINT_PATH]
4500
+ : [HOMEOWNER_ENDPOINT_PATH, HOMEOWNER_CLOSE_ENDPOINT_PATH];
4501
+ };
4502
+ /**
4503
+ * The base the endpoint paths are built from.
4504
+ *
4505
+ * A configured `serverUrl` arrives in either of two shapes: the base
4506
+ * (`https://agentic.xapp.ai/<appId>`), which is what this class was written for, or the
4507
+ * full stream endpoint (`https://agentic.xapp.ai/<appId>/chat/stream`), which is the form
4508
+ * issue #1403 documents as the Dev and Prod defaults. Appending unconditionally turns the
4509
+ * second into `.../chat/stream/chat/stream`.
4510
+ *
4511
+ * Taking `serverUrl` verbatim instead is not the fix: `/chat/close` has to be derived
4512
+ * from something, and a widget configured with the full stream URL would then send every
4513
+ * close request to the stream endpoint. So normalise back to the base and build both
4514
+ * paths from it, which leaves either configured form working.
4515
+ *
4516
+ * Only the paths belonging to the CONFIGURED mode come off. A homeowner URL handed to a
4517
+ * business_owner widget is a misconfiguration, not something to quietly reinterpret, and
4518
+ * an appId that merely ends in `/stream` is not an endpoint path.
4519
+ */
4520
+ MCPChat.prototype.getBaseUrl = function () {
4495
4521
  // Normalize serverUrl to remove trailing slashes
4496
4522
  var serverUrl = this.config.serverUrl.replace(/\/+$/, "");
4497
- var mode = this.config.mode || "homeowner";
4498
- if (mode === "business_owner") {
4499
- return "".concat(serverUrl).concat(BUSINESS_OWNER_ENDPOINT_PATH);
4523
+ for (var _i = 0, _a = this.getEndpointPaths(); _i < _a.length; _i++) {
4524
+ var path = _a[_i];
4525
+ if (serverUrl.endsWith(path)) {
4526
+ return serverUrl.slice(0, -path.length);
4527
+ }
4500
4528
  }
4501
- // homeowner mode - serverUrl should already include appId
4502
- return "".concat(serverUrl).concat(HOMEOWNER_ENDPOINT_PATH);
4529
+ return serverUrl;
4530
+ };
4531
+ MCPChat.prototype.getEndpoint = function () {
4532
+ return "".concat(this.getBaseUrl()).concat(this.getEndpointPaths()[0]);
4503
4533
  };
4504
4534
  MCPChat.prototype.getCloseEndpoint = function () {
4505
- var serverUrl = this.config.serverUrl.replace(/\/+$/, "");
4506
- var mode = this.config.mode || "homeowner";
4507
- if (mode === "business_owner") {
4508
- return "".concat(serverUrl).concat(BUSINESS_OWNER_CLOSE_ENDPOINT_PATH);
4509
- }
4510
- return "".concat(serverUrl).concat(HOMEOWNER_CLOSE_ENDPOINT_PATH);
4535
+ return "".concat(this.getBaseUrl()).concat(this.getEndpointPaths()[1]);
4511
4536
  };
4512
4537
  MCPChat.prototype.sendNewMessage = function (msg, user) {
4513
4538
  log("SERVER: new message: ".concat(JSON.stringify(msg)));
@@ -35435,11 +35460,23 @@ var MessageList = function (_a) {
35435
35460
  var _a = containerRef.current, scrollTop = _a.scrollTop, scrollHeight = _a.scrollHeight, clientHeight = _a.clientHeight;
35436
35461
  return Math.abs(scrollHeight - clientHeight - scrollTop) < 10;
35437
35462
  }, []);
35438
- // Scroll to bottom
35463
+ /**
35464
+ * Pin the list to its newest message.
35465
+ *
35466
+ * Scrolls THIS container, deliberately - not `messagesEndRef.scrollIntoView()`.
35467
+ * scrollIntoView walks every scrollable ancestor, and its default `block: "start"` puts
35468
+ * the target at the top of each one. On first open the greeting and its chips arrive,
35469
+ * this effect fires, and the whole chat panel was smoothly dragged upward until the end
35470
+ * marker reached the top - leaving only the suggestion chips and the input on screen
35471
+ * until the user scrolled back by hand. Device, 2026-09-10.
35472
+ *
35473
+ * A container scroll cannot touch an ancestor, which is the entire point.
35474
+ */
35439
35475
  var scrollToBottom = React.useCallback(function (behavior) {
35440
35476
  if (behavior === void 0) { behavior = "smooth"; }
35441
- if (messagesEndRef.current) {
35442
- messagesEndRef.current.scrollIntoView({ behavior: behavior });
35477
+ var container = containerRef.current;
35478
+ if (container) {
35479
+ container.scrollTo({ top: container.scrollHeight, behavior: behavior });
35443
35480
  }
35444
35481
  }, []);
35445
35482
  // Always scroll to bottom when messages change (unless disabled)
@@ -35472,16 +35509,16 @@ var MessageList = function (_a) {
35472
35509
  }, [hasWsButton, isAtBottom, scrollToBottom, disableAutoScroll]);
35473
35510
  var handleSend = React.useCallback(function (msg) {
35474
35511
  onSend(msg);
35475
- if (!disableAutoScroll && messagesEndRef.current) {
35476
- messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
35512
+ if (!disableAutoScroll) {
35513
+ scrollToBottom("smooth");
35477
35514
  }
35478
- }, [onSend, disableAutoScroll]);
35515
+ }, [onSend, disableAutoScroll, scrollToBottom]);
35479
35516
  var handleWrite = React.useCallback(function (msg) {
35480
35517
  onWrite(msg);
35481
- if (!disableAutoScroll && messagesEndRef.current) {
35482
- messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
35518
+ if (!disableAutoScroll) {
35519
+ scrollToBottom("smooth");
35483
35520
  }
35484
- }, [onWrite, disableAutoScroll]);
35521
+ }, [onWrite, disableAutoScroll, scrollToBottom]);
35485
35522
  var ctxCache = React.useMemo(function () {
35486
35523
  return new MiddlewareContextFactory({
35487
35524
  send: handleSend,