@yushaw/sanqian-chat 0.2.20 → 0.2.22

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.
@@ -81,7 +81,8 @@ __export(renderer_exports, {
81
81
  useResourcePicker: () => useResourcePicker,
82
82
  useStandaloneI18n: () => useStandaloneI18n,
83
83
  useStandaloneTheme: () => useStandaloneTheme,
84
- useTheme: () => useTheme
84
+ useTheme: () => useTheme,
85
+ useWindowDragLock: () => useWindowDragLock
85
86
  });
86
87
  module.exports = __toCommonJS(renderer_exports);
87
88
 
@@ -228,10 +229,12 @@ function useChat(options) {
228
229
  case "start": {
229
230
  currentRunIdRef.current = event.run_id;
230
231
  if (pendingCancelRef.current) {
231
- const pendingCancel = pendingCancelFnRef.current;
232
- clearPendingCancel();
233
- pendingCancel?.();
234
- cancelRef.current = null;
232
+ const pendingCancel = pendingCancelFnRef.current || cancelRef.current;
233
+ if (pendingCancel) {
234
+ clearPendingCancel();
235
+ pendingCancel();
236
+ cancelRef.current = null;
237
+ }
235
238
  }
236
239
  break;
237
240
  }
@@ -648,8 +651,11 @@ function useChat(options) {
648
651
  }
649
652
  }
650
653
  }, [clearPendingCancel, flushTypewriter, onConversationChange, onError, resetStreamBuffers]);
651
- const sendMessage = (0, import_react.useCallback)(async (content, sendOptions) => {
652
- if (!content.trim()) return;
654
+ const trySendMessage = (0, import_react.useCallback)(async (content, sendOptions) => {
655
+ const trimmedContent = content.trim();
656
+ const hasAttachedResources = (sendOptions?.attachedResources?.length ?? 0) > 0;
657
+ const hasSessionResources = sessionResources.length > 0;
658
+ if (!trimmedContent && !hasAttachedResources && !hasSessionResources) return false;
653
659
  setError(null);
654
660
  clearPendingCancel();
655
661
  suppressStreamRef.current = false;
@@ -662,7 +668,7 @@ function useChat(options) {
662
668
  const userMessage = {
663
669
  id: crypto.randomUUID(),
664
670
  role: "user",
665
- content: content.trim(),
671
+ content: trimmedContent,
666
672
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
667
673
  attachedResources: sendOptions?.attachedResources?.length ? sendOptions.attachedResources : void 0
668
674
  };
@@ -676,14 +682,16 @@ function useChat(options) {
676
682
  blocks: [],
677
683
  isComplete: false
678
684
  };
679
- setMessages((prev) => [...prev, userMessage, assistantMessage]);
685
+ const shouldRenderUserMessage = trimmedContent.length > 0 || hasAttachedResources;
686
+ setMessages(
687
+ (prev) => shouldRenderUserMessage ? [...prev, userMessage, assistantMessage] : [...prev, assistantMessage]
688
+ );
680
689
  setIsLoading(true);
681
690
  setIsStreaming(true);
682
691
  const sessionResourceIds = sessionResources.map((r) => r.fullId);
683
692
  const savedSessionResources = sessionResourceIds.length > 0 ? [...sessionResources] : null;
684
693
  try {
685
694
  if (!adapter.isConnected()) await adapter.connect();
686
- const trimmedContent = content.trim();
687
695
  const shouldSendHistory = !conversationIdRef.current;
688
696
  const historyMessages = shouldSendHistory ? messagesRef.current.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: m.content })) : [];
689
697
  const apiMessages = [...historyMessages, { role: "user", content: trimmedContent }];
@@ -704,6 +712,12 @@ function useChat(options) {
704
712
  }
705
713
  );
706
714
  cancelRef.current = cancel;
715
+ if (pendingCancelRef.current) {
716
+ clearPendingCancel();
717
+ cancel();
718
+ cancelRef.current = null;
719
+ }
720
+ return true;
707
721
  } catch (err) {
708
722
  const errorMessage = err instanceof Error ? err.message : "Failed to send message";
709
723
  setError(errorMessage);
@@ -720,8 +734,12 @@ function useChat(options) {
720
734
  }
721
735
  setIsLoading(false);
722
736
  setIsStreaming(false);
737
+ return false;
723
738
  }
724
739
  }, [adapter, clearPendingCancel, handleStreamEvent, onError, resetStreamBuffers, sessionResources]);
