@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.js CHANGED
@@ -1,4 +1,4 @@
1
- import React6, { Component, useState, useEffect, forwardRef, memo, useMemo, useRef, useCallback, createElement, lazy as lazy$1, Suspense } from 'react';
1
+ import React6, { Component, useState, useEffect, forwardRef, memo, useMemo, useRef, useCallback, createElement, lazy as lazy$1, Suspense, useSyncExternalStore } from 'react';
2
2
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
3
3
  import { createPortal } from 'react-dom';
4
4
 
@@ -5448,34 +5448,34 @@ function subcontent(events, eventIndex) {
5448
5448
  let stream;
5449
5449
  let previous3;
5450
5450
  let index2 = -1;
5451
- let current = token;
5451
+ let current3 = token;
5452
5452
  let adjust = 0;
5453
5453
  let start2 = 0;
5454
5454
  const breaks = [start2];
5455
- while (current) {
5456
- while (events.get(++startPosition)[1] !== current) {
5455
+ while (current3) {
5456
+ while (events.get(++startPosition)[1] !== current3) {
5457
5457
  }
5458
5458
  startPositions.push(startPosition);
5459
- if (!current._tokenizer) {
5460
- stream = context.sliceStream(current);
5461
- if (!current.next) {
5459
+ if (!current3._tokenizer) {
5460
+ stream = context.sliceStream(current3);
5461
+ if (!current3.next) {
5462
5462
  stream.push(null);
5463
5463
  }
5464
5464
  if (previous3) {
5465
- tokenizer.defineSkip(current.start);
5465
+ tokenizer.defineSkip(current3.start);
5466
5466
  }
5467
- if (current._isInFirstContentOfListItem) {
5467
+ if (current3._isInFirstContentOfListItem) {
5468
5468
  tokenizer._gfmTasklistFirstContentOfListItem = true;
5469
5469
  }
5470
5470
  tokenizer.write(stream);
5471
- if (current._isInFirstContentOfListItem) {
5471
+ if (current3._isInFirstContentOfListItem) {
5472
5472
  tokenizer._gfmTasklistFirstContentOfListItem = void 0;
5473
5473
  }
5474
5474
  }
5475
- previous3 = current;
5476
- current = current.next;
5475
+ previous3 = current3;
5476
+ current3 = current3.next;
5477
5477
  }
5478
- current = token;
5478
+ current3 = token;
5479
5479
  while (++index2 < childEvents.length) {
5480
5480
  if (
5481
5481
  // Find a void token that includes a break.
@@ -5483,15 +5483,15 @@ function subcontent(events, eventIndex) {
5483
5483
  ) {
5484
5484
  start2 = index2 + 1;
5485
5485
  breaks.push(start2);
5486
- current._tokenizer = void 0;
5487
- current.previous = void 0;
5488
- current = current.next;
5486
+ current3._tokenizer = void 0;
5487
+ current3.previous = void 0;
5488
+ current3 = current3.next;
5489
5489
  }
5490
5490
  }
5491
5491
  tokenizer.events = [];
5492
- if (current) {
5493
- current._tokenizer = void 0;
5494
- current.previous = void 0;
5492
+ if (current3) {
5493
+ current3._tokenizer = void 0;
5494
+ current3.previous = void 0;
5495
5495
  } else {
5496
5496
  breaks.pop();
5497
5497
  }
@@ -13506,9 +13506,9 @@ function isAsciiAlphaNumeric(code4) {
13506
13506
  function isEntityInAttributeInvalidEnd(code4) {
13507
13507
  return code4 === CharCodes.EQUALS || isAsciiAlphaNumeric(code4);
13508
13508
  }
13509
- function determineBranch(decodeTree, current, nodeIndex, char) {
13510
- const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
13511
- const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
13509
+ function determineBranch(decodeTree, current3, nodeIndex, char) {
13510
+ const branchCount = (current3 & BinTrieFlags.BRANCH_LENGTH) >> 7;
13511
+ const jumpOffset = current3 & BinTrieFlags.JUMP_TABLE;
13512
13512
  if (branchCount === 0) {
13513
13513
  return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1;
13514
13514
  }
@@ -13747,19 +13747,19 @@ var init_decode = __esm({
13747
13747
  */
13748
13748
  stateNamedEntity(input, offset) {
13749
13749
  const { decodeTree } = this;
13750
- let current = decodeTree[this.treeIndex];
13751
- let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
13750
+ let current3 = decodeTree[this.treeIndex];
13751
+ let valueLength = (current3 & BinTrieFlags.VALUE_LENGTH) >> 14;
13752
13752
  for (; offset < input.length; offset++, this.excess++) {
13753
13753
  const char = input.charCodeAt(offset);
13754
- this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
13754
+ this.treeIndex = determineBranch(decodeTree, current3, this.treeIndex + Math.max(1, valueLength), char);
13755
13755
  if (this.treeIndex < 0) {
13756
13756
  return this.result === 0 || // If we are parsing an attribute
13757
13757
  this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity,
13758
13758
  (valueLength === 0 || // And there should be no invalid characters.
13759
13759
  isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity();
13760
13760
  }
13761
- current = decodeTree[this.treeIndex];
13762
- valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
13761
+ current3 = decodeTree[this.treeIndex];
13762
+ valueLength = (current3 & BinTrieFlags.VALUE_LENGTH) >> 14;
13763
13763
  if (valueLength !== 0) {
13764
13764
  if (char === CharCodes.SEMI) {
13765
13765
  return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
@@ -19815,8 +19815,8 @@ function startTagInForeignContent(p, token) {
19815
19815
  popUntilHtmlOrIntegrationPoint(p);
19816
19816
  p._startTagOutsideForeignContent(token);
19817
19817
  } else {
19818
- const current = p._getAdjustedCurrentElement();
19819
- const currentNs = p.treeAdapter.getNamespaceURI(current);
19818
+ const current3 = p._getAdjustedCurrentElement();
19819
+ const currentNs = p.treeAdapter.getNamespaceURI(current3);
19820
19820
  if (currentNs === NS.MATHML) {
19821
19821
  adjustTokenMathMLAttrs(token);
19822
19822
  } else if (currentNs === NS.SVG) {
@@ -20004,21 +20004,21 @@ var init_parser = __esm({
20004
20004
  }
20005
20005
  (_b = (_a4 = this.treeAdapter).onItemPop) === null || _b === void 0 ? void 0 : _b.call(_a4, node2, this.openElements.current);
20006
20006
  if (isTop) {
20007
- let current;
20007
+ let current3;
20008
20008
  let currentTagId;
20009
20009
  if (this.openElements.stackTop === 0 && this.fragmentContext) {
20010
- current = this.fragmentContext;
20010
+ current3 = this.fragmentContext;
20011
20011
  currentTagId = this.fragmentContextID;
20012
20012
  } else {
20013
- ({ current, currentTagId } = this.openElements);
20013
+ ({ current: current3, currentTagId } = this.openElements);
20014
20014
  }
20015
- this._setContextModes(current, currentTagId);
20015
+ this._setContextModes(current3, currentTagId);
20016
20016
  }
20017
20017
  }
20018
- _setContextModes(current, tid) {
20019
- const isHTML = current === this.document || current && this.treeAdapter.getNamespaceURI(current) === NS.HTML;
20018
+ _setContextModes(current3, tid) {
20019
+ const isHTML = current3 === this.document || current3 && this.treeAdapter.getNamespaceURI(current3) === NS.HTML;
20020
20020
  this.currentNotInHTML = !isHTML;
20021
- this.tokenizer.inForeignNode = !isHTML && current !== void 0 && tid !== void 0 && !this._isIntegrationPoint(tid, current);
20021
+ this.tokenizer.inForeignNode = !isHTML && current3 !== void 0 && tid !== void 0 && !this._isIntegrationPoint(tid, current3);
20022
20022
  }
20023
20023
  /** @protected */
20024
20024
  _switchToTextParsing(currentToken, nextTokenizerState) {
@@ -20216,22 +20216,22 @@ var init_parser = __esm({
20216
20216
  shouldProcessStartTagTokenInForeignContent(token) {
20217
20217
  if (!this.currentNotInHTML)
20218
20218
  return false;
20219
- let current;
20219
+ let current3;
20220
20220
  let currentTagId;
20221
20221
  if (this.openElements.stackTop === 0 && this.fragmentContext) {
20222
- current = this.fragmentContext;
20222
+ current3 = this.fragmentContext;
20223
20223
  currentTagId = this.fragmentContextID;
20224
20224
  } else {
20225
- ({ current, currentTagId } = this.openElements);
20225
+ ({ current: current3, currentTagId } = this.openElements);
20226
20226
  }
20227
- if (token.tagID === TAG_ID.SVG && this.treeAdapter.getTagName(current) === TAG_NAMES.ANNOTATION_XML && this.treeAdapter.getNamespaceURI(current) === NS.MATHML) {
20227
+ if (token.tagID === TAG_ID.SVG && this.treeAdapter.getTagName(current3) === TAG_NAMES.ANNOTATION_XML && this.treeAdapter.getNamespaceURI(current3) === NS.MATHML) {
20228
20228
  return false;
20229
20229
  }
20230
20230
  return (
20231
20231
  // Check that `current` is not an integration point for HTML or MathML elements.
20232
20232
  this.tokenizer.inForeignNode || // If it _is_ an integration point, then we might have to check that it is not an HTML
20233
20233
  // integration point.
20234
- (token.tagID === TAG_ID.MGLYPH || token.tagID === TAG_ID.MALIGNMARK) && currentTagId !== void 0 && !this._isIntegrationPoint(currentTagId, current, NS.HTML)
20234
+ (token.tagID === TAG_ID.MGLYPH || token.tagID === TAG_ID.MALIGNMARK) && currentTagId !== void 0 && !this._isIntegrationPoint(currentTagId, current3, NS.HTML)
20235
20235
  );
20236
20236
  }
20237
20237
  /** @protected */
@@ -21191,8 +21191,8 @@ function startTag(node2, state) {
21191
21191
  const tagName = node2.tagName.toLowerCase();
21192
21192
  if (state.parser.tokenizer.state === TokenizerMode.PLAINTEXT) return;
21193
21193
  resetTokenizer(state, pointStart(node2));
21194
- const current = state.parser.openElements.current;
21195
- let ns = "namespaceURI" in current ? current.namespaceURI : webNamespaces.html;
21194
+ const current3 = state.parser.openElements.current;
21195
+ let ns = "namespaceURI" in current3 ? current3.namespaceURI : webNamespaces.html;
21196
21196
  if (ns === webNamespaces.html && tagName === "svg") {
21197
21197
  ns = webNamespaces.svg;
21198
21198
  }
@@ -50461,11 +50461,13 @@ var init_Markdown = __esm({
50461
50461
  // Suppress the default <pre> wrapper — code renderer owns the full block structure
50462
50462
  pre: ({ node: _node, children }) => /* @__PURE__ */ jsx(Fragment, { children }),
50463
50463
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50464
- code: ({ node: _node, inline, className, children, ...props }) => {
50464
+ code: ({ node: _node, className, children, ...props }) => {
50465
50465
  const langMatch = /language-(\w+)/.exec(className || "");
50466
50466
  const language = langMatch ? langMatch[1] : "";
50467
- if (!inline) {
50468
- return language ? /* @__PURE__ */ jsxs("div", { className: "not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: [
50467
+ const isBlock = Boolean(language) || String(children).includes("\n");
50468
+ const codeText2 = String(children).replace(/^\s*\n+/, "").replace(/\s+$/, "");
50469
+ if (isBlock) {
50470
+ return language ? /* @__PURE__ */ jsxs("div", { className: "sa-code-block not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: [
50469
50471
  /* @__PURE__ */ jsx("div", { className: "bg-neutral px-4 py-2 text-xs text-neutral-content font-mono", children: language }),
50470
50472
  /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsx(
50471
50473
  prism_default2,
@@ -50475,13 +50477,13 @@ var init_Markdown = __esm({
50475
50477
  PreTag: "div",
50476
50478
  className: "!my-0 !rounded-none text-sm",
50477
50479
  customStyle: { margin: 0, borderRadius: 0, padding: "1rem" },
50478
- codeTagProps: { style: { backgroundColor: "transparent", padding: 0, fontSize: "inherit", textDecoration: "none" } },
50480
+ codeTagProps: { style: { backgroundColor: "transparent", padding: 0, fontSize: "inherit" } },
50479
50481
  wrapLongLines: false,
50480
50482
  ...props,
50481
- children: String(children).replace(/\n$/, "")
50483
+ children: codeText2
50482
50484
  }
50483
50485
  ) })
50484
- ] }) : /* @__PURE__ */ jsx("div", { className: "not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: /* @__PURE__ */ 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__ */ jsx("code", { ...props, children }) }) });
50486
+ ] }) : /* @__PURE__ */ jsx("div", { className: "sa-code-block not-prose my-4 rounded-lg overflow-hidden border border-base-300", children: /* @__PURE__ */ 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__ */ jsx("code", { ...props, children: codeText2 }) }) });
50485
50487
  }
50486
50488
  return /* @__PURE__ */ jsx("code", { className: "bg-base-200 text-teal-700 px-1.5 py-0.5 rounded text-sm font-mono break-words", ...props, children });
50487
50489
  },
@@ -66191,6 +66193,7 @@ __export(services_exports, {
66191
66193
  deleteArtifact: () => deleteArtifact,
66192
66194
  deleteBookmark: () => deleteBookmark,
66193
66195
  deleteThread: () => deleteThread,
66196
+ deleteUserConversation: () => deleteUserConversation,
66194
66197
  getActions: () => getActions,
66195
66198
  getAllArtifacts: () => getAllArtifacts,
66196
66199
  getAllBookmarks: () => getAllBookmarks,
@@ -66330,6 +66333,34 @@ function injectBundleStyles() {
66330
66333
  daisyThemes.type = "text/css";
66331
66334
  daisyThemes.href = "https://cdn.jsdelivr.net/npm/daisyui@5/themes.css";
66332
66335
  document.head.appendChild(daisyThemes);
66336
+ const codeStyles = document.createElement("style");
66337
+ codeStyles.setAttribute("data-sa-code-styles", "true");
66338
+ codeStyles.textContent = `
66339
+ .sa-code-block, .sa-code-block * {
66340
+ text-decoration: none !important;
66341
+ text-shadow: none !important;
66342
+ border: 0;
66343
+ list-style: none;
66344
+ }
66345
+ .sa-code-block pre, .sa-code-block code, .sa-code-block span {
66346
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace !important;
66347
+ line-height: 1.5 !important;
66348
+ letter-spacing: normal !important;
66349
+ }
66350
+ .sa-code-block code { background: transparent !important; }
66351
+
66352
+ /* Tailwind v4 dropped the browser default of cursor:pointer on buttons, so
66353
+ anything clickable in our components would otherwise show an arrow. */
66354
+ .sa-ui button:not(:disabled),
66355
+ .sa-ui [role="button"]:not([aria-disabled="true"]),
66356
+ .sa-ui summary,
66357
+ .sa-ui label[for],
66358
+ .sa-ui select {
66359
+ cursor: pointer;
66360
+ }
66361
+ .sa-ui button:disabled { cursor: not-allowed; }
66362
+ `;
66363
+ document.head.appendChild(codeStyles);
66333
66364
  }
66334
66365
  async function requestBundle(client, options) {
66335
66366
  injectBundleStyles();
@@ -67146,6 +67177,29 @@ async function deleteThread(client, userId, threadId, clearCache = true, timeout
67146
67177
  };
67147
67178
  }
67148
67179
  }
67180
+ async function deleteUserConversation(client, userPrompt, userId, clearCache = true, timeout = 3e4) {
67181
+ try {
67182
+ const response = await client.requestData({
67183
+ collection: "user-conversations",
67184
+ operation: "deleteByUserPrompt",
67185
+ params: { userPrompt, userId, clearCache },
67186
+ timeout
67187
+ });
67188
+ if (response.error) {
67189
+ return { success: false, error: response.error };
67190
+ }
67191
+ const data = response.data;
67192
+ if (data?.success === false) {
67193
+ return { success: false, error: data.message || "Failed to delete the message" };
67194
+ }
67195
+ return { success: true, message: data?.message, queriesDeleted: data?.queriesDeleted };
67196
+ } catch (error51) {
67197
+ return {
67198
+ success: false,
67199
+ error: error51 instanceof Error ? error51.message : "Failed to delete the message"
67200
+ };
67201
+ }
67202
+ }
67149
67203
  async function updateThreadTitle(client, userId, threadId, title, timeout = 15e3) {
67150
67204
  try {
67151
67205
  const response = await client.requestData({
@@ -67206,6 +67260,10 @@ var SuperatomClient = class {
67206
67260
  this.errorHandlers = /* @__PURE__ */ new Set();
67207
67261
  this.connectPromise = null;
67208
67262
  this.connectResolve = null;
67263
+ // Supplies a valid session token before each (re)connect — see ClientConfig.getAccessToken.
67264
+ this.getAccessToken = null;
67265
+ // Waiting for getAccessToken; the socket does not exist yet.
67266
+ this.fetchingToken = false;
67209
67267
  // Heartbeat — detects zombie sockets that readyState=OPEN can't catch.
67210
67268
  this.heartbeatInterval = null;
67211
67269
  this.pongDeadline = null;
@@ -67224,6 +67282,7 @@ var SuperatomClient = class {
67224
67282
  this.boundHandlePageShow = null;
67225
67283
  const parsed = ClientConfigSchema.parse(config2);
67226
67284
  this.config = parsed;
67285
+ this.getAccessToken = config2.getAccessToken ?? null;
67227
67286
  this.registerGlobals();
67228
67287
  }
67229
67288
  registerGlobals() {
@@ -67375,21 +67434,7 @@ var SuperatomClient = class {
67375
67434
  * callbacks so the eventual real close doesn't double-fire everything.
67376
67435
  */
67377
67436
  markAsDead(reasonMessage) {
67378
- if (this.socket) {
67379
- const dying = this.socket;
67380
- this.socket = null;
67381
- try {
67382
- dying.onopen = null;
67383
- dying.onmessage = null;
67384
- dying.onerror = null;
67385
- dying.onclose = null;
67386
- if (dying.readyState === WebSocket.OPEN || dying.readyState === WebSocket.CONNECTING) {
67387
- dying.close();
67388
- }
67389
- } catch (err) {
67390
- this.log("error", "Failed to detach dying socket:", err);
67391
- }
67392
- }
67437
+ this.discardSocket();
67393
67438
  this.stopHeartbeat();
67394
67439
  this.failPendingRequests(new Error(reasonMessage));
67395
67440
  this.disconnectionHandlers.forEach((handler) => handler());
@@ -67406,21 +67451,39 @@ var SuperatomClient = class {
67406
67451
  this.cancelReconnectTimer();
67407
67452
  this.reconnectAttempts = 0;
67408
67453
  this.shouldReconnect = true;
67409
- if (this.socket) {
67410
- try {
67411
- const old = this.socket;
67412
- this.socket = null;
67413
- if (old.readyState === WebSocket.OPEN || old.readyState === WebSocket.CONNECTING) {
67414
- old.close();
67415
- }
67416
- } catch (err) {
67417
- this.log("error", "Failed to close old socket during forceReconnect:", err);
67418
- }
67419
- }
67454
+ const wasOpen = this.discardSocket();
67420
67455
  this.stopHeartbeat();
67421
67456
  this.failPendingRequests(new Error("Connection reset"));
67457
+ if (wasOpen) this.disconnectionHandlers.forEach((handler) => handler());
67422
67458
  return this.connect();
67423
67459
  }
67460
+ /**
67461
+ * Drop the current socket without triggering its handlers: they are detached
67462
+ * first, so its eventual real close is a no-op (on Linux/Chrome that close
67463
+ * can arrive 30-60s later). Returns whether it was open.
67464
+ */
67465
+ discardSocket() {
67466
+ const old = this.socket;
67467
+ if (!old) return false;
67468
+ this.socket = null;
67469
+ const wasOpen = old.readyState === WebSocket.OPEN;
67470
+ try {
67471
+ old.onopen = null;
67472
+ old.onmessage = null;
67473
+ old.onerror = null;
67474
+ old.onclose = null;
67475
+ if (wasOpen || old.readyState === WebSocket.CONNECTING) {
67476
+ old.close();
67477
+ }
67478
+ } catch (err) {
67479
+ this.log("error", "Failed to detach old socket:", err);
67480
+ }
67481
+ return wasOpen;
67482
+ }
67483
+ /** A connection attempt is under way — fetching the session token, or the socket handshake. */
67484
+ isConnecting() {
67485
+ return this.fetchingToken || this.socket?.readyState === WebSocket.CONNECTING;
67486
+ }
67424
67487
  /**
67425
67488
  * Attach window-level listeners that catch network changes faster than
67426
67489
  * the socket's own onclose. The browser's onclose for a half-open TCP can
@@ -67442,15 +67505,18 @@ var SuperatomClient = class {
67442
67505
  this.boundHandleVisibilityChange = () => {
67443
67506
  if (typeof document === "undefined") return;
67444
67507
  if (document.visibilityState !== "visible") return;
67445
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
67508
+ if (this.socket?.readyState === WebSocket.OPEN) {
67509
+ this.sendHeartbeatProbe();
67510
+ } else if (this.isConnecting()) {
67511
+ return;
67512
+ } else if (this.shouldReconnect) {
67446
67513
  this.forceReconnect().catch((err) => {
67447
67514
  this.log("error", "forceReconnect from visibility failed:", err);
67448
67515
  });
67449
- } else {
67450
- this.sendHeartbeatProbe();
67451
67516
  }
67452
67517
  };
67453
- this.boundHandlePageShow = (() => {
67518
+ this.boundHandlePageShow = ((event) => {
67519
+ if (!event.persisted) return;
67454
67520
  this.forceReconnect().catch((err) => {
67455
67521
  this.log("error", "forceReconnect from pageshow failed:", err);
67456
67522
  });
@@ -67565,13 +67631,50 @@ var SuperatomClient = class {
67565
67631
  this.log("warn", "Already connected");
67566
67632
  return Promise.resolve();
67567
67633
  }
67568
- if (this.socket?.readyState === WebSocket.CONNECTING && this.connectPromise) {
67634
+ if ((this.socket?.readyState === WebSocket.CONNECTING || this.fetchingToken) && this.connectPromise) {
67569
67635
  this.log("warn", "Connection already in progress");
67570
67636
  return this.connectPromise;
67571
67637
  }
67572
67638
  this.connectPromise = new Promise((resolve) => {
67573
67639
  this.connectResolve = resolve;
67574
67640
  });
67641
+ if (this.getAccessToken) {
67642
+ void this.openWithFreshToken();
67643
+ } else {
67644
+ this.openSocket();
67645
+ }
67646
+ return this.connectPromise;
67647
+ }
67648
+ /**
67649
+ * Get a valid session token, then open the socket. The relay checks the token
67650
+ * only at handshake, so every (re)connect must present one that is still valid.
67651
+ *
67652
+ * A fatal auth failure (see SuperAtomAuthError.fatal) stops reconnecting — the
67653
+ * session reports it to the host, and retrying would only repeat it. Anything
67654
+ * else goes through the normal back-off.
67655
+ */
67656
+ async openWithFreshToken() {
67657
+ this.fetchingToken = true;
67658
+ try {
67659
+ this.config.token = await this.getAccessToken();
67660
+ } catch (error51) {
67661
+ this.fetchingToken = false;
67662
+ const err = error51 instanceof Error ? error51 : new Error("Could not get a session token");
67663
+ this.errorHandlers.forEach((handler) => handler(err));
67664
+ if (error51?.fatal) {
67665
+ this.log("error", "Sign-in failed; not reconnecting:", err.message);
67666
+ this.shouldReconnect = false;
67667
+ this.cancelReconnectTimer();
67668
+ } else {
67669
+ this.scheduleReconnect();
67670
+ }
67671
+ return;
67672
+ }
67673
+ this.fetchingToken = false;
67674
+ if (!this.shouldReconnect) return;
67675
+ this.openSocket();
67676
+ }
67677
+ openSocket() {
67575
67678
  try {
67576
67679
  this.socket = new WebSocket(this.getWebSocketUrl());
67577
67680
  this.setupSocketHandlers();
@@ -67582,7 +67685,6 @@ var SuperatomClient = class {
67582
67685
  this.errorHandlers.forEach((handler) => handler(err));
67583
67686
  this.scheduleReconnect();
67584
67687
  }
67585
- return this.connectPromise;
67586
67688
  }
67587
67689
  /**
67588
67690
  * Disconnect from WebSocket server
@@ -67606,6 +67708,7 @@ var SuperatomClient = class {
67606
67708
  const merged = { ...this.config, ...config2 };
67607
67709
  const parsed = ClientConfigSchema.parse(merged);
67608
67710
  this.config = parsed;
67711
+ if ("getAccessToken" in config2) this.getAccessToken = config2.getAccessToken ?? null;
67609
67712
  }
67610
67713
  /**
67611
67714
  * Reconnect with new configuration
@@ -68032,6 +68135,197 @@ var SuperatomClient = class {
68032
68135
  }
68033
68136
  };
68034
68137
 
68138
+ // src/auth.ts
68139
+ var DEFAULT_API_URL = "https://sa-api.superatom.ai";
68140
+ var SuperAtomAuthError = class extends Error {
68141
+ constructor(code4, message, options) {
68142
+ super(message);
68143
+ this.name = "SuperAtomAuthError";
68144
+ this.code = code4;
68145
+ this.reason = options.reason;
68146
+ this.status = options.status;
68147
+ this.fatal = options.fatal;
68148
+ if (options.cause !== void 0) this.cause = options.cause;
68149
+ }
68150
+ };
68151
+ var MIN_REMAINING_MS = 6e4;
68152
+ var RENEW_AHEAD_MS = 12e4;
68153
+ var RENEW_RETRY_MS = 3e4;
68154
+ var RETRIES_BEFORE_REPORTING = 2;
68155
+ var EXCHANGE_TIMEOUT_MS = 15e3;
68156
+ function expiryOf(token) {
68157
+ try {
68158
+ const payload = token.split(".")[1];
68159
+ if (!payload) return null;
68160
+ const json3 = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
68161
+ return typeof json3.exp === "number" ? json3.exp * 1e3 : null;
68162
+ } catch {
68163
+ return null;
68164
+ }
68165
+ }
68166
+ var SdkSession = class {
68167
+ constructor(opts) {
68168
+ this.token = null;
68169
+ this.expiresAt = 0;
68170
+ this.currentUser = null;
68171
+ /** The one renewal in flight — every caller shares it. */
68172
+ this.inFlight = null;
68173
+ this.fatalError = null;
68174
+ /** Retryable failures in a row; reset by a successful exchange. */
68175
+ this.retries = 0;
68176
+ this.renewTimer = null;
68177
+ this.stopped = false;
68178
+ this.opts = opts;
68179
+ this.apiUrl = (opts.apiUrl || DEFAULT_API_URL).replace(/\/+$/, "");
68180
+ }
68181
+ get user() {
68182
+ return this.currentUser;
68183
+ }
68184
+ /** The fatal error that stopped the session, if any. */
68185
+ get error() {
68186
+ return this.fatalError;
68187
+ }
68188
+ /**
68189
+ * A token with at least a minute left — the cached one, or a fresh exchange.
68190
+ * Rejects with SuperAtomAuthError.
68191
+ */
68192
+ getAccessToken() {
68193
+ if (this.fatalError) return Promise.reject(this.fatalError);
68194
+ if (this.token && this.expiresAt - Date.now() > MIN_REMAINING_MS) {
68195
+ return Promise.resolve(this.token);
68196
+ }
68197
+ return this.renew();
68198
+ }
68199
+ /** Clear a fatal error so the next getAccessToken() tries again (e.g. after the user signs in again). */
68200
+ reset() {
68201
+ this.fatalError = null;
68202
+ this.stopped = false;
68203
+ }
68204
+ /** Stop renewing — the host unmounted or disconnected. */
68205
+ stop() {
68206
+ this.stopped = true;
68207
+ this.clearRenewTimer();
68208
+ }
68209
+ renew() {
68210
+ if (!this.inFlight) {
68211
+ this.opts.onSigningIn?.(true);
68212
+ this.inFlight = this.exchange().finally(() => {
68213
+ this.inFlight = null;
68214
+ this.opts.onSigningIn?.(false);
68215
+ });
68216
+ }
68217
+ return this.inFlight;
68218
+ }
68219
+ async exchange() {
68220
+ let customerToken;
68221
+ try {
68222
+ customerToken = await this.opts.getToken();
68223
+ } catch (err) {
68224
+ throw this.fail(new SuperAtomAuthError("get_token_failed", "getToken() failed", { fatal: true, cause: err }));
68225
+ }
68226
+ if (typeof customerToken !== "string" || !customerToken.trim()) {
68227
+ throw this.fail(new SuperAtomAuthError("get_token_failed", "getToken() returned no token", { fatal: true }));
68228
+ }
68229
+ const controller = new AbortController();
68230
+ const timeout = setTimeout(() => controller.abort(), EXCHANGE_TIMEOUT_MS);
68231
+ let res;
68232
+ try {
68233
+ res = await fetch(`${this.apiUrl}/auth/sdk/exchange`, {
68234
+ method: "POST",
68235
+ headers: { "Content-Type": "application/json" },
68236
+ body: JSON.stringify({ projectId: this.opts.projectId, token: customerToken.trim() }),
68237
+ signal: controller.signal
68238
+ });
68239
+ } catch (err) {
68240
+ throw this.fail(new SuperAtomAuthError("network", "Could not reach Superatom to sign in", { fatal: false, cause: err }));
68241
+ } finally {
68242
+ clearTimeout(timeout);
68243
+ }
68244
+ const body = await res.json().catch(() => ({}));
68245
+ if (!res.ok) {
68246
+ const message = typeof body?.error === "string" ? body.error : `Sign-in failed (${res.status})`;
68247
+ const fatal = res.status >= 400 && res.status < 500 && res.status !== 429;
68248
+ throw this.fail(
68249
+ new SuperAtomAuthError(fatal ? "exchange_rejected" : "exchange_unavailable", message, {
68250
+ status: res.status,
68251
+ reason: typeof body?.code === "string" ? body.code : void 0,
68252
+ fatal
68253
+ })
68254
+ );
68255
+ }
68256
+ if (typeof body?.token !== "string" || !body.user?.id) {
68257
+ throw this.fail(
68258
+ new SuperAtomAuthError("exchange_unavailable", "Unexpected sign-in response", { status: res.status, fatal: false })
68259
+ );
68260
+ }
68261
+ if (Array.isArray(body.warnings)) {
68262
+ for (const w of body.warnings) console.warn("[SuperAtom] Sign-in warning:", w);
68263
+ }
68264
+ this.token = body.token;
68265
+ this.expiresAt = expiryOf(body.token) ?? Date.now() + 15 * 6e4;
68266
+ this.currentUser = {
68267
+ id: body.user.id,
68268
+ email: body.user.email,
68269
+ name: body.user.name,
68270
+ role: body.user.role,
68271
+ externalRoles: Array.isArray(body.user.externalRoles) ? body.user.externalRoles : []
68272
+ };
68273
+ this.log(`Signed in as ${this.currentUser.email}; token valid until ${new Date(this.expiresAt).toISOString()}`);
68274
+ if (this.retries) {
68275
+ this.retries = 0;
68276
+ this.opts.onRetrying?.(null);
68277
+ }
68278
+ this.opts.onSession?.(this.currentUser);
68279
+ this.scheduleRenew(this.expiresAt - RENEW_AHEAD_MS - Date.now());
68280
+ return body.token;
68281
+ }
68282
+ /** Record a failure: a fatal one stops the session and is reported once. */
68283
+ fail(error51) {
68284
+ if (error51.fatal) {
68285
+ this.fatalError = error51;
68286
+ this.clearRenewTimer();
68287
+ console.error(`[SuperAtom] Sign-in failed: ${error51.message}`, error51);
68288
+ try {
68289
+ this.opts.onAuthError?.(error51);
68290
+ } catch (err) {
68291
+ console.error("[SuperAtom] onAuthError threw:", err);
68292
+ }
68293
+ } else {
68294
+ this.retries++;
68295
+ if (this.retries === RETRIES_BEFORE_REPORTING) {
68296
+ console.warn(`[SuperAtom] ${error51.message} (${this.apiUrl}) \u2014 retrying`);
68297
+ }
68298
+ if (this.retries >= RETRIES_BEFORE_REPORTING) this.opts.onRetrying?.(error51);
68299
+ this.log(`Sign-in attempt failed, will retry: ${error51.message}`);
68300
+ this.scheduleRenew(RENEW_RETRY_MS);
68301
+ }
68302
+ return error51;
68303
+ }
68304
+ /**
68305
+ * Renew in the background ahead of expiry. Timers are paused while the machine
68306
+ * sleeps and fire on wake; a reconnect after wake also renews first if needed.
68307
+ */
68308
+ scheduleRenew(inMs) {
68309
+ this.clearRenewTimer();
68310
+ if (this.stopped || this.fatalError) return;
68311
+ this.renewTimer = setTimeout(() => {
68312
+ this.renewTimer = null;
68313
+ if (this.stopped || this.fatalError) return;
68314
+ this.renew().catch(() => {
68315
+ });
68316
+ }, Math.max(inMs, 5e3));
68317
+ }
68318
+ clearRenewTimer() {
68319
+ if (this.renewTimer) {
68320
+ clearTimeout(this.renewTimer);
68321
+ this.renewTimer = null;
68322
+ }
68323
+ }
68324
+ log(message) {
68325
+ if (this.opts.debug) console.log(`[SuperAtom] ${message}`);
68326
+ }
68327
+ };
68328
+
68035
68329
  // src/setup.ts
68036
68330
  function setup(components) {
68037
68331
  if (typeof window === "undefined") {
@@ -68668,13 +68962,13 @@ var ExpressionEvaluator = class {
68668
68962
  resolveDataPath(path2, data) {
68669
68963
  if (!path2 || !data) return void 0;
68670
68964
  try {
68671
- return path2.split(".").reduce((current, key) => {
68672
- if (current === null || current === void 0) return void 0;
68965
+ return path2.split(".").reduce((current3, key) => {
68966
+ if (current3 === null || current3 === void 0) return void 0;
68673
68967
  if (!isNaN(Number(key))) {
68674
68968
  const index2 = parseInt(key, 10);
68675
- return Array.isArray(current) ? current[index2] : current;
68969
+ return Array.isArray(current3) ? current3[index2] : current3;
68676
68970
  }
68677
- return current[key];
68971
+ return current3[key];
68678
68972
  }, data);
68679
68973
  } catch (error51) {
68680
68974
  console.warn("Error resolving data path:", path2, error51);
@@ -69153,6 +69447,125 @@ function resetRegistration() {
69153
69447
  registeredKeys.forEach((key) => delete COMP_REGISTRY[key]);
69154
69448
  registeredKeys.clear();
69155
69449
  }
69450
+ var current = null;
69451
+ var retrying = null;
69452
+ var signingIn = false;
69453
+ var chatReady = false;
69454
+ var listeners = /* @__PURE__ */ new Set();
69455
+ function setAuthStatus(error51) {
69456
+ if (current === error51) return;
69457
+ current = error51;
69458
+ if (error51) retrying = null;
69459
+ listeners.forEach((l) => l());
69460
+ }
69461
+ function setRetryingStatus(error51) {
69462
+ if (retrying === error51) return;
69463
+ retrying = error51;
69464
+ listeners.forEach((l) => l());
69465
+ }
69466
+ function setSigningIn(value) {
69467
+ if (signingIn === value) return;
69468
+ signingIn = value;
69469
+ listeners.forEach((l) => l());
69470
+ }
69471
+ function useSigningIn() {
69472
+ return useSyncExternalStore(
69473
+ (l) => {
69474
+ listeners.add(l);
69475
+ return () => listeners.delete(l);
69476
+ },
69477
+ () => signingIn,
69478
+ () => false
69479
+ );
69480
+ }
69481
+ function setChatReady(value) {
69482
+ if (chatReady === value) return;
69483
+ chatReady = value;
69484
+ listeners.forEach((l) => l());
69485
+ }
69486
+ function useStartingUp() {
69487
+ return useSyncExternalStore(
69488
+ (l) => {
69489
+ listeners.add(l);
69490
+ return () => listeners.delete(l);
69491
+ },
69492
+ () => !chatReady,
69493
+ () => false
69494
+ );
69495
+ }
69496
+ function useRetryingStatus() {
69497
+ return useSyncExternalStore(
69498
+ (l) => {
69499
+ listeners.add(l);
69500
+ return () => listeners.delete(l);
69501
+ },
69502
+ () => retrying,
69503
+ () => null
69504
+ );
69505
+ }
69506
+ function useAuthStatus() {
69507
+ return useSyncExternalStore(
69508
+ (l) => {
69509
+ listeners.add(l);
69510
+ return () => listeners.delete(l);
69511
+ },
69512
+ () => current,
69513
+ () => null
69514
+ );
69515
+ }
69516
+ var SETUP_REASONS = /* @__PURE__ */ new Set([
69517
+ "bad_request",
69518
+ "bad_project_id",
69519
+ "unknown_project",
69520
+ "bad_sub",
69521
+ "bad_email",
69522
+ "bad_roles",
69523
+ "bad_access",
69524
+ "bad_access_values",
69525
+ "bad_access_filters",
69526
+ "access_not_set_up",
69527
+ "lifetime_too_long",
69528
+ "issued_in_future",
69529
+ "email_linked_elsewhere"
69530
+ ]);
69531
+ function authStatusMessage(error51) {
69532
+ switch (error51.code) {
69533
+ case "get_token_failed":
69534
+ return "Your session has ended. Please sign in again to continue.";
69535
+ case "exchange_rejected":
69536
+ if (error51.reason === "inactive") return "This account has been deactivated.";
69537
+ if (error51.reason === "sdk_not_enabled" || error51.status === 403) {
69538
+ return "This account cannot use the chat. Please contact your administrator.";
69539
+ }
69540
+ if (error51.reason && SETUP_REASONS.has(error51.reason)) {
69541
+ return `This app\u2019s Superatom sign-in is not set up correctly: ${error51.message}`;
69542
+ }
69543
+ return "Your session is no longer valid. Please sign in again to continue.";
69544
+ default:
69545
+ return "Could not connect to Superatom. Please try again in a moment.";
69546
+ }
69547
+ }
69548
+ function retryingStatusMessage(error51) {
69549
+ return error51.code === "network" ? "Cannot reach Superatom \u2014 retrying\u2026" : "Superatom is not responding \u2014 retrying\u2026";
69550
+ }
69551
+ var current2 = {};
69552
+ var listeners2 = /* @__PURE__ */ new Set();
69553
+ function setMessageActions(actions) {
69554
+ current2 = actions;
69555
+ listeners2.forEach((l) => l());
69556
+ }
69557
+ function useMessageActions() {
69558
+ return useSyncExternalStore(
69559
+ (l) => {
69560
+ listeners2.add(l);
69561
+ return () => listeners2.delete(l);
69562
+ },
69563
+ () => current2,
69564
+ () => current2
69565
+ );
69566
+ }
69567
+
69568
+ // src/ui/hooks/useSuperAtom.ts
69156
69569
  function newThreadId() {
69157
69570
  return `thread_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
69158
69571
  }
@@ -69312,17 +69725,41 @@ function buildResponseIssueSchema(response, messageId) {
69312
69725
  var MAX_ABORTED_TRACKED = 100;
69313
69726
  var THREADS_LIMIT = 50;
69314
69727
  var THREAD_MESSAGES_LIMIT = 50;
69315
- function useSuperAtom({ token, userId, projectId, debug = false, streaming = false }) {
69316
- const resolvedUserId = (() => {
69728
+ function useSuperAtom({
69729
+ token,
69730
+ userId,
69731
+ projectId,
69732
+ getToken,
69733
+ onAuthError,
69734
+ apiUrl,
69735
+ wsUrl,
69736
+ debug = false,
69737
+ streaming = false
69738
+ }) {
69739
+ const sdkSignIn = !!getToken;
69740
+ if (sdkSignIn && token) throw new Error("[SuperAtom] Pass either token or getToken, not both");
69741
+ const [user, setUser] = useState(null);
69742
+ const [authError, setAuthError] = useState(null);
69743
+ const [isSigningIn, setIsSigningIn] = useState(false);
69744
+ const [signInAttempt, setSignInAttempt] = useState(0);
69745
+ const directUserId = (() => {
69746
+ if (sdkSignIn) return void 0;
69317
69747
  if (token) {
69318
69748
  const payload = decodeJWT(token);
69319
69749
  const id = payload.sub ?? payload.userId;
69320
69750
  if (!id) throw new Error("[SuperAtom] JWT decoded but no userId or sub field found in payload");
69321
69751
  return id;
69322
69752
  }
69323
- if (!userId) throw new Error("[SuperAtom] Either token or userId must be provided");
69753
+ if (!userId) throw new Error("[SuperAtom] Either token, getToken or userId must be provided");
69324
69754
  return userId;
69325
69755
  })();
69756
+ const resolvedUserId = directUserId ?? user?.id ?? "";
69757
+ const getTokenRef = useRef(getToken);
69758
+ getTokenRef.current = getToken;
69759
+ const onAuthErrorRef = useRef(onAuthError);
69760
+ onAuthErrorRef.current = onAuthError;
69761
+ const sessionRef = useRef(null);
69762
+ const setupDoneRef = useRef(false);
69326
69763
  const clientRef = useRef(null);
69327
69764
  const tokenRef = useRef(token);
69328
69765
  tokenRef.current = token;
@@ -69349,7 +69786,60 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69349
69786
  injectBundleStyles();
69350
69787
  const timeoutId = setTimeout(async () => {
69351
69788
  if (cancelled) return;
69352
- const client = new SuperatomClient({ userId: resolvedUserId, projectId, type: "runtime", debug, token: tokenRef.current });
69789
+ let clientUserId = directUserId;
69790
+ let session = null;
69791
+ if (sdkSignIn) {
69792
+ session = new SdkSession({
69793
+ projectId,
69794
+ apiUrl,
69795
+ debug,
69796
+ getToken: () => getTokenRef.current(),
69797
+ onAuthError: (err) => {
69798
+ setAuthError(err);
69799
+ setAuthStatus(err);
69800
+ onAuthErrorRef.current?.(err);
69801
+ },
69802
+ onSession: (u) => {
69803
+ setAuthError(null);
69804
+ setAuthStatus(null);
69805
+ setUser(u);
69806
+ },
69807
+ // Superatom unreachable or down, after a couple of tries: say so in the
69808
+ // chat instead of sitting on "Connecting…".
69809
+ onRetrying: (err) => setRetryingStatus(err),
69810
+ // Lets the chat show "Signing in…" instead of an empty chat while a
69811
+ // sign-in — including one after reconnect() — is in flight.
69812
+ onSigningIn: (value) => {
69813
+ setSigningIn(value);
69814
+ setIsSigningIn(value);
69815
+ }
69816
+ });
69817
+ sessionRef.current = session;
69818
+ for (let delay = 1e3; ; delay = Math.min(delay * 2, 3e4)) {
69819
+ try {
69820
+ await session.getAccessToken();
69821
+ break;
69822
+ } catch (err) {
69823
+ if (cancelled || err?.fatal) return;
69824
+ await new Promise((r2) => setTimeout(r2, delay));
69825
+ if (cancelled) return;
69826
+ }
69827
+ }
69828
+ if (cancelled) {
69829
+ session.stop();
69830
+ return;
69831
+ }
69832
+ clientUserId = session.user.id;
69833
+ }
69834
+ const activeSession = session;
69835
+ const client = new SuperatomClient({
69836
+ userId: clientUserId,
69837
+ projectId,
69838
+ type: "runtime",
69839
+ debug,
69840
+ ...wsUrl ? { baseUrl: wsUrl } : {},
69841
+ ...activeSession ? { getAccessToken: () => activeSession.getAccessToken() } : { token: tokenRef.current }
69842
+ });
69353
69843
  clientRef.current = client;
69354
69844
  client.onError((err) => console.error("[SuperAtom] Error:", err));
69355
69845
  try {
@@ -69366,11 +69856,11 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69366
69856
  };
69367
69857
  const unsub = client.onConnect(done);
69368
69858
  const cancelPoll = setInterval(() => {
69369
- if (cancelled) done();
69859
+ if (cancelled || activeSession?.error) done();
69370
69860
  }, 50);
69371
69861
  });
69372
- if (cancelled) {
69373
- client.disconnect();
69862
+ if (cancelled || activeSession?.error) {
69863
+ if (cancelled) client.disconnect();
69374
69864
  return;
69375
69865
  }
69376
69866
  window.dispatchEvent(new CustomEvent("sa:client-connected"));
@@ -69446,7 +69936,11 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69446
69936
  ]);
69447
69937
  };
69448
69938
  }
69449
- if (!cancelled) setBundleReady(true);
69939
+ if (!cancelled) {
69940
+ setupDoneRef.current = true;
69941
+ setBundleReady(true);
69942
+ setChatReady(true);
69943
+ }
69450
69944
  } catch (err) {
69451
69945
  console.error("[SuperAtom] Setup failed:", err);
69452
69946
  }
@@ -69460,9 +69954,26 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69460
69954
  }
69461
69955
  clientRef.current?.disconnect();
69462
69956
  clientRef.current = null;
69957
+ sessionRef.current?.stop();
69958
+ sessionRef.current = null;
69959
+ setAuthStatus(null);
69960
+ setupDoneRef.current = false;
69463
69961
  setBundleReady(false);
69962
+ setChatReady(false);
69464
69963
  };
69465
- }, [resolvedUserId, projectId, debug]);
69964
+ }, [directUserId, sdkSignIn, projectId, apiUrl, wsUrl, debug, signInAttempt]);
69965
+ const reconnect = useCallback(() => {
69966
+ const session = sessionRef.current;
69967
+ const client = clientRef.current;
69968
+ session?.reset();
69969
+ setAuthError(null);
69970
+ setAuthStatus(null);
69971
+ if (client && setupDoneRef.current) {
69972
+ client.forceReconnect().catch((err) => console.error("[SuperAtom] Reconnect failed:", err));
69973
+ } else {
69974
+ setSignInAttempt((n) => n + 1);
69975
+ }
69976
+ }, []);
69466
69977
  const sendMessage = useCallback(async () => {
69467
69978
  const text9 = input.trim();
69468
69979
  const client = clientRef.current;
@@ -69854,6 +70365,39 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69854
70365
  if (threadId.current === id) newChat();
69855
70366
  return { success: true };
69856
70367
  }, [resolvedUserId, newChat]);
70368
+ const deleteMessage = useCallback(async (messageId) => {
70369
+ const client = clientRef.current;
70370
+ if (!client) return { success: false, error: "Not connected" };
70371
+ const list4 = messagesRef.current;
70372
+ const index2 = list4.findIndex((m) => m.id === messageId);
70373
+ if (index2 === -1) return { success: false, error: "Message not found" };
70374
+ let questionIndex = list4[index2]?.role === "user" ? index2 : -1;
70375
+ if (questionIndex === -1) {
70376
+ for (let i = index2 - 1; i >= 0; i--) {
70377
+ if (list4[i]?.role === "user") {
70378
+ questionIndex = i;
70379
+ break;
70380
+ }
70381
+ }
70382
+ }
70383
+ const question = questionIndex >= 0 ? list4[questionIndex]?.text ?? "" : "";
70384
+ if (!question.trim()) return { success: false, error: "Could not find the question for this answer" };
70385
+ const res = await deleteUserConversation(client, question, resolvedUserId);
70386
+ if (!res.success) return res;
70387
+ const removed = /* @__PURE__ */ new Set();
70388
+ for (let i = questionIndex; i < list4.length; i++) {
70389
+ const msg = list4[i];
70390
+ if (!msg) continue;
70391
+ if (i > questionIndex && msg.role === "user") break;
70392
+ removed.add(msg.id);
70393
+ }
70394
+ setMessages((prev) => prev.filter((m) => !removed.has(m.id)));
70395
+ return { success: true };
70396
+ }, [resolvedUserId]);
70397
+ useEffect(() => {
70398
+ setMessageActions({ deleteMessage });
70399
+ return () => setMessageActions({});
70400
+ }, [deleteMessage]);
69857
70401
  const renameThread = useCallback(async (id, title) => {
69858
70402
  const client = clientRef.current;
69859
70403
  if (!client) return { success: false, error: "Not connected" };
@@ -69877,7 +70421,12 @@ function useSuperAtom({ token, userId, projectId, debug = false, streaming = fal
69877
70421
  isLoadingThread,
69878
70422
  refreshThreads,
69879
70423
  deleteThread: deleteThread2,
69880
- renameThread
70424
+ renameThread,
70425
+ isNewChat: bundleReady && !isLoadingThread && messages2.length === 0,
70426
+ user,
70427
+ authError,
70428
+ isSigningIn,
70429
+ reconnect
69881
70430
  };
69882
70431
  }
69883
70432
 
@@ -69973,6 +70522,9 @@ function ChatInput({
69973
70522
  isResponding = false,
69974
70523
  onStop
69975
70524
  }) {
70525
+ const authError = useAuthStatus();
70526
+ const startingUp = useStartingUp();
70527
+ const isLocked = disabled || !!authError;
69976
70528
  const textareaRef = useRef(null);
69977
70529
  const historyRef = useRef(null);
69978
70530
  const [isMultiline, setIsMultiline] = useState(false);
@@ -70026,14 +70578,16 @@ function ChatInput({
70026
70578
  }, 0);
70027
70579
  };
70028
70580
  const handleSend = useCallback(() => {
70029
- if (!input.trim() || disabled) return;
70581
+ if (!input.trim() || isLocked) return;
70030
70582
  const history = getOrCreateHistory();
70031
70583
  history?.addCommand(input.trim());
70032
70584
  history?.resetNavigation();
70033
70585
  onSend();
70034
- }, [input, disabled, onSend, getOrCreateHistory]);
70586
+ }, [input, isLocked, onSend, getOrCreateHistory]);
70035
70587
  const themeColor = getThemeColor();
70036
- return /* @__PURE__ */ jsx("div", { className: `shrink-0 bg-base-100 border-t border-base-300 px-4 py-2 ${className}`, children: /* @__PURE__ */ 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__ */ jsx(
70588
+ const effectivePlaceholder = authError ? authStatusMessage(authError) : placeholder;
70589
+ if (startingUp) return null;
70590
+ return /* @__PURE__ */ jsx("div", { className: `sa-ui shrink-0 bg-base-100 border-t border-base-300 px-4 py-2 ${className}`, children: /* @__PURE__ */ 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__ */ jsx(
70037
70591
  "div",
70038
70592
  {
70039
70593
  className: `flex flex-col ${isMultiline ? "rounded-3xl" : "rounded-full"} border-2 shadow-sm transition-all duration-200 hover:shadow-md`,
@@ -70053,7 +70607,7 @@ function ChatInput({
70053
70607
  autoCapitalize: "off",
70054
70608
  ref: textareaRef,
70055
70609
  value: input,
70056
- placeholder,
70610
+ placeholder: effectivePlaceholder,
70057
70611
  onChange: (e) => setInput(e.currentTarget.value),
70058
70612
  onBlur: () => setIsFocused(false),
70059
70613
  onFocus: () => setIsFocused(true),
@@ -70084,7 +70638,7 @@ function ChatInput({
70084
70638
  },
70085
70639
  rows: 1,
70086
70640
  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]",
70087
- disabled,
70641
+ disabled: isLocked,
70088
70642
  style: { maxHeight: "200px" }
70089
70643
  }
70090
70644
  ),
@@ -70106,11 +70660,11 @@ function ChatInput({
70106
70660
  "button",
70107
70661
  {
70108
70662
  onClick: handleSend,
70109
- disabled: !input.trim() || disabled,
70663
+ disabled: !input.trim() || isLocked,
70110
70664
  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`,
70111
70665
  style: {
70112
- backgroundColor: input.trim() && !disabled ? themeColor : "transparent",
70113
- color: input.trim() && !disabled ? "white" : themeColor
70666
+ backgroundColor: input.trim() && !isLocked ? themeColor : "transparent",
70667
+ color: input.trim() && !isLocked ? "white" : themeColor
70114
70668
  },
70115
70669
  children: /* @__PURE__ */ jsx(Icon, { icon: "ph:paper-plane-right-fill", width: 20, height: 20, className: "sm:w-6 sm:h-6" })
70116
70670
  }
@@ -70126,6 +70680,86 @@ init_iconify();
70126
70680
  // src/ui/components/MessageBubble.tsx
70127
70681
  init_iconify();
70128
70682
  init_Markdown();
70683
+
70684
+ // src/ui/components/ConfirmDeleteModal.tsx
70685
+ init_iconify();
70686
+ function ConfirmDeleteModal({
70687
+ isOpen,
70688
+ onClose,
70689
+ onConfirm,
70690
+ isDeleting,
70691
+ title = "Delete",
70692
+ message = "Are you sure you want to delete this?"
70693
+ }) {
70694
+ useEffect(() => {
70695
+ if (!isOpen) return;
70696
+ const handleEscape = (e) => {
70697
+ if (e.key === "Escape" && !isDeleting) {
70698
+ onClose();
70699
+ }
70700
+ };
70701
+ document.addEventListener("keydown", handleEscape);
70702
+ document.body.style.overflow = "hidden";
70703
+ return () => {
70704
+ document.removeEventListener("keydown", handleEscape);
70705
+ document.body.style.overflow = "unset";
70706
+ };
70707
+ }, [isOpen, onClose, isDeleting]);
70708
+ const handleBackdropClick = (e) => {
70709
+ if (e.target === e.currentTarget && !isDeleting) {
70710
+ onClose();
70711
+ }
70712
+ };
70713
+ if (!isOpen) return null;
70714
+ return createPortal(
70715
+ /* @__PURE__ */ jsx(
70716
+ "div",
70717
+ {
70718
+ className: "sa-ui fixed inset-0 flex items-center justify-center bg-black/50 backdrop-blur-sm",
70719
+ style: { zIndex: 9999 },
70720
+ onClick: handleBackdropClick,
70721
+ children: /* @__PURE__ */ jsxs("div", { className: "bg-base-100 rounded-xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden", children: [
70722
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-base-300", children: [
70723
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-base-content", children: title }),
70724
+ /* @__PURE__ */ jsx(
70725
+ "button",
70726
+ {
70727
+ type: "button",
70728
+ disabled: isDeleting,
70729
+ onClick: onClose,
70730
+ className: "p-1 rounded-lg hover:bg-base-200 transition-colors",
70731
+ children: /* @__PURE__ */ jsx(Icon, { icon: "ph:x-bold", width: 20, height: 20, className: "text-base-content/60" })
70732
+ }
70733
+ )
70734
+ ] }),
70735
+ /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsx("p", { className: "text-base-content/80", children: message }) }),
70736
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 p-4 border-t border-base-300", children: [
70737
+ /* @__PURE__ */ jsx(
70738
+ "button",
70739
+ {
70740
+ type: "button",
70741
+ onClick: onClose,
70742
+ disabled: isDeleting,
70743
+ className: "px-4 py-2 rounded-lg text-base-content hover:bg-base-200 transition-colors",
70744
+ children: "Cancel"
70745
+ }
70746
+ ),
70747
+ isDeleting ? /* @__PURE__ */ jsx("div", { className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white flex items-center justify-center", children: /* @__PURE__ */ jsx(Icon, { icon: "eos-icons:loading", width: 20, height: 20, className: "animate-spin" }) }) : /* @__PURE__ */ jsx(
70748
+ "button",
70749
+ {
70750
+ type: "button",
70751
+ onClick: onConfirm,
70752
+ className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white hover:bg-red-700 transition-colors",
70753
+ children: "Delete"
70754
+ }
70755
+ )
70756
+ ] })
70757
+ ] })
70758
+ }
70759
+ ),
70760
+ document.body
70761
+ );
70762
+ }
70129
70763
  function StopButton({ onStop }) {
70130
70764
  return /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs(
70131
70765
  "button",
@@ -70151,17 +70785,40 @@ function MessageBubble({ message, onStop }) {
70151
70785
  const isTextOnly = message.isTextOnlyResponse === true;
70152
70786
  const isAborted = message.aborted === true;
70153
70787
  const responseComplete = hasComponent || isTextOnly || !isStreaming && hasText;
70788
+ const [watchedGeneration] = useState(() => !responseComplete);
70154
70789
  useEffect(() => {
70155
- if (message.role !== "assistant") return;
70156
- startTime.current = Date.now();
70790
+ if (message.role !== "assistant" || !watchedGeneration) return;
70157
70791
  if (responseComplete) {
70158
70792
  setElapsedMs(Date.now() - startTime.current);
70159
70793
  return;
70160
70794
  }
70795
+ setElapsedMs(Date.now() - startTime.current);
70161
70796
  const interval = setInterval(() => setElapsedMs(Date.now() - startTime.current), 100);
70162
70797
  return () => clearInterval(interval);
70163
- }, [message.role, responseComplete]);
70798
+ }, [message.role, responseComplete, watchedGeneration]);
70164
70799
  const formatMs = (ms) => ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
70800
+ const { deleteMessage } = useMessageActions();
70801
+ const [confirmOpen, setConfirmOpen] = useState(false);
70802
+ const [isDeleting, setIsDeleting] = useState(false);
70803
+ const handleDelete2 = async () => {
70804
+ if (!deleteMessage) return;
70805
+ setIsDeleting(true);
70806
+ const res = await deleteMessage(message.id);
70807
+ setIsDeleting(false);
70808
+ setConfirmOpen(false);
70809
+ if (!res.success) console.error("[SuperAtom] Could not delete the message:", res.error);
70810
+ };
70811
+ const deleteButton = deleteMessage && responseComplete && !isAborted ? /* @__PURE__ */ jsx("div", { className: "flex justify-end mt-2", children: /* @__PURE__ */ jsx(
70812
+ "button",
70813
+ {
70814
+ onClick: () => setConfirmOpen(true),
70815
+ disabled: isDeleting,
70816
+ title: "Delete this answer",
70817
+ "aria-label": "Delete this answer",
70818
+ 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",
70819
+ children: /* @__PURE__ */ jsx("svg", { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ 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" }) })
70820
+ }
70821
+ ) }) : null;
70165
70822
  const accordionHeader = useMemo(() => {
70166
70823
  if (isAborted) return hasText ? "Stopped" : "Stopped (no response yet)";
70167
70824
  if (!hasText) return "Thinking...";
@@ -70197,7 +70854,7 @@ function MessageBubble({ message, onStop }) {
70197
70854
  children: [
70198
70855
  /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-base-content truncate text-left flex-1", children: accordionHeader }),
70199
70856
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 shrink-0", children: [
70200
- !message.fromHistory && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-teal-50 border border-teal-200", children: [
70857
+ !message.fromHistory && watchedGeneration && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-teal-50 border border-teal-200", children: [
70201
70858
  responseComplete ? /* @__PURE__ */ jsx("svg", { className: "text-teal-600", width: 12, height: 12, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" }) }) : /* @__PURE__ */ jsx("span", { className: "loading loading-spinner loading-xs text-teal-600" }),
70202
70859
  /* @__PURE__ */ jsx("span", { className: "text-xs text-teal-700 font-medium font-mono", children: formatMs(elapsedMs) })
70203
70860
  ] }),
@@ -70247,8 +70904,25 @@ function MessageBubble({ message, onStop }) {
70247
70904
  /* @__PURE__ */ jsx("span", { className: "text-sm text-base-content/70", children: "Generating component..." })
70248
70905
  ] })
70249
70906
  ] }) }),
70250
- hasComponent && /* @__PURE__ */ jsx("div", { id: `box-${message.id}`, className: "w-full px-4 lg:px-16 xl:px-24 2xl:px-32", children: /* @__PURE__ */ 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__ */ jsx(DSLRenderer, { dsl: message.schema.ui, data: message.schema.data ?? {} }) }) }),
70251
- !isStreaming && !hasComponent && hasText && isTextOnly && !isAborted && /* @__PURE__ */ jsx("div", { className: "flex justify-center w-full px-4", children: /* @__PURE__ */ jsx("div", { className: "w-full md:w-[60vw]", children: /* @__PURE__ */ jsx("div", { className: "rounded-xl shadow-sm bg-base-100 border-2 border-base-300 p-4 text-base-content", children: /* @__PURE__ */ jsx(Markdown_default, { content: fullText, isStreaming: false }) }) }) })
70907
+ hasComponent && /* @__PURE__ */ jsxs("div", { id: `box-${message.id}`, className: "w-full px-4 lg:px-16 xl:px-24 2xl:px-32", children: [
70908
+ /* @__PURE__ */ 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__ */ jsx(DSLRenderer, { dsl: message.schema.ui, data: message.schema.data ?? {} }) }),
70909
+ deleteButton
70910
+ ] }),
70911
+ !isStreaming && !hasComponent && hasText && isTextOnly && !isAborted && /* @__PURE__ */ jsx("div", { className: "flex justify-center w-full px-4", children: /* @__PURE__ */ jsxs("div", { className: "w-full md:w-[60vw]", children: [
70912
+ /* @__PURE__ */ jsx("div", { className: "rounded-xl shadow-sm bg-base-100 border-2 border-base-300 p-4 text-base-content", children: /* @__PURE__ */ jsx(Markdown_default, { content: fullText, isStreaming: false }) }),
70913
+ deleteButton
70914
+ ] }) }),
70915
+ /* @__PURE__ */ jsx(
70916
+ ConfirmDeleteModal,
70917
+ {
70918
+ isOpen: confirmOpen,
70919
+ onClose: () => setConfirmOpen(false),
70920
+ onConfirm: handleDelete2,
70921
+ isDeleting,
70922
+ title: "Delete this answer?",
70923
+ 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."
70924
+ }
70925
+ )
70252
70926
  ] });
70253
70927
  }
70254
70928
  function DynamicSpacer({ messages: messages2 }) {
@@ -70262,6 +70936,7 @@ function MessageList({
70262
70936
  bundleReady,
70263
70937
  connectingText = "Connecting to SuperAtom...",
70264
70938
  emptyText = "Start a conversation",
70939
+ emptyState,
70265
70940
  className = "",
70266
70941
  isResponding = false,
70267
70942
  onStop,
@@ -70345,11 +71020,20 @@ function MessageList({
70345
71020
  }
70346
71021
  }, 300);
70347
71022
  }, [messages2]);
70348
- return /* @__PURE__ */ jsxs("div", { className: `flex-1 relative overflow-hidden ${className}`, children: [
70349
- /* @__PURE__ */ jsx("main", { ref: containerRef, className: "h-full overflow-y-auto py-6", children: !bundleReady || isLoadingThread && messages2.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-base-content/50", children: [
71023
+ const authError = useAuthStatus();
71024
+ const retrying2 = useRetryingStatus();
71025
+ const signingIn2 = useSigningIn();
71026
+ return /* @__PURE__ */ jsxs("div", { className: `sa-ui flex-1 relative overflow-hidden ${className}`, children: [
71027
+ /* @__PURE__ */ jsx("main", { ref: containerRef, className: "h-full overflow-y-auto py-6", children: signingIn2 && !bundleReady ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-base-content/50", children: [
71028
+ /* @__PURE__ */ jsx("span", { className: "loading loading-spinner loading-lg" }),
71029
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Signing in to Superatom\u2026" })
71030
+ ] }) : authError ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 px-6 text-center", children: [
71031
+ /* @__PURE__ */ jsx(Icon, { icon: "mdi:lock-alert-outline", width: 28, height: 28, className: "text-base-content/40" }),
71032
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-base-content/70 max-w-sm", children: authStatusMessage(authError) })
71033
+ ] }) : !bundleReady || isLoadingThread && messages2.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col items-center justify-center gap-3 text-base-content/50", children: [
70350
71034
  /* @__PURE__ */ jsx("span", { className: "loading loading-spinner loading-md" }),
70351
- /* @__PURE__ */ jsx("span", { className: "text-sm", children: !bundleReady ? connectingText : loadingThreadText })
70352
- ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
71035
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: !bundleReady ? retrying2 ? retryingStatusMessage(retrying2) : connectingText : loadingThreadText })
71036
+ ] }) : messages2.length === 0 && emptyState ? /* @__PURE__ */ jsx("div", { className: "h-full flex flex-col", children: emptyState }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
70353
71037
  messages2.length === 0 && /* @__PURE__ */ jsx("div", { className: "text-center mt-24", children: /* @__PURE__ */ jsx("p", { className: "text-base-content/40 text-sm", children: emptyText }) }),
70354
71038
  messages2.map((msg) => /* @__PURE__ */ jsx(
70355
71039
  MessageBubble,
@@ -70731,87 +71415,8 @@ var MenuV1RendererCollapsed = ({ items, onItemClick }) => {
70731
71415
  groupIndex < groupedItems.length - 1 && /* @__PURE__ */ jsx("div", { className: "w-8 border-t border-base-300 my-2" })
70732
71416
  ] }, groupIndex)) });
70733
71417
  };
70734
-
70735
- // src/ui/components/ConfirmDeleteModal.tsx
70736
- init_iconify();
70737
- function ConfirmDeleteModal({
70738
- isOpen,
70739
- onClose,
70740
- onConfirm,
70741
- isDeleting,
70742
- title = "Delete",
70743
- message = "Are you sure you want to delete this?"
70744
- }) {
70745
- useEffect(() => {
70746
- if (!isOpen) return;
70747
- const handleEscape = (e) => {
70748
- if (e.key === "Escape" && !isDeleting) {
70749
- onClose();
70750
- }
70751
- };
70752
- document.addEventListener("keydown", handleEscape);
70753
- document.body.style.overflow = "hidden";
70754
- return () => {
70755
- document.removeEventListener("keydown", handleEscape);
70756
- document.body.style.overflow = "unset";
70757
- };
70758
- }, [isOpen, onClose, isDeleting]);
70759
- const handleBackdropClick = (e) => {
70760
- if (e.target === e.currentTarget && !isDeleting) {
70761
- onClose();
70762
- }
70763
- };
70764
- if (!isOpen) return null;
70765
- return createPortal(
70766
- /* @__PURE__ */ jsx(
70767
- "div",
70768
- {
70769
- className: "fixed inset-0 flex items-center justify-center bg-black/50 backdrop-blur-sm",
70770
- style: { zIndex: 9999 },
70771
- onClick: handleBackdropClick,
70772
- children: /* @__PURE__ */ jsxs("div", { className: "bg-base-100 rounded-xl shadow-2xl w-full max-w-sm mx-4 overflow-hidden", children: [
70773
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b border-base-300", children: [
70774
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-base-content", children: title }),
70775
- /* @__PURE__ */ jsx(
70776
- "button",
70777
- {
70778
- type: "button",
70779
- disabled: isDeleting,
70780
- onClick: onClose,
70781
- className: "p-1 rounded-lg hover:bg-base-200 transition-colors",
70782
- children: /* @__PURE__ */ jsx(Icon, { icon: "ph:x-bold", width: 20, height: 20, className: "text-base-content/60" })
70783
- }
70784
- )
70785
- ] }),
70786
- /* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsx("p", { className: "text-base-content/80", children: message }) }),
70787
- /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 p-4 border-t border-base-300", children: [
70788
- /* @__PURE__ */ jsx(
70789
- "button",
70790
- {
70791
- type: "button",
70792
- onClick: onClose,
70793
- disabled: isDeleting,
70794
- className: "px-4 py-2 rounded-lg text-base-content hover:bg-base-200 transition-colors",
70795
- children: "Cancel"
70796
- }
70797
- ),
70798
- isDeleting ? /* @__PURE__ */ jsx("div", { className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white flex items-center justify-center", children: /* @__PURE__ */ jsx(Icon, { icon: "eos-icons:loading", width: 20, height: 20, className: "animate-spin" }) }) : /* @__PURE__ */ jsx(
70799
- "button",
70800
- {
70801
- type: "button",
70802
- onClick: onConfirm,
70803
- className: "min-w-20 px-4 py-2 rounded-lg bg-red-600 text-white hover:bg-red-700 transition-colors",
70804
- children: "Delete"
70805
- }
70806
- )
70807
- ] })
70808
- ] })
70809
- }
70810
- ),
70811
- document.body
70812
- );
70813
- }
70814
71418
  var CHATS_OPEN_KEY = "chats-section-open";
71419
+ var SIDEBAR_COLLAPSED_KEY = "sa-sidebar-collapsed";
70815
71420
  function Sidebar({
70816
71421
  children,
70817
71422
  className = "",
@@ -70873,8 +71478,21 @@ function Sidebar({
70873
71478
  setThreadToDelete(null);
70874
71479
  }
70875
71480
  };
70876
- const [collapsed, setCollapsed] = useState(true);
70877
- const [showContent, setShowContent] = useState(false);
71481
+ const startingUp = useStartingUp();
71482
+ const [collapsed, setCollapsed] = useState(() => {
71483
+ try {
71484
+ return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === "true";
71485
+ } catch {
71486
+ return false;
71487
+ }
71488
+ });
71489
+ const [showContent, setShowContent] = useState(() => {
71490
+ try {
71491
+ return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) !== "true";
71492
+ } catch {
71493
+ return true;
71494
+ }
71495
+ });
70878
71496
  const [menuV1Data, setMenuV1Data] = useState(null);
70879
71497
  const [isChatsOpen, setIsChatsOpen] = useState(() => {
70880
71498
  try {
@@ -70918,6 +71536,10 @@ function Sidebar({
70918
71536
  function toggle() {
70919
71537
  const next2 = !collapsed;
70920
71538
  setCollapsed(next2);
71539
+ try {
71540
+ localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(next2));
71541
+ } catch {
71542
+ }
70921
71543
  if (!next2) {
70922
71544
  setTimeout(() => setShowContent(true), 150);
70923
71545
  } else {
@@ -70941,10 +71563,11 @@ function Sidebar({
70941
71563
  });
70942
71564
  }
70943
71565
  }
71566
+ if (startingUp) return null;
70944
71567
  return /* @__PURE__ */ jsxs(
70945
71568
  "aside",
70946
71569
  {
70947
- 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}`,
71570
+ 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}`,
70948
71571
  children: [
70949
71572
  !collapsed && showContent && /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full w-60", children: [
70950
71573
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-3 border-b border-base-300 flex-shrink-0", children: [
@@ -71114,7 +71737,7 @@ function NewChatHero({
71114
71737
  subtitle = "Ask a question to start a new chat",
71115
71738
  className = ""
71116
71739
  }) {
71117
- return /* @__PURE__ */ jsxs("div", { className: `flex-1 min-h-0 flex flex-col items-center justify-center gap-6 px-4 ${className}`, children: [
71740
+ return /* @__PURE__ */ jsxs("div", { className: `sa-ui flex-1 min-h-0 flex flex-col items-center justify-center gap-6 px-4 ${className}`, children: [
71118
71741
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 text-center", children: [
71119
71742
  /* @__PURE__ */ jsx("h1", { className: "text-2xl md:text-3xl font-semibold text-base-content", children: title }),
71120
71743
  /* @__PURE__ */ jsx("p", { className: "text-sm text-base-content/50", children: subtitle })
@@ -71124,8 +71747,8 @@ function NewChatHero({
71124
71747
  }
71125
71748
 
71126
71749
  // src/index.ts
71127
- var SDK_VERSION = "0.0.2";
71750
+ var SDK_VERSION = "0.0.3";
71128
71751
 
71129
- export { ActionsRequestMessageSchema, ActionsRequestPayloadSchema, ActionsResponseMessageSchema, ActionsResponsePayloadSchema, BindingSchema3 as BindingSchema, BundleChunkMessageSchema, BundleChunkPayloadSchema, BundleErrorMessageSchema, BundleErrorPayloadSchema, BundleRequestMessageSchema, BundleRequestPayloadSchema, COMP_REGISTRY, ChatInput, ClientConfigSchema, ComponentSchema, ComponentsSendMessageSchema, ComponentsSendPayloadSchema, ConfirmDeleteModal, DSLErrorBoundary, DSLRenderer, DSLRendererPropsSchema3 as DSLRendererPropsSchema, DataRequestMessageSchema, DataRequestPayloadSchema, DataResponseMessageSchema, DataResponsePayloadSchema, DynamicComponent, ExpressionSchema3 as ExpressionSchema, ForDirectiveSchema3 as ForDirectiveSchema, MenuDataSchema, MenuQueryFiltersSchema, MenuV1Renderer, MenuV1RendererCollapsed, MenusRequestMessageSchema, MenusRequestPayloadSchema, MenusResponseMessageSchema, MenusResponsePayloadSchema, MessageBubble, MessageList, MessageParticipantSchema, MessageSchema, NewChatHero, QuerySpecSchema3 as QuerySpecSchema, SDK_VERSION, Sidebar, SuperatomClient, UIComponentSchema3 as UIComponentSchema, UIElementSchema3 as UIElementSchema, UILogEntrySchema, UILogsMessageSchema, UILogsPayloadSchema, UserPromptAbortRequestMessageSchema, UserPromptAbortRequestPayloadSchema, UserPromptRequestMessageSchema, UserPromptRequestPayloadSchema, UserPromptResponseMessageSchema, UserPromptResponsePayloadSchema, UserPromptSuggestionsRequestMessageSchema, UserPromptSuggestionsRequestPayloadSchema, UserPromptSuggestionsResponseMessageSchema, UserPromptSuggestionsResponsePayloadSchema, getComponent, getComponentStates, getComponents, hasComponents, registerBundleComponents, resetAllFailedComponents, resetFailedComponent, resetRegistration, sanitizeDSL, services_exports as services, setup, useDynamicComponent, useSuperAtom, validateDSL, validateUIComponent, validateUIElement, withConditionalErrorBoundary };
71752
+ export { ActionsRequestMessageSchema, ActionsRequestPayloadSchema, ActionsResponseMessageSchema, ActionsResponsePayloadSchema, BindingSchema3 as BindingSchema, BundleChunkMessageSchema, BundleChunkPayloadSchema, BundleErrorMessageSchema, BundleErrorPayloadSchema, BundleRequestMessageSchema, BundleRequestPayloadSchema, COMP_REGISTRY, ChatInput, ClientConfigSchema, ComponentSchema, ComponentsSendMessageSchema, ComponentsSendPayloadSchema, ConfirmDeleteModal, DEFAULT_API_URL, DSLErrorBoundary, DSLRenderer, DSLRendererPropsSchema3 as DSLRendererPropsSchema, DataRequestMessageSchema, DataRequestPayloadSchema, DataResponseMessageSchema, DataResponsePayloadSchema, DynamicComponent, ExpressionSchema3 as ExpressionSchema, ForDirectiveSchema3 as ForDirectiveSchema, MenuDataSchema, MenuQueryFiltersSchema, MenuV1Renderer, MenuV1RendererCollapsed, MenusRequestMessageSchema, MenusRequestPayloadSchema, MenusResponseMessageSchema, MenusResponsePayloadSchema, MessageBubble, MessageList, MessageParticipantSchema, MessageSchema, NewChatHero, QuerySpecSchema3 as QuerySpecSchema, SDK_VERSION, SdkSession, Sidebar, SuperAtomAuthError, SuperatomClient, UIComponentSchema3 as UIComponentSchema, UIElementSchema3 as UIElementSchema, UILogEntrySchema, UILogsMessageSchema, UILogsPayloadSchema, UserPromptAbortRequestMessageSchema, UserPromptAbortRequestPayloadSchema, UserPromptRequestMessageSchema, UserPromptRequestPayloadSchema, UserPromptResponseMessageSchema, UserPromptResponsePayloadSchema, UserPromptSuggestionsRequestMessageSchema, UserPromptSuggestionsRequestPayloadSchema, UserPromptSuggestionsResponseMessageSchema, UserPromptSuggestionsResponsePayloadSchema, getComponent, getComponentStates, getComponents, hasComponents, registerBundleComponents, resetAllFailedComponents, resetFailedComponent, resetRegistration, sanitizeDSL, services_exports as services, setup, useDynamicComponent, useSuperAtom, validateDSL, validateUIComponent, validateUIElement, withConditionalErrorBoundary };
71130
71753
  //# sourceMappingURL=index.js.map
71131
71754
  //# sourceMappingURL=index.js.map