@superatomai/application-react 0.0.2 → 0.0.4

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.cjs CHANGED
@@ -5454,34 +5454,34 @@ function subcontent(events, eventIndex) {
5454
5454
  let stream;
5455
5455
  let previous3;
5456
5456
  let index2 = -1;
5457
- let current = token;
5457
+ let current3 = token;
5458
5458
  let adjust = 0;
5459
5459
  let start2 = 0;
5460
5460
  const breaks = [start2];
5461
- while (current) {
5462
- while (events.get(++startPosition)[1] !== current) {
5461
+ while (current3) {
5462
+ while (events.get(++startPosition)[1] !== current3) {
5463
5463
  }
5464
5464
  startPositions.push(startPosition);
5465
- if (!current._tokenizer) {
5466
- stream = context.sliceStream(current);
5467
- if (!current.next) {
5465
+ if (!current3._tokenizer) {
5466
+ stream = context.sliceStream(current3);
5467
+ if (!current3.next) {
5468
5468
  stream.push(null);
5469
5469
  }
5470
5470
  if (previous3) {
5471
- tokenizer.defineSkip(current.start);
5471
+ tokenizer.defineSkip(current3.start);
5472
5472
  }
5473
- if (current._isInFirstContentOfListItem) {
5473
+ if (current3._isInFirstContentOfListItem) {
5474
5474
  tokenizer._gfmTasklistFirstContentOfListItem = true;
5475
5475
  }
5476
5476
  tokenizer.write(stream);
5477
- if (current._isInFirstContentOfListItem) {
5477
+ if (current3._isInFirstContentOfListItem) {
5478
5478
  tokenizer._gfmTasklistFirstContentOfListItem = void 0;
5479
5479
  }
5480
5480
  }
5481
- previous3 = current;
5482
- current = current.next;
5481
+ previous3 = current3;
5482
+ current3 = current3.next;
5483
5483
  }
5484
- current = token;
5484
+ current3 = token;
5485
5485
  while (++index2 < childEvents.length) {
5486
5486
  if (
5487
5487
  // Find a void token that includes a break.
@@ -5489,15 +5489,15 @@ function subcontent(events, eventIndex) {
5489
5489
  ) {
5490
5490
  start2 = index2 + 1;
5491
5491
  breaks.push(start2);
5492
- current._tokenizer = void 0;
5493
- current.previous = void 0;
5494
- current = current.next;
5492
+ current3._tokenizer = void 0;
5493
+ current3.previous = void 0;
5494
+ current3 = current3.next;
5495
5495
  }
5496
5496
  }
5497
5497
  tokenizer.events = [];
5498
- if (current) {
5499
- current._tokenizer = void 0;
5500
- current.previous = void 0;
5498
+ if (current3) {
5499
+ current3._tokenizer = void 0;
5500
+ current3.previous = void 0;
5501
5501
  } else {
5502
5502
  breaks.pop();
5503
5503
  }
@@ -13512,9 +13512,9 @@ function isAsciiAlphaNumeric(code4) {
13512
13512
  function isEntityInAttributeInvalidEnd(code4) {
13513
13513
  return code4 === CharCodes.EQUALS || isAsciiAlphaNumeric(code4);
13514
13514
  }
13515
- function determineBranch(decodeTree, current, nodeIndex, char) {
13516
- const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
13517
- const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
13515
+ function determineBranch(decodeTree, current3, nodeIndex, char) {
13516
+ const branchCount = (current3 & BinTrieFlags.BRANCH_LENGTH) >> 7;
13517
+ const jumpOffset = current3 & BinTrieFlags.JUMP_TABLE;
13518
13518
  if (branchCount === 0) {
13519
13519
  return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1;
13520
13520
  }
@@ -13753,19 +13753,19 @@ var init_decode = __esm({
13753
13753
  */
13754
13754
  stateNamedEntity(input, offset) {
13755
13755
  const { decodeTree } = this;
13756
- let current = decodeTree[this.treeIndex];
13757
- let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
13756
+ let current3 = decodeTree[this.treeIndex];
13757
+ let valueLength = (current3 & BinTrieFlags.VALUE_LENGTH) >> 14;
13758
13758
  for (; offset < input.length; offset++, this.excess++) {
13759
13759
  const char = input.charCodeAt(offset);
13760
- this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
13760
+ this.treeIndex = determineBranch(decodeTree, current3, this.treeIndex + Math.max(1, valueLength), char);
13761
13761
  if (this.treeIndex < 0) {
13762
13762
  return this.result === 0 || // If we are parsing an attribute
13763
13763
  this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity,
13764
13764
  (valueLength === 0 || // And there should be no invalid characters.
13765
13765
  isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity();
13766
13766
  }
13767
- current = decodeTree[this.treeIndex];
13768
- valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
13767
+ current3 = decodeTree[this.treeIndex];
13768
+ valueLength = (current3 & BinTrieFlags.VALUE_LENGTH) >> 14;
13769
13769
  if (valueLength !== 0) {
13770
13770
  if (char === CharCodes.SEMI) {
13771
13771
  return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
@@ -19821,8 +19821,8 @@ function startTagInForeignContent(p, token) {
19821
19821
  popUntilHtmlOrIntegrationPoint(p);
19822
19822
  p._startTagOutsideForeignContent(token);
19823
19823
  } else {
19824
- const current = p._getAdjustedCurrentElement();
19825
- const currentNs = p.treeAdapter.getNamespaceURI(current);
19824
+ const current3 = p._getAdjustedCurrentElement();
19825
+ const currentNs = p.treeAdapter.getNamespaceURI(current3);
19826
19826
  if (currentNs === NS.MATHML) {
19827
19827
  adjustTokenMathMLAttrs(token);
19828
19828
  } else if (currentNs === NS.SVG) {
@@ -20010,21 +20010,21 @@ var init_parser = __esm({
20010
20010
  }
20011
20011
  (_b = (_a4 = this.treeAdapter).onItemPop) === null || _b === void 0 ? void 0 : _b.call(_a4, node2, this.openElements.current);
20012
20012
  if (isTop) {
20013
- let current;
20013
+ let current3;
20014
20014
  let currentTagId;
20015
20015
  if (this.openElements.stackTop === 0 && this.fragmentContext) {
20016
- current = this.fragmentContext;
20016
+ current3 = this.fragmentContext;
20017
20017
  currentTagId = this.fragmentContextID;
20018
20018
  } else {
20019
- ({ current, currentTagId } = this.openElements);
20019
+ ({ current: current3, currentTagId } = this.openElements);
20020
20020
  }
20021
- this._setContextModes(current, currentTagId);
20021
+ this._setContextModes(current3, currentTagId);
20022
20022
  }
20023
20023
  }
20024
- _setContextModes(current, tid) {
20025
- const isHTML = current === this.document || current && this.treeAdapter.getNamespaceURI(current) === NS.HTML;
20024
+ _setContextModes(current3, tid) {
20025
+ const isHTML = current3 === this.document || current3 && this.treeAdapter.getNamespaceURI(current3) === NS.HTML;
20026
20026
  this.currentNotInHTML = !isHTML;
20027
- this.tokenizer.inForeignNode = !isHTML && current !== void 0 && tid !== void 0 && !this._isIntegrationPoint(tid, current);
20027
+ this.tokenizer.inForeignNode = !isHTML && current3 !== void 0 && tid !== void 0 && !this._isIntegrationPoint(tid, current3);
20028
20028
  }
20029
20029
  /** @protected */
20030
20030
  _switchToTextParsing(currentToken, nextTokenizerState) {
@@ -20222,22 +20222,22 @@ var init_parser = __esm({
20222
20222
  shouldProcessStartTagTokenInForeignContent(token) {
20223
20223
  if (!this.currentNotInHTML)
20224
20224
  return false;
20225
- let current;
20225
+ let current3;
20226
20226
  let currentTagId;
20227
20227
  if (this.openElements.stackTop === 0 && this.fragmentContext) {
20228
- current = this.fragmentContext;
20228
+ current3 = this.fragmentContext;
20229
20229
  currentTagId = this.fragmentContextID;
20230
20230
  } else {
20231
- ({ current, currentTagId } = this.openElements);
20231
+ ({ current: current3, currentTagId } = this.openElements);
20232
20232
  }
20233
- if (token.tagID === TAG_ID.SVG && this.treeAdapter.getTagName(current) === TAG_NAMES.ANNOTATION_XML && this.treeAdapter.getNamespaceURI(current) === NS.MATHML) {
20233
+ if (token.tagID === TAG_ID.SVG && this.treeAdapter.getTagName(current3) === TAG_NAMES.ANNOTATION_XML && this.treeAdapter.getNamespaceURI(current3) === NS.MATHML) {
20234
20234
  return false;
20235
20235
  }
20236
20236
  return (
20237
20237
  // Check that `current` is not an integration point for HTML or MathML elements.
20238
20238
  this.tokenizer.inForeignNode || // If it _is_ an integration point, then we might have to check that it is not an HTML
20239
20239
  // integration point.
20240
- (token.tagID === TAG_ID.MGLYPH || token.tagID === TAG_ID.MALIGNMARK) && currentTagId !== void 0 && !this._isIntegrationPoint(currentTagId, current, NS.HTML)
20240
+ (token.tagID === TAG_ID.MGLYPH || token.tagID === TAG_ID.MALIGNMARK) && currentTagId !== void 0 && !this._isIntegrationPoint(currentTagId, current3, NS.HTML)
20241
20241
  );
20242
20242
  }
20243
20243
  /** @protected */
@@ -21197,8 +21197,8 @@ function startTag(node2, state) {
21197
21197
  const tagName = node2.tagName.toLowerCase();
21198
21198
  if (state.parser.tokenizer.state === TokenizerMode.PLAINTEXT) return;
21199
21199
  resetTokenizer(state, pointStart(node2));
21200
- const current = state.parser.openElements.current;
21201
- let ns = "namespaceURI" in current ? current.namespaceURI : webNamespaces.html;
21200
+ const current3 = state.parser.openElements.current;
21201
+ let ns = "namespaceURI" in current3 ? current3.namespaceURI : webNamespaces.html;
21202
21202
  if (ns === webNamespaces.html && tagName === "svg") {
21203
21203
  ns = webNamespaces.svg;
21204
21204
  }
@@ -50467,11 +50467,13 @@ var init_Markdown = __esm({
50467
50467
  // Suppress the default <pre> wrapper — code renderer owns the full block structure
50468
50468
  pre: ({ node: _node, children }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }),
50469
50469
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50470
- code: ({ node: _node, inline, className, children, ...props }) => {
50470
+ code: ({ node: _node, className, children, ...props }) => {
50471
50471
  const langMatch = /language-(\w+)/.exec(className || "");
50472
50472
  const language = langMatch ? langMatch[1] : "";
50473
- if (!inline) {
50474
- return language ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: [
50473
+ const isBlock = Boolean(language) || String(children).includes("\n");
50474
+ const codeText2 = String(children).replace(/^\s*\n+/, "").replace(/\s+$/, "");
50475
+ if (isBlock) {
50476
+ return language ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sa-code-block not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: [
50475
50477
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-neutral px-4 py-2 text-xs text-neutral-content font-mono", children: language }),
50476
50478
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
50477
50479
  prism_default2,
@@ -50481,13 +50483,13 @@ var init_Markdown = __esm({
50481
50483
  PreTag: "div",
50482
50484
  className: "!my-0 !rounded-none text-sm",
50483
50485
  customStyle: { margin: 0, borderRadius: 0, padding: "1rem" },
50484
- codeTagProps: { style: { backgroundColor: "transparent", padding: 0, fontSize: "inherit", textDecoration: "none" } },
50486
+ codeTagProps: { style: { backgroundColor: "transparent", padding: 0, fontSize: "inherit" } },
50485
50487
  wrapLongLines: false,
50486
50488
  ...props,
50487
- children: String(children).replace(/\n$/, "")
50489
+ children: codeText2
50488
50490
  }
50489
50491
  ) })
50490
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-base-200 text-base-content p-4 overflow-x-auto text-sm font-mono whitespace-pre-wrap break-words", children: /* @__PURE__ */ jsxRuntime.jsx("code", { ...props, children }) }) });
50492
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sa-code-block not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "bg-base-200 text-base-content p-4 overflow-x-auto text-sm font-mono whitespace-pre-wrap break-words", children: /* @__PURE__ */ jsxRuntime.jsx("code", { ...props, children: codeText2 }) }) });
50491
50493
  }
50492
50494
  return /* @__PURE__ */ jsxRuntime.jsx("code", { className: "bg-base-200 text-teal-700 px-1.5 py-0.5 rounded text-sm font-mono break-words", ...props, children });
50493
50495
  },
@@ -66197,6 +66199,7 @@ __export(services_exports, {
66197
66199
  deleteArtifact: () => deleteArtifact,
66198
66200
  deleteBookmark: () => deleteBookmark,
66199
66201
  deleteThread: () => deleteThread,
66202
+ deleteUserConversation: () => deleteUserConversation,
66200
66203
  getActions: () => getActions,
66201
66204
  getAllArtifacts: () => getAllArtifacts,
66202
66205
  getAllBookmarks: () => getAllBookmarks,
@@ -66336,6 +66339,34 @@ function injectBundleStyles() {
66336
66339
  daisyThemes.type = "text/css";
66337
66340
  daisyThemes.href = "https://cdn.jsdelivr.net/npm/daisyui@5/themes.css";
66338
66341
  document.head.appendChild(daisyThemes);
66342
+ const codeStyles = document.createElement("style");
66343
+ codeStyles.setAttribute("data-sa-code-styles", "true");
66344
+ codeStyles.textContent = `
66345
+ .sa-code-block, .sa-code-block * {
66346
+ text-decoration: none !important;
66347
+ text-shadow: none !important;
66348
+ border: 0;
66349
+ list-style: none;
66350
+ }
66351
+ .sa-code-block pre, .sa-code-block code, .sa-code-block span {
66352
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace !important;
66353
+ line-height: 1.5 !important;
66354
+ letter-spacing: normal !important;
66355
+ }
66356
+ .sa-code-block code { background: transparent !important; }
66357
+
66358
+ /* Tailwind v4 dropped the browser default of cursor:pointer on buttons, so
66359
+ anything clickable in our components would otherwise show an arrow. */
66360
+ .sa-ui button:not(:disabled),
66361
+ .sa-ui [role="button"]:not([aria-disabled="true"]),
66362
+ .sa-ui summary,
66363
+ .sa-ui label[for],
66364
+ .sa-ui select {
66365
+ cursor: pointer;
66366
+ }
66367
+ .sa-ui button:disabled { cursor: not-allowed; }
66368
+ `;
66369
+ document.head.appendChild(codeStyles);
66339
66370
  }
66340
66371
  async function requestBundle(client, options) {
66341
66372
  injectBundleStyles();
@@ -67152,6 +67183,29 @@ async function deleteThread(client, userId, threadId, clearCache = true, timeout
67152
67183
  };
67153
67184
  }
67154
67185
  }
67186
+ async function deleteUserConversation(client, userPrompt, userId, clearCache = true, timeout = 3e4) {
67187
+ try {
67188
+ const response = await client.requestData({
67189
+ collection: "user-conversations",
67190
+ operation: "deleteByUserPrompt",
67191
+ params: { userPrompt, userId, clearCache },
67192
+ timeout
67193
+ });
67194
+ if (response.error) {
67195
+ return { success: false, error: response.error };
67196
+ }
67197
+ const data = response.data;
67198
+ if (data?.success === false) {
67199
+ return { success: false, error: data.message || "Failed to delete the message" };
67200
+ }
67201
+ return { success: true, message: data?.message, queriesDeleted: data?.queriesDeleted };
67202
+ } catch (error51) {
67203
+ return {
67204
+ success: false,
67205
+ error: error51 instanceof Error ? error51.message : "Failed to delete the message"
67206
+ };
67207
+ }
67208
+ }
67155
67209
  async function updateThreadTitle(client, userId, threadId, title, timeout = 15e3) {
67156
67210
  try {
67157
67211
  const response = await client.requestData({
@@ -67212,6 +67266,10 @@ var SuperatomClient = class {
67212
67266
  this.errorHandlers = /* @__PURE__ */ new Set();
67213
67267
  this.connectPromise = null;
67214
67268
  this.connectResolve = null;
67269
+ // Supplies a valid session token before each (re)connect — see ClientConfig.getAccessToken.
67270
+ this.getAccessToken = null;
67271
+ // Waiting for getAccessToken; the socket does not exist yet.
67272
+ this.fetchingToken = false;
67215
67273
  // Heartbeat — detects zombie sockets that readyState=OPEN can't catch.
67216
67274
  this.heartbeatInterval = null;
67217
67275
  this.pongDeadline = null;
@@ -67230,6 +67288,7 @@ var SuperatomClient = class {
67230
67288
  this.boundHandlePageShow = null;
67231
67289
  const parsed = ClientConfigSchema.parse(config2);
67232
67290
  this.config = parsed;
67291
+ this.getAccessToken = config2.getAccessToken ?? null;
67233
67292
  this.registerGlobals();
67234
67293
  }
67235
67294
  registerGlobals() {
@@ -67381,21 +67440,7 @@ var SuperatomClient = class {
67381
67440
  * callbacks so the eventual real close doesn't double-fire everything.
67382
67441
  */
67383
67442
  markAsDead(reasonMessage) {
67384
- if (this.socket) {
67385
- const dying = this.socket;
67386
- this.socket = null;
67387
- try {
67388
- dying.onopen = null;
67389
- dying.onmessage = null;
67390
- dying.onerror = null;
67391
- dying.onclose = null;
67392
- if (dying.readyState === WebSocket.OPEN || dying.readyState === WebSocket.CONNECTING) {
67393
- dying.close();
67394
- }
67395
- } catch (err) {
67396
- this.log("error", "Failed to detach dying socket:", err);
67397
- }
67398
- }
67443
+ this.discardSocket();
67399
67444
  this.stopHeartbeat();
67400
67445
  this.failPendingRequests(new Error(reasonMessage));
67401
67446
  this.disconnectionHandlers.forEach((handler) => handler());
@@ -67412,21 +67457,39 @@ var SuperatomClient = class {
67412
67457
  this.cancelReconnectTimer();
67413
67458
  this.reconnectAttempts = 0;
67414
67459
  this.shouldReconnect = true;
67415
- if (this.socket) {
67416
- try {
67417
- const old = this.socket;
67418
- this.socket = null;
67419
- if (old.readyState === WebSocket.OPEN || old.readyState === WebSocket.CONNECTING) {
67420
- old.close();
67421
- }
67422
- } catch (err) {
67423
- this.log("error", "Failed to close old socket during forceReconnect:", err);
67424
- }
67425
- }
67460
+ const wasOpen = this.discardSocket();
67426
67461
  this.stopHeartbeat();
67427
67462
  this.failPendingRequests(new Error("Connection reset"));
67463
+ if (wasOpen) this.disconnectionHandlers.forEach((handler) => handler());
67428
67464
  return this.connect();
67429
67465
  }
67466
+ /**
67467
+ * Drop the current socket without triggering its handlers: they are detached
67468
+ * first, so its eventual real close is a no-op (on Linux/Chrome that close
67469
+ * can arrive 30-60s later). Returns whether it was open.
67470
+ */
67471
+ discardSocket() {
67472
+ const old = this.socket;
67473
+ if (!old) return false;
67474
+ this.socket = null;
67475
+ const wasOpen = old.readyState === WebSocket.OPEN;
67476
+ try {
67477
+ old.onopen = null;
67478
+ old.onmessage = null;
67479
+ old.onerror = null;
67480
+ old.onclose = null;
67481
+ if (wasOpen || old.readyState === WebSocket.CONNECTING) {
67482
+ old.close();
67483
+ }
67484
+ } catch (err) {
67485
+ this.log("error", "Failed to detach old socket:", err);
67486
+ }
67487
+ return wasOpen;
67488
+ }
67489
+ /** A connection attempt is under way — fetching the session token, or the socket handshake. */
67490
+ isConnecting() {
67491
+ return this.fetchingToken || this.socket?.readyState === WebSocket.CONNECTING;
67492
+ }
67430
67493
  /**
67431
67494
  * Attach window-level listeners that catch network changes faster than
67432
67495
  * the socket's own onclose. The browser's onclose for a half-open TCP can
@@ -67448,15 +67511,18 @@ var SuperatomClient = class {
67448
67511
  this.boundHandleVisibilityChange = () => {
67449
67512
  if (typeof document === "undefined") return;
67450
67513
  if (document.visibilityState !== "visible") return;
67451
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
67514
+ if (this.socket?.readyState === WebSocket.OPEN) {
67515
+ this.sendHeartbeatProbe();
67516
+ } else if (this.isConnecting()) {
67517
+ return;
67518
+ } else if (this.shouldReconnect) {
67452
67519
  this.forceReconnect().catch((err) => {
67453
67520
  this.log("error", "forceReconnect from visibility failed:", err);
67454
67521
  });
67455
- } else {
67456
- this.sendHeartbeatProbe();
67457
67522
  }
67458
67523
  };
67459
- this.boundHandlePageShow = (() => {
67524
+ this.boundHandlePageShow = ((event) => {
67525
+ if (!event.persisted) return;
67460
67526
  this.forceReconnect().catch((err) => {
67461
67527
  this.log("error", "forceReconnect from pageshow failed:", err);
67462
67528
  });
@@ -67571,13 +67637,50 @@ var SuperatomClient = class {
67571
67637
  this.log("warn", "Already connected");
67572
67638
  return Promise.resolve();
67573
67639
  }
67574
- if (this.socket?.readyState === WebSocket.CONNECTING && this.connectPromise) {
67640
+ if ((this.socket?.readyState === WebSocket.CONNECTING || this.fetchingToken) && this.connectPromise) {
67575
67641
  this.log("warn", "Connection already in progress");
67576
67642
  return this.connectPromise;
67577
67643
  }
67578
67644
  this.connectPromise = new Promise((resolve) => {
67579
67645
  this.connectResolve = resolve;
67580
67646
  });
67647
+ if (this.getAccessToken) {
67648
+ void this.openWithFreshToken();
67649
+ } else {
67650
+ this.openSocket();
67651
+ }
67652
+ return this.connectPromise;
67653
+ }
67654
+ /**
67655
+ * Get a valid session token, then open the socket. The relay checks the token
67656
+ * only at handshake, so every (re)connect must present one that is still valid.
67657
+ *
67658
+ * A fatal auth failure (see SuperAtomAuthError.fatal) stops reconnecting — the
67659
+ * session reports it to the host, and retrying would only repeat it. Anything
67660
+ * else goes through the normal back-off.
67661
+ */
67662
+ async openWithFreshToken() {
67663
+ this.fetchingToken = true;
67664
+ try {
67665
+ this.config.token = await this.getAccessToken();
67666
+ } catch (error51) {
67667
+ this.fetchingToken = false;
67668
+ const err = error51 instanceof Error ? error51 : new Error("Could not get a session token");
67669
+ this.errorHandlers.forEach((handler) => handler(err));
67670
+ if (error51?.fatal) {
67671
+ this.log("error", "Sign-in failed; not reconnecting:", err.message);
67672
+ this.shouldReconnect = false;
67673
+ this.cancelReconnectTimer();
67674
+ } else {
67675
+ this.scheduleReconnect();
67676
+ }
67677
+ return;
67678
+ }
67679
+ this.fetchingToken = false;
67680
+ if (!this.shouldReconnect) return;
67681
+ this.openSocket();
67682
+ }
67683
+ openSocket() {
67581
67684
  try {
67582
67685
  this.socket = new WebSocket(this.getWebSocketUrl());
67583
67686
  this.setupSocketHandlers();
@@ -67588,7 +67691,6 @@ var SuperatomClient = class {
67588
67691
  this.errorHandlers.forEach((handler) => handler(err));
67589
67692
  this.scheduleReconnect();
67590
67693
  }
67591
- return this.connectPromise;
67592
67694
  }
67593
67695
  /**
67594
67696
  * Disconnect from WebSocket server
@@ -67612,6 +67714,7 @@ var SuperatomClient = class {
67612
67714
  const merged = { ...this.config, ...config2 };
67613
67715
  const parsed = ClientConfigSchema.parse(merged);
67614
67716
  this.config = parsed;
67717
+ if ("getAccessToken" in config2) this.getAccessToken = config2.getAccessToken ?? null;
67615
67718
  }
67616
67719
  /**
67617
67720
  * Reconnect with new configuration
@@ -68038,6 +68141,197 @@ var SuperatomClient = class {
68038
68141
  }
68039
68142
  };
68040
68143
 
68144
+ // src/auth.ts
68145
+ var DEFAULT_API_URL = "https://sa-api.superatom.ai";
68146
+ var SuperAtomAuthError = class extends Error {
68147
+ constructor(code4, message, options) {
68148
+ super(message);
68149
+ this.name = "SuperAtomAuthError";
68150
+ this.code = code4;
68151
+ this.reason = options.reason;
68152
+ this.status = options.status;
68153
+ this.fatal = options.fatal;
68154
+ if (options.cause !== void 0) this.cause = options.cause;
68155
+ }
68156
+ };
68157
+ var MIN_REMAINING_MS = 6e4;
68158
+ var RENEW_AHEAD_MS = 12e4;
68159
+ var RENEW_RETRY_MS = 3e4;
68160
+ var RETRIES_BEFORE_REPORTING = 2;
68161
+ var EXCHANGE_TIMEOUT_MS = 15e3;
68162
+ function expiryOf(token) {
68163
+ try {
68164
+ const payload = token.split(".")[1];
68165
+ if (!payload) return null;
68166
+ const json3 = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
68167
+ return typeof json3.exp === "number" ? json3.exp * 1e3 : null;
68168
+ } catch {
68169
+ return null;
68170
+ }
68171
+ }
68172
+ var SdkSession = class {
68173
+ constructor(opts) {
68174
+ this.token = null;
68175
+ this.expiresAt = 0;
68176
+ this.currentUser = null;
68177
+ /** The one renewal in flight — every caller shares it. */
68178
+ this.inFlight = null;
68179
+ this.fatalError = null;
68180
+ /** Retryable failures in a row; reset by a successful exchange. */
68181
+ this.retries = 0;
68182
+ this.renewTimer = null;
68183
+ this.stopped = false;
68184
+ this.opts = opts;
68185
+ this.apiUrl = (opts.apiUrl || DEFAULT_API_URL).replace(/\/+$/, "");
68186
+ }
68187
+ get user() {
68188
+ return this.currentUser;
68189
+ }
68190
+ /** The fatal error that stopped the session, if any. */
68191
+ get error() {
68192
+ return this.fatalError;
68193
+ }
68194
+ /**
68195
+ * A token with at least a minute left — the cached one, or a fresh exchange.
68196
+ * Rejects with SuperAtomAuthError.
68197
+ */
68198
+ getAccessToken() {
68199
+ if (this.fatalError) return Promise.reject(this.fatalError);
68200
+ if (this.token && this.expiresAt - Date.now() > MIN_REMAINING_MS) {
68201
+ return Promise.resolve(this.token);
68202
+ }
68203
+ return this.renew();
68204
+ }
68205
+ /** Clear a fatal error so the next getAccessToken() tries again (e.g. after the user signs in again). */
68206
+ reset() {
68207
+ this.fatalError = null;
68208
+ this.stopped = false;
68209
+ }
68210
+ /** Stop renewing — the host unmounted or disconnected. */
68211
+ stop() {
68212
+ this.stopped = true;
68213
+ this.clearRenewTimer();
68214
+ }
68215
+ renew() {
68216
+ if (!this.inFlight) {
68217
+ this.opts.onSigningIn?.(true);
68218
+ this.inFlight = this.exchange().finally(() => {
68219
+ this.inFlight = null;
68220
+ this.opts.onSigningIn?.(false);
68221
+ });
68222
+ }
68223
+ return this.inFlight;
68224
+ }
68225
+ async exchange() {
68226
+ let customerToken;
68227
+ try {
68228
+ customerToken = await this.opts.getToken();
68229
+ } catch (err) {
68230
+ throw this.fail(new SuperAtomAuthError("get_token_failed", "getToken() failed", { fatal: true, cause: err }));
68231
+ }
68232
+ if (typeof customerToken !== "string" || !customerToken.trim()) {
68233
+ throw this.fail(new SuperAtomAuthError("get_token_failed", "getToken() returned no token", { fatal: true }));
68234
+ }
68235
+ const controller = new AbortController();
68236
+ const timeout = setTimeout(() => controller.abort(), EXCHANGE_TIMEOUT_MS);
68237
+ let res;
68238
+ try {
68239
+ res = await fetch(`${this.apiUrl}/auth/sdk/exchange`, {
68240
+ method: "POST",
68241
+ headers: { "Content-Type": "application/json" },
68242
+ body: JSON.stringify({ projectId: this.opts.projectId, token: customerToken.trim() }),
68243
+ signal: controller.signal
68244
+ });
68245
+ } catch (err) {
68246
+ throw this.fail(new SuperAtomAuthError("network", "Could not reach Superatom to sign in", { fatal: false, cause: err }));
68247
+ } finally {
68248
+ clearTimeout(timeout);
68249
+ }
68250
+ const body = await res.json().catch(() => ({}));
68251
+ if (!res.ok) {
68252
+ const message = typeof body?.error === "string" ? body.error : `Sign-in failed (${res.status})`;
68253
+ const fatal = res.status >= 400 && res.status < 500 && res.status !== 429;
68254
+ throw this.fail(
68255
+ new SuperAtomAuthError(fatal ? "exchange_rejected" : "exchange_unavailable", message, {
68256
+ status: res.status,
68257
+ reason: typeof body?.code === "string" ? body.code : void 0,
68258
+ fatal
68259
+ })
68260
+ );
68261
+ }
68262
+ if (typeof body?.token !== "string" || !body.user?.id) {
68263
+ throw this.fail(
68264
+ new SuperAtomAuthError("exchange_unavailable", "Unexpected sign-in response", { status: res.status, fatal: false })
68265
+ );
68266
+ }
68267
+ if (Array.isArray(body.warnings)) {
68268
+ for (const w of body.warnings) console.warn("[SuperAtom] Sign-in warning:", w);
68269
+ }
68270
+ this.token = body.token;
68271
+ this.expiresAt = expiryOf(body.token) ?? Date.now() + 15 * 6e4;
68272
+ this.currentUser = {
68273
+ id: body.user.id,
68274
+ email: body.user.email,
68275
+ name: body.user.name,
68276
+ role: body.user.role,
68277
+ externalRoles: Array.isArray(body.user.externalRoles) ? body.user.externalRoles : []
68278
+ };
68279
+ this.log(`Signed in as ${this.currentUser.email}; token valid until ${new Date(this.expiresAt).toISOString()}`);
68280
+ if (this.retries) {
68281
+ this.retries = 0;
68282
+ this.opts.onRetrying?.(null);
68283
+ }
68284
+ this.opts.onSession?.(this.currentUser);
68285
+ this.scheduleRenew(this.expiresAt - RENEW_AHEAD_MS - Date.now());
68286
+ return body.token;
68287
+ }
68288
+ /** Record a failure: a fatal one stops the session and is reported once. */
68289
+ fail(error51) {
68290
+ if (error51.fatal) {
68291
+ this.fatalError = error51;
68292
+ this.clearRenewTimer();
68293
+ console.error(`[SuperAtom] Sign-in failed: ${error51.message}`, error51);
68294
+ try {
68295
+ this.opts.onAuthError?.(error51);
68296
+ } catch (err) {
68297
+ console.error("[SuperAtom] onAuthError threw:", err);
68298
+ }
68299
+ } else {
68300
+ this.retries++;
68301
+ if (this.retries === RETRIES_BEFORE_REPORTING) {
68302
+ console.warn(`[SuperAtom] ${error51.message} (${this.apiUrl}) \u2014 retrying`);
68303
+ }
68304
+ if (this.retries >= RETRIES_BEFORE_REPORTING) this.opts.onRetrying?.(error51);
68305
+ this.log(`Sign-in attempt failed, will retry: ${error51.message}`);
68306
+ this.scheduleRenew(RENEW_RETRY_MS);
68307
+ }
68308
+ return error51;
68309
+ }
68310
+ /**
68311
+ * Renew in the background ahead of expiry. Timers are paused while the machine
68312
+ * sleeps and fire on wake; a reconnect after wake also renews first if needed.
68313
+ */
68314
+ scheduleRenew(inMs) {
68315
+ this.clearRenewTimer();
68316
+ if (this.stopped || this.fatalError) return;
68317
+ this.renewTimer = setTimeout(() => {
68318
+ this.renewTimer = null;
68319
+ if (this.stopped || this.fatalError) return;
68320
+ this.renew().catch(() => {
68321
+ });
68322
+ }, Math.max(inMs, 5e3));
68323
+ }
68324
+ clearRenewTimer() {
68325
+ if (this.renewTimer) {
68326
+ clearTimeout(this.renewTimer);
68327
+ this.renewTimer = null;
68328
+ }
68329
+ }
68330
+ log(message) {
68331
+ if (this.opts.debug) console.log(`[SuperAtom] ${message}`);
68332
+ }
68333
+ };
68334
+
68041
68335
  // src/setup.ts
68042
68336
  function setup(components) {
68043
68337
  if (typeof window === "undefined") {
@@ -68674,13 +68968,13 @@ var ExpressionEvaluator = class {
68674
68968
  resolveDataPath(path2, data) {
68675
68969
  if (!path2 || !data) return void 0;
68676
68970
  try {
68677
- return path2.split(".").reduce((current, key) => {
68678
- if (current === null || current === void 0) return void 0;
68971
+ return path2.split(".").reduce((current3, key) => {
68972
+ if (current3 === null || current3 === void 0) return void 0;
68679
68973
  if (!isNaN(Number(key))) {
68680
68974
  const index2 = parseInt(key, 10);
68681
- return Array.isArray(current) ? current[index2] : current;
68975
+ return Array.isArray(current3) ? current3[index2] : current3;
68682
68976
  }
68683
- return current[key];
68977
+ return current3[key];
68684
68978
  }, data);
68685
68979
  } catch (error51) {
68686
68980
  console.warn("Error resolving data path:", path2, error51);
@@ -69159,6 +69453,125 @@ function resetRegistration() {
69159
69453
  registeredKeys.forEach((key) => delete COMP_REGISTRY[key]);
69160
69454
  registeredKeys.clear();
69161
69455
  }
69456
+ var current = null;
69457
+ var retrying = null;
69458
+ var signingIn = false;
69459
+ var chatReady = false;
69460
+ var listeners = /* @__PURE__ */ new Set();
69461
+ function setAuthStatus(error51) {
69462
+ if (current === error51) return;
69463
+ current = error51;
69464
+ if (error51) retrying = null;
69465
+ listeners.forEach((l) => l());
69466
+ }
69467
+ function setRetryingStatus(error51) {
69468
+ if (retrying === error51) return;
69469
+ retrying = error51;
69470
+ listeners.forEach((l) => l());
69471
+ }
69472
+ function setSigningIn(value) {
69473
+ if (signingIn === value) return;
69474
+ signingIn = value;
69475
+ listeners.forEach((l) => l());
69476
+ }
69477
+ function useSigningIn() {
69478
+ return React6.useSyncExternalStore(
69479
+ (l) => {
69480
+ listeners.add(l);
69481
+ return () => listeners.delete(l);
69482
+ },
69483
+ () => signingIn,
69484
+ () => false
69485
+ );
69486
+ }
69487
+ function setChatReady(value) {
69488
+ if (chatReady === value) return;
69489
+ chatReady = value;
69490
+ listeners.forEach((l) => l());
69491
+ }
69492
+ function useStartingUp() {
69493
+ return React6.useSyncExternalStore(
69494
+ (l) => {
69495
+ listeners.add(l);
69496
+ return () => listeners.delete(l);
69497
+ },
69498
+ () => !chatReady,
69499
+ () => false
69500
+ );
69501
+ }
69502
+ function useRetryingStatus() {
69503
+ return React6.useSyncExternalStore(
69504
+ (l) => {
69505
+ listeners.add(l);
69506
+ return () => listeners.delete(l);
69507
+ },
69508
+ () => retrying,
69509
+ () => null
69510
+ );
69511
+ }
69512
+ function useAuthStatus() {
69513
+ return React6.useSyncExternalStore(
69514
+ (l) => {
69515
+ listeners.add(l);
69516
+ return () => listeners.delete(l);
69517
+ },
69518
+ () => current,
69519
+ () => null
69520
+ );
69521
+ }
69522
+ var SETUP_REASONS = /* @__PURE__ */ new Set([
69523
+ "bad_request",
69524
+ "bad_project_id",
69525
+ "unknown_project",
69526
+ "bad_sub",
69527
+ "bad_email",
69528
+ "bad_roles",
69529
+ "bad_access",
69530
+ "bad_access_values",
69531
+ "bad_access_filters",
69532
+ "access_not_set_up",
69533
+ "lifetime_too_long",
69534
+ "issued_in_future",
69535
+ "email_linked_elsewhere"
69536
+ ]);
69537
+ function authStatusMessage(error51) {
69538
+ switch (error51.code) {
69539
+ case "get_token_failed":
69540
+ return "Your session has ended. Please sign in again to continue.";
69541
+ case "exchange_rejected":
69542
+ if (error51.reason === "inactive") return "This account has been deactivated.";
69543
+ if (error51.reason === "sdk_not_enabled" || error51.status === 403) {
69544
+ return "This account cannot use the chat. Please contact your administrator.";
69545
+ }
69546
+ if (error51.reason && SETUP_REASONS.has(error51.reason)) {
69547
+ return `This app\u2019s Superatom sign-in is not set up correctly: ${error51.message}`;
69548
+ }
69549
+ return "Your session is no longer valid. Please sign in again to continue.";
69550
+ default:
69551
+ return "Could not connect to Superatom. Please try again in a moment.";
69552
+ }
69553
+ }
69554
+ function retryingStatusMessage(error51) {
69555
+ return error51.code === "network" ? "Cannot reach Superatom \u2014 retrying\u2026" : "Superatom is not responding \u2014 retrying\u2026";
69556
+ }
69557
+ var current2 = {};
69558
+ var listeners2 = /* @__PURE__ */ new Set();
69559
+ function setMessageActions(actions) {
69560
+ current2 = actions;
69561
+ listeners2.forEach((l) => l());
69562
+ }
69563
+ function useMessageActions() {
69564
+ return React6.useSyncExternalStore(
69565
+ (l) => {
69566
+ listeners2.add(l);
69567
+ return () => listeners2.delete(l);
69568
+ },
69569
+ () => current2,
69570
+ () => current2
69571
+ );
69572
+ }
69573
+
69574
+ // src/ui/hooks/useSuperAtom.ts
69162
69575
  function newThreadId() {
69163
69576
  return `thread_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
69164
69577
  }
@@ -69318,17 +69731,41 @@ function buildResponseIssueSchema(response, messageId) {
69318
69731
  var MAX_ABORTED_TRACKED = 100;
69319
69732
  var THREADS_LIMIT = 50;
69320
69733
  var THREAD_MESSAGES_LIMIT = 50;
69321
- function useSuperAtom({ token, userId, projectId, debug = false, streaming = false }) {
69322
- const resolvedUserId = (() => {
69734
+ function useSuperAtom({
69735
+ token,
69736
+ userId,
69737
+ projectId,
69738
+ getToken,
69739
+ onAuthError,
69740
+ apiUrl,
69741
+ wsUrl,
69742
+ debug = false,
69743
+ streaming = false
69744
+ }) {
69745
+ const sdkSignIn = !!getToken;
69746
+ if (sdkSignIn && token) throw new Error("[SuperAtom] Pass either token or getToken, not both");
69747
+ const [user, setUser] = React6.useState(null);
69748
+ const [authError, setAuthError] = React6.useState(null);
69749
+ const [isSigningIn, setIsSigningIn] = React6.useState(false);
69750
+ const [signInAttempt, setSignInAttempt] = React6.useState(0);
69751
+ const directUserId = (() => {
69752
+ if (sdkSignIn) return void 0;
69323
69753
  if (token) {
69324
69754
  const payload = decodeJWT(token);
69325
69755
  const id = payload.sub ?? payload.userId;
69326
69756
  if (!id) throw new Error("[SuperAtom] JWT decoded but no userId or sub field found in payload");
69327
69757
  return id;
69328
69758
  }
69329
- if (!userId) throw new Error("[SuperAtom] Either token or userId must be provided");
69759
+ if (!userId) throw new Error("[SuperAtom] Either token, getToken or userId must be provided");
69330
69760
  return userId;
69331
69761
  })();
69762
+ const resolvedUserId = directUserId ?? user?.id ?? "";
69763
+ const getTokenRef = React6.useRef(getToken);
69764
+ getTokenRef.current = getToken;
69765
+ const onAuthErrorRef = React6.useRef(onAuthError);
69766
+ onAuthErrorRef.current = onAuthError;
69767
+ const sessionRef = React6.useRef(null);
69768
+ const setupDoneRef = React6.useRef(false);
69332
69769
  const clientRef = React6.useRef(null);
69333
69770
  const tokenRef = React6.useRef(token);
69334
69771
  tokenRef.current = token;
@@ -69355,7 +69792,60 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69355
69792
  injectBundleStyles();
69356
69793
  const timeoutId = setTimeout(async () => {
69357
69794
  if (cancelled) return;
69358
- const client = new SuperatomClient({ userId: resolvedUserId, projectId, type: "runtime", debug, token: tokenRef.current });
69795
+ let clientUserId = directUserId;
69796
+ let session = null;
69797
+ if (sdkSignIn) {
69798
+ session = new SdkSession({
69799
+ projectId,
69800
+ apiUrl,
69801
+ debug,
69802
+ getToken: () => getTokenRef.current(),
69803
+ onAuthError: (err) => {
69804
+ setAuthError(err);
69805
+ setAuthStatus(err);
69806
+ onAuthErrorRef.current?.(err);
69807
+ },
69808
+ onSession: (u) => {
69809
+ setAuthError(null);
69810
+ setAuthStatus(null);
69811
+ setUser(u);
69812
+ },
69813
+ // Superatom unreachable or down, after a couple of tries: say so in the
69814
+ // chat instead of sitting on "Connecting…".
69815
+ onRetrying: (err) => setRetryingStatus(err),
69816
+ // Lets the chat show "Signing in…" instead of an empty chat while a
69817
+ // sign-in — including one after reconnect() — is in flight.
69818
+ onSigningIn: (value) => {
69819
+ setSigningIn(value);
69820
+ setIsSigningIn(value);
69821
+ }
69822
+ });
69823
+ sessionRef.current = session;
69824
+ for (let delay = 1e3; ; delay = Math.min(delay * 2, 3e4)) {
69825
+ try {
69826
+ await session.getAccessToken();
69827
+ break;
69828
+ } catch (err) {
69829
+ if (cancelled || err?.fatal) return;
69830
+ await new Promise((r2) => setTimeout(r2, delay));
69831
+ if (cancelled) return;
69832
+ }
69833
+ }
69834
+ if (cancelled) {
69835
+ session.stop();
69836
+ return;
69837
+ }
69838
+ clientUserId = session.user.id;
69839
+ }
69840
+ const activeSession = session;
69841
+ const client = new SuperatomClient({
69842
+ userId: clientUserId,
69843
+ projectId,
69844
+ type: "runtime",
69845
+ debug,
69846
+ ...wsUrl ? { baseUrl: wsUrl } : {},
69847
+ ...activeSession ? { getAccessToken: () => activeSession.getAccessToken() } : { token: tokenRef.current }
69848
+ });
69359
69849
  clientRef.current = client;
69360
69850
  client.onError((err) => console.error("[SuperAtom] Error:", err));
69361
69851
  try {
@@ -69372,11 +69862,11 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69372
69862
  };
69373
69863
  const unsub = client.onConnect(done);
69374
69864
  const cancelPoll = setInterval(() => {
69375
- if (cancelled) done();
69865
+ if (cancelled || activeSession?.error) done();
69376
69866
  }, 50);
69377
69867
  });
69378
- if (cancelled) {
69379
- client.disconnect();
69868
+ if (cancelled || activeSession?.error) {
69869
+ if (cancelled) client.disconnect();
69380
69870
  return;
69381
69871
  }
69382
69872
  window.dispatchEvent(new CustomEvent("sa:client-connected"));
@@ -69452,7 +69942,11 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69452
69942
  ]);
69453
69943
  };
69454
69944
  }
69455
- if (!cancelled) setBundleReady(true);
69945
+ if (!cancelled) {
69946
+ setupDoneRef.current = true;
69947
+ setBundleReady(true);
69948
+ setChatReady(true);
69949
+ }
69456
69950
  } catch (err) {
69457
69951
  console.error("[SuperAtom] Setup failed:", err);
69458
69952
  }
@@ -69466,9 +69960,26 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69466
69960
  }
69467
69961
  clientRef.current?.disconnect();
69468
69962
  clientRef.current = null;
69963
+ sessionRef.current?.stop();
69964
+ sessionRef.current = null;
69965
+ setAuthStatus(null);
69966
+ setupDoneRef.current = false;
69469
69967
  setBundleReady(false);
69968
+ setChatReady(false);
69470
69969
  };
69471
- }, [resolvedUserId, projectId, debug]);
69970
+ }, [directUserId, sdkSignIn, projectId, apiUrl, wsUrl, debug, signInAttempt]);
69971
+ const reconnect = React6.useCallback(() => {
69972
+ const session = sessionRef.current;
69973
+ const client = clientRef.current;
69974
+ session?.reset();
69975
+ setAuthError(null);
69976
+ setAuthStatus(null);
69977
+ if (client && setupDoneRef.current) {
69978
+ client.forceReconnect().catch((err) => console.error("[SuperAtom] Reconnect failed:", err));
69979
+ } else {
69980
+ setSignInAttempt((n) => n + 1);
69981
+ }
69982
+ }, []);
69472
69983
  const sendMessage = React6.useCallback(async () => {
69473
69984
  const text9 = input.trim();
69474
69985
  const client = clientRef.current;
@@ -69860,6 +70371,39 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69860
70371
  if (threadId.current === id) newChat();
69861
70372
  return { success: true };
69862
70373
  }, [resolvedUserId, newChat]);
70374
+ const deleteMessage = React6.useCallback(async (messageId) => {
70375
+ const client = clientRef.current;
70376
+ if (!client) return { success: false, error: "Not connected" };
70377
+ const list4 = messagesRef.current;
70378
+ const index2 = list4.findIndex((m) => m.id === messageId);
70379
+ if (index2 === -1) return { success: false, error: "Message not found" };
70380
+ let questionIndex = list4[index2]?.role === "user" ? index2 : -1;
70381
+ if (questionIndex === -1) {
70382
+ for (let i = index2 - 1; i >= 0; i--) {
70383
+ if (list4[i]?.role === "user") {
70384
+ questionIndex = i;
70385
+ break;
70386
+ }
70387
+ }
70388
+ }
70389
+ const question = questionIndex >= 0 ? list4[questionIndex]?.text ?? "" : "";
70390
+ if (!question.trim()) return { success: false, error: "Could not find the question for this answer" };
70391
+ const res = await deleteUserConversation(client, question, resolvedUserId);
70392
+ if (!res.success) return res;
70393
+ const removed = /* @__PURE__ */ new Set();
70394
+ for (let i = questionIndex; i < list4.length; i++) {
70395
+ const msg = list4[i];
70396
+ if (!msg) continue;
70397
+ if (i > questionIndex && msg.role === "user") break;
70398
+ removed.add(msg.id);
70399
+ }
70400
+ setMessages((prev) => prev.filter((m) => !removed.has(m.id)));
70401
+ return { success: true };
70402
+ }, [resolvedUserId]);
70403
+ React6.useEffect(() => {
70404
+ setMessageActions({ deleteMessage });
70405
+ return () => setMessageActions({});
70406
+ }, [deleteMessage]);
69863
70407
  const renameThread = React6.useCallback(async (id, title) => {
69864
70408
  const client = clientRef.current;
69865
70409
  if (!client) return { success: false, error: "Not connected" };
@@ -69883,7 +70427,12 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69883
70427
  isLoadingThread,
69884
70428
  refreshThreads,
69885
70429
  deleteThread: deleteThread2,
69886
- renameThread
70430
+ renameThread,
70431
+ isNewChat: bundleReady && !isLoadingThread && messages2.length === 0,
70432
+ user,
70433
+ authError,
70434
+ isSigningIn,
70435
+ reconnect
69887
70436
  };
69888
70437
  }
69889
70438
 
@@ -69979,6 +70528,9 @@ function ChatInput({
69979
70528
  isResponding = false,
69980
70529
  onStop
69981
70530
  }) {
70531
+ const authError = useAuthStatus();
70532
+ const startingUp = useStartingUp();
70533
+ const isLocked = disabled || !!authError;
69982
70534
  const textareaRef = React6.useRef(null);
69983
70535
  const historyRef = React6.useRef(null);
69984
70536
  const [isMultiline, setIsMultiline] = React6.useState(false);
@@ -70032,14 +70584,16 @@ function ChatInput({
70032
70584
  }, 0);
70033
70585
  };
70034
70586
  const handleSend = React6.useCallback(() => {
70035
- if (!input.trim() || disabled) return;
70587
+ if (!input.trim() || isLocked) return;
70036
70588
  const history = getOrCreateHistory();
70037
70589
  history?.addCommand(input.trim());
70038
70590
  history?.resetNavigation();
70039
70591
  onSend();
70040
- }, [input, disabled, onSend, getOrCreateHistory]);
70592
+ }, [input, isLocked, onSend, getOrCreateHistory]);
70041
70593
  const themeColor = getThemeColor();
70042
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `shrink-0 bg-base-100 border-t border-base-300 px-4 py-2 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `group ${isMultiline ? "rounded-3xl" : "rounded-full"} bg-base-200 transition-all duration-200 w-full max-w-2xl mx-auto`, children: /* @__PURE__ */ jsxRuntime.jsx(
70594
+ const effectivePlaceholder = authError ? authStatusMessage(authError) : placeholder;
70595
+ if (startingUp) return null;
70596
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `sa-ui shrink-0 bg-base-100 border-t border-base-300 px-4 py-2 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `group ${isMultiline ? "rounded-3xl" : "rounded-full"} bg-base-200 transition-all duration-200 w-full max-w-2xl mx-auto`, children: /* @__PURE__ */ jsxRuntime.jsx(
70043
70597
  "div",
70044
70598
  {
70045
70599
  className: `flex flex-col ${isMultiline ? "rounded-3xl" : "rounded-full"} border-2 shadow-sm transition-all duration-200 hover:shadow-md`,
@@ -70059,7 +70613,7 @@ function ChatInput({
70059
70613
  autoCapitalize: "off",
70060
70614
  ref: textareaRef,
70061
70615
  value: input,
70062
- placeholder,
70616
+ placeholder: effectivePlaceholder,
70063
70617
  onChange: (e) => setInput(e.currentTarget.value),
70064
70618
  onBlur: () => setIsFocused(false),
70065
70619
  onFocus: () => setIsFocused(true),
@@ -70090,7 +70644,7 @@ function ChatInput({
70090
70644
  },
70091
70645
  rows: 1,
70092
70646
  className: "textarea textarea-ghost w-full focus:outline-none focus:bg-transparent min-h-[40px] resize-none overflow-y-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]",
70093
- disabled,
70647
+ disabled: isLocked,
70094
70648
  style: { maxHeight: "200px" }
70095
70649
  }
70096
70650
  ),
@@ -70112,11 +70666,11 @@ function ChatInput({
70112
70666
  "button",
70113
70667
  {
70114
70668
  onClick: handleSend,
70115
- disabled: !input.trim() || disabled,
70669
+ disabled: !input.trim() || isLocked,
70116
70670
  className: `transition-colors duration-150 rounded-full p-2 flex-shrink-0 ${isMultiline ? "mb-1" : ""} disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-transparent disabled:text-base-content/40`,
70117
70671
  style: {
70118
- backgroundColor: input.trim() && !disabled ? themeColor : "transparent",
70119
- color: input.trim() && !disabled ? "white" : themeColor
70672
+ backgroundColor: input.trim() && !isLocked ? themeColor : "transparent",
70673
+ color: input.trim() && !isLocked ? "white" : themeColor
70120
70674
  },
70121
70675
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "ph:paper-plane-right-fill", width: 20, height: 20, className: "sm:w-6 sm:h-6" })
70122
70676
  }
@@ -70132,6 +70686,86 @@ init_iconify();
70132
70686
  // src/ui/components/MessageBubble.tsx
70133
70687
  init_iconify();
70134
70688
  init_Markdown();
70689
+
70690
+ // src/ui/components/ConfirmDeleteModal.tsx
70691
+ init_iconify();
70692
+ function ConfirmDeleteModal({
70693
+ isOpen,
70694
+ onClose,
70695
+ onConfirm,
70696
+ isDeleting,
70697
+ title = "Delete",
70698
+ message = "Are you sure you want to delete this?"
70699
+ }) {
70700
+ React6.useEffect(() => {
70701
+ if (!isOpen) return;
70702
+ const handleEscape = (e) => {
70703
+ if (e.key === "Escape" && !isDeleting) {
70704
+ onClose();
70705
+ }
70706
+ };
70707
+ document.addEventListener("keydown", handleEscape);
70708
+ document.body.style.overflow = "hidden";
70709
+ return () => {
70710
+ document.removeEventListener("keydown", handleEscape);
70711
+ document.body.style.overflow = "unset";
70712
+ };
70713
+ }, [isOpen, onClose, isDeleting]);
70714
+ const handleBackdropClick = (e) => {
70715
+ if (e.target === e.currentTarget && !isDeleting) {
70716
+ onClose();
70717
+ }
70718
+ };
70719
+ if (!isOpen) return null;
70720
+ return reactDom.createPortal(
70721
+ /* @__PURE__ */ jsxRuntime.jsx(
70722
+ "div",
70723
+ {
70724
+ className: "sa-ui fixed inset-0 flex items-center justify-center bg-black/50 backdrop-blur-sm",
70725
+ style: { zIndex: 9999 },
70726
+ onClick: handleBackdropClick,
70727
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-base-100 rounded-xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden", children: [
70728
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-4 border-b border-base-300", children: [
70729
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-base-content", children: title }),
70730
+ /* @__PURE__ */ jsxRuntime.jsx(
70731
+ "button",
70732
+ {
70733
+ type: "button",
70734
+ disabled: isDeleting,
70735
+ onClick: onClose,
70736
+ className: "p-1 rounded-lg hover:bg-base-200 transition-colors",
70737
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "ph:x-bold", width: 20, height: 20, className: "text-base-content/60" })
70738
+ }
70739
+ )
70740
+ ] }),
70741
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base-content/80", children: message }) }),
70742
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 p-4 border-t border-base-300", children: [
70743
+ /* @__PURE__ */ jsxRuntime.jsx(
70744
+ "button",
70745
+ {
70746
+ type: "button",
70747
+ onClick: onClose,
70748
+ disabled: isDeleting,
70749
+ className: "px-4 py-2 rounded-lg text-base-content hover:bg-base-200 transition-colors",
70750
+ children: "Cancel"
70751
+ }
70752
+ ),
70753
+ isDeleting ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "eos-icons:loading", width: 20, height: 20, className: "animate-spin" }) }) : /* @__PURE__ */ jsxRuntime.jsx(
70754
+ "button",
70755
+ {
70756
+ type: "button",
70757
+ onClick: onConfirm,
70758
+ className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white hover:bg-red-700 transition-colors",
70759
+ children: "Delete"
70760
+ }
70761
+ )
70762
+ ] })
70763
+ ] })
70764
+ }
70765
+ ),
70766
+ document.body
70767
+ );
70768
+ }
70135
70769
  function StopButton({ onStop }) {
70136
70770
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
70137
70771
  "button",
@@ -70157,17 +70791,40 @@ function MessageBubble({ message, onStop }) {
70157
70791
  const isTextOnly = message.isTextOnlyResponse === true;
70158
70792
  const isAborted = message.aborted === true;
70159
70793
  const responseComplete = hasComponent || isTextOnly || !isStreaming && hasText;
70794
+ const [watchedGeneration] = React6.useState(() => !responseComplete);
70160
70795
  React6.useEffect(() => {
70161
- if (message.role !== "assistant") return;
70162
- startTime.current = Date.now();
70796
+ if (message.role !== "assistant" || !watchedGeneration) return;
70163
70797
  if (responseComplete) {
70164
70798
  setElapsedMs(Date.now() - startTime.current);
70165
70799
  return;
70166
70800
  }
70801
+ setElapsedMs(Date.now() - startTime.current);
70167
70802
  const interval = setInterval(() => setElapsedMs(Date.now() - startTime.current), 100);
70168
70803
  return () => clearInterval(interval);
70169
- }, [message.role, responseComplete]);
70804
+ }, [message.role, responseComplete, watchedGeneration]);
70170
70805
  const formatMs = (ms) => ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
70806
+ const { deleteMessage } = useMessageActions();
70807
+ const [confirmOpen, setConfirmOpen] = React6.useState(false);
70808
+ const [isDeleting, setIsDeleting] = React6.useState(false);
70809
+ const handleDelete2 = async () => {
70810
+ if (!deleteMessage) return;
70811
+ setIsDeleting(true);
70812
+ const res = await deleteMessage(message.id);
70813
+ setIsDeleting(false);
70814
+ setConfirmOpen(false);
70815
+ if (!res.success) console.error("[SuperAtom] Could not delete the message:", res.error);
70816
+ };
70817
+ const deleteButton = deleteMessage && responseComplete && !isAborted ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
70818
+ "button",
70819
+ {
70820
+ onClick: () => setConfirmOpen(true),
70821
+ disabled: isDeleting,
70822
+ title: "Delete this answer",
70823
+ "aria-label": "Delete this answer",
70824
+ className: "cursor-pointer p-2 rounded-lg shadow-md bg-base-100 text-base-content/40 hover:text-red-500 hover:shadow-lg transition-colors duration-150 disabled:opacity-50 disabled:cursor-wait",
70825
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) })
70826
+ }
70827
+ ) }) : null;
70171
70828
  const accordionHeader = React6.useMemo(() => {
70172
70829
  if (isAborted) return hasText ? "Stopped" : "Stopped (no response yet)";
70173
70830
  if (!hasText) return "Thinking...";
@@ -70203,7 +70860,7 @@ function MessageBubble({ message, onStop }) {
70203
70860
  children: [
70204
70861
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-base-content truncate text-left flex-1", children: accordionHeader }),
70205
70862
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 shrink-0", children: [
70206
- !message.fromHistory && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-teal-50 border border-teal-200", children: [
70863
+ !message.fromHistory && watchedGeneration && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-teal-50 border border-teal-200", children: [
70207
70864
  responseComplete ? /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "text-teal-600", width: 12, height: 12, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" }) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-xs text-teal-600" }),
70208
70865
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-teal-700 font-medium font-mono", children: formatMs(elapsedMs) })
70209
70866
  ] }),
@@ -70253,8 +70910,25 @@ function MessageBubble({ message, onStop }) {
70253
70910
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-base-content/70", children: "Generating component..." })
70254
70911
  ] })
70255
70912
  ] }) }),
70256
- hasComponent && /* @__PURE__ */ jsxRuntime.jsx("div", { id: `box-${message.id}`, className: "w-full px-4 lg:px-16 xl:px-24 2xl:px-32", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl shadow-sm bg-base-100 border-2 border-base-300 min-h-[85vh] overflow-auto flex items-center justify-center p-5", children: /* @__PURE__ */ jsxRuntime.jsx(DSLRenderer, { dsl: message.schema.ui, data: message.schema.data ?? {} }) }) }),
70257
- !isStreaming && !hasComponent && hasText && isTextOnly && !isAborted && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center w-full px-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full md:w-[60vw]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl shadow-sm bg-base-100 border-2 border-base-300 p-4 text-base-content", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown_default, { content: fullText, isStreaming: false }) }) }) })
70913
+ hasComponent && /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `box-${message.id}`, className: "w-full px-4 lg:px-16 xl:px-24 2xl:px-32", children: [
70914
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl shadow-sm bg-base-100 border-2 border-base-300 min-h-[85vh] overflow-auto flex items-center justify-center p-5", children: /* @__PURE__ */ jsxRuntime.jsx(DSLRenderer, { dsl: message.schema.ui, data: message.schema.data ?? {} }) }),
70915
+ deleteButton
70916
+ ] }),
70917
+ !isStreaming && !hasComponent && hasText && isTextOnly && !isAborted && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center w-full px-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full md:w-[60vw]", children: [
70918
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl shadow-sm bg-base-100 border-2 border-base-300 p-4 text-base-content", children: /* @__PURE__ */ jsxRuntime.jsx(Markdown_default, { content: fullText, isStreaming: false }) }),
70919
+ deleteButton
70920
+ ] }) }),
70921
+ /* @__PURE__ */ jsxRuntime.jsx(
70922
+ ConfirmDeleteModal,
70923
+ {
70924
+ isOpen: confirmOpen,
70925
+ onClose: () => setConfirmOpen(false),
70926
+ onConfirm: handleDelete2,
70927
+ isDeleting,
70928
+ title: "Delete this answer?",
70929
+ message: "The question and its answer are removed from this chat and from your history. Anything cached for it is cleared too, so asking again re-runs it."
70930
+ }
70931
+ )
70258
70932
  ] });
70259
70933
  }
70260
70934
  function DynamicSpacer({ messages: messages2 }) {
@@ -70268,6 +70942,7 @@ function MessageList({
70268
70942
  bundleReady,
70269
70943
  connectingText = "Connecting to SuperAtom...",
70270
70944
  emptyText = "Start a conversation",
70945
+ emptyState,
70271
70946
  className = "",
70272
70947
  isResponding = false,
70273
70948
  onStop,
@@ -70351,11 +71026,20 @@ function MessageList({
70351
71026
  }
70352
71027
  }, 300);
70353
71028
  }, [messages2]);
70354
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex-1 relative overflow-hidden ${className}`, children: [
70355
- /* @__PURE__ */ jsxRuntime.jsx("main", { ref: containerRef, className: "h-full overflow-y-auto py-6", children: !bundleReady || isLoadingThread && messages2.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-base-content/50", children: [
71029
+ const authError = useAuthStatus();
71030
+ const retrying2 = useRetryingStatus();
71031
+ const signingIn2 = useSigningIn();
71032
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `sa-ui flex-1 relative overflow-hidden ${className}`, children: [
71033
+ /* @__PURE__ */ jsxRuntime.jsx("main", { ref: containerRef, className: "h-full overflow-y-auto py-6", children: signingIn2 && !bundleReady ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-base-content/50", children: [
71034
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-lg" }),
71035
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: "Signing in to Superatom\u2026" })
71036
+ ] }) : authError ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 px-6 text-center", children: [
71037
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "mdi:lock-alert-outline", width: 28, height: 28, className: "text-base-content/40" }),
71038
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-base-content/70 max-w-sm", children: authStatusMessage(authError) })
71039
+ ] }) : !bundleReady || isLoadingThread && messages2.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-base-content/50", children: [
70356
71040
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "loading loading-spinner loading-md" }),
70357
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: !bundleReady ? connectingText : loadingThreadText })
70358
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
71041
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: !bundleReady ? retrying2 ? retryingStatusMessage(retrying2) : connectingText : loadingThreadText })
71042
+ ] }) : messages2.length === 0 && emptyState ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex flex-col", children: emptyState }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
70359
71043
  messages2.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mt-24", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base-content/40 text-sm", children: emptyText }) }),
70360
71044
  messages2.map((msg) => /* @__PURE__ */ jsxRuntime.jsx(
70361
71045
  MessageBubble,
@@ -70737,87 +71421,8 @@ var MenuV1RendererCollapsed = ({ items, onItemClick }) => {
70737
71421
  groupIndex < groupedItems.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 border-t border-base-300 my-2" })
70738
71422
  ] }, groupIndex)) });
70739
71423
  };
70740
-
70741
- // src/ui/components/ConfirmDeleteModal.tsx
70742
- init_iconify();
70743
- function ConfirmDeleteModal({
70744
- isOpen,
70745
- onClose,
70746
- onConfirm,
70747
- isDeleting,
70748
- title = "Delete",
70749
- message = "Are you sure you want to delete this?"
70750
- }) {
70751
- React6.useEffect(() => {
70752
- if (!isOpen) return;
70753
- const handleEscape = (e) => {
70754
- if (e.key === "Escape" && !isDeleting) {
70755
- onClose();
70756
- }
70757
- };
70758
- document.addEventListener("keydown", handleEscape);
70759
- document.body.style.overflow = "hidden";
70760
- return () => {
70761
- document.removeEventListener("keydown", handleEscape);
70762
- document.body.style.overflow = "unset";
70763
- };
70764
- }, [isOpen, onClose, isDeleting]);
70765
- const handleBackdropClick = (e) => {
70766
- if (e.target === e.currentTarget && !isDeleting) {
70767
- onClose();
70768
- }
70769
- };
70770
- if (!isOpen) return null;
70771
- return reactDom.createPortal(
70772
- /* @__PURE__ */ jsxRuntime.jsx(
70773
- "div",
70774
- {
70775
- className: "fixed inset-0 flex items-center justify-center bg-black/50 backdrop-blur-sm",
70776
- style: { zIndex: 9999 },
70777
- onClick: handleBackdropClick,
70778
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-base-100 rounded-xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden", children: [
70779
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-4 border-b border-base-300", children: [
70780
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-base-content", children: title }),
70781
- /* @__PURE__ */ jsxRuntime.jsx(
70782
- "button",
70783
- {
70784
- type: "button",
70785
- disabled: isDeleting,
70786
- onClick: onClose,
70787
- className: "p-1 rounded-lg hover:bg-base-200 transition-colors",
70788
- children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "ph:x-bold", width: 20, height: 20, className: "text-base-content/60" })
70789
- }
70790
- )
70791
- ] }),
70792
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base-content/80", children: message }) }),
70793
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 p-4 border-t border-base-300", children: [
70794
- /* @__PURE__ */ jsxRuntime.jsx(
70795
- "button",
70796
- {
70797
- type: "button",
70798
- onClick: onClose,
70799
- disabled: isDeleting,
70800
- className: "px-4 py-2 rounded-lg text-base-content hover:bg-base-200 transition-colors",
70801
- children: "Cancel"
70802
- }
70803
- ),
70804
- isDeleting ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "eos-icons:loading", width: 20, height: 20, className: "animate-spin" }) }) : /* @__PURE__ */ jsxRuntime.jsx(
70805
- "button",
70806
- {
70807
- type: "button",
70808
- onClick: onConfirm,
70809
- className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white hover:bg-red-700 transition-colors",
70810
- children: "Delete"
70811
- }
70812
- )
70813
- ] })
70814
- ] })
70815
- }
70816
- ),
70817
- document.body
70818
- );
70819
- }
70820
71424
  var CHATS_OPEN_KEY = "chats-section-open";
71425
+ var SIDEBAR_COLLAPSED_KEY = "sa-sidebar-collapsed";
70821
71426
  function Sidebar({
70822
71427
  children,
70823
71428
  className = "",
@@ -70879,8 +71484,21 @@ function Sidebar({
70879
71484
  setThreadToDelete(null);
70880
71485
  }
70881
71486
  };
70882
- const [collapsed, setCollapsed] = React6.useState(true);
70883
- const [showContent, setShowContent] = React6.useState(false);
71487
+ const startingUp = useStartingUp();
71488
+ const [collapsed, setCollapsed] = React6.useState(() => {
71489
+ try {
71490
+ return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === "true";
71491
+ } catch {
71492
+ return false;
71493
+ }
71494
+ });
71495
+ const [showContent, setShowContent] = React6.useState(() => {
71496
+ try {
71497
+ return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) !== "true";
71498
+ } catch {
71499
+ return true;
71500
+ }
71501
+ });
70884
71502
  const [menuV1Data, setMenuV1Data] = React6.useState(null);
70885
71503
  const [isChatsOpen, setIsChatsOpen] = React6.useState(() => {
70886
71504
  try {
@@ -70924,6 +71542,10 @@ function Sidebar({
70924
71542
  function toggle() {
70925
71543
  const next2 = !collapsed;
70926
71544
  setCollapsed(next2);
71545
+ try {
71546
+ localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(next2));
71547
+ } catch {
71548
+ }
70927
71549
  if (!next2) {
70928
71550
  setTimeout(() => setShowContent(true), 150);
70929
71551
  } else {
@@ -70947,10 +71569,11 @@ function Sidebar({
70947
71569
  });
70948
71570
  }
70949
71571
  }
71572
+ if (startingUp) return null;
70950
71573
  return /* @__PURE__ */ jsxRuntime.jsxs(
70951
71574
  "aside",
70952
71575
  {
70953
- className: `flex flex-col shrink-0 border-r border-base-300 transition-all duration-300 ease-in-out overflow-hidden bg-base-200 ${collapsed ? "w-16" : "w-60"} ${className}`,
71576
+ className: `sa-ui flex flex-col shrink-0 border-r border-base-300 transition-all duration-300 ease-in-out overflow-hidden bg-base-200 ${collapsed ? "w-16" : "w-60"} ${className}`,
70954
71577
  children: [
70955
71578
  !collapsed && showContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full w-60", children: [
70956
71579
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 py-3 border-b border-base-300 flex-shrink-0", children: [
@@ -71120,7 +71743,7 @@ function NewChatHero({
71120
71743
  subtitle = "Ask a question to start a new chat",
71121
71744
  className = ""
71122
71745
  }) {
71123
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex-1 min-h-0 flex flex-col items-center justify-center gap-6 px-4 ${className}`, children: [
71746
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `sa-ui flex-1 min-h-0 flex flex-col items-center justify-center gap-6 px-4 ${className}`, children: [
71124
71747
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 text-center", children: [
71125
71748
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl font-semibold text-base-content", children: title }),
71126
71749
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-base-content/50", children: subtitle })
@@ -71130,7 +71753,7 @@ function NewChatHero({
71130
71753
  }
71131
71754
 
71132
71755
  // src/index.ts
71133
- var SDK_VERSION = "0.0.2";
71756
+ var SDK_VERSION = "0.0.3";
71134
71757
 
71135
71758
  exports.ActionsRequestMessageSchema = ActionsRequestMessageSchema;
71136
71759
  exports.ActionsRequestPayloadSchema = ActionsRequestPayloadSchema;
@@ -71150,6 +71773,7 @@ exports.ComponentSchema = ComponentSchema;
71150
71773
  exports.ComponentsSendMessageSchema = ComponentsSendMessageSchema;
71151
71774
  exports.ComponentsSendPayloadSchema = ComponentsSendPayloadSchema;
71152
71775
  exports.ConfirmDeleteModal = ConfirmDeleteModal;
71776
+ exports.DEFAULT_API_URL = DEFAULT_API_URL;
71153
71777
  exports.DSLErrorBoundary = DSLErrorBoundary;
71154
71778
  exports.DSLRenderer = DSLRenderer;
71155
71779
  exports.DSLRendererPropsSchema = DSLRendererPropsSchema3;
@@ -71175,7 +71799,9 @@ exports.MessageSchema = MessageSchema;
71175
71799
  exports.NewChatHero = NewChatHero;
71176
71800
  exports.QuerySpecSchema = QuerySpecSchema3;
71177
71801
  exports.SDK_VERSION = SDK_VERSION;
71802
+ exports.SdkSession = SdkSession;
71178
71803
  exports.Sidebar = Sidebar;
71804
+ exports.SuperAtomAuthError = SuperAtomAuthError;
71179
71805
  exports.SuperatomClient = SuperatomClient;
71180
71806
  exports.UIComponentSchema = UIComponentSchema3;
71181
71807
  exports.UIElementSchema = UIElementSchema3;