740
+ const sendMessage = (0, import_react.useCallback)(async (content, sendOptions) => {
741
+ await trySendMessage(content, sendOptions);
742
+ }, [trySendMessage]);
725
743
  const stopStreaming = (0, import_react.useCallback)(() => {
726
744
  const shouldDelayCancel = !currentRunIdRef.current;
727
745
  if (shouldDelayCancel) {
@@ -860,6 +878,7 @@ function useChat(options) {
860
878
  conversationTitle,
861
879
  pendingInterrupt,
862
880
  sendMessage,
881
+ trySendMessage,
863
882
  stopStreaming,
864
883
  clearMessages,
865
884
  setError,
@@ -1398,6 +1417,35 @@ var CSS_VARIABLES = `/**
1398
1417
  user-select: text;
1399
1418
  }
1400
1419
 
1420
+ /* ========================================
1421
+ Draggable Regions (Electron)
1422
+ ======================================== */
1423
+ .chat-drag-region {
1424
+ -webkit-app-region: drag;
1425
+ }
1426
+
1427
+ /*
1428
+ * Interactive descendants inside drag regions must explicitly opt out of drag,
1429
+ * otherwise icon-only click targets can become non-clickable in Electron.
1430
+ */
1431
+ .chat-drag-region button,
1432
+ .chat-drag-region button *,
1433
+ .chat-drag-region a,
1434
+ .chat-drag-region a *,
1435
+ .chat-drag-region input,
1436
+ .chat-drag-region textarea,
1437
+ .chat-drag-region select,
1438
+ .chat-drag-region [role="button"],
1439
+ .chat-drag-region [role="button"] * {
1440
+ -webkit-app-region: no-drag;
1441
+ }
1442
+
1443
+ /* Modal overlays should get click priority over drag regions. */
1444
+ :root[data-window-drag-locked="true"] .chat-drag-region {
1445
+ -webkit-app-region: no-drag !important;
1446
+ pointer-events: none !important;
1447
+ }
1448
+
1401
1449
  /* ========================================
1402
1450
  Font Size Scaling
1403
1451
  Applies zoom to scale entire chat UI including rem units.
@@ -1593,6 +1641,16 @@ code {
1593
1641
  animation: chat-cursor-breathing 1s ease-in-out infinite;
1594
1642
  }
1595
1643
 
1644
+ /* Streaming cursor - version-agnostic fallback (works across streamdown 1.x/2.x) */
1645
+ .chat-streaming-cursor {
1646
+ --streamdown-caret: '\u258C';
1647
+ }
1648
+ .chat-streaming-cursor > *:last-child::after {
1649
+ content: var(--streamdown-caret, '\u258C');
1650
+ color: var(--chat-accent) !important;
1651
+ animation: chat-cursor-breathing 1s ease-in-out infinite !important;
1652
+ }
1653
+
1596
1654
  @keyframes chat-fade-in {
1597
1655
  from {
1598
1656
  opacity: 0;
@@ -5132,6 +5190,8 @@ function useFocusPersistence({
5132
5190
  const container = containerRef.current;
5133
5191
  if (!container) return;
5134
5192
  const handleClick = (e) => {
5193
+ if (e.button !== 0 || e.ctrlKey) return;
5194
+ if (!(e.target instanceof Element)) return;
5135
5195
  const target = e.target;
5136
5196
  if (!container.contains(target)) return;
5137
5197
  if (target.closest('button, a, input, textarea, [contenteditable], [role="menu"], [role="dialog"], [role="listbox"]')) {
@@ -5486,6 +5546,50 @@ function useResourcePicker(options) {
5486
5546
  };
5487
5547
  }
5488
5548
 
5549
+ // src/renderer/hooks/useWindowDragLock.ts
5550
+ var import_react13 = require("react");
5551
+ var DRAG_LOCK_ATTR = "data-window-drag-locked";
5552
+ var DRAG_LOCK_STATE_KEY = "__sanqian_window_drag_lock_state__";
5553
+ function hasDocument() {
5554
+ return typeof document !== "undefined";
5555
+ }
5556
+ function getWindowDragLockState() {
5557
+ const globalRef = globalThis;
5558
+ if (!globalRef[DRAG_LOCK_STATE_KEY]) {
5559
+ globalRef[DRAG_LOCK_STATE_KEY] = { count: 0 };
5560
+ }
5561
+ return globalRef[DRAG_LOCK_STATE_KEY];
5562
+ }
5563
+ function applyDragLockState() {
5564
+ if (!hasDocument()) return;
5565
+ const root = document.documentElement;
5566
+ if (getWindowDragLockState().count > 0) {
5567
+ root.setAttribute(DRAG_LOCK_ATTR, "true");
5568
+ } else {
5569
+ root.removeAttribute(DRAG_LOCK_ATTR);
5570
+ }
5571
+ }
5572
+ function acquireWindowDragLock() {
5573
+ if (!hasDocument()) return () => {
5574
+ };
5575
+ const state = getWindowDragLockState();
5576
+ state.count += 1;
5577
+ applyDragLockState();
5578
+ let released = false;
5579
+ return () => {
5580
+ if (released) return;
5581
+ released = true;
5582
+ state.count = Math.max(0, state.count - 1);
5583
+ applyDragLockState();
5584
+ };
5585
+ }
5586
+ function useWindowDragLock(active) {
5587
+ (0, import_react13.useEffect)(() => {
5588
+ if (!active) return;
5589
+ return acquireWindowDragLock();
5590
+ }, [active]);
5591
+ }
5592
+
5489
5593
  // src/core/history.ts
5490
5594
  function safeParseArgs(value) {
5491
5595
  if (!value) return void 0;
@@ -5534,6 +5638,50 @@ function getNumericMessageId(message) {
5534
5638
  }
5535
5639
  return void 0;
5536
5640
  }
5641
+ function parseAttachedResourceRef(rawRef) {
5642
+ const segments = rawRef.split(":");
5643
+ if (segments.length < 2) return null;
5644
+ const providerId = segments.slice(0, -1).join(":").trim();
5645
+ const resourceId = segments[segments.length - 1].trim();
5646
+ if (!providerId || !resourceId) return null;
5647
+ return { providerId, resourceId };
5648
+ }
5649
+ function normalizeAttachedResources(raw) {
5650
+ if (!Array.isArray(raw)) return void 0;
5651
+ const resources = [];
5652
+ const seen = /* @__PURE__ */ new Set();
5653
+ for (const item of raw) {
5654
+ let providerId = "";
5655
+ let resourceId = "";
5656
+ let entry = null;
5657
+ if (typeof item === "string") {
5658
+ const parsed = parseAttachedResourceRef(item.trim());
5659
+ if (parsed) {
5660
+ providerId = parsed.providerId;
5661
+ resourceId = parsed.resourceId;
5662
+ }
5663
+ } else if (item && typeof item === "object") {
5664
+ entry = item;
5665
+ const providerRaw = typeof entry.providerId === "string" ? entry.providerId : typeof entry.provider_id === "string" ? entry.provider_id : "";
5666
+ const resourceRaw = typeof entry.resourceId === "string" ? entry.resourceId : typeof entry.resource_id === "string" ? entry.resource_id : "";
5667
+ providerId = providerRaw.trim();
5668
+ resourceId = resourceRaw.trim();
5669
+ }
5670
+ if (!providerId || !resourceId) continue;
5671
+ const dedupeKey = `${providerId}\0${resourceId}`;
5672
+ if (seen.has(dedupeKey)) continue;
5673
+ seen.add(dedupeKey);
5674
+ resources.push({
5675
+ providerId,
5676
+ resourceId,
5677
+ title: entry && typeof entry.title === "string" && entry.title.trim().length > 0 ? entry.title.trim() : resourceId,
5678
+ summary: entry && typeof entry.summary === "string" && entry.summary.trim().length > 0 ? entry.summary.trim() : void 0,
5679
+ type: entry && typeof entry.type === "string" && entry.type.trim().length > 0 ? entry.type.trim() : void 0,
5680
+ icon: entry && typeof entry.icon === "string" && entry.icon.trim().length > 0 ? entry.icon.trim() : void 0
5681
+ });
5682
+ }
5683
+ return resources.length > 0 ? resources : void 0;
5684
+ }
5537
5685
  function mergeConsecutiveAssistantMessages(rawMessages) {
5538
5686
  const result = [];
5539
5687
  let i = 0;
@@ -5703,7 +5851,8 @@ function mergeConsecutiveAssistantMessages(rawMessages) {
5703
5851
  timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
5704
5852
  toolCalls: parseToolCalls(msg.toolCalls || msg.tool_calls),
5705
5853
  thinking: msg.thinking || void 0,
5706
- filePaths: msg.filePaths
5854
+ filePaths: msg.filePaths,
5855
+ attachedResources: normalizeAttachedResources(msg.attachedResources || msg.attached_resources)
5707
5856
  });
5708
5857
  i++;
5709
5858
  }
@@ -6387,38 +6536,38 @@ function createSdkAdapter(config) {
6387
6536
  var import_browser = require("@yushaw/sanqian-sdk/browser");
6388
6537
 
6389
6538
  // src/renderer/components/MessageList.tsx
6390
- var import_react13 = require("react");
6539
+ var import_react14 = require("react");
6391
6540
  var import_jsx_runtime3 = require("react/jsx-runtime");
6392
6541
  var SCROLL_THRESHOLD = 100;
6393
- var MessageList = (0, import_react13.memo)(function MessageList2({
6542
+ var MessageList = (0, import_react14.memo)(function MessageList2({
6394
6543
  messages,
6395
6544
  className = "",
6396
6545
  renderMessage,
6397
6546
  autoScroll = true,
6398
6547
  scrollBehavior = "smooth"
6399
6548
  }) {
6400
- const containerRef = (0, import_react13.useRef)(null);
6401
- const isNearBottomRef = (0, import_react13.useRef)(true);
6402
- const checkIfNearBottom = (0, import_react13.useCallback)(() => {
6549
+ const containerRef = (0, import_react14.useRef)(null);
6550
+ const isNearBottomRef = (0, import_react14.useRef)(true);
6551
+ const checkIfNearBottom = (0, import_react14.useCallback)(() => {
6403
6552
  const container = containerRef.current;
6404
6553
  if (!container) return true;
6405
6554
  return container.scrollTop <= SCROLL_THRESHOLD;
6406
6555
  }, []);
6407
- const handleScroll = (0, import_react13.useCallback)(() => {
6556
+ const handleScroll = (0, import_react14.useCallback)(() => {
6408
6557
  isNearBottomRef.current = checkIfNearBottom();
6409
6558
  }, [checkIfNearBottom]);
6410
- const scrollToBottom = (0, import_react13.useCallback)(
6559
+ const scrollToBottom = (0, import_react14.useCallback)(
6411
6560
  (behavior = scrollBehavior) => {
6412
6561
  containerRef.current?.scrollTo({ top: 0, behavior });
6413
6562
  },
6414
6563
  [scrollBehavior]
6415
6564
  );
6416
- (0, import_react13.useEffect)(() => {
6565
+ (0, import_react14.useEffect)(() => {
6417
6566
  if (autoScroll && isNearBottomRef.current) {
6418
6567
  scrollToBottom();
6419
6568
  }
6420
6569
  }, [messages, autoScroll, scrollToBottom]);
6421
- (0, import_react13.useEffect)(() => {
6570
+ (0, import_react14.useEffect)(() => {
6422
6571
  scrollToBottom("instant");
6423
6572
  isNearBottomRef.current = true;
6424
6573
  }, [scrollToBottom]);
@@ -6436,9 +6585,9 @@ var MessageList = (0, import_react13.memo)(function MessageList2({
6436
6585
  });
6437
6586
 
6438
6587
  // src/renderer/components/MessageBubble.tsx
6439
- var import_react14 = require("react");
6588
+ var import_react15 = require("react");
6440
6589
  var import_jsx_runtime4 = require("react/jsx-runtime");
6441
- var MessageBubble = (0, import_react14.memo)(function MessageBubble2({
6590
+ var MessageBubble = (0, import_react15.memo)(function MessageBubble2({
6442
6591
  message,
6443
6592
  className = "",
6444
6593
  children,
@@ -6446,29 +6595,26 @@ var MessageBubble = (0, import_react14.memo)(function MessageBubble2({
6446
6595
  }) {
6447
6596
  const isStreaming = message.isStreaming ?? false;
6448
6597
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, "data-role": message.role, "data-streaming": isStreaming, children: [
6449
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "message-content", children: renderContent ? renderContent(message.content, isStreaming) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
6450
- message.content,
6451
- isStreaming && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "streaming-cursor", children: "\u258C" })
6452
- ] }) }),
6598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "message-content", children: renderContent ? renderContent(message.content, isStreaming) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: message.content }) }),
6453
6599
  children
6454
6600
  ] });
6455
6601
  });
6456
6602
 
6457
6603
  // src/renderer/components/SanqianChat.tsx
6458
- var import_react28 = require("react");
6604
+ var import_react29 = require("react");
6459
6605
 
6460
6606
  // src/renderer/utils/chatConfig.ts
6461
- var import_react15 = require("react");
6607
+ var import_react16 = require("react");
6462
6608
  var getSystemTheme = () => {
6463
6609
  if (typeof window === "undefined") return "light";
6464
6610
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
6465
6611
  };
6466
6612
  function useResolvedTheme(mode = "auto") {
6467
- const [resolvedTheme, setResolvedTheme] = (0, import_react15.useState)(() => {
6613
+ const [resolvedTheme, setResolvedTheme] = (0, import_react16.useState)(() => {
6468
6614
  if (mode === "auto") return getSystemTheme();
6469
6615
  return mode === "dark" ? "dark" : "light";
6470
6616
  });
6471
- (0, import_react15.useEffect)(() => {
6617
+ (0, import_react16.useEffect)(() => {
6472
6618
  if (mode !== "auto") {
6473
6619
  setResolvedTheme(mode === "dark" ? "dark" : "light");
6474
6620
  return;
@@ -6530,7 +6676,7 @@ function getBaseColors(isDark) {
6530
6676
  };
6531
6677
  }
6532
6678
  function useAccentStyle(accentColor) {
6533
- return (0, import_react15.useMemo)(() => {
6679
+ return (0, import_react16.useMemo)(() => {
6534
6680
  if (!accentColor) return void 0;
6535
6681
  const rgb = parseColorToRgb(accentColor);
6536
6682
  if (!rgb) {
@@ -6572,7 +6718,7 @@ function resolveOnPin(onPin) {
6572
6718
  }
6573
6719
 
6574
6720
  // src/renderer/utils/useChatHeader.tsx
6575
- var import_react16 = require("react");
6721
+ var import_react17 = require("react");
6576
6722
 
6577
6723
  // src/renderer/assets/sanqianLogo.ts
6578
6724
  var SANQIAN_LOGO_DATA_URI = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MTUuODcgNDI4LjIiPgogIDxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+CiAgICA8ZyBpZD0iQzhvaUxXIj4KICAgICAgPGc+CiAgICAgICAgPHBhdGggZD0iTTQwNC45LDE1OC4wM2wxLjk4LTEuMDFjLS4zMSw2LjM1LDEuMDQsMTIuNTYsMS43NCwxOC44Mi41Myw0LjcyLDEuNjIsOC41NywxLjI2LDE0LjY4LTEuMDcsMTguNDEtNy4wOCwzNS45Ni0xMS45OCw1My41bDcuOTUtMTYuNTRjMy43MS0xMi4wOCw2Ljc4LTI0LjI0LDguMDUtMzYuODdsMS45Ny0uMWMtMS43MSw0LjkxLTEuNDQsMTAuNjYtMi4yNiwxNS43NC00LjY3LDI5LjA1LTE3LjgzLDU4LjI3LTM1LjMxLDgxLjY5LTguNzYsMTEuNzMtMTkuNzgsMjEuMzUtMjguNDQsMzIuNTYtMi45OSwzLjg4LTEyLjM4LDcuOTktMTYuMjMsMTAuNTUtMi4wOCwxLjM4LTQuOTQsNS44Ny02LjQ4LDcuMDEtNiw0LjQ1LTE3LjE5LDUuNDMtMjIuMjYsMTEuNDcsMS4xLDEuMTksMy42OS0uNTQsNC45NS0xLjA1LDE1LjQ0LTYuMiwyOC45NC0xMy45Niw0Mi42MS0yMy40LDQuNjMsMS4wNi0uODQsMi4zMS0xLjg5LDMuMDgtNDEuOTYsMzAuNTUtOTAuNDYsNDYuMy0xNDEuOTQsMjcuNjQtMTkuMzEtNy0yNi4yMS0xOC45Ny00MC41NS0yNi40NS0xMy4xLTYuODMtMTUuOTktMTEuNTctMjUuMTgtMjIuNTQtMi45My0zLjUtMTAuNzEtMTEuNDctMTIuNDItMTQuODYtLjU2LTEuMTEtNi4yLTIwLjQ4LTYuMjEtMjEuNi0uMDQtNC4zNSwzLjE0LTkuOTcsMy42My0xNC44NC44LTcuOTMtMi45Ny0xOC45MSwzLTI0LjQ5LjY3LDMuMTQuNzgsNi41NSwxLjkxLDkuNjEuNTEsMS4zNy0uMzYsMS43LDIuMDksMS40LDEuOTQtNC4yNi0uMzYtOS45OS4xNy0xMy4yNSwxLjIxLTcuNDcsOC4zLTIxLjg0LDEzLjMzLTI3Ljc2LDUuMzUtNi4yOSwxNC4xMy0xNC4wOSwyMi41MS0xNC40OWwtMTAsOS40OSw2Ljk5LTEuNWMtMy4wNCw0LjgxLTcuNDUsOC43My0xMC40MSwxMy41OS01LjA2LDguMjktNS44NiwxNy44MS02LjU5LDI3LjQzbDQuMDIsMTkuNDljMi4wOC0uOTQuNTUtNS4wNSwxLjItNy4yOS4yNC0uODMsMi4yNi0zLjA5LDIuNzgtMi43LDMuMiwxNC4wNiwxMC40NSwyNC41NSwyMC40MiwzNC42MSwyNS4xLDI1LjM1LDY0LjA0LDMyLjc4LDk3LjI3LDE5LjU2LDguMDMtMy4xOSwxNS43LTguOTYsMjMuMzItMTIuMTdsLTIsMi45OGgxLjQ5YzE3LjE1LTEyLjY2LDMzLjYzLTI3LjExLDQyLjUzLTQ2Ljk5bDIsLjUxLS4wMyw1LjQ4LDguMy0xNi4yLDkuNy0yNy43OWMuNDUsNy41OS0yLjYxLDE0LjY3LTQuNDQsMjEuODYtLjkyLDMuNjItLjQ1LDUuNzgtMi4zOSwxMC44LTYuNTksMTcuMDgtMTkuMTgsMzMuMzEtMzIuMTYsNDUuODVsMy45OC0uNTFjLS4wMy40Ny4xNywxLjA1LDAsMS40OS0xLjg3LDQuNzItMjQuMzMsMjAuMTQtMjkuNzEsMjMuMy0xMC4zMSw2LjA1LTIxLjY3LDExLjEtMzMuMjcsMTQuMjMtLjE2LDEuODUsMy4yNi45OCw0LjM3Ljg2LDM2LjgtNC4wMiw3NS4yOC0yOS43NCw5NS40NS02MC41NSwxMS40MS0xNy40MywxNi42Ni0zMy4yNCwyMS44Ny01My4xMy44OC0zLjM2LDMuMzUtNi4wOSwyLjMyLTEwLjE5LDEuNjctLjI2LDEuMDcsMi40MSwxLjA0LDMuNTMtLjA5LDIuODUtLjkyLDUuMjMtMS4wOSw3LjkzLS4wNywxLjA4LS42NCwzLjc5LDEuMDMsMy41Mmw0Ljk2LTE5LjU0YzEuNTYtMTcuMzEsMy41NC0zMi4yOS43NS00OS42Ni0xLjAxLTYuMy00LjA2LTEzLjMyLTQuNzItMTkuMy0uMjQtMi4xNy40LTIuNjQsMS4yNS0uMjQuNzcsMi4xNywxLjE5LDQuNTIsMS43Nyw2Ljc1LDEuNzEuMjcsMS4wNy0yLjQuOTEtMy40LS42LTMuNTctMy4xLTEwLjk4LTQuNDQtMTQuNTctMy43OS0xMC4yMS0xMC4zMS0xOS42My0xMy40Ny0zMC4wMSwxLjU0Ljk1LDMuMTksMy43MSw0LjE5LDUuMywxMy4xMywyMC45OCwyMC42OSw0NC4zMiwyNC44Miw2OC43WiIvPgogICAgICAgIDxnPgogICAgICAgICAgPHBhdGggZD0iTTIyMC44OSw0MDYuMDJjLS4yNiwxLjMzLDEuODMsMS42LDIuNzQsMS43NSwzMi4xLDUuMjksNTguMjgsMS41MSw4OC4yNC0xMS4yNmwyOS4wMS0xNS40OGMtNC43Nyw1LjEyLTEyLjEyLDkuNjQtMTguMzMsMTMuMTYtNy4wNCwzLjk5LTE5LjI3LDcuNzItMjQuNjgsMTEuMzItLjQxLjI3LTEuMzIsMS4yNC0uOTksMS41aDUuOTljLTYuNjcsMS40Ny0xMy40OCw0LjgzLTE5Ljk3LDYuNTQtNS4xMywxLjM1LTE3LjY2LDIuMzctMjEuMDQsMy45Ni0uNDYuMjEtMS4yNiwxLjMtLjk4LDEuNTFsNC45OSwxYy0yOC4zLDUuNTktNTcuMTQtLjQ4LTg0LjQ4LTguMDEtMS42LDEuMDUuNzUsMS4xOCwxLjQ1LDEuNTMsMTMuMzMsNi42OSwyOS4yMiwxMC4wNyw0NC4wNCwxMS40OWwtMS4wMSwxLjk4LDUsLjAyYy4yNSwxLjcyLTIuMzcsMS4wNi0zLjQ4Ljk4LTMxLjc4LTIuNDEtNjcuMzktMTYuNTMtOTMuNDMtMzQuNTgtMTcuNDYtMTIuMS0zMS4xNS0yNy4wMy00NS41NC00MS40NWwtMjAuNTMtMzEuOTYtMSwzLjk5Yy0uMTItLjcxLTEuOTktOS4yNC00LjAxLTYuOTguNjksMi43NSwyLjYzLDUuNDQsMy41NCw3Ljk1LjM0Ljk0LDEuMzIsMy4yNS0uNTMsMy4wMy0xMC42OC0yMS44NS0xNy44My00NS45NS0xOS4wNS03MC40NS0uNDItOC40OSwzLjMtNDEuNCw4LjE2LTQ3LjQyLjE3LS4yMSwzLjA2LDEuMTgsMy44Ny0yLjY1bC0xLjk4LS40N2MtLjg1LTUuMDQsMy42NC02LjUyLDUuNjYtOS44MnM5LjM3LTE2LjcsMTAuMzEtMTkuNzFjLjE2LS41MS4yNy0xLjYzLS40OC0xLjQ4LTIuNzUsMi4xOC0zLjgzLDUuNTktNS42Niw4LjM0LS42OCwxLjAyLTIuMDIsMy4xMi0xLjgzLjE4LDExLjc1LTI0LjQ3LDI4LjEzLTQyLjQxLDUwLjY1LTU3LjM1LDUuOTYtMy45NSwxMi44Mi02LjUsMTguMzMtMTEuMTYtOS43My41LTIyLjE4LDQuMzItMzAuOTksOC41LTMuMywxLjU3LTguNTIsNi4wOC0xMS4wNyw2Ljk2LTEuNjQuNTctMy4wOC0uNi01LjIxLjUtMS44LjkzLTEwLjY0LDguNi0xMi43NCwxMC41MS0yNC43OSwyMi41Ni00MS4yNiw1Ny41MS00Niw5MC41MS0xLjYxLjA1LTIuMDUtMy41My0yLjA0LTQuNTMuMDUtNC4wOSwxLjMtMTAuMjksMi4wNS0xNC40NmwtMy44OS4xYy0yLjIsMTEuOTYtNS43OSwyMy41OC01LjEyLDM1Ljg4bC0yLTYuOTljLTEuNDctLjA5LTEuNzYsMy41OS0xLjg3LDQuNjEtMi40NywyMy44NSw2LjI4LDU4LjIxLDE1LjQsODAuMzYsMS4wMiwyLjQ5LDkuMTQsMTcuMjEsOC40OCwxOC4wMS0xLjU5LDEuOTItMi4xNC0xLjQ5LTIuNDctMS45OS0xMy4zNC0yMC4yMi0yMS42Mi00My4xLTI1LjU0LTY3LjAxbC0xLjk4LDEuMDFjLTIuMzYtMTUuMTEtNC45NS0yOS4wNy00LjA1LTQ0LjU0LjMyLTUuNDQsMi42Mi0xMC44NywyLjAzLTE2LjQ2bC0zLjk4LDE2LjUxYy0uOTUsMTAuMzEtLjU5LDIwLjY4LTEuMDEsMzEtLjA1LDEuMjEuNzYsMy43Mi0uOTksMy40OC0yLjAxLTE4LjI0LS41NC0zNi4zMSwzLjI4LTU0LjIsNS42OS0yNi42LDE4LjI4LTU0LjI3LDM2LjE5LTc0LjgxLDEyLjUzLTE0LjM3LDI4LjEyLTI3Ljk5LDQyLjg4LTM4LjY4LDIuMDktMS41MSw1LjA0LTMuOCw3LjMxLTUuMTMsOC4xLTQuNzIsMTcuODQtNS45OSwyNS4zMS0xMS42OC0uNTgtMS4xMS0zLjA2LS4zNi00LjA2LS4wNi00LjkzLDEuNDUtMTIuMTUsNC4wNy0xNi45Miw2LjA4LTUuOTIsMi41LTExLjU0LDUuNzEtMTcsOS4wNmwtMy4wMS0xLjU2YzcuMzEtMy4yNywxMy41My03LjI4LDIxLjAzLTEwLjQ4LDQxLjYzLTE3LjcyLDEwNS4zMi0yMCwxMzcuMzYsMTYuNTMsNy40NSw4LjQ5LDE5LjI1LDIyLjU4LDI1LjA1LDMwLjk5LDEuODgsMi43MiwyLjc5LDUuNzQsMS41NSw4Ljk0aDMuMDJjLS41OC45NC0uOTksNi41Ni0uOCw2Ljg0LjQ0LjY1LDIuOTcuNjQsMy4zOCwyLjY0LjI5LDEuNDMsMS40OSwxNC43LDEuNDMsMTUuOTUtLjAzLjYtMS44MS40Ny0yLjAxLDEuMjUtLjg0LDMuMjcuNDcsMTIuNTEtLjA3LDE2LjY4LS45NSw3LjM0LTYuMTksMTAuMTItOS44MywxNS4yNy0xLjI4LDEuODEtMS45OCw2LjU3LTQuMTUsNy44NC0xLjc3LDEuMDQtNC45NS42NC03LjE4LDEuNjEtMi4zNCwxLjAyLTMuNTEsNi42Ni03Ljc4LDQuOTNsMi45OS0xMWMtMy4zOSw0LjA2LTEzLjU2LDEwLjAzLTEwLjIxLjI4LDEuMzEtMy44MS44NC0zLjI4LDEuNTEtNi41NC44MS0zLjkyLDMuNzktMTIuOTIsMy43MS0xNi4xNi0uMDEtLjQyLTEuOTktLjk4LTIuNy0yLjM2LTQuMDItNy44Mi02LjM1LTE1LjE3LTEyLjY2LTIxLjg3LTExLjMtMTItNDIuMzMtMTguMi01Ny44OS0xNC4xLTE2Ljg4LDQuNDUtNDQuNjUsMjYuNDQtNTEuMyw0Mi43LTgsMTkuNTgtMTUuMTcsMzkuNjctMTIuMyw2MS4zOCw0LjA5LDMwLjg2LDE2LjE2LDUzLjYsMzMuNCw3OC42LDEuNTMsMi4yMiw0LjIxLDMuNTksNS40NCw2LjA1LTMuNzktMS4zNy03Ljc1LTUuOTQtMTAuNTEtOC45OC0xMC45My0xMS45OS0xOS44OS0yNi4yNS0yNi41LTQxLjAxLTMuMDMsMS40LDMuMjksNi43Mi0uOTcsNy45OWwtNy4wMy0xNy45OS0xLjk4LDEuOTljLS40Mi0yLjYtMS4wOC04LjIzLTQuMDItOC40OCw0LjMsMjguMjgsMTIuOTgsNTIuNjYsMzEuMDQsNzQuOTYsMjcuNDgsMzMuOTQsNzIuOTMsNjEuMzIsMTE3LjM1LDYyLjY1bDguNjIsMS44OGgtMjFaIi8+CiAgICAgICAgICA8cGF0aCBkPSJNMjg4LjQ4LDQxLjQzYzE3LDEzLjA0LDI2LjQ2LDI0LjkxLDM2LjU4LDQzLjkyLDE1Ljc5LDI5LjY4LDIyLjc5LDYyLjM3LDE1LjgyLDk1LjY3LTEuNjcuMjYtMS4wOS0yLjQzLTEuMDMtMy41My4yLTMuNDksMS42NC02Ljg3LDEuMDMtMTAuNDdsLTEuNTEuOThjLTIuOCwyMS43Mi0xMC41OSw0NS44NC0yMy4wNSw2My45NS01LjY4LDguMjYtOS44NSwxMC41NS0xNi45NSwxNi4wNS0yLjk1LDIuMjktNi4xOSw4LjE3LTkuNDgsNy41MiwxLjA3LTEuODMsNS44NS0zLjk1LDQuNDktNi41MWwtMTAuNDksNyw4Ljk5LTguNS0xLjQ4LS40OWMtMTguNjIsMTcuMTktNDguNjUsMjkuNDYtNzMuNTEsMTkuNDQtMS42LS42NS0yLjQ1LjEtMS45OS0yLjQ1LDEzLjc2LDYuMTcsMjcuODEsMi4wMyw0MC45Ny0zLjUyLDM2LjAyLTE1LjE5LDU3LjE2LTUzLjQ4LDU2LjkyLTkyLjA5LS4wMi0zLjE5LS41Mi0xMS43LTIuODktMTMuMzksMS44OSwxMC0uMywyMi40Ny0zLjMxLDMyLjIxLS4zMiwxLjA1LS4yNiwzLjAzLTEuNjksMi43OSw5Ljc1LTUxLjA4LTIwLjUyLTEwMC41NC02My43MS0xMjUuOC02LjQ1LTMuNzctMTQuMzUtNS42Ni0xOS41Ni0xMC40OWwtMTguNzQtNi43YzIuMDktLjA3LDQuMTgtLjE0LDYuMjYuMjMsMjAuOTcsMy43NywzOS44NCwxNC4zNiw1OC40NCwyMy41NSwxLjczLjg1LDMuNDUsMS44Myw1LjA0LDIuOSwyLjgyLDEuODgsOC42OSw5LjgxLDkuNzcsMTAuMy40My4yLDEuNjQuMjUsMS40OC0uNDktMzEuNzMtMzkuNzgtODMuNTMtNTguNjMtMTMzLjk5LTUzLjUybDcuOTktMi45OGMtMTguMjUuMjEtMzcuNDcsNC40MS01NC41MiwxMC45Ny0yLjg2LjI4LS44Ny0xLjI5LjE2LTEuODUsMS4yMS0uNjUsMTQuNzItNS4xMiwxMy4zNy02LjEyLTE2LjY3LDMuNjUtMzMuMTIsMTEuNy00Ny41MiwyMC45Ny05LjE1LDUuODktMTUuODIsMTIuMTMtMjMuOTcsMTkuMDItMS4xMi45NS0xLjYzLDIuNDItMy41LDEuOTksMy42My0zLjQyLDQuMzEtNy41Myw3LjgyLTEwLjksNS40Mi01LjIyLDE2LjA3LTEzLjEzLDIyLjQ0LTE3LjM5LDMyLjA3LTIxLjQ1LDY5LjEzLTMxLjM4LDEwNy43NC0zMC43MS0uNTMtMS41NS01LjAxLTEuNzktNi41MS0xLjk4LTMuOTEtLjQ5LTEyLjY5LjQ4LTE0LjgyLjAxLS43My0uMTYtLjYtMS45Mi0xLjMzLTIuMDYtMi45OC0uNTMtMTAuNzgsMS43LTE0LjMzLDEuMDItLjI0LTEuMzcsMS43OC0xLjUxLDIuNzUtMS43Miw2LjU5LTEuNDQsMTMuOTgtMS4xLDIwLjcyLTIuMzEsMS43Ny0uMzIsMi45OSwxLjEsMi41Mi0xLjk1bC0zOC45OSwzLjk4LDE2Ljk5LTQtNC45OS0xLjk4YzUyLjEyLTcuOTIsMTA0LjI4LDcuNzMsMTQ1LjU5LDM5LjRaIi8+CiAgICAgICAgICA8cGF0aCBkPSJNODAuODksMzQ2LjAxYy00LjUyLjUxLTMuNDQtMi45NC00LjEtMy44Ni0uMTItLjE2LTEuNDMuNTMtMS45MS4yNC0zLjE0LTEuODktMy4yMi02LjEzLTMuMDEtOS4zN2wtMS45OCwxLjk5LTIuMDItMy41MWMxLjk1LS44LDIuMjktMS4yMSwyLjAxLTMuNDhsMTAuOTksMTcuOThaIi8+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4=";
@@ -6615,11 +6761,11 @@ var resolveLogoNode = (logo, size, alt = SANQIAN_LOGO_ALT) => {
6615
6761
 
6616
6762
  // src/renderer/utils/useChatHeader.tsx
6617
6763
  var useChatHeader = (config) => {
6618
- const resolvedOnClose = (0, import_react16.useMemo)(() => resolveOnClose(config?.onClose), [config?.onClose]);
6619
- const resolvedOnPin = (0, import_react16.useMemo)(() => resolveOnPin(config?.onPin), [config?.onPin]);
6620
- const [isPinned, setIsPinned] = (0, import_react16.useState)(config?.alwaysOnTop ?? false);
6621
- const isFirstRender = (0, import_react16.useRef)(true);
6622
- (0, import_react16.useEffect)(() => {
6764
+ const resolvedOnClose = (0, import_react17.useMemo)(() => resolveOnClose(config?.onClose), [config?.onClose]);
6765
+ const resolvedOnPin = (0, import_react17.useMemo)(() => resolveOnPin(config?.onPin), [config?.onPin]);
6766
+ const [isPinned, setIsPinned] = (0, import_react17.useState)(config?.alwaysOnTop ?? false);
6767
+ const isFirstRender = (0, import_react17.useRef)(true);
6768
+ (0, import_react17.useEffect)(() => {
6623
6769
  if (isFirstRender.current) {
6624
6770
  isFirstRender.current = false;
6625
6771
  return;
@@ -6631,8 +6777,8 @@ var useChatHeader = (config) => {
6631
6777
  }, [config?.alwaysOnTop, resolvedOnPin]);
6632
6778
  const showPin = !!resolvedOnPin || typeof config?.alwaysOnTop === "boolean";
6633
6779
  const showClose = !!resolvedOnClose;
6634
- const logoNode = (0, import_react16.useMemo)(() => resolveLogoNode(config?.logo, "header"), [config?.logo]);
6635
- const togglePin = (0, import_react16.useCallback)(() => {
6780
+ const logoNode = (0, import_react17.useMemo)(() => resolveLogoNode(config?.logo, "header"), [config?.logo]);
6781
+ const togglePin = (0, import_react17.useCallback)(() => {
6636
6782
  setIsPinned((prev) => {
6637
6783
  const nextPinned = !prev;
6638
6784
  resolvedOnPin?.(nextPinned);
@@ -6650,14 +6796,14 @@ var useChatHeader = (config) => {
6650
6796
  };
6651
6797
 
6652
6798
  // src/renderer/components/SanqianMessageList.tsx
6653
- var import_react21 = require("react");
6799
+ var import_react22 = require("react");
6654
6800
  var import_react_virtuoso = require("react-virtuoso");
6655
6801
 
6656
6802
  // src/renderer/components/SanqianChatMessage.tsx
6657
- var import_react20 = require("react");
6803
+ var import_react21 = require("react");
6658
6804
 
6659
6805
  // src/renderer/renderers/MarkdownRenderer.tsx
6660
- var import_react17 = require("react");
6806
+ var import_react18 = require("react");
6661
6807
  var import_streamdown = require("streamdown");
6662
6808
  var import_rehype_harden = require("rehype-harden");
6663
6809
  var import_remark_gfm = __toESM(require("remark-gfm"));
@@ -6671,7 +6817,7 @@ function safeParseUrl(href) {
6671
6817
  return void 0;
6672
6818
  }
6673
6819
  }
6674
- var MarkdownRenderer = (0, import_react17.memo)(function MarkdownRenderer2({
6820
+ var MarkdownRenderer = (0, import_react18.memo)(function MarkdownRenderer2({
6675
6821
  content,
6676
6822
  isStreaming = false,
6677
6823
  className = "",
@@ -6679,11 +6825,11 @@ var MarkdownRenderer = (0, import_react17.memo)(function MarkdownRenderer2({
6679
6825
  onLinkClick,
6680
6826
  linkHandler
6681
6827
  }) {
6682
- const allowedProtocols = (0, import_react17.useMemo)(() => {
6828
+ const allowedProtocols = (0, import_react18.useMemo)(() => {
6683
6829
  const custom = linkHandler?.allowedProtocols || [];
6684
6830
  return [.../* @__PURE__ */ new Set([...DEFAULT_PROTOCOLS, ...custom])];
6685
6831
  }, [linkHandler?.allowedProtocols]);
6686
- const rehypePlugins = (0, import_react17.useMemo)(() => {
6832
+ const rehypePlugins = (0, import_react18.useMemo)(() => {
6687
6833
  const origin = typeof window !== "undefined" ? window.location.origin : "http://localhost";
6688
6834
  return [
6689
6835
  [import_rehype_harden.harden, {
@@ -6697,7 +6843,7 @@ var MarkdownRenderer = (0, import_react17.memo)(function MarkdownRenderer2({
6697
6843
  import_streamdown.defaultRehypePlugins.katex
6698
6844
  ];
6699
6845
  }, [allowedProtocols]);
6700
- const handleLinkClick = (0, import_react17.useCallback)((href, event) => {
6846
+ const handleLinkClick = (0, import_react18.useCallback)((href, event) => {
6701
6847
  if (linkHandler?.onLinkClick) {
6702
6848
  const linkEvent = {
6703
6849
  href,
@@ -6715,19 +6861,12 @@ var MarkdownRenderer = (0, import_react17.memo)(function MarkdownRenderer2({
6715
6861
  onLinkClick(href, event);
6716
6862
  }
6717
6863
  }, [linkHandler, onLinkClick]);
6718
- const remarkPlugins = (0, import_react17.useMemo)(() => {
6864
+ const remarkPlugins = (0, import_react18.useMemo)(() => {
6719
6865
  return [import_remark_gfm.default, import_streamdown.defaultRemarkPlugins.math];
6720
6866
  }, []);
6721
- const customComponents = (0, import_react17.useMemo)(() => {
6867
+ const customComponents = (0, import_react18.useMemo)(() => {
6722
6868
  const comps = {};
6723
6869
  comps.p = ({ children }) => {
6724
- if (isStreaming && children && typeof children === "string" && children.endsWith("\u258C")) {
6725
- const textWithoutCursor = children.slice(0, -1);
6726
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
6727
- textWithoutCursor,
6728
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: "var(--chat-accent)" }, className: "chat-cursor-breathing", children: "\u258C" })
6729
- ] });
6730
- }
6731
6870
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children });
6732
6871
  };
6733
6872
  const hasLinkHandler = onLinkClick || linkHandler?.onLinkClick || components?.a;
@@ -6770,15 +6909,27 @@ var MarkdownRenderer = (0, import_react17.memo)(function MarkdownRenderer2({
6770
6909
  }
6771
6910
  return comps;
6772
6911
  }, [components, isStreaming, handleLinkClick, onLinkClick, linkHandler]);
6773
- const displayContent = isStreaming ? content.replace(/\n+$/, "") + "\u258C" : content;
6774
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: `${PROSE_CLASSES}${className ? ` ${className}` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_streamdown.Streamdown, { remarkPlugins, rehypePlugins, components: customComponents, children: displayContent }) });
6912
+ const displayContent = isStreaming ? content.replace(/\n+$/, "") : content;
6913
+ const streamClassName = isStreaming ? "chat-streaming-cursor" : void 0;
6914
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: `${PROSE_CLASSES}${className ? ` ${className}` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
6915
+ import_streamdown.Streamdown,
6916
+ {
6917
+ remarkPlugins,
6918
+ rehypePlugins,
6919
+ components: customComponents,
6920
+ mode: isStreaming ? "streaming" : "static",
6921
+ isAnimating: isStreaming,
6922
+ className: streamClassName,
6923
+ children: displayContent
6924
+ }
6925
+ ) });
6775
6926
  });
6776
6927
 
6777
6928
  // src/renderer/components/IntermediateSteps.tsx
6778
- var import_react19 = require("react");
6929
+ var import_react20 = require("react");
6779
6930
 
6780
6931
  // src/renderer/renderers/ToolArgumentsDisplay.tsx
6781
- var import_react18 = require("react");
6932
+ var import_react19 = require("react");
6782
6933
  var import_jsx_runtime7 = require("react/jsx-runtime");
6783
6934
  function formatValue(value, indent = 0) {
6784
6935
  const indentStr = " ".repeat(indent);
@@ -6838,7 +6989,7 @@ function formatValue(value, indent = 0) {
6838
6989
  }
6839
6990
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[var(--chat-text)]", children: String(value) });
6840
6991
  }
6841
- var ToolArgumentsDisplay = (0, import_react18.memo)(function ToolArgumentsDisplay2({
6992
+ var ToolArgumentsDisplay = (0, import_react19.memo)(function ToolArgumentsDisplay2({
6842
6993
  args,
6843
6994
  className = ""
6844
6995
  }) {
@@ -6904,7 +7055,7 @@ function ExpandableText({
6904
7055
  style,
6905
7056
  maxLines = 3
6906
7057
  }) {
6907
- const [isExpanded, setIsExpanded] = (0, import_react19.useState)(false);
7058
+ const [isExpanded, setIsExpanded] = (0, import_react20.useState)(false);
6908
7059
  const trimmed = content.trim();
6909
7060
  const lines = trimmed.split("\n");
6910
7061
  const needsExpand = lines.length > maxLines;
@@ -6925,7 +7076,7 @@ function ExpandableText({
6925
7076
  );
6926
7077
  }
6927
7078
  function ToolCallItem({ toolCall, toolResult }) {
6928
- const [expanded, setExpanded] = (0, import_react19.useState)(false);
7079
+ const [expanded, setExpanded] = (0, import_react20.useState)(false);
6929
7080
  const hasArgs = toolCall.toolArgs && Object.keys(toolCall.toolArgs).length > 0;
6930
7081
  const rawName = cleanToolName(toolCall.toolName);
6931
7082
  const displayName = rawName && !/^[{[]/.test(rawName.trim()) ? rawName : toolCall.toolCallId || "tool";
@@ -6963,7 +7114,7 @@ function StreamingToolCallItem({
6963
7114
  isActive,
6964
7115
  strings
6965
7116
  }) {
6966
- const [expanded, setExpanded] = (0, import_react19.useState)(false);
7117
+ const [expanded, setExpanded] = (0, import_react20.useState)(false);
6967
7118
  const hasArgs = toolCall.toolArgs && Object.keys(toolCall.toolArgs).length > 0;
6968
7119
  const rawName = cleanToolName(toolCall.toolName);
6969
7120
  const displayName = rawName && !/^[{[]/.test(rawName.trim()) ? rawName : toolCall.toolCallId || "tool";
@@ -7031,7 +7182,7 @@ function groupBlocksImpl(blocks) {
7031
7182
  }
7032
7183
  return rounds;
7033
7184
  }
7034
- var ThinkingSection = (0, import_react19.memo)(function ThinkingSection2({
7185
+ var ThinkingSection = (0, import_react20.memo)(function ThinkingSection2({
7035
7186
  thinking,
7036
7187
  currentThinking,
7037
7188
  isStreaming,
@@ -7040,16 +7191,16 @@ var ThinkingSection = (0, import_react19.memo)(function ThinkingSection2({
7040
7191
  className = "",
7041
7192
  strings = {}
7042
7193
  }) {
7043
- const contentRef = (0, import_react19.useRef)(null);
7044
- const isUserScrollingRef = (0, import_react19.useRef)(false);
7045
- const [loadingSymbolIndex, setLoadingSymbolIndex] = (0, import_react19.useState)(0);
7046
- const [manualExpanded, setManualExpanded] = (0, import_react19.useState)(false);
7194
+ const contentRef = (0, import_react20.useRef)(null);
7195
+ const isUserScrollingRef = (0, import_react20.useRef)(false);
7196
+ const [loadingSymbolIndex, setLoadingSymbolIndex] = (0, import_react20.useState)(0);
7197
+ const [manualExpanded, setManualExpanded] = (0, import_react20.useState)(false);
7047
7198
  const effectiveIsStreaming = isComplete ? false : isStreaming;
7048
7199
  const effectiveIsPaused = isComplete ? false : isPaused;
7049
7200
  const isExpanded = effectiveIsStreaming || effectiveIsPaused || manualExpanded;
7050
7201
  const displayContent = effectiveIsStreaming || effectiveIsPaused ? currentThinking || thinking : thinking;
7051
7202
  const hasContent = displayContent && displayContent.split("\n").filter((line) => line.trim() && !line.trim().match(/^─+$/)).length > 0;
7052
- (0, import_react19.useEffect)(() => {
7203
+ (0, import_react20.useEffect)(() => {
7053
7204
  if (effectiveIsStreaming) {
7054
7205
  const interval = setInterval(() => {
7055
7206
  setLoadingSymbolIndex((prev) => (prev + 1) % LOADING_SYMBOLS.length);
@@ -7057,13 +7208,13 @@ var ThinkingSection = (0, import_react19.memo)(function ThinkingSection2({
7057
7208
  return () => clearInterval(interval);
7058
7209
  }
7059
7210
  }, [effectiveIsStreaming]);
7060
- (0, import_react19.useEffect)(() => {
7211
+ (0, import_react20.useEffect)(() => {
7061
7212
  if (effectiveIsStreaming && isExpanded && contentRef.current && !isUserScrollingRef.current) {
7062
7213
  contentRef.current.scrollTop = contentRef.current.scrollHeight;
7063
7214
  }
7064
7215
  }, [displayContent, effectiveIsStreaming, isExpanded]);
7065
- const prevManualExpandedRef = (0, import_react19.useRef)(manualExpanded);
7066
- (0, import_react19.useEffect)(() => {
7216
+ const prevManualExpandedRef = (0, import_react20.useRef)(manualExpanded);
7217
+ (0, import_react20.useEffect)(() => {
7067
7218
  const wasCollapsed = !prevManualExpandedRef.current;
7068
7219
  const isNowExpanded = manualExpanded;
7069
7220
  if (wasCollapsed && isNowExpanded && !effectiveIsStreaming && contentRef.current) {
@@ -7071,13 +7222,13 @@ var ThinkingSection = (0, import_react19.memo)(function ThinkingSection2({
7071
7222
  }
7072
7223
  prevManualExpandedRef.current = manualExpanded;
7073
7224
  }, [manualExpanded, effectiveIsStreaming]);
7074
- const handleScroll = (0, import_react19.useCallback)(() => {
7225
+ const handleScroll = (0, import_react20.useCallback)(() => {
7075
7226
  if (!contentRef.current || !effectiveIsStreaming) return;
7076
7227
  const { scrollTop, scrollHeight, clientHeight } = contentRef.current;
7077
7228
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
7078
7229
  isUserScrollingRef.current = !isAtBottom;
7079
7230
  }, [effectiveIsStreaming]);
7080
- (0, import_react19.useEffect)(() => {
7231
+ (0, import_react20.useEffect)(() => {
7081
7232
  if (!effectiveIsStreaming) {
7082
7233
  isUserScrollingRef.current = false;
7083
7234
  }
@@ -7116,14 +7267,14 @@ var ThinkingSection = (0, import_react19.memo)(function ThinkingSection2({
7116
7267
  isExpanded && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: contentRef, onScroll: handleScroll, className: "mt-1.5 ml-2 max-h-60 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { className: "whitespace-pre-wrap m-0 font-sans italic", style: { color: "var(--chat-text)", opacity: 0.7, fontSize: "0.8125rem", lineHeight: "1.3" }, children: displayContent?.trim() }) })
7117
7268
  ] });
7118
7269
  });
7119
- var IntermediateSteps = (0, import_react19.memo)(function IntermediateSteps2({
7270
+ var IntermediateSteps = (0, import_react20.memo)(function IntermediateSteps2({
7120
7271
  blocks,
7121
7272
  className = "",
7122
7273
  defaultExpanded = false,
7123
7274
  strings = {}
7124
7275
  }) {
7125
- const [isExpanded, setIsExpanded] = (0, import_react19.useState)(defaultExpanded);
7126
- const rounds = (0, import_react19.useMemo)(() => groupBlocksIntoRounds(blocks), [blocks]);
7276
+ const [isExpanded, setIsExpanded] = (0, import_react20.useState)(defaultExpanded);
7277
+ const rounds = (0, import_react20.useMemo)(() => groupBlocksIntoRounds(blocks), [blocks]);
7127
7278
  if (rounds.length === 0) return null;
7128
7279
  const stepCount = rounds.filter((round) => round.toolCalls.length > 0).length;
7129
7280
  if (stepCount === 0) return null;
@@ -7205,7 +7356,7 @@ var IntermediateSteps = (0, import_react19.memo)(function IntermediateSteps2({
7205
7356
  }) })
7206
7357
  ] });
7207
7358
  });
7208
- var StreamingTimeline = (0, import_react19.memo)(function StreamingTimeline2({
7359
+ var StreamingTimeline = (0, import_react20.memo)(function StreamingTimeline2({
7209
7360
  blocks,
7210
7361
  currentThinking = "",
7211
7362
  isThinkingStreaming = false,
@@ -7214,14 +7365,14 @@ var StreamingTimeline = (0, import_react19.memo)(function StreamingTimeline2({
7214
7365
  className = "",
7215
7366
  strings = {}
7216
7367
  }) {
7217
- const [isExpanded, setIsExpanded] = (0, import_react19.useState)(true);
7218
- const isUserScrollingRef = (0, import_react19.useRef)(false);
7219
- const timelineRef = (0, import_react19.useRef)(null);
7368
+ const [isExpanded, setIsExpanded] = (0, import_react20.useState)(true);
7369
+ const isUserScrollingRef = (0, import_react20.useRef)(false);
7370
+ const timelineRef = (0, import_react20.useRef)(null);
7220
7371
  const isThinkingActive = isThinkingStreaming;
7221
7372
  const isToolsActive = isToolCallsStreaming;
7222
- const rounds = (0, import_react19.useMemo)(() => groupAllBlocksIntoRounds(blocks), [blocks]);
7373
+ const rounds = (0, import_react20.useMemo)(() => groupAllBlocksIntoRounds(blocks), [blocks]);
7223
7374
  const activeThinking = currentThinking;
7224
- const displayRounds = (0, import_react19.useMemo)(() => {
7375
+ const displayRounds = (0, import_react20.useMemo)(() => {
7225
7376
  const result = [];
7226
7377
  for (let roundIdx = 0; roundIdx < rounds.length; roundIdx++) {
7227
7378
  const round = rounds[roundIdx];
@@ -7283,18 +7434,18 @@ var StreamingTimeline = (0, import_react19.memo)(function StreamingTimeline2({
7283
7434
  }, [rounds, activeThinking, isThinkingActive, isToolsActive]);
7284
7435
  const stepCount = rounds.filter((round) => round.toolCalls.length > 0).length;
7285
7436
  const summary = `${stepCount} ${strings.steps || "\u6B65"}`;
7286
- const handleScroll = (0, import_react19.useCallback)(() => {
7437
+ const handleScroll = (0, import_react20.useCallback)(() => {
7287
7438
  if (!timelineRef.current) return;
7288
7439
  const { scrollTop, scrollHeight, clientHeight } = timelineRef.current;
7289
7440
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 20;
7290
7441
  isUserScrollingRef.current = !isAtBottom;
7291
7442
  }, []);
7292
- (0, import_react19.useEffect)(() => {
7443
+ (0, import_react20.useEffect)(() => {
7293
7444
  if (isExpanded && timelineRef.current && !isUserScrollingRef.current) {
7294
7445
  timelineRef.current.scrollTop = timelineRef.current.scrollHeight;
7295
7446
  }
7296
7447
  }, [displayRounds, isExpanded]);
7297
- (0, import_react19.useEffect)(() => {
7448
+ (0, import_react20.useEffect)(() => {
7298
7449
  if (!isThinkingActive && !isToolsActive) {
7299
7450
  isUserScrollingRef.current = false;
7300
7451
  }
@@ -7380,27 +7531,101 @@ var StreamingTimeline = (0, import_react19.memo)(function StreamingTimeline2({
7380
7531
  ] });
7381
7532
  });
7382
7533
 
7383
- // src/renderer/components/SanqianChatMessage.tsx
7384
- var import_jsx_runtime9 = require("react/jsx-runtime");
7385
- var SanqianChatMessage = (0, import_react20.memo)(function SanqianChatMessage2({ message }) {
7386
- if (message.role === "tool") return null;
7534
+ // src/renderer/utils/intermediateDisplay.ts
7535
+ function normalizeToolResult(result) {
7536
+ if (result === void 0 || result === null) return void 0;
7537
+ if (typeof result === "string") {
7538
+ return result.trim() ? result : void 0;
7539
+ }
7540
+ try {
7541
+ return JSON.stringify(result, null, 2);
7542
+ } catch {
7543
+ return String(result);
7544
+ }
7545
+ }
7546
+ function buildFallbackBlocksFromToolCalls(toolCalls) {
7547
+ if (!toolCalls || toolCalls.length === 0) return void 0;
7548
+ const blocks = [];
7549
+ let timestamp = Date.now();
7550
+ for (const toolCall of toolCalls) {
7551
+ const toolStatus = toolCall.status || (toolCall.result !== void 0 ? "completed" : "running");
7552
+ blocks.push({
7553
+ type: "tool_call",
7554
+ content: "",
7555
+ timestamp: timestamp++,
7556
+ toolName: toolCall.name,
7557
+ toolArgs: toolCall.args,
7558
+ toolCallId: toolCall.id,
7559
+ toolStatus,
7560
+ isIntermediate: true
7561
+ });
7562
+ const resultContent = normalizeToolResult(toolCall.result);
7563
+ if (resultContent) {
7564
+ blocks.push({
7565
+ type: "tool_result",
7566
+ content: resultContent,
7567
+ timestamp: timestamp++,
7568
+ toolName: toolCall.name,
7569
+ toolCallId: toolCall.id,
7570
+ isIntermediate: true
7571
+ });
7572
+ }
7573
+ }
7574
+ return blocks.length > 0 ? blocks : void 0;
7575
+ }
7576
+ function getAssistantDisplayState(message) {
7387
7577
  const isUser = message.role === "user";
7388
- const hasToolCalls = (message.toolCalls?.length ?? 0) > 0;
7389
- const hasToolBlocks = message.blocks?.some((b) => b.type === "tool_call" || b.type === "tool_result") ?? false;
7578
+ const displayBlocks = message.blocks && message.blocks.length > 0 ? message.blocks : buildFallbackBlocksFromToolCalls(message.toolCalls);
7579
+ const hasToolCalls = Boolean(message.toolCalls && message.toolCalls.length > 0);
7580
+ const hasToolBlocks = Boolean(
7581
+ displayBlocks && displayBlocks.some((block) => block.type === "tool_call" || block.type === "tool_result")
7582
+ );
7390
7583
  const hasToolActivity = hasToolCalls || hasToolBlocks;
7391
- const hasIntermediateBlocks = message.blocks?.some((b) => b.isIntermediate) ?? false;
7392
- const showIntermediateSteps = Boolean(message.isComplete && hasIntermediateBlocks && hasToolActivity);
7393
- const showStreamingTimeline = Boolean(!isUser && !showIntermediateSteps && hasToolActivity);
7584
+ const hasRunningToolCalls = Boolean(
7585
+ message.toolCalls?.some((tool) => tool.status === "running") || displayBlocks?.some((block) => block.type === "tool_call" && block.toolStatus === "running")
7586
+ );
7587
+ const isEffectivelyComplete = Boolean(
7588
+ message.isComplete || !message.isStreaming && !message.isThinkingStreaming && !message.isToolCallsStreaming && !hasRunningToolCalls
7589
+ );
7590
+ const hasIntermediateBlocks = Boolean(displayBlocks?.some((block) => block.isIntermediate));
7394
7591
  const hasThinking = Boolean(message.thinking || message.isThinkingStreaming || message.currentThinking);
7592
+ const showIntermediateSteps = Boolean(
7593
+ !isUser && isEffectivelyComplete && hasToolActivity && hasIntermediateBlocks
7594
+ );
7595
+ const showStreamingTimeline = Boolean(!isUser && !showIntermediateSteps && hasToolActivity);
7395
7596
  const showThinkingSection = Boolean(!isUser && !hasToolActivity && !showIntermediateSteps && hasThinking);
7396
- const isToolCallsStreaming = (message.isToolCallsStreaming ?? (message.toolCalls?.some((tc) => tc.status === "running") ?? false)) || (message.blocks?.some((b) => b.type === "tool_call" && b.toolStatus === "running") ?? false);
7397
- const isThinkingPaused = message.isThinkingPaused ?? Boolean(!message.isThinkingStreaming && message.currentThinking && !message.isComplete);
7597
+ return {
7598
+ displayBlocks,
7599
+ hasToolActivity,
7600
+ isEffectivelyComplete,
7601
+ showIntermediateSteps,
7602
+ showStreamingTimeline,
7603
+ showThinkingSection
7604
+ };
7605
+ }
7606
+
7607
+ // src/renderer/components/SanqianChatMessage.tsx
7608
+ var import_jsx_runtime9 = require("react/jsx-runtime");
7609
+ var SanqianChatMessage = (0, import_react21.memo)(function SanqianChatMessage2({ message }) {
7610
+ if (message.role === "tool") return null;
7611
+ const isUser = message.role === "user";
7612
+ const {
7613
+ displayBlocks,
7614
+ isEffectivelyComplete,
7615
+ showIntermediateSteps,
7616
+ showStreamingTimeline,
7617
+ showThinkingSection
7618
+ } = getAssistantDisplayState(message);
7619
+ const isToolCallsStreaming = (message.isToolCallsStreaming ?? (message.toolCalls?.some((tc) => tc.status === "running") ?? false)) || (displayBlocks?.some((b) => b.type === "tool_call" && b.toolStatus === "running") ?? false);
7620
+ const isThinkingPaused = message.isThinkingPaused ?? Boolean(!message.isThinkingStreaming && message.currentThinking && !isEffectivelyComplete);
7398
7621
  const showStreamingPlaceholder = Boolean(
7399
7622
  !isUser && message.isStreaming && !message.content && !showStreamingTimeline && !showIntermediateSteps
7400
7623
  );
7401
7624
  const hasAttachedResources = isUser && message.attachedResources && message.attachedResources.length > 0;
7625
+ const hasUserText = isUser && message.content.trim().length > 0;
7626
+ const shouldRenderBubble = !isUser || hasUserText;
7402
7627
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `flex ${isUser ? "justify-end" : "justify-start"} mb-4`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `text-left ${isUser ? "max-w-[80%]" : "w-full"}`, children: [
7403
- !isUser && showIntermediateSteps && message.blocks && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IntermediateSteps, { blocks: message.blocks }),
7628
+ !isUser && showIntermediateSteps && displayBlocks && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(IntermediateSteps, { blocks: displayBlocks }),
7404
7629
  showThinkingSection && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7405
7630
  ThinkingSection,
7406
7631
  {
@@ -7408,21 +7633,21 @@ var SanqianChatMessage = (0, import_react20.memo)(function SanqianChatMessage2({
7408
7633
  currentThinking: message.currentThinking,
7409
7634
  isStreaming: message.isThinkingStreaming,
7410
7635
  isPaused: isThinkingPaused,
7411
- isComplete: message.isComplete
7636
+ isComplete: isEffectivelyComplete
7412
7637
  }
7413
7638
  ),
7414
- showStreamingTimeline && message.blocks && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7639
+ showStreamingTimeline && displayBlocks && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7415
7640
  StreamingTimeline,
7416
7641
  {
7417
- blocks: message.blocks,
7642
+ blocks: displayBlocks,
7418
7643
  currentThinking: message.currentThinking,
7419
7644
  isThinkingStreaming: message.isThinkingStreaming,
7420
7645
  isToolCallsStreaming,
7421
- isComplete: message.isComplete
7646
+ isComplete: isEffectivelyComplete
7422
7647
  }
7423
7648
  ),
7424
7649
  hasAttachedResources && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MessageAttachedResources, { resources: message.attachedResources }),
7425
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `relative group ${isUser ? "inline-block max-w-full" : "block w-full"}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7650
+ shouldRenderBubble && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `relative group ${isUser ? "inline-block max-w-full" : "block w-full"}`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7426
7651
  "div",
7427
7652
  {
7428
7653
  className: `message-bubble-wrapper ${isUser ? "rounded-2xl shadow-sm bg-[var(--chat-accent)] text-white px-4 py-3 whitespace-pre-wrap break-words" : "text-[var(--chat-text)] break-words"}`,
@@ -7438,7 +7663,7 @@ var SanqianChatMessage = (0, import_react20.memo)(function SanqianChatMessage2({
7438
7663
  ) })
7439
7664
  ] }) });
7440
7665
  });
7441
- var MessageAttachedResources = (0, import_react20.memo)(function MessageAttachedResources2({
7666
+ var MessageAttachedResources = (0, import_react21.memo)(function MessageAttachedResources2({
7442
7667
  resources
7443
7668
  }) {
7444
7669
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-wrap gap-1.5 mb-2 justify-end", children: resources.map((resource, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
@@ -7460,9 +7685,9 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
7460
7685
  var AT_BOTTOM_THRESHOLD = 50;
7461
7686
  var INCREASE_VIEWPORT_BY = { top: 1500, bottom: 1500 };
7462
7687
  function ResizeAwareMessage({ message, onHeightChange }) {
7463
- const containerRef = (0, import_react21.useRef)(null);
7464
- const prevHeightRef = (0, import_react21.useRef)(0);
7465
- (0, import_react21.useEffect)(() => {
7688
+ const containerRef = (0, import_react22.useRef)(null);
7689
+ const prevHeightRef = (0, import_react22.useRef)(0);
7690
+ (0, import_react22.useEffect)(() => {
7466
7691
  const container = containerRef.current;
7467
7692
  if (!container || !onHeightChange) return;
7468
7693
  let rafId = null;
@@ -7484,16 +7709,16 @@ function ResizeAwareMessage({ message, onHeightChange }) {
7484
7709
  }, [onHeightChange]);
7485
7710
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: containerRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SanqianChatMessage, { message }) });
7486
7711
  }
7487
- var SanqianMessageList = (0, import_react21.forwardRef)(
7712
+ var SanqianMessageList = (0, import_react22.forwardRef)(
7488
7713
  function SanqianMessageList2({ messages, isLoading, className = "", emptyState, onClickEmpty, onAtBottomChange }, ref) {
7489
- const virtuosoRef = (0, import_react21.useRef)(null);
7490
- const [, setAtBottomState] = (0, import_react21.useState)(true);
7491
- const atBottomRef = (0, import_react21.useRef)(true);
7492
- const isManualScrollingRef = (0, import_react21.useRef)(false);
7493
- const manualScrollTimeoutRef = (0, import_react21.useRef)(null);
7494
- const isFollowOutputScrollingRef = (0, import_react21.useRef)(false);
7495
- const followOutputScrollTimeoutRef = (0, import_react21.useRef)(null);
7496
- const setAtBottom = (0, import_react21.useCallback)(
7714
+ const virtuosoRef = (0, import_react22.useRef)(null);
7715
+ const [, setAtBottomState] = (0, import_react22.useState)(true);
7716
+ const atBottomRef = (0, import_react22.useRef)(true);
7717
+ const isManualScrollingRef = (0, import_react22.useRef)(false);
7718
+ const manualScrollTimeoutRef = (0, import_react22.useRef)(null);
7719
+ const isFollowOutputScrollingRef = (0, import_react22.useRef)(false);
7720
+ const followOutputScrollTimeoutRef = (0, import_react22.useRef)(null);
7721
+ const setAtBottom = (0, import_react22.useCallback)(
7497
7722
  (value) => {
7498
7723
  if (isManualScrollingRef.current) return;
7499
7724
  if (isFollowOutputScrollingRef.current && !value) return;
@@ -7503,18 +7728,18 @@ var SanqianMessageList = (0, import_react21.forwardRef)(
7503
7728
  },
7504
7729
  [onAtBottomChange]
7505
7730
  );
7506
- const visibleMessages = (0, import_react21.useMemo)(
7731
+ const visibleMessages = (0, import_react22.useMemo)(
7507
7732
  () => messages.filter((message) => message.role !== "tool"),
7508
7733
  [messages]
7509
7734
  );
7510
7735
  const lastMessage = visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1] : null;
7511
7736
  const hasStreamingActivity = isLoading || (lastMessage?.isStreaming ?? false);
7512
- const allItems = (0, import_react21.useMemo)(() => {
7737
+ const allItems = (0, import_react22.useMemo)(() => {
7513
7738
  const items = [...visibleMessages];
7514
7739
  items.push({ id: "footer-spacer", type: "footer" });
7515
7740
  return items;
7516
7741
  }, [visibleMessages]);
7517
- const scrollToBottom = (0, import_react21.useCallback)(
7742
+ const scrollToBottom = (0, import_react22.useCallback)(
7518
7743
  (behavior = "smooth", source) => {
7519
7744
  if (!virtuosoRef.current || allItems.length === 0) return;
7520
7745
  const isManualScroll = source === "scroll-button";
@@ -7540,14 +7765,14 @@ var SanqianMessageList = (0, import_react21.forwardRef)(
7540
7765
  },
7541
7766
  [allItems.length, onAtBottomChange]
7542
7767
  );
7543
- (0, import_react21.useImperativeHandle)(
7768
+ (0, import_react22.useImperativeHandle)(
7544
7769
  ref,
7545
7770
  () => ({
7546
7771
  scrollToBottom
7547
7772
  }),
7548
7773
  [scrollToBottom]
7549
7774
  );
7550
- (0, import_react21.useEffect)(() => {
7775
+ (0, import_react22.useEffect)(() => {
7551
7776
  return () => {
7552
7777
  if (manualScrollTimeoutRef.current) {
7553
7778
  clearTimeout(manualScrollTimeoutRef.current);
@@ -7557,13 +7782,13 @@ var SanqianMessageList = (0, import_react21.forwardRef)(
7557
7782
  }
7558
7783
  };
7559
7784
  }, []);
7560
- const handleMessageHeightChange = (0, import_react21.useCallback)(() => {
7785
+ const handleMessageHeightChange = (0, import_react22.useCallback)(() => {
7561
7786
  if (isManualScrollingRef.current) return;
7562
7787
  if (atBottomRef.current && hasStreamingActivity) {
7563
7788
  scrollToBottom("auto", "height-change");
7564
7789
  }
7565
7790
  }, [scrollToBottom, hasStreamingActivity]);
7566
- const handleClick = (0, import_react21.useCallback)(
7791
+ const handleClick = (0, import_react22.useCallback)(
7567
7792
  (e) => {
7568
7793
  const selection = window.getSelection();
7569
7794
  if (!selection || selection.isCollapsed) {
@@ -7574,7 +7799,7 @@ var SanqianMessageList = (0, import_react21.forwardRef)(
7574
7799
  },
7575
7800
  [onClickEmpty]
7576
7801
  );
7577
- const handleFollowOutput = (0, import_react21.useCallback)((isAtBottom) => {
7802
+ const handleFollowOutput = (0, import_react22.useCallback)((isAtBottom) => {
7578
7803
  if (isAtBottom) {
7579
7804
  if (followOutputScrollTimeoutRef.current) {
7580
7805
  clearTimeout(followOutputScrollTimeoutRef.current);
@@ -7588,7 +7813,7 @@ var SanqianMessageList = (0, import_react21.forwardRef)(
7588
7813
  }
7589
7814
  return false;
7590
7815
  }, []);
7591
- const itemContent = (0, import_react21.useCallback)(
7816
+ const itemContent = (0, import_react22.useCallback)(
7592
7817
  (_index, item) => {
7593
7818
  if ("type" in item && item.type === "footer") {
7594
7819
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "h-28" });
@@ -7622,16 +7847,17 @@ var SanqianMessageList = (0, import_react21.forwardRef)(
7622
7847
  );
7623
7848
 
7624
7849
  // src/renderer/components/ChatInput.tsx
7625
- var import_react22 = require("react");
7850
+ var import_react23 = require("react");
7626
7851
  var import_jsx_runtime11 = require("react/jsx-runtime");
7627
- var ChatInput = (0, import_react22.memo)(
7628
- (0, import_react22.forwardRef)(function ChatInput2({
7852
+ var ChatInput = (0, import_react23.memo)(
7853
+ (0, import_react23.forwardRef)(function ChatInput2({
7629
7854
  onSend,
7630
7855
  onStop,
7631
7856
  placeholder = "Type a message...",
7632
7857
  disabled = false,
7633
7858
  isStreaming = false,
7634
7859
  isLoading = false,
7860
+ allowEmptySubmit = false,
7635
7861
  sendLabel = "Send",
7636
7862
  stopLabel = "Stop",
7637
7863
  autoFocus = false,
@@ -7640,12 +7866,14 @@ var ChatInput = (0, import_react22.memo)(
7640
7866
  className = "",
7641
7867
  style
7642
7868
  }, ref) {
7643
- const [text, setText] = (0, import_react22.useState)("");
7644
- const textareaRef = (0, import_react22.useRef)(null);
7645
- const canSend = text.trim().length > 0 && !disabled && !isLoading;
7869
+ const [text, setText] = (0, import_react23.useState)("");
7870
+ const textareaRef = (0, import_react23.useRef)(null);
7871
+ const submitInFlightRef = (0, import_react23.useRef)(false);
7872
+ const inputRevisionRef = (0, import_react23.useRef)(0);
7873
+ const canSend = (text.trim().length > 0 || allowEmptySubmit) && !disabled && !isLoading;
7646
7874
  const showStopButton = isStreaming && !!onStop;
7647
7875
  const showSpinner = isLoading && !showStopButton;
7648
- (0, import_react22.useImperativeHandle)(
7876
+ (0, import_react23.useImperativeHandle)(
7649
7877
  ref,
7650
7878
  () => ({
7651
7879
  focus: () => textareaRef.current?.focus(),
@@ -7655,26 +7883,46 @@ var ChatInput = (0, import_react22.memo)(
7655
7883
  }),
7656
7884
  [text]
7657
7885
  );
7658
- (0, import_react22.useEffect)(() => {
7886
+ (0, import_react23.useEffect)(() => {
7659
7887
  if (autoFocus) {
7660
7888
  const timer = setTimeout(() => textareaRef.current?.focus(), 100);
7661
7889
  return () => clearTimeout(timer);
7662
7890
  }
7663
7891
  }, [autoFocus]);
7664
- const handleSubmit = (0, import_react22.useCallback)(
7665
- (e) => {
7892
+ const handleSubmit = (0, import_react23.useCallback)(
7893
+ async (e) => {
7666
7894
  e?.preventDefault();
7667
7895
  if (!canSend) return;
7668
- onSend(text.trim());
7896
+ if (submitInFlightRef.current) return;
7897
+ const draft = text;
7898
+ const payload = draft.trim();
7899
+ const submitRevision = inputRevisionRef.current;
7900
+ submitInFlightRef.current = true;
7669
7901
  setText("");
7902
+ const restoreDraft = () => {
7903
+ setText((current) => {
7904
+ if (inputRevisionRef.current !== submitRevision) return current;
7905
+ return current.length === 0 ? draft : current;
7906
+ });
7907
+ };
7908
+ try {
7909
+ const result = await onSend(payload);
7910
+ if (result === false) {
7911
+ restoreDraft();
7912
+ }
7913
+ } catch {
7914
+ restoreDraft();
7915
+ } finally {
7916
+ submitInFlightRef.current = false;
7917
+ }
7670
7918
  },
7671
7919
  [text, canSend, onSend]
7672
7920
  );
7673
- const handleKeyDown = (0, import_react22.useCallback)(
7921
+ const handleKeyDown = (0, import_react23.useCallback)(
7674
7922
  (e) => {
7675
7923
  if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
7676
7924
  e.preventDefault();
7677
- handleSubmit();
7925
+ void handleSubmit();
7678
7926
  }
7679
7927
  if (e.key === "Escape" && showStopButton) {
7680
7928
  e.preventDefault();
@@ -7685,60 +7933,73 @@ var ChatInput = (0, import_react22.memo)(
7685
7933
  );
7686
7934
  const leftPadding = leftSlot ? "2rem" : "0.75rem";
7687
7935
  const rightPadding = rightSlot ? "2.5rem" : "0.75rem";
7688
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("form", { onSubmit: handleSubmit, className: `chat-input-form${className ? ` ${className}` : ""}`, style, children: [
7689
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-box", children: [
7690
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7691
- "div",
7692
- {
7693
- className: "chat-input-replica",
7694
- style: { paddingLeft: leftPadding, paddingRight: rightPadding },
7695
- "aria-hidden": "true",
7696
- children: text + " "
7697
- }
7698
- ),
7699
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7700
- "textarea",
7701
- {
7702
- ref: textareaRef,
7703
- value: text,
7704
- onChange: (e) => setText(e.target.value),
7705
- onKeyDown: handleKeyDown,
7706
- placeholder,
7707
- disabled,
7708
- rows: 1,
7709
- className: "chat-input-textarea",
7710
- style: { paddingLeft: leftPadding, paddingRight: rightPadding }
7711
- }
7712
- ),
7713
- leftSlot && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-left-slot", children: leftSlot }),
7714
- rightSlot && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-right-slot", children: rightSlot })
7715
- ] }),
7716
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-send-wrapper", children: [
7717
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7718
- "button",
7719
- {
7720
- type: showStopButton ? "button" : "submit",
7721
- onClick: showStopButton ? onStop : void 0,
7722
- disabled: !showStopButton && !canSend,
7723
- className: "chat-input-send-btn",
7724
- "aria-label": showStopButton ? stopLabel : sendLabel,
7725
- children: showSpinner ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", className: "animate-spin", children: [
7726
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", opacity: "0.25" }),
7727
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { fill: "currentColor", opacity: "0.75", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
7728
- ] }) : showStopButton ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18" }) })
7729
- }
7730
- ),
7731
- showStopButton && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-stop-tooltip", role: "note", children: [
7732
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: stopLabel }),
7733
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-stop-hotkey", children: "Esc" })
7734
- ] })
7735
- ] })
7736
- ] });
7936
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7937
+ "form",
7938
+ {
7939
+ onSubmit: (e) => {
7940
+ void handleSubmit(e);
7941
+ },
7942
+ className: `chat-input-form${className ? ` ${className}` : ""}`,
7943
+ style,
7944
+ children: [
7945
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-box", children: [
7946
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7947
+ "div",
7948
+ {
7949
+ className: "chat-input-replica",
7950
+ style: { paddingLeft: leftPadding, paddingRight: rightPadding },
7951
+ "aria-hidden": "true",
7952
+ children: text + " "
7953
+ }
7954
+ ),
7955
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7956
+ "textarea",
7957
+ {
7958
+ ref: textareaRef,
7959
+ value: text,
7960
+ onChange: (e) => {
7961
+ inputRevisionRef.current += 1;
7962
+ setText(e.target.value);
7963
+ },
7964
+ onKeyDown: handleKeyDown,
7965
+ placeholder,
7966
+ disabled,
7967
+ rows: 1,
7968
+ className: "chat-input-textarea",
7969
+ style: { paddingLeft: leftPadding, paddingRight: rightPadding }
7970
+ }
7971
+ ),
7972
+ leftSlot && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-left-slot", children: leftSlot }),
7973
+ rightSlot && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-right-slot", children: rightSlot })
7974
+ ] }),
7975
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-send-wrapper", children: [
7976
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7977
+ "button",
7978
+ {
7979
+ type: showStopButton ? "button" : "submit",
7980
+ onClick: showStopButton ? onStop : void 0,
7981
+ disabled: !showStopButton && !canSend,
7982
+ className: "chat-input-send-btn",
7983
+ "aria-label": showStopButton ? stopLabel : sendLabel,
7984
+ children: showSpinner ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", className: "animate-spin", children: [
7985
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", opacity: "0.25" }),
7986
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { fill: "currentColor", opacity: "0.75", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
7987
+ ] }) : showStopButton ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18" }) })
7988
+ }
7989
+ ),
7990
+ showStopButton && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-stop-tooltip", role: "note", children: [
7991
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: stopLabel }),
7992
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-stop-hotkey", children: "Esc" })
7993
+ ] })
7994
+ ] })
7995
+ ]
7996
+ }
7997
+ );
7737
7998
  })
7738
7999
  );
7739
8000
 
7740
8001
  // src/renderer/components/HitlCard.tsx
7741
- var import_react23 = require("react");
8002
+ var import_react24 = require("react");
7742
8003
  var import_jsx_runtime12 = require("react/jsx-runtime");
7743
8004
  var defaultStrings = {
7744
8005
  approve: "Approve",
@@ -7777,7 +8038,7 @@ var riskColors = {
7777
8038
  icon: "!!"
7778
8039
  }
7779
8040
  };
7780
- var HitlCard = (0, import_react23.memo)(function HitlCard2({
8041
+ var HitlCard = (0, import_react24.memo)(function HitlCard2({
7781
8042
  interrupt,
7782
8043
  onApprove,
7783
8044
  onReject,
@@ -7789,16 +8050,16 @@ var HitlCard = (0, import_react23.memo)(function HitlCard2({
7789
8050
  const t = { ...defaultStrings, ...strings };
7790
8051
  const isApproval = interrupt.type === "approval_request";
7791
8052
  const isUserInput = interrupt.type === "user_input_request";
7792
- const [answer, setAnswer] = (0, import_react23.useState)(interrupt.default || "");
7793
- const [selectedIndices, setSelectedIndices] = (0, import_react23.useState)([]);
7794
- const [isComposing, setIsComposing] = (0, import_react23.useState)(false);
7795
- const [timeLeft, setTimeLeft] = (0, import_react23.useState)(interrupt.timeout ?? null);
7796
- const [rememberChoice, setRememberChoice] = (0, import_react23.useState)(false);
7797
- const inputRef = (0, import_react23.useRef)(null);
7798
- const textareaRef = (0, import_react23.useRef)(null);
8053
+ const [answer, setAnswer] = (0, import_react24.useState)(interrupt.default || "");
8054
+ const [selectedIndices, setSelectedIndices] = (0, import_react24.useState)([]);
8055
+ const [isComposing, setIsComposing] = (0, import_react24.useState)(false);
8056
+ const [timeLeft, setTimeLeft] = (0, import_react24.useState)(interrupt.timeout ?? null);
8057
+ const [rememberChoice, setRememberChoice] = (0, import_react24.useState)(false);
8058
+ const inputRef = (0, import_react24.useRef)(null);
8059
+ const textareaRef = (0, import_react24.useRef)(null);
7799
8060
  const riskLevel = interrupt.risk_level || "medium";
7800
8061
  const riskStyle = riskColors[riskLevel];
7801
- (0, import_react23.useEffect)(() => {
8062
+ (0, import_react24.useEffect)(() => {
7802
8063
  if (isUserInput) {
7803
8064
  if (!interrupt.options || interrupt.options.length === 0) {
7804
8065
  inputRef.current?.focus();
@@ -7806,7 +8067,7 @@ var HitlCard = (0, import_react23.memo)(function HitlCard2({
7806
8067
  }
7807
8068
  }
7808
8069
  }, [isUserInput, interrupt.options]);
7809
- (0, import_react23.useEffect)(() => {
8070
+ (0, import_react24.useEffect)(() => {
7810
8071
  if (timeLeft === null || timeLeft <= 0) return;
7811
8072
  const timer = setInterval(() => {
7812
8073
  setTimeLeft((prev) => {
@@ -8004,12 +8265,12 @@ var HitlCard = (0, import_react23.memo)(function HitlCard2({
8004
8265
  });
8005
8266
 
8006
8267
  // src/renderer/primitives/AlertBanner.tsx
8007
- var import_react24 = require("react");
8268
+ var import_react25 = require("react");
8008
8269
  var import_jsx_runtime13 = (
8009
8270
  // Error icon (circle with X)
8010
8271
  require("react/jsx-runtime")
8011
8272
  );
8012
- var AlertBanner = (0, import_react24.memo)(function AlertBanner2({
8273
+ var AlertBanner = (0, import_react25.memo)(function AlertBanner2({
8013
8274
  type,
8014
8275
  message,
8015
8276
  action,
@@ -8019,7 +8280,7 @@ var AlertBanner = (0, import_react24.memo)(function AlertBanner2({
8019
8280
  maxLines = 3,
8020
8281
  strings
8021
8282
  }) {
8022
- const [isExpanded, setIsExpanded] = (0, import_react24.useState)(false);
8283
+ const [isExpanded, setIsExpanded] = (0, import_react25.useState)(false);
8023
8284
  const defaultIcon = type === "error" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("svg", { className: "size-4 shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
8024
8285
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
8025
8286
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M15 9l-6 6M9 9l6 6", strokeLinecap: "round" })
@@ -8081,10 +8342,10 @@ var AlertBanner = (0, import_react24.memo)(function AlertBanner2({
8081
8342
  });
8082
8343
 
8083
8344
  // src/renderer/components/AddResourceButton.tsx
8084
- var import_react26 = require("react");
8345
+ var import_react27 = require("react");
8085
8346
 
8086
8347
  // src/renderer/components/ResourcePicker.tsx
8087
- var import_react25 = require("react");
8348
+ var import_react26 = require("react");
8088
8349
  var import_jsx_runtime14 = require("react/jsx-runtime");
8089
8350
  var STRINGS = {
8090
8351
  en: {
@@ -8102,7 +8363,7 @@ var STRINGS = {
8102
8363
  attached: "\u5DF2\u6DFB\u52A0"
8103
8364
  }
8104
8365
  };
8105
- var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8366
+ var ResourcePicker = (0, import_react26.memo)(function ResourcePicker2({
8106
8367
  isOpen,
8107
8368
  onClose,
8108
8369
  providers,
@@ -8124,14 +8385,14 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8124
8385
  className = ""
8125
8386
  }) {
8126
8387
  const strings = STRINGS[locale];
8127
- const pickerRef = (0, import_react25.useRef)(null);
8128
- const searchInputRef = (0, import_react25.useRef)(null);
8129
- const sentinelRef = (0, import_react25.useRef)(null);
8130
- const listRef = (0, import_react25.useRef)(null);
8131
- const scrollRestoredRef = (0, import_react25.useRef)(null);
8132
- const [position, setPosition] = (0, import_react25.useState)({ top: 0, left: 0, width: 280 });
8388
+ const pickerRef = (0, import_react26.useRef)(null);
8389
+ const searchInputRef = (0, import_react26.useRef)(null);
8390
+ const sentinelRef = (0, import_react26.useRef)(null);
8391
+ const listRef = (0, import_react26.useRef)(null);
8392
+ const scrollRestoredRef = (0, import_react26.useRef)(null);
8393
+ const [position, setPosition] = (0, import_react26.useState)({ top: 0, left: 0, width: 280 });
8133
8394
  const getScrollKey = (providerId) => `resource-picker-scroll-${providerId}`;
8134
- (0, import_react25.useEffect)(() => {
8395
+ (0, import_react26.useEffect)(() => {
8135
8396
  if (!isOpen || !anchorRef?.current) return;
8136
8397
  const updatePosition = () => {
8137
8398
  if (!anchorRef?.current) return;
@@ -8148,7 +8409,7 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8148
8409
  window.addEventListener("resize", updatePosition);
8149
8410
  return () => window.removeEventListener("resize", updatePosition);
8150
8411
  }, [isOpen, anchorRef]);
8151
- (0, import_react25.useEffect)(() => {
8412
+ (0, import_react26.useEffect)(() => {
8152
8413
  if (!isOpen) return;
8153
8414
  const handleClickOutside = (e) => {
8154
8415
  if (pickerRef.current && !pickerRef.current.contains(e.target)) {
@@ -8161,17 +8422,17 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8161
8422
  document.addEventListener("mousedown", handleClickOutside);
8162
8423
  return () => document.removeEventListener("mousedown", handleClickOutside);
8163
8424
  }, [isOpen, onClose, anchorRef]);
8164
- (0, import_react25.useEffect)(() => {
8425
+ (0, import_react26.useEffect)(() => {
8165
8426
  if (selectedProviderId && searchInputRef.current) {
8166
8427
  searchInputRef.current.focus();
8167
8428
  }
8168
8429
  }, [selectedProviderId]);
8169
- (0, import_react25.useEffect)(() => {
8430
+ (0, import_react26.useEffect)(() => {
8170
8431
  if (isOpen) {
8171
8432
  scrollRestoredRef.current = null;
8172
8433
  }
8173
8434
  }, [isOpen]);
8174
- (0, import_react25.useEffect)(() => {
8435
+ (0, import_react26.useEffect)(() => {
8175
8436
  if (!isOpen || !selectedProviderId || !listRef.current || resources.length === 0) return;
8176
8437
  if (scrollRestoredRef.current === selectedProviderId) return;
8177
8438
  const savedScroll = sessionStorage.getItem(getScrollKey(selectedProviderId));
@@ -8185,12 +8446,12 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8185
8446
  }
8186
8447
  scrollRestoredRef.current = selectedProviderId;
8187
8448
  }, [isOpen, selectedProviderId, resources.length]);
8188
- const handleListScroll = (0, import_react25.useCallback)((e) => {
8449
+ const handleListScroll = (0, import_react26.useCallback)((e) => {
8189
8450
  if (!selectedProviderId) return;
8190
8451
  const scrollTop = e.currentTarget.scrollTop;
8191
8452
  sessionStorage.setItem(getScrollKey(selectedProviderId), String(scrollTop));
8192
8453
  }, [selectedProviderId]);
8193
- (0, import_react25.useEffect)(() => {
8454
+ (0, import_react26.useEffect)(() => {
8194
8455
  if (!isOpen) return;
8195
8456
  const handleKeyDown = (e) => {
8196
8457
  if (e.key === "Escape") {
@@ -8201,7 +8462,7 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8201
8462
  document.addEventListener("keydown", handleKeyDown);
8202
8463
  return () => document.removeEventListener("keydown", handleKeyDown);
8203
8464
  }, [isOpen, onClose]);
8204
- (0, import_react25.useEffect)(() => {
8465
+ (0, import_react26.useEffect)(() => {
8205
8466
  if (!sentinelRef.current || !hasMore || isLoadingResources) return;
8206
8467
  const observer = new IntersectionObserver(
8207
8468
  (entries) => {
@@ -8214,13 +8475,13 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8214
8475
  observer.observe(sentinelRef.current);
8215
8476
  return () => observer.disconnect();
8216
8477
  }, [hasMore, isLoadingResources, onLoadMore]);
8217
- const handleTabClick = (0, import_react25.useCallback)((providerId) => {
8478
+ const handleTabClick = (0, import_react26.useCallback)((providerId) => {
8218
8479
  if (providerId !== selectedProviderId) {
8219
8480
  onSearchChange("");
8220
8481
  onSelectProvider(providerId);
8221
8482
  }
8222
8483
  }, [selectedProviderId, onSelectProvider, onSearchChange]);
8223
- const handleResourceClick = (0, import_react25.useCallback)((item) => {
8484
+ const handleResourceClick = (0, import_react26.useCallback)((item) => {
8224
8485
  onAttachResource(item);
8225
8486
  }, [onAttachResource]);
8226
8487
  if (!isOpen) return null;
@@ -8481,7 +8742,7 @@ var STRINGS2 = {
8481
8742
  uploadImage: "\u4E0A\u4F20\u56FE\u7247"
8482
8743
  }
8483
8744
  };
8484
- var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8745
+ var AddResourceButton = (0, import_react27.memo)(function AddResourceButton2({
8485
8746
  providers,
8486
8747
  isLoadingProviders,
8487
8748
  onUploadFile,
@@ -8502,11 +8763,11 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8502
8763
  themeClass = ""
8503
8764
  }) {
8504
8765
  const strings = STRINGS2[locale];
8505
- const [isMenuOpen, setIsMenuOpen] = (0, import_react26.useState)(false);
8506
- const [isPickerOpen, setIsPickerOpen] = (0, import_react26.useState)(false);
8507
- const buttonRef = (0, import_react26.useRef)(null);
8508
- const menuRef = (0, import_react26.useRef)(null);
8509
- (0, import_react26.useEffect)(() => {
8766
+ const [isMenuOpen, setIsMenuOpen] = (0, import_react27.useState)(false);
8767
+ const [isPickerOpen, setIsPickerOpen] = (0, import_react27.useState)(false);
8768
+ const buttonRef = (0, import_react27.useRef)(null);
8769
+ const menuRef = (0, import_react27.useRef)(null);
8770
+ (0, import_react27.useEffect)(() => {
8510
8771
  if (!isMenuOpen) return;
8511
8772
  const handleClickOutside = (e) => {
8512
8773
  if (menuRef.current && !menuRef.current.contains(e.target) && buttonRef.current && !buttonRef.current.contains(e.target)) {
@@ -8516,7 +8777,7 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8516
8777
  document.addEventListener("mousedown", handleClickOutside);
8517
8778
  return () => document.removeEventListener("mousedown", handleClickOutside);
8518
8779
  }, [isMenuOpen]);
8519
- (0, import_react26.useEffect)(() => {
8780
+ (0, import_react27.useEffect)(() => {
8520
8781
  if (!isMenuOpen) return;
8521
8782
  const handleKeyDown = (e) => {
8522
8783
  if (e.key === "Escape") {
@@ -8526,7 +8787,7 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8526
8787
  document.addEventListener("keydown", handleKeyDown);
8527
8788
  return () => document.removeEventListener("keydown", handleKeyDown);
8528
8789
  }, [isMenuOpen]);
8529
- const handleButtonClick = (0, import_react26.useCallback)(() => {
8790
+ const handleButtonClick = (0, import_react27.useCallback)(() => {
8530
8791
  if (isPickerOpen) {
8531
8792
  setIsPickerOpen(false);
8532
8793
  onSelectProvider(null);
@@ -8540,31 +8801,31 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8540
8801
  }
8541
8802
  setIsMenuOpen(!isMenuOpen);
8542
8803
  }, [isMenuOpen, isPickerOpen, onSelectProvider, providers, onUploadFile, onUploadImage]);
8543
- const handleUploadFile = (0, import_react26.useCallback)(() => {
8804
+ const handleUploadFile = (0, import_react27.useCallback)(() => {
8544
8805
  setIsMenuOpen(false);
8545
8806
  onUploadFile?.();
8546
8807
  }, [onUploadFile]);
8547
- const handleUploadImage = (0, import_react26.useCallback)(() => {
8808
+ const handleUploadImage = (0, import_react27.useCallback)(() => {
8548
8809
  setIsMenuOpen(false);
8549
8810
  onUploadImage?.();
8550
8811
  }, [onUploadImage]);
8551
- const handleProviderClick = (0, import_react26.useCallback)((providerId) => {
8812
+ const handleProviderClick = (0, import_react27.useCallback)((providerId) => {
8552
8813
  setIsMenuOpen(false);
8553
8814
  onSelectProvider(providerId);
8554
8815
  setIsPickerOpen(true);
8555
8816
  }, [onSelectProvider]);
8556
- const handlePickerClose = (0, import_react26.useCallback)(() => {
8817
+ const handlePickerClose = (0, import_react27.useCallback)(() => {
8557
8818
  setIsPickerOpen(false);
8558
8819
  onSelectProvider(null);
8559
8820
  }, [onSelectProvider]);
8560
- const handlePickerSelectProvider = (0, import_react26.useCallback)((providerId) => {
8821
+ const handlePickerSelectProvider = (0, import_react27.useCallback)((providerId) => {
8561
8822
  if (providerId === null) {
8562
8823
  setIsPickerOpen(false);
8563
8824
  setIsMenuOpen(true);
8564
8825
  }
8565
8826
  onSelectProvider(providerId);
8566
8827
  }, [onSelectProvider]);
8567
- const handleAttachResource = (0, import_react26.useCallback)((item) => {
8828
+ const handleAttachResource = (0, import_react27.useCallback)((item) => {
8568
8829
  onAttachResource(item);
8569
8830
  setIsPickerOpen(false);
8570
8831
  onSelectProvider(null);
@@ -8718,9 +8979,9 @@ var addResourceButtonStyles = `
8718
8979
  `;
8719
8980
 
8720
8981
  // src/renderer/components/AttachedResourceTags.tsx
8721
- var import_react27 = require("react");
8982
+ var import_react28 = require("react");
8722
8983
  var import_jsx_runtime16 = require("react/jsx-runtime");
8723
- var AttachedResourceTags = (0, import_react27.memo)(function AttachedResourceTags2({
8984
+ var AttachedResourceTags = (0, import_react28.memo)(function AttachedResourceTags2({
8724
8985
  resources,
8725
8986
  onRemove,
8726
8987
  disabled
@@ -8799,7 +9060,7 @@ var attachedResourceTagsStyles = `
8799
9060
 
8800
9061
  // src/renderer/components/SanqianChat.tsx
8801
9062
  var import_jsx_runtime17 = require("react/jsx-runtime");
8802
- var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
9063
+ var SanqianChat = (0, import_react29.memo)(function SanqianChat2({
8803
9064
  adapter,
8804
9065
  placeholder,
8805
9066
  autoConnect = true,
@@ -8811,22 +9072,22 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8811
9072
  config,
8812
9073
  logo
8813
9074
  }) {
8814
- const chatContainerRef = (0, import_react28.useRef)(null);
8815
- const chatInputRef = (0, import_react28.useRef)(null);
8816
- const messageListRef = (0, import_react28.useRef)(null);
8817
- const onStateChangeRef = (0, import_react28.useRef)(onStateChange);
8818
- (0, import_react28.useEffect)(() => {
9075
+ const chatContainerRef = (0, import_react29.useRef)(null);
9076
+ const chatInputRef = (0, import_react29.useRef)(null);
9077
+ const messageListRef = (0, import_react29.useRef)(null);
9078
+ const onStateChangeRef = (0, import_react29.useRef)(onStateChange);
9079
+ (0, import_react29.useEffect)(() => {
8819
9080
  onStateChangeRef.current = onStateChange;
8820
9081
  }, [onStateChange]);
8821
9082
  const resolvedConfig = useResolvedUiConfig(config);
8822
9083
  const { themeClass, isDarkMode } = useResolvedTheme(resolvedConfig?.theme ?? "auto");
8823
9084
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
8824
9085
  const resolvedLogo = logo ?? resolvedConfig?.logo;
8825
- const strings = (0, import_react28.useMemo)(
9086
+ const strings = (0, import_react29.useMemo)(
8826
9087
  () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
8827
9088
  [resolvedConfig?.locale, resolvedConfig?.strings]
8828
9089
  );
8829
- const headerConfig = (0, import_react28.useMemo)(
9090
+ const headerConfig = (0, import_react29.useMemo)(
8830
9091
  () => {
8831
9092
  if (!resolvedConfig) {
8832
9093
  return resolvedLogo ? { logo: resolvedLogo } : void 0;
@@ -8839,7 +9100,7 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8839
9100
  [resolvedConfig, resolvedLogo]
8840
9101
  );
8841
9102
  const { logoNode, showPin, showClose, isPinned, togglePin, resolvedOnClose } = useChatHeader(headerConfig);
8842
- const emptyLogoNode = (0, import_react28.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
9103
+ const emptyLogoNode = (0, import_react29.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
8843
9104
  const showHeader = !!(logoNode || showClose || showPin);
8844
9105
  useChatStyles();
8845
9106
  useConnection({ adapter, autoConnect });
@@ -8849,7 +9110,7 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8849
9110
  onConversationChange
8850
9111
  });
8851
9112
  const resourcePicker = useResourcePicker({ adapter });
8852
- const resourceProviders = (0, import_react28.useMemo)(
9113
+ const resourceProviders = (0, import_react29.useMemo)(
8853
9114
  () => resourcePicker.providers.filter((p) => p.hasGetList),
8854
9115
  [resourcePicker.providers]
8855
9116
  );
@@ -8865,13 +9126,17 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8865
9126
  chat.submitHitlInput({ cancelled: true });
8866
9127
  }
8867
9128
  };
8868
- const handleSendMessage = (0, import_react28.useCallback)((content) => {
8869
- chat.sendMessage(content, {
8870
- attachedResources: resourcePicker.attachedResources.length > 0 ? resourcePicker.attachedResources : void 0
9129
+ const handleSendMessage = (0, import_react29.useCallback)(async (content) => {
9130
+ const attachedResources = resourcePicker.attachedResources.length > 0 ? [...resourcePicker.attachedResources] : void 0;
9131
+ const sent = await chat.trySendMessage(content, {
9132
+ attachedResources
8871
9133
  });
8872
- resourcePicker.clearAttachedResources();
9134
+ if (sent) {
9135
+ resourcePicker.clearAttachedResources();
9136
+ }
9137
+ return sent;
8873
9138
  }, [chat, resourcePicker]);
8874
- (0, import_react28.useEffect)(() => {
9139
+ (0, import_react29.useEffect)(() => {
8875
9140
  onStateChangeRef.current?.({
8876
9141
  messages: chat.messages,
8877
9142
  conversationId: chat.conversationId
@@ -8987,6 +9252,7 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8987
9252
  stopLabel: strings.inputStop,
8988
9253
  isStreaming: chat.isStreaming,
8989
9254
  isLoading: chat.isLoading,
9255
+ allowEmptySubmit: resourcePicker.attachedResources.length > 0 || chat.sessionResources.length > 0,
8990
9256
  disabled: !!chat.pendingInterrupt || chat.isLoading && !chat.isStreaming,
8991
9257
  leftSlot: resourceProviders.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8992
9258
  AddResourceButton,
@@ -9017,17 +9283,17 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
9017
9283
  });
9018
9284
 
9019
9285
  // src/renderer/components/FloatingChat.tsx
9020
- var import_react34 = require("react");
9286
+ var import_react35 = require("react");
9021
9287
 
9022
9288
  // src/renderer/components/ModeToggleButton.tsx
9023
- var import_react29 = require("react");
9289
+ var import_react30 = require("react");
9024
9290
  var import_jsx_runtime18 = (
9025
9291
  // Float out icon - two overlapping panels (macOS style)
9026
9292
  require("react/jsx-runtime")
9027
9293
  );
9028
9294
  function ModeToggleButton({ className, locale = "en" }) {
9029
9295
  const { isEmbedded, toggleMode } = useChatPanel();
9030
- const strings = (0, import_react29.useMemo)(() => resolveChatStrings(locale), [locale]);
9296
+ const strings = (0, import_react30.useMemo)(() => resolveChatStrings(locale), [locale]);
9031
9297
  const handleClick = async () => {
9032
9298
  await toggleMode();
9033
9299
  };
@@ -9056,7 +9322,7 @@ function ModeToggleButton({ className, locale = "en" }) {
9056
9322
  }
9057
9323
 
9058
9324
  // src/renderer/components/AttachButton.tsx
9059
- var import_react30 = require("react");
9325
+ var import_react31 = require("react");
9060
9326
  var import_jsx_runtime19 = (
9061
9327
  // Attached state: filled icon
9062
9328
  require("react/jsx-runtime")
@@ -9064,7 +9330,7 @@ var import_jsx_runtime19 = (
9064
9330
  function AttachButton({ className, locale = "en" }) {
9065
9331
  const { isAttached, isAvailable, toggle } = useAttachState();
9066
9332
  const { isFloating } = useChatPanel();
9067
- const strings = (0, import_react30.useMemo)(() => resolveChatStrings(locale), [locale]);
9333
+ const strings = (0, import_react31.useMemo)(() => resolveChatStrings(locale), [locale]);
9068
9334
  if (!isFloating || !isAvailable) return null;
9069
9335
  const handleClick = async () => {
9070
9336
  await toggle();
@@ -9094,16 +9360,16 @@ function AttachButton({ className, locale = "en" }) {
9094
9360
  }
9095
9361
 
9096
9362
  // src/renderer/components/Resizer.tsx
9097
- var import_react31 = require("react");
9363
+ var import_react32 = require("react");
9098
9364
  var import_jsx_runtime20 = require("react/jsx-runtime");
9099
9365
  function useRafThrottle(fn) {
9100
- const rafRef = (0, import_react31.useRef)(null);
9101
- const argsRef = (0, import_react31.useRef)(null);
9102
- const fnRef = (0, import_react31.useRef)(fn);
9103
- (0, import_react31.useLayoutEffect)(() => {
9366
+ const rafRef = (0, import_react32.useRef)(null);
9367
+ const argsRef = (0, import_react32.useRef)(null);
9368
+ const fnRef = (0, import_react32.useRef)(fn);
9369
+ (0, import_react32.useLayoutEffect)(() => {
9104
9370
  fnRef.current = fn;
9105
9371
  });
9106
- const throttledFn = (0, import_react31.useCallback)((...args) => {
9372
+ const throttledFn = (0, import_react32.useCallback)((...args) => {
9107
9373
  argsRef.current = args;
9108
9374
  if (rafRef.current === null) {
9109
9375
  rafRef.current = requestAnimationFrame(() => {
@@ -9114,7 +9380,7 @@ function useRafThrottle(fn) {
9114
9380
  });
9115
9381
  }
9116
9382
  }, []);
9117
- (0, import_react31.useEffect)(() => {
9383
+ (0, import_react32.useEffect)(() => {
9118
9384
  return () => {
9119
9385
  if (rafRef.current !== null) {
9120
9386
  cancelAnimationFrame(rafRef.current);
@@ -9132,19 +9398,19 @@ function Resizer({
9132
9398
  className = ""
9133
9399
  }) {
9134
9400
  const { isEmbedded, width, setWidth, onResizeEnd } = useChatPanel();
9135
- const [isHovered, setIsHovered] = (0, import_react31.useState)(false);
9136
- const [isDragging, setIsDragging] = (0, import_react31.useState)(false);
9137
- const startX = (0, import_react31.useRef)(0);
9138
- const startWidth = (0, import_react31.useRef)(0);
9139
- const widthRef = (0, import_react31.useRef)(width);
9140
- (0, import_react31.useLayoutEffect)(() => {
9401
+ const [isHovered, setIsHovered] = (0, import_react32.useState)(false);
9402
+ const [isDragging, setIsDragging] = (0, import_react32.useState)(false);
9403
+ const startX = (0, import_react32.useRef)(0);
9404
+ const startWidth = (0, import_react32.useRef)(0);
9405
+ const widthRef = (0, import_react32.useRef)(width);
9406
+ (0, import_react32.useLayoutEffect)(() => {
9141
9407
  widthRef.current = width;
9142
9408
  }, [width]);
9143
9409
  const throttledSetWidth = useRafThrottle(setWidth);
9144
- const handleDoubleClick = (0, import_react31.useCallback)(() => {
9410
+ const handleDoubleClick = (0, import_react32.useCallback)(() => {
9145
9411
  setWidth(defaultWidth, true);
9146
9412
  }, [defaultWidth, setWidth]);
9147
- const handleMouseDown = (0, import_react31.useCallback)((e) => {
9413
+ const handleMouseDown = (0, import_react32.useCallback)((e) => {
9148
9414
  e.preventDefault();
9149
9415
  setIsDragging(true);
9150
9416
  startX.current = e.screenX;
@@ -9152,7 +9418,7 @@ function Resizer({
9152
9418
  document.body.style.cursor = "col-resize";
9153
9419
  document.body.style.userSelect = "none";
9154
9420
  }, []);
9155
- (0, import_react31.useEffect)(() => {
9421
+ (0, import_react32.useEffect)(() => {
9156
9422
  if (!isDragging) return;
9157
9423
  const handleMouseMove = (e) => {
9158
9424
  const deltaX = e.screenX - startX.current;
@@ -9229,15 +9495,15 @@ function Resizer({
9229
9495
  }
9230
9496
 
9231
9497
  // src/renderer/components/ResourceChip.tsx
9232
- var import_react32 = require("react");
9498
+ var import_react33 = require("react");
9233
9499
  var import_jsx_runtime21 = require("react/jsx-runtime");
9234
- var ResourceChip = (0, import_react32.memo)(function ResourceChip2({
9500
+ var ResourceChip = (0, import_react33.memo)(function ResourceChip2({
9235
9501
  resource,
9236
9502
  onRemove,
9237
9503
  disabled,
9238
9504
  locale: localeProp
9239
9505
  }) {
9240
- const i18nContext = (0, import_react32.useContext)(I18nContext);
9506
+ const i18nContext = (0, import_react33.useContext)(I18nContext);
9241
9507
  const locale = i18nContext?.locale ?? localeProp ?? "en";
9242
9508
  const strings = resolveChatStrings(locale);
9243
9509
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `resource-chip ${disabled ? "disabled" : ""}`, children: [
@@ -9256,7 +9522,7 @@ var ResourceChip = (0, import_react32.memo)(function ResourceChip2({
9256
9522
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("style", { children: resourceChipStyles })
9257
9523
  ] });
9258
9524
  });
9259
- var ResourceChipList = (0, import_react32.memo)(function ResourceChipList2({
9525
+ var ResourceChipList = (0, import_react33.memo)(function ResourceChipList2({
9260
9526
  resources,
9261
9527
  onRemove,
9262
9528
  disabled
@@ -9335,7 +9601,7 @@ var resourceChipStyles = `
9335
9601
  `;
9336
9602
 
9337
9603
  // src/renderer/hooks/useWindowBackgroundSync.ts
9338
- var import_react33 = require("react");
9604
+ var import_react34 = require("react");
9339
9605
  var cachedPlatform = null;
9340
9606
  var getPlatform = () => {
9341
9607
  if (cachedPlatform !== null) return cachedPlatform;
@@ -9353,7 +9619,7 @@ var setBackgroundColor = (color) => {
9353
9619
  }
9354
9620
  };
9355
9621
  function useWindowBackgroundSync(isDarkMode) {
9356
- (0, import_react33.useEffect)(() => {
9622
+ (0, import_react34.useEffect)(() => {
9357
9623
  if (getPlatform() !== "win32") return;
9358
9624
  const colors = getBaseColors(isDarkMode);
9359
9625
  setBackgroundColor(colors.bg);
@@ -9362,7 +9628,7 @@ function useWindowBackgroundSync(isDarkMode) {
9362
9628
 
9363
9629
  // src/renderer/components/FloatingChat.tsx
9364
9630
  var import_jsx_runtime22 = require("react/jsx-runtime");
9365
- var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9631
+ var FloatingChat = (0, import_react35.memo)(function FloatingChat2({
9366
9632
  messages,
9367
9633
  isLoading,
9368
9634
  isStreaming,
@@ -9385,8 +9651,8 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9385
9651
  header,
9386
9652
  footer
9387
9653
  }) {
9388
- const chatContainerRef = (0, import_react34.useRef)(null);
9389
- const chatInputRef = (0, import_react34.useRef)(null);
9654
+ const chatContainerRef = (0, import_react35.useRef)(null);
9655
+ const chatInputRef = (0, import_react35.useRef)(null);
9390
9656
  useChatStyles();
9391
9657
  useFocusPersistence({
9392
9658
  containerRef: chatContainerRef,
@@ -9398,18 +9664,18 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9398
9664
  const { themeClass, isDarkMode } = useResolvedTheme(themeMode);
9399
9665
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
9400
9666
  useWindowBackgroundSync(isDarkMode);
9401
- const rootStyle = (0, import_react34.useMemo)(() => ({
9667
+ const rootStyle = (0, import_react35.useMemo)(() => ({
9402
9668
  ...accentStyle || {},
9403
9669
  minHeight: "100vh",
9404
9670
  minWidth: "100vw"
9405
9671
  }), [accentStyle]);
9406
9672
  const resolvedLogo = logo ?? resolvedConfig?.logo;
9407
9673
  const resolvedLocale = resolvedConfig?.locale ?? locale;
9408
- const resolvedStrings = (0, import_react34.useMemo)(
9674
+ const resolvedStrings = (0, import_react35.useMemo)(
9409
9675
  () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
9410
9676
  [resolvedLocale, resolvedConfig?.strings]
9411
9677
  );
9412
- const headerConfig = (0, import_react34.useMemo)(
9678
+ const headerConfig = (0, import_react35.useMemo)(
9413
9679
  () => {
9414
9680
  if (!resolvedConfig) {
9415
9681
  return resolvedLogo ? { logo: resolvedLogo } : void 0;
@@ -9422,7 +9688,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9422
9688
  [resolvedConfig, resolvedLogo]
9423
9689
  );
9424
9690
  const { logoNode, showPin, showClose, isPinned, togglePin, resolvedOnClose } = useChatHeader(headerConfig);
9425
- const emptyLogoNode = (0, import_react34.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
9691
+ const emptyLogoNode = (0, import_react35.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
9426
9692
  const inputPlaceholder = placeholder ?? resolvedStrings.inputPlaceholder;
9427
9693
  const showHeader = !!(header || logoNode || showPin || showClose);
9428
9694
  const defaultHeader = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("header", { className: "flex h-9 flex-shrink-0 items-center px-2 border-b chat-divider-border", children: [
@@ -9467,7 +9733,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9467
9733
  ] })
9468
9734
  ] });
9469
9735
  const resolvedHeader = header ?? (showHeader ? defaultHeader : null);
9470
- const defaultRenderMessage = (0, import_react34.useCallback)(
9736
+ const defaultRenderMessage = (0, import_react35.useCallback)(
9471
9737
  (message) => {
9472
9738
  if (message.role === "tool") return null;
9473
9739
  const isUser = message.role === "user";
@@ -9481,7 +9747,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9481
9747
  },
9482
9748
  [renderContent]
9483
9749
  );
9484
- const defaultRenderHitl = (0, import_react34.useCallback)(
9750
+ const defaultRenderHitl = (0, import_react35.useCallback)(
9485
9751
  (interrupt, onApprove, onReject) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg m-2", children: [
9486
9752
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium mb-2", children: interrupt.type === "approval_request" ? resolvedStrings.hitlApprovalRequired : resolvedStrings.hitlInputRequired }),
9487
9753
  interrupt.tool && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { className: "text-sm mb-2", children: [
@@ -9562,6 +9828,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9562
9828
  stopLabel: resolvedStrings.inputStop,
9563
9829
  isStreaming,
9564
9830
  isLoading,
9831
+ allowEmptySubmit: !!sessionResources && sessionResources.length > 0,
9565
9832
  disabled: !!pendingInterrupt || isLoading && !isStreaming,
9566
9833
  autoFocus: true
9567
9834
  }
@@ -9571,10 +9838,10 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9571
9838
  });
9572
9839
 
9573
9840
  // src/renderer/components/HistoryList.tsx
9574
- var import_react35 = require("react");
9841
+ var import_react36 = require("react");
9575
9842
  var import_jsx_runtime23 = require("react/jsx-runtime");
9576
9843
  function DeleteButton({ onClick, title, colors }) {
9577
- const [isHovered, setIsHovered] = (0, import_react35.useState)(false);
9844
+ const [isHovered, setIsHovered] = (0, import_react36.useState)(false);
9578
9845
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9579
9846
  "button",
9580
9847
  {
@@ -9618,7 +9885,7 @@ function DeleteButton({ onClick, title, colors }) {
9618
9885
  );
9619
9886
  }
9620
9887
  function LoadMoreButton({ onClick, colors, children }) {
9621
- const [isHovered, setIsHovered] = (0, import_react35.useState)(false);
9888
+ const [isHovered, setIsHovered] = (0, import_react36.useState)(false);
9622
9889
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9623
9890
  "button",
9624
9891
  {
@@ -9668,7 +9935,7 @@ function formatRelativeTime(dateStr, strings) {
9668
9935
  return date.toLocaleDateString(void 0, { month: "short", day: "numeric" });
9669
9936
  }
9670
9937
  }
9671
- var HistoryList = (0, import_react35.memo)(function HistoryList2({
9938
+ var HistoryList = (0, import_react36.memo)(function HistoryList2({
9672
9939
  conversations,
9673
9940
  selectedId,
9674
9941
  isLoading,
@@ -9680,13 +9947,13 @@ var HistoryList = (0, import_react35.memo)(function HistoryList2({
9680
9947
  isDarkMode = false,
9681
9948
  strings = {}
9682
9949
  }) {
9683
- const [hoveredId, setHoveredId] = (0, import_react35.useState)(null);
9684
- const loadMoreRef = (0, import_react35.useRef)(null);
9685
- const isLoadingRef = (0, import_react35.useRef)(isLoading);
9686
- (0, import_react35.useEffect)(() => {
9950
+ const [hoveredId, setHoveredId] = (0, import_react36.useState)(null);
9951
+ const loadMoreRef = (0, import_react36.useRef)(null);
9952
+ const isLoadingRef = (0, import_react36.useRef)(isLoading);
9953
+ (0, import_react36.useEffect)(() => {
9687
9954
  isLoadingRef.current = isLoading;
9688
9955
  }, [isLoading]);
9689
- (0, import_react35.useEffect)(() => {
9956
+ (0, import_react36.useEffect)(() => {
9690
9957
  if (!hasMore || loadError || !onLoadMore) return;
9691
9958
  const sentinel = loadMoreRef.current;
9692
9959
  if (!sentinel) return;
@@ -9783,10 +10050,10 @@ var HistoryList = (0, import_react35.memo)(function HistoryList2({
9783
10050
  });
9784
10051
 
9785
10052
  // src/renderer/components/HistoryModal.tsx
9786
- var import_react36 = require("react");
10053
+ var import_react37 = require("react");
9787
10054
  var import_jsx_runtime24 = require("react/jsx-runtime");
9788
10055
  var ANIMATION_DURATION = 120;
9789
- var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
10056
+ var HistoryModal = (0, import_react37.memo)(function HistoryModal2({
9790
10057
  isOpen,
9791
10058
  onClose,
9792
10059
  title,
@@ -9794,10 +10061,10 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9794
10061
  children,
9795
10062
  isDarkMode = false
9796
10063
  }) {
9797
- const modalRef = (0, import_react36.useRef)(null);
9798
- const [isClosing, setIsClosing] = (0, import_react36.useState)(false);
9799
- const [shouldRender, setShouldRender] = (0, import_react36.useState)(isOpen);
9800
- (0, import_react36.useEffect)(() => {
10064
+ const modalRef = (0, import_react37.useRef)(null);
10065
+ const [isClosing, setIsClosing] = (0, import_react37.useState)(false);
10066
+ const [shouldRender, setShouldRender] = (0, import_react37.useState)(isOpen);
10067
+ (0, import_react37.useEffect)(() => {
9801
10068
  if (isOpen) {
9802
10069
  setShouldRender(true);
9803
10070
  setIsClosing(false);
@@ -9809,7 +10076,7 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9809
10076
  }, ANIMATION_DURATION);
9810
10077
  }
9811
10078
  }, [isOpen, shouldRender, isClosing]);
9812
- const handleClose = (0, import_react36.useCallback)(() => {
10079
+ const handleClose = (0, import_react37.useCallback)(() => {
9813
10080
  setIsClosing(true);
9814
10081
  setTimeout(() => {
9815
10082
  setShouldRender(false);
@@ -9817,7 +10084,7 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9817
10084
  onClose();
9818
10085
  }, ANIMATION_DURATION);
9819
10086
  }, [onClose]);
9820
- (0, import_react36.useEffect)(() => {
10087
+ (0, import_react37.useEffect)(() => {
9821
10088
  if (!shouldRender || isClosing) return;
9822
10089
  const handleKeyDown = (e) => {
9823
10090
  if (e.key === "Escape") {
@@ -9828,11 +10095,11 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9828
10095
  document.addEventListener("keydown", handleKeyDown);
9829
10096
  return () => document.removeEventListener("keydown", handleKeyDown);
9830
10097
  }, [shouldRender, isClosing, handleClose]);
9831
- (0, import_react36.useEffect)(() => {
10098
+ (0, import_react37.useEffect)(() => {
9832
10099
  if (!isOpen || !modalRef.current) return;
9833
10100
  modalRef.current.focus();
9834
10101
  }, [isOpen]);
9835
- const handleBackdropClick = (0, import_react36.useCallback)(
10102
+ const handleBackdropClick = (0, import_react37.useCallback)(
9836
10103
  (e) => {
9837
10104
  if (e.target === e.currentTarget && !isClosing) {
9838
10105
  handleClose();
@@ -9971,7 +10238,7 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9971
10238
  });
9972
10239
 
9973
10240
  // src/renderer/components/CompactChat.tsx
9974
- var import_react37 = require("react");
10241
+ var import_react38 = require("react");
9975
10242
  var import_react_dom = require("react-dom");
9976
10243
 
9977
10244
  // src/renderer/components/PanelControls.tsx
@@ -10011,7 +10278,7 @@ var PanelControls = {
10011
10278
 
10012
10279
  // src/renderer/components/CompactChat.tsx
10013
10280
  var import_jsx_runtime26 = require("react/jsx-runtime");
10014
- var CompactChat = (0, import_react37.memo)(function CompactChat2({
10281
+ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10015
10282
  adapter,
10016
10283
  config,
10017
10284
  logo,
@@ -10047,12 +10314,12 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10047
10314
  const { themeClass, isDarkMode: resolvedIsDarkMode } = useResolvedTheme(themeMode);
10048
10315
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
10049
10316
  const resolvedLogo = logo ?? resolvedConfig?.logo;
10050
- const baseStrings = (0, import_react37.useMemo)(
10317
+ const baseStrings = (0, import_react38.useMemo)(
10051
10318
  () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
10052
10319
  [resolvedConfig?.locale, resolvedConfig?.strings]
10053
10320
  );
10054
- const mergedStrings = (0, import_react37.useMemo)(() => ({ ...baseStrings, ...strings }), [baseStrings, strings]);
10055
- const headerConfig = (0, import_react37.useMemo)(
10321
+ const mergedStrings = (0, import_react38.useMemo)(() => ({ ...baseStrings, ...strings }), [baseStrings, strings]);
10322
+ const headerConfig = (0, import_react38.useMemo)(
10056
10323
  () => {
10057
10324
  if (!resolvedConfig) {
10058
10325
  return resolvedLogo ? { logo: resolvedLogo } : void 0;
@@ -10065,13 +10332,14 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10065
10332
  [resolvedConfig, resolvedLogo]
10066
10333
  );
10067
10334
  const { logoNode, showPin, showClose, isPinned, togglePin, resolvedOnClose } = useChatHeader(headerConfig);
10068
- const emptyLogoNode = (0, import_react37.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
10335
+ const emptyLogoNode = (0, import_react38.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
10069
10336
  const { isEmbedded, toggleMode, hide: hidePanel } = useChatPanel();
10070
- const chatContainerRef = (0, import_react37.useRef)(null);
10071
- const chatInputRef = (0, import_react37.useRef)(null);
10072
- const [showHistory, setShowHistory] = (0, import_react37.useState)(false);
10073
- const [connectionAlert, setConnectionAlert] = (0, import_react37.useState)(null);
10074
- const portalContainerRef = (0, import_react37.useRef)(inputPortalContainer ?? null);
10337
+ const chatContainerRef = (0, import_react38.useRef)(null);
10338
+ const chatInputRef = (0, import_react38.useRef)(null);
10339
+ const [showHistory, setShowHistory] = (0, import_react38.useState)(false);
10340
+ useWindowDragLock(showHistory);
10341
+ const [connectionAlert, setConnectionAlert] = (0, import_react38.useState)(null);
10342
+ const portalContainerRef = (0, import_react38.useRef)(inputPortalContainer ?? null);
10075
10343
  portalContainerRef.current = inputPortalContainer ?? null;
10076
10344
  const shouldRenderInputExternally = !!inputPortalContainer;
10077
10345
  const inputPlaceholder = placeholder ?? mergedStrings.inputPlaceholder;
@@ -10110,49 +10378,55 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10110
10378
  onError
10111
10379
  });
10112
10380
  const resourcePicker = useResourcePicker({ adapter });
10113
- const resourceProviders = (0, import_react37.useMemo)(
10381
+ const resourceProviders = (0, import_react38.useMemo)(
10114
10382
  () => resourcePicker.providers.filter((p) => p.hasGetList),
10115
10383
  [resourcePicker.providers]
10116
10384
  );
10117
- const handleHitlCancel = (0, import_react37.useCallback)(() => {
10385
+ const handleHitlCancel = (0, import_react38.useCallback)(() => {
10118
10386
  if (chat.pendingInterrupt?.type === "approval_request") {
10119
10387
  chat.rejectHitl();
10120
10388
  } else {
10121
10389
  chat.submitHitlInput({ cancelled: true });
10122
10390
  }
10123
10391
  }, [chat.pendingInterrupt?.type, chat.rejectHitl, chat.submitHitlInput]);
10124
- const handleSendWithResources = (0, import_react37.useCallback)((content) => {
10125
- const resources = resourcePicker.attachedResources;
10126
- chat.sendMessage(content, resources.length > 0 ? { attachedResources: resources } : void 0);
10127
- resourcePicker.clearAttachedResources();
10128
- }, [chat.sendMessage, resourcePicker.attachedResources, resourcePicker.clearAttachedResources]);
10129
- (0, import_react37.useEffect)(() => {
10392
+ const handleSendWithResources = (0, import_react38.useCallback)(async (content) => {
10393
+ const resources = [...resourcePicker.attachedResources];
10394
+ const sent = await chat.trySendMessage(
10395
+ content,
10396
+ resources.length > 0 ? { attachedResources: resources } : void 0
10397
+ );
10398
+ if (sent) {
10399
+ resourcePicker.clearAttachedResources();
10400
+ }
10401
+ return sent;
10402
+ }, [chat.trySendMessage, resourcePicker.attachedResources, resourcePicker.clearAttachedResources]);
10403
+ (0, import_react38.useEffect)(() => {
10130
10404
  if (sendMessageRef) {
10131
10405
  sendMessageRef.current = chat.sendMessage;
10132
10406
  }
10133
10407
  }, [sendMessageRef, chat.sendMessage]);
10134
- (0, import_react37.useEffect)(() => {
10408
+ (0, import_react38.useEffect)(() => {
10135
10409
  if (newConversationRef) {
10136
10410
  newConversationRef.current = chat.newConversation;
10137
10411
  }
10138
10412
  }, [newConversationRef, chat.newConversation]);
10139
- (0, import_react37.useEffect)(() => {
10413
+ (0, import_react38.useEffect)(() => {
10140
10414
  if (parentFocusInputRef) {
10141
10415
  parentFocusInputRef.current = () => chatInputRef.current?.focus();
10142
10416
  }
10143
10417
  }, [parentFocusInputRef]);
10144
- (0, import_react37.useEffect)(() => {
10418
+ (0, import_react38.useEffect)(() => {
10145
10419
  if (parentSetTextRef) {
10146
10420
  parentSetTextRef.current = (text) => chatInputRef.current?.setValue(text);
10147
10421
  }
10148
10422
  }, [parentSetTextRef]);
10149
- (0, import_react37.useEffect)(() => {
10423
+ (0, import_react38.useEffect)(() => {
10150
10424
  if (!adapter.onFocusInput) return;
10151
10425
  return adapter.onFocusInput(() => {
10152
10426
  chatInputRef.current?.focus();
10153
10427
  });
10154
10428
  }, [adapter]);
10155
- (0, import_react37.useEffect)(() => {
10429
+ (0, import_react38.useEffect)(() => {
10156
10430
  if (onMessageReceived && chat.messages.length > 0) {
10157
10431
  const lastMessage = chat.messages[chat.messages.length - 1];
10158
10432
  if (lastMessage.role === "assistant" && !lastMessage.isStreaming) {
@@ -10160,12 +10434,12 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10160
10434
  }
10161
10435
  }
10162
10436
  }, [chat.messages, onMessageReceived]);
10163
- (0, import_react37.useEffect)(() => {
10437
+ (0, import_react38.useEffect)(() => {
10164
10438
  if (onLoadingChange) {
10165
10439
  onLoadingChange(chat.isLoading);
10166
10440
  }
10167
10441
  }, [chat.isLoading, onLoadingChange]);
10168
- (0, import_react37.useEffect)(() => {
10442
+ (0, import_react38.useEffect)(() => {
10169
10443
  if (onStateChange) {
10170
10444
  onStateChange({
10171
10445
  messages: chat.messages,
@@ -10173,24 +10447,24 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10173
10447
  });
10174
10448
  }
10175
10449
  }, [chat.messages, chat.conversationId, onStateChange]);
10176
- (0, import_react37.useEffect)(() => {
10450
+ (0, import_react38.useEffect)(() => {
10177
10451
  if (connection.isConnected) {
10178
10452
  resourcePicker.refreshProviders();
10179
10453
  }
10180
10454
  }, [connection.isConnected]);
10181
- (0, import_react37.useEffect)(() => {
10455
+ (0, import_react38.useEffect)(() => {
10182
10456
  if (showHistory && connection.isConnected) {
10183
10457
  conversations.loadConversations();
10184
10458
  }
10185
10459
  }, [showHistory, connection.isConnected]);
10186
- const handleSelectConversation = (0, import_react37.useCallback)(
10460
+ const handleSelectConversation = (0, import_react38.useCallback)(
10187
10461
  async (id) => {
10188
10462
  setShowHistory(false);
10189
10463
  await chat.loadConversation(id);
10190
10464
  },
10191
10465
  [chat]
10192
10466
  );
10193
- const handleDeleteConversation = (0, import_react37.useCallback)(
10467
+ const handleDeleteConversation = (0, import_react38.useCallback)(
10194
10468
  async (id) => {
10195
10469
  await conversations.deleteConversation(id);
10196
10470
  if (id === chat.conversationId) {
@@ -10199,14 +10473,14 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10199
10473
  },
10200
10474
  [conversations, chat]
10201
10475
  );
10202
- const handleNewChat = (0, import_react37.useCallback)(() => {
10476
+ const handleNewChat = (0, import_react38.useCallback)(() => {
10203
10477
  chat.newConversation();
10204
10478
  setShowHistory(false);
10205
10479
  setTimeout(() => {
10206
10480
  chatInputRef.current?.focus();
10207
10481
  }, 0);
10208
10482
  }, [chat]);
10209
- (0, import_react37.useEffect)(() => {
10483
+ (0, import_react38.useEffect)(() => {
10210
10484
  const handleKeyDown = (e) => {
10211
10485
  const isMac2 = navigator.platform.includes("Mac");
10212
10486
  const modifierKey = isMac2 ? e.metaKey : e.ctrlKey;
@@ -10220,20 +10494,19 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10220
10494
  }, [handleNewChat]);
10221
10495
  const isMac = typeof navigator !== "undefined" && navigator.platform.includes("Mac");
10222
10496
  const shortcutKey = isMac ? "\u2318N" : "Ctrl+N";
10223
- const defaultRenderMessage = (0, import_react37.useCallback)((message) => {
10497
+ const defaultRenderMessage = (0, import_react38.useCallback)((message) => {
10224
10498
  if (message.role === "tool") return null;
10225
10499
  const isUser = message.role === "user";
10226
- const hasToolCalls = message.toolCalls && message.toolCalls.length > 0;
10227
- const hasToolBlocks = message.blocks && message.blocks.some((b) => b.type === "tool_call" || b.type === "tool_result");
10228
- const hasToolActivity = Boolean(hasToolCalls || hasToolBlocks);
10229
- const hasIntermediateBlocks = message.blocks && message.blocks.some((b) => b.isIntermediate);
10230
- const showIntermediateSteps = message.isComplete && hasIntermediateBlocks && hasToolActivity;
10231
- const showStreamingTimeline = !isUser && !showIntermediateSteps && hasToolActivity;
10232
- const hasThinking = message.thinking || message.isThinkingStreaming || message.currentThinking;
10233
- const showThinkingSection = !isUser && !hasToolActivity && !showIntermediateSteps && hasThinking;
10234
- const isToolCallsStreaming = message.isToolCallsStreaming ?? ((message.toolCalls?.some((tc) => tc.status === "running") ?? false) || (message.blocks?.some((b) => b.type === "tool_call" && b.toolStatus === "running") ?? false));
10500
+ const {
10501
+ displayBlocks,
10502
+ isEffectivelyComplete,
10503
+ showIntermediateSteps,
10504
+ showStreamingTimeline,
10505
+ showThinkingSection
10506
+ } = getAssistantDisplayState(message);
10507
+ const isToolCallsStreaming = message.isToolCallsStreaming ?? ((message.toolCalls?.some((tc) => tc.status === "running") ?? false) || (displayBlocks?.some((b) => b.type === "tool_call" && b.toolStatus === "running") ?? false));
10235
10508
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: `flex ${isUser ? "justify-end" : "justify-start"} mb-4`, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: `text-left ${isUser ? "max-w-[80%]" : "w-full"}`, children: [
10236
- !isUser && showIntermediateSteps && message.blocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(IntermediateSteps, { blocks: message.blocks, strings: mergedStrings }),
10509
+ !isUser && showIntermediateSteps && displayBlocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(IntermediateSteps, { blocks: displayBlocks, strings: mergedStrings }),
10237
10510
  showThinkingSection && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10238
10511
  ThinkingSection,
10239
10512
  {
@@ -10241,18 +10514,18 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10241
10514
  currentThinking: message.currentThinking,
10242
10515
  isStreaming: message.isThinkingStreaming,
10243
10516
  isPaused: message.isThinkingPaused,
10244
- isComplete: message.isComplete,
10517
+ isComplete: isEffectivelyComplete,
10245
10518
  strings: mergedStrings
10246
10519
  }
10247
10520
  ),
10248
- showStreamingTimeline && message.blocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10521
+ showStreamingTimeline && displayBlocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10249
10522
  StreamingTimeline,
10250
10523
  {
10251
- blocks: message.blocks,
10524
+ blocks: displayBlocks,
10252
10525
  currentThinking: message.currentThinking,
10253
10526
  isThinkingStreaming: message.isThinkingStreaming,
10254
10527
  isToolCallsStreaming,
10255
- isComplete: message.isComplete,
10528
+ isComplete: isEffectivelyComplete,
10256
10529
  strings: mergedStrings
10257
10530
  }
10258
10531
  ),
@@ -10274,11 +10547,11 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10274
10547
  ] }) });
10275
10548
  }, [mergedStrings, linkHandler]);
10276
10549
  const containerClass = floating ? `relative chat-window-container ${themeClass}${className ? ` ${className}` : ""}` : `relative flex h-full flex-col bg-[var(--chat-bg)] text-[var(--chat-text)] ${themeClass}${className ? ` ${className}` : ""}`;
10277
- const baseColors = (0, import_react37.useMemo)(() => {
10550
+ const baseColors = (0, import_react38.useMemo)(() => {
10278
10551
  if (!floating) return null;
10279
10552
  return getBaseColors(resolvedIsDarkMode);
10280
10553
  }, [floating, resolvedIsDarkMode]);
10281
- const containerStyle = (0, import_react37.useMemo)(() => {
10554
+ const containerStyle = (0, import_react38.useMemo)(() => {
10282
10555
  const baseStyle = floating && baseColors ? {
10283
10556
  ...accentStyle,
10284
10557
  // Override CSS variables with tinted colors for consistency with app
@@ -10306,7 +10579,7 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10306
10579
  "data-chat-font-size": resolvedConfig?.fontSize || "normal",
10307
10580
  children: [
10308
10581
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PanelResizer, {}),
10309
- !hideHeader && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("header", { className: "flex flex-shrink-0 items-center px-2 border-b chat-divider-border", style: { height: 42, WebkitAppRegion: "drag" }, children: isEmbedded ? (
10582
+ !hideHeader && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("header", { className: "flex flex-shrink-0 items-center px-2 border-b chat-divider-border chat-drag-region", style: { height: 42, WebkitAppRegion: "drag" }, children: isEmbedded ? (
10310
10583
  // Embedded mode: left-aligned buttons (collapse, float, history, new chat)
10311
10584
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-0.5", style: { WebkitAppRegion: "no-drag" }, children: [
10312
10585
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "chat-tooltip-wrapper", children: [
@@ -10481,6 +10754,7 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10481
10754
  (() => {
10482
10755
  if (hideInput && !shouldRenderInputExternally) return null;
10483
10756
  const disableInput = !!chat.pendingInterrupt || chat.isLoading && !chat.isStreaming;
10757
+ const allowEmptySubmit = resourcePicker.attachedResources.length > 0 || chat.sessionResources.length > 0;
10484
10758
  const addResourceButtonSlot = resourceProviders.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10485
10759
  AddResourceButton,
10486
10760
  {
@@ -10514,6 +10788,7 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10514
10788
  disabled: disableInput,
10515
10789
  isStreaming: chat.isStreaming,
10516
10790
  isLoading: chat.isLoading,
10791
+ allowEmptySubmit,
10517
10792
  autoFocus: true,
10518
10793
  leftSlot: addResourceButtonSlot
10519
10794
  }
@@ -10662,5 +10937,6 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10662
10937
  useResourcePicker,
10663
10938
  useStandaloneI18n,
10664
10939
  useStandaloneTheme,
10665
- useTheme
10940
+ useTheme,
10941
+ useWindowDragLock
10666
10942
  });