@yushaw/sanqian-chat 0.2.19 → 0.2.21

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,13 @@ 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 canSend = (text.trim().length > 0 || allowEmptySubmit) && !disabled && !isLoading;
7646
7873
  const showStopButton = isStreaming && !!onStop;
7647
7874
  const showSpinner = isLoading && !showStopButton;
7648
- (0, import_react22.useImperativeHandle)(
7875
+ (0, import_react23.useImperativeHandle)(
7649
7876
  ref,
7650
7877
  () => ({
7651
7878
  focus: () => textareaRef.current?.focus(),
@@ -7655,26 +7882,35 @@ var ChatInput = (0, import_react22.memo)(
7655
7882
  }),
7656
7883
  [text]
7657
7884
  );
7658
- (0, import_react22.useEffect)(() => {
7885
+ (0, import_react23.useEffect)(() => {
7659
7886
  if (autoFocus) {
7660
7887
  const timer = setTimeout(() => textareaRef.current?.focus(), 100);
7661
7888
  return () => clearTimeout(timer);
7662
7889
  }
7663
7890
  }, [autoFocus]);
7664
- const handleSubmit = (0, import_react22.useCallback)(
7665
- (e) => {
7891
+ const handleSubmit = (0, import_react23.useCallback)(
7892
+ async (e) => {
7666
7893
  e?.preventDefault();
7667
7894
  if (!canSend) return;
7668
- onSend(text.trim());
7669
- setText("");
7895
+ if (submitInFlightRef.current) return;
7896
+ submitInFlightRef.current = true;
7897
+ try {
7898
+ const result = await onSend(text.trim());
7899
+ if (result !== false) {
7900
+ setText("");
7901
+ }
7902
+ } catch {
7903
+ } finally {
7904
+ submitInFlightRef.current = false;
7905
+ }
7670
7906
  },
7671
7907
  [text, canSend, onSend]
7672
7908
  );
7673
- const handleKeyDown = (0, import_react22.useCallback)(
7909
+ const handleKeyDown = (0, import_react23.useCallback)(
7674
7910
  (e) => {
7675
7911
  if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
7676
7912
  e.preventDefault();
7677
- handleSubmit();
7913
+ void handleSubmit();
7678
7914
  }
7679
7915
  if (e.key === "Escape" && showStopButton) {
7680
7916
  e.preventDefault();
@@ -7685,60 +7921,70 @@ var ChatInput = (0, import_react22.memo)(
7685
7921
  );
7686
7922
  const leftPadding = leftSlot ? "2rem" : "0.75rem";
7687
7923
  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
- ] });
7924
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
7925
+ "form",
7926
+ {
7927
+ onSubmit: (e) => {
7928
+ void handleSubmit(e);
7929
+ },
7930
+ className: `chat-input-form${className ? ` ${className}` : ""}`,
7931
+ style,
7932
+ children: [
7933
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-box", children: [
7934
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7935
+ "div",
7936
+ {
7937
+ className: "chat-input-replica",
7938
+ style: { paddingLeft: leftPadding, paddingRight: rightPadding },
7939
+ "aria-hidden": "true",
7940
+ children: text + " "
7941
+ }
7942
+ ),
7943
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7944
+ "textarea",
7945
+ {
7946
+ ref: textareaRef,
7947
+ value: text,
7948
+ onChange: (e) => setText(e.target.value),
7949
+ onKeyDown: handleKeyDown,
7950
+ placeholder,
7951
+ disabled,
7952
+ rows: 1,
7953
+ className: "chat-input-textarea",
7954
+ style: { paddingLeft: leftPadding, paddingRight: rightPadding }
7955
+ }
7956
+ ),
7957
+ leftSlot && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-left-slot", children: leftSlot }),
7958
+ rightSlot && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-right-slot", children: rightSlot })
7959
+ ] }),
7960
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-send-wrapper", children: [
7961
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7962
+ "button",
7963
+ {
7964
+ type: showStopButton ? "button" : "submit",
7965
+ onClick: showStopButton ? onStop : void 0,
7966
+ disabled: !showStopButton && !canSend,
7967
+ className: "chat-input-send-btn",
7968
+ "aria-label": showStopButton ? stopLabel : sendLabel,
7969
+ children: showSpinner ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", className: "animate-spin", children: [
7970
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", opacity: "0.25" }),
7971
+ /* @__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" })
7972
+ ] }) : 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" }) })
7973
+ }
7974
+ ),
7975
+ showStopButton && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "chat-input-stop-tooltip", role: "note", children: [
7976
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: stopLabel }),
7977
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "chat-input-stop-hotkey", children: "Esc" })
7978
+ ] })
7979
+ ] })
7980
+ ]
7981
+ }
7982
+ );
7737
7983
  })
7738
7984
  );
7739
7985
 
7740
7986
  // src/renderer/components/HitlCard.tsx
7741
- var import_react23 = require("react");
7987
+ var import_react24 = require("react");
7742
7988
  var import_jsx_runtime12 = require("react/jsx-runtime");
7743
7989
  var defaultStrings = {
7744
7990
  approve: "Approve",
@@ -7777,7 +8023,7 @@ var riskColors = {
7777
8023
  icon: "!!"
7778
8024
  }
7779
8025
  };
7780
- var HitlCard = (0, import_react23.memo)(function HitlCard2({
8026
+ var HitlCard = (0, import_react24.memo)(function HitlCard2({
7781
8027
  interrupt,
7782
8028
  onApprove,
7783
8029
  onReject,
@@ -7789,16 +8035,16 @@ var HitlCard = (0, import_react23.memo)(function HitlCard2({
7789
8035
  const t = { ...defaultStrings, ...strings };
7790
8036
  const isApproval = interrupt.type === "approval_request";
7791
8037
  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);
8038
+ const [answer, setAnswer] = (0, import_react24.useState)(interrupt.default || "");
8039
+ const [selectedIndices, setSelectedIndices] = (0, import_react24.useState)([]);
8040
+ const [isComposing, setIsComposing] = (0, import_react24.useState)(false);
8041
+ const [timeLeft, setTimeLeft] = (0, import_react24.useState)(interrupt.timeout ?? null);
8042
+ const [rememberChoice, setRememberChoice] = (0, import_react24.useState)(false);
8043
+ const inputRef = (0, import_react24.useRef)(null);
8044
+ const textareaRef = (0, import_react24.useRef)(null);
7799
8045
  const riskLevel = interrupt.risk_level || "medium";
7800
8046
  const riskStyle = riskColors[riskLevel];
7801
- (0, import_react23.useEffect)(() => {
8047
+ (0, import_react24.useEffect)(() => {
7802
8048
  if (isUserInput) {
7803
8049
  if (!interrupt.options || interrupt.options.length === 0) {
7804
8050
  inputRef.current?.focus();
@@ -7806,7 +8052,7 @@ var HitlCard = (0, import_react23.memo)(function HitlCard2({
7806
8052
  }
7807
8053
  }
7808
8054
  }, [isUserInput, interrupt.options]);
7809
- (0, import_react23.useEffect)(() => {
8055
+ (0, import_react24.useEffect)(() => {
7810
8056
  if (timeLeft === null || timeLeft <= 0) return;
7811
8057
  const timer = setInterval(() => {
7812
8058
  setTimeLeft((prev) => {
@@ -8004,12 +8250,12 @@ var HitlCard = (0, import_react23.memo)(function HitlCard2({
8004
8250
  });
8005
8251
 
8006
8252
  // src/renderer/primitives/AlertBanner.tsx
8007
- var import_react24 = require("react");
8253
+ var import_react25 = require("react");
8008
8254
  var import_jsx_runtime13 = (
8009
8255
  // Error icon (circle with X)
8010
8256
  require("react/jsx-runtime")
8011
8257
  );
8012
- var AlertBanner = (0, import_react24.memo)(function AlertBanner2({
8258
+ var AlertBanner = (0, import_react25.memo)(function AlertBanner2({
8013
8259
  type,
8014
8260
  message,
8015
8261
  action,
@@ -8019,7 +8265,7 @@ var AlertBanner = (0, import_react24.memo)(function AlertBanner2({
8019
8265
  maxLines = 3,
8020
8266
  strings
8021
8267
  }) {
8022
- const [isExpanded, setIsExpanded] = (0, import_react24.useState)(false);
8268
+ const [isExpanded, setIsExpanded] = (0, import_react25.useState)(false);
8023
8269
  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
8270
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
8025
8271
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M15 9l-6 6M9 9l6 6", strokeLinecap: "round" })
@@ -8081,10 +8327,10 @@ var AlertBanner = (0, import_react24.memo)(function AlertBanner2({
8081
8327
  });
8082
8328
 
8083
8329
  // src/renderer/components/AddResourceButton.tsx
8084
- var import_react26 = require("react");
8330
+ var import_react27 = require("react");
8085
8331
 
8086
8332
  // src/renderer/components/ResourcePicker.tsx
8087
- var import_react25 = require("react");
8333
+ var import_react26 = require("react");
8088
8334
  var import_jsx_runtime14 = require("react/jsx-runtime");
8089
8335
  var STRINGS = {
8090
8336
  en: {
@@ -8102,7 +8348,7 @@ var STRINGS = {
8102
8348
  attached: "\u5DF2\u6DFB\u52A0"
8103
8349
  }
8104
8350
  };
8105
- var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8351
+ var ResourcePicker = (0, import_react26.memo)(function ResourcePicker2({
8106
8352
  isOpen,
8107
8353
  onClose,
8108
8354
  providers,
@@ -8124,14 +8370,14 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8124
8370
  className = ""
8125
8371
  }) {
8126
8372
  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 });
8373
+ const pickerRef = (0, import_react26.useRef)(null);
8374
+ const searchInputRef = (0, import_react26.useRef)(null);
8375
+ const sentinelRef = (0, import_react26.useRef)(null);
8376
+ const listRef = (0, import_react26.useRef)(null);
8377
+ const scrollRestoredRef = (0, import_react26.useRef)(null);
8378
+ const [position, setPosition] = (0, import_react26.useState)({ top: 0, left: 0, width: 280 });
8133
8379
  const getScrollKey = (providerId) => `resource-picker-scroll-${providerId}`;
8134
- (0, import_react25.useEffect)(() => {
8380
+ (0, import_react26.useEffect)(() => {
8135
8381
  if (!isOpen || !anchorRef?.current) return;
8136
8382
  const updatePosition = () => {
8137
8383
  if (!anchorRef?.current) return;
@@ -8148,7 +8394,7 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8148
8394
  window.addEventListener("resize", updatePosition);
8149
8395
  return () => window.removeEventListener("resize", updatePosition);
8150
8396
  }, [isOpen, anchorRef]);
8151
- (0, import_react25.useEffect)(() => {
8397
+ (0, import_react26.useEffect)(() => {
8152
8398
  if (!isOpen) return;
8153
8399
  const handleClickOutside = (e) => {
8154
8400
  if (pickerRef.current && !pickerRef.current.contains(e.target)) {
@@ -8161,17 +8407,17 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8161
8407
  document.addEventListener("mousedown", handleClickOutside);
8162
8408
  return () => document.removeEventListener("mousedown", handleClickOutside);
8163
8409
  }, [isOpen, onClose, anchorRef]);
8164
- (0, import_react25.useEffect)(() => {
8410
+ (0, import_react26.useEffect)(() => {
8165
8411
  if (selectedProviderId && searchInputRef.current) {
8166
8412
  searchInputRef.current.focus();
8167
8413
  }
8168
8414
  }, [selectedProviderId]);
8169
- (0, import_react25.useEffect)(() => {
8415
+ (0, import_react26.useEffect)(() => {
8170
8416
  if (isOpen) {
8171
8417
  scrollRestoredRef.current = null;
8172
8418
  }
8173
8419
  }, [isOpen]);
8174
- (0, import_react25.useEffect)(() => {
8420
+ (0, import_react26.useEffect)(() => {
8175
8421
  if (!isOpen || !selectedProviderId || !listRef.current || resources.length === 0) return;
8176
8422
  if (scrollRestoredRef.current === selectedProviderId) return;
8177
8423
  const savedScroll = sessionStorage.getItem(getScrollKey(selectedProviderId));
@@ -8185,12 +8431,12 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8185
8431
  }
8186
8432
  scrollRestoredRef.current = selectedProviderId;
8187
8433
  }, [isOpen, selectedProviderId, resources.length]);
8188
- const handleListScroll = (0, import_react25.useCallback)((e) => {
8434
+ const handleListScroll = (0, import_react26.useCallback)((e) => {
8189
8435
  if (!selectedProviderId) return;
8190
8436
  const scrollTop = e.currentTarget.scrollTop;
8191
8437
  sessionStorage.setItem(getScrollKey(selectedProviderId), String(scrollTop));
8192
8438
  }, [selectedProviderId]);
8193
- (0, import_react25.useEffect)(() => {
8439
+ (0, import_react26.useEffect)(() => {
8194
8440
  if (!isOpen) return;
8195
8441
  const handleKeyDown = (e) => {
8196
8442
  if (e.key === "Escape") {
@@ -8201,7 +8447,7 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8201
8447
  document.addEventListener("keydown", handleKeyDown);
8202
8448
  return () => document.removeEventListener("keydown", handleKeyDown);
8203
8449
  }, [isOpen, onClose]);
8204
- (0, import_react25.useEffect)(() => {
8450
+ (0, import_react26.useEffect)(() => {
8205
8451
  if (!sentinelRef.current || !hasMore || isLoadingResources) return;
8206
8452
  const observer = new IntersectionObserver(
8207
8453
  (entries) => {
@@ -8214,13 +8460,13 @@ var ResourcePicker = (0, import_react25.memo)(function ResourcePicker2({
8214
8460
  observer.observe(sentinelRef.current);
8215
8461
  return () => observer.disconnect();
8216
8462
  }, [hasMore, isLoadingResources, onLoadMore]);
8217
- const handleTabClick = (0, import_react25.useCallback)((providerId) => {
8463
+ const handleTabClick = (0, import_react26.useCallback)((providerId) => {
8218
8464
  if (providerId !== selectedProviderId) {
8219
8465
  onSearchChange("");
8220
8466
  onSelectProvider(providerId);
8221
8467
  }
8222
8468
  }, [selectedProviderId, onSelectProvider, onSearchChange]);
8223
- const handleResourceClick = (0, import_react25.useCallback)((item) => {
8469
+ const handleResourceClick = (0, import_react26.useCallback)((item) => {
8224
8470
  onAttachResource(item);
8225
8471
  }, [onAttachResource]);
8226
8472
  if (!isOpen) return null;
@@ -8481,7 +8727,7 @@ var STRINGS2 = {
8481
8727
  uploadImage: "\u4E0A\u4F20\u56FE\u7247"
8482
8728
  }
8483
8729
  };
8484
- var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8730
+ var AddResourceButton = (0, import_react27.memo)(function AddResourceButton2({
8485
8731
  providers,
8486
8732
  isLoadingProviders,
8487
8733
  onUploadFile,
@@ -8502,11 +8748,11 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8502
8748
  themeClass = ""
8503
8749
  }) {
8504
8750
  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)(() => {
8751
+ const [isMenuOpen, setIsMenuOpen] = (0, import_react27.useState)(false);
8752
+ const [isPickerOpen, setIsPickerOpen] = (0, import_react27.useState)(false);
8753
+ const buttonRef = (0, import_react27.useRef)(null);
8754
+ const menuRef = (0, import_react27.useRef)(null);
8755
+ (0, import_react27.useEffect)(() => {
8510
8756
  if (!isMenuOpen) return;
8511
8757
  const handleClickOutside = (e) => {
8512
8758
  if (menuRef.current && !menuRef.current.contains(e.target) && buttonRef.current && !buttonRef.current.contains(e.target)) {
@@ -8516,7 +8762,7 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8516
8762
  document.addEventListener("mousedown", handleClickOutside);
8517
8763
  return () => document.removeEventListener("mousedown", handleClickOutside);
8518
8764
  }, [isMenuOpen]);
8519
- (0, import_react26.useEffect)(() => {
8765
+ (0, import_react27.useEffect)(() => {
8520
8766
  if (!isMenuOpen) return;
8521
8767
  const handleKeyDown = (e) => {
8522
8768
  if (e.key === "Escape") {
@@ -8526,7 +8772,7 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8526
8772
  document.addEventListener("keydown", handleKeyDown);
8527
8773
  return () => document.removeEventListener("keydown", handleKeyDown);
8528
8774
  }, [isMenuOpen]);
8529
- const handleButtonClick = (0, import_react26.useCallback)(() => {
8775
+ const handleButtonClick = (0, import_react27.useCallback)(() => {
8530
8776
  if (isPickerOpen) {
8531
8777
  setIsPickerOpen(false);
8532
8778
  onSelectProvider(null);
@@ -8540,31 +8786,31 @@ var AddResourceButton = (0, import_react26.memo)(function AddResourceButton2({
8540
8786
  }
8541
8787
  setIsMenuOpen(!isMenuOpen);
8542
8788
  }, [isMenuOpen, isPickerOpen, onSelectProvider, providers, onUploadFile, onUploadImage]);
8543
- const handleUploadFile = (0, import_react26.useCallback)(() => {
8789
+ const handleUploadFile = (0, import_react27.useCallback)(() => {
8544
8790
  setIsMenuOpen(false);
8545
8791
  onUploadFile?.();
8546
8792
  }, [onUploadFile]);
8547
- const handleUploadImage = (0, import_react26.useCallback)(() => {
8793
+ const handleUploadImage = (0, import_react27.useCallback)(() => {
8548
8794
  setIsMenuOpen(false);
8549
8795
  onUploadImage?.();
8550
8796
  }, [onUploadImage]);
8551
- const handleProviderClick = (0, import_react26.useCallback)((providerId) => {
8797
+ const handleProviderClick = (0, import_react27.useCallback)((providerId) => {
8552
8798
  setIsMenuOpen(false);
8553
8799
  onSelectProvider(providerId);
8554
8800
  setIsPickerOpen(true);
8555
8801
  }, [onSelectProvider]);
8556
- const handlePickerClose = (0, import_react26.useCallback)(() => {
8802
+ const handlePickerClose = (0, import_react27.useCallback)(() => {
8557
8803
  setIsPickerOpen(false);
8558
8804
  onSelectProvider(null);
8559
8805
  }, [onSelectProvider]);
8560
- const handlePickerSelectProvider = (0, import_react26.useCallback)((providerId) => {
8806
+ const handlePickerSelectProvider = (0, import_react27.useCallback)((providerId) => {
8561
8807
  if (providerId === null) {
8562
8808
  setIsPickerOpen(false);
8563
8809
  setIsMenuOpen(true);
8564
8810
  }
8565
8811
  onSelectProvider(providerId);
8566
8812
  }, [onSelectProvider]);
8567
- const handleAttachResource = (0, import_react26.useCallback)((item) => {
8813
+ const handleAttachResource = (0, import_react27.useCallback)((item) => {
8568
8814
  onAttachResource(item);
8569
8815
  setIsPickerOpen(false);
8570
8816
  onSelectProvider(null);
@@ -8718,9 +8964,9 @@ var addResourceButtonStyles = `
8718
8964
  `;
8719
8965
 
8720
8966
  // src/renderer/components/AttachedResourceTags.tsx
8721
- var import_react27 = require("react");
8967
+ var import_react28 = require("react");
8722
8968
  var import_jsx_runtime16 = require("react/jsx-runtime");
8723
- var AttachedResourceTags = (0, import_react27.memo)(function AttachedResourceTags2({
8969
+ var AttachedResourceTags = (0, import_react28.memo)(function AttachedResourceTags2({
8724
8970
  resources,
8725
8971
  onRemove,
8726
8972
  disabled
@@ -8799,7 +9045,7 @@ var attachedResourceTagsStyles = `
8799
9045
 
8800
9046
  // src/renderer/components/SanqianChat.tsx
8801
9047
  var import_jsx_runtime17 = require("react/jsx-runtime");
8802
- var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
9048
+ var SanqianChat = (0, import_react29.memo)(function SanqianChat2({
8803
9049
  adapter,
8804
9050
  placeholder,
8805
9051
  autoConnect = true,
@@ -8811,22 +9057,22 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8811
9057
  config,
8812
9058
  logo
8813
9059
  }) {
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)(() => {
9060
+ const chatContainerRef = (0, import_react29.useRef)(null);
9061
+ const chatInputRef = (0, import_react29.useRef)(null);
9062
+ const messageListRef = (0, import_react29.useRef)(null);
9063
+ const onStateChangeRef = (0, import_react29.useRef)(onStateChange);
9064
+ (0, import_react29.useEffect)(() => {
8819
9065
  onStateChangeRef.current = onStateChange;
8820
9066
  }, [onStateChange]);
8821
9067
  const resolvedConfig = useResolvedUiConfig(config);
8822
9068
  const { themeClass, isDarkMode } = useResolvedTheme(resolvedConfig?.theme ?? "auto");
8823
9069
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
8824
9070
  const resolvedLogo = logo ?? resolvedConfig?.logo;
8825
- const strings = (0, import_react28.useMemo)(
9071
+ const strings = (0, import_react29.useMemo)(
8826
9072
  () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
8827
9073
  [resolvedConfig?.locale, resolvedConfig?.strings]
8828
9074
  );
8829
- const headerConfig = (0, import_react28.useMemo)(
9075
+ const headerConfig = (0, import_react29.useMemo)(
8830
9076
  () => {
8831
9077
  if (!resolvedConfig) {
8832
9078
  return resolvedLogo ? { logo: resolvedLogo } : void 0;
@@ -8839,7 +9085,7 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8839
9085
  [resolvedConfig, resolvedLogo]
8840
9086
  );
8841
9087
  const { logoNode, showPin, showClose, isPinned, togglePin, resolvedOnClose } = useChatHeader(headerConfig);
8842
- const emptyLogoNode = (0, import_react28.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
9088
+ const emptyLogoNode = (0, import_react29.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
8843
9089
  const showHeader = !!(logoNode || showClose || showPin);
8844
9090
  useChatStyles();
8845
9091
  useConnection({ adapter, autoConnect });
@@ -8849,7 +9095,7 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8849
9095
  onConversationChange
8850
9096
  });
8851
9097
  const resourcePicker = useResourcePicker({ adapter });
8852
- const resourceProviders = (0, import_react28.useMemo)(
9098
+ const resourceProviders = (0, import_react29.useMemo)(
8853
9099
  () => resourcePicker.providers.filter((p) => p.hasGetList),
8854
9100
  [resourcePicker.providers]
8855
9101
  );
@@ -8865,13 +9111,17 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8865
9111
  chat.submitHitlInput({ cancelled: true });
8866
9112
  }
8867
9113
  };
8868
- const handleSendMessage = (0, import_react28.useCallback)((content) => {
8869
- chat.sendMessage(content, {
8870
- attachedResources: resourcePicker.attachedResources.length > 0 ? resourcePicker.attachedResources : void 0
9114
+ const handleSendMessage = (0, import_react29.useCallback)(async (content) => {
9115
+ const attachedResources = resourcePicker.attachedResources.length > 0 ? [...resourcePicker.attachedResources] : void 0;
9116
+ const sent = await chat.trySendMessage(content, {
9117
+ attachedResources
8871
9118
  });
8872
- resourcePicker.clearAttachedResources();
9119
+ if (sent) {
9120
+ resourcePicker.clearAttachedResources();
9121
+ }
9122
+ return sent;
8873
9123
  }, [chat, resourcePicker]);
8874
- (0, import_react28.useEffect)(() => {
9124
+ (0, import_react29.useEffect)(() => {
8875
9125
  onStateChangeRef.current?.({
8876
9126
  messages: chat.messages,
8877
9127
  conversationId: chat.conversationId
@@ -8987,6 +9237,7 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
8987
9237
  stopLabel: strings.inputStop,
8988
9238
  isStreaming: chat.isStreaming,
8989
9239
  isLoading: chat.isLoading,
9240
+ allowEmptySubmit: resourcePicker.attachedResources.length > 0 || chat.sessionResources.length > 0,
8990
9241
  disabled: !!chat.pendingInterrupt || chat.isLoading && !chat.isStreaming,
8991
9242
  leftSlot: resourceProviders.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
8992
9243
  AddResourceButton,
@@ -9017,17 +9268,17 @@ var SanqianChat = (0, import_react28.memo)(function SanqianChat2({
9017
9268
  });
9018
9269
 
9019
9270
  // src/renderer/components/FloatingChat.tsx
9020
- var import_react34 = require("react");
9271
+ var import_react35 = require("react");
9021
9272
 
9022
9273
  // src/renderer/components/ModeToggleButton.tsx
9023
- var import_react29 = require("react");
9274
+ var import_react30 = require("react");
9024
9275
  var import_jsx_runtime18 = (
9025
9276
  // Float out icon - two overlapping panels (macOS style)
9026
9277
  require("react/jsx-runtime")
9027
9278
  );
9028
9279
  function ModeToggleButton({ className, locale = "en" }) {
9029
9280
  const { isEmbedded, toggleMode } = useChatPanel();
9030
- const strings = (0, import_react29.useMemo)(() => resolveChatStrings(locale), [locale]);
9281
+ const strings = (0, import_react30.useMemo)(() => resolveChatStrings(locale), [locale]);
9031
9282
  const handleClick = async () => {
9032
9283
  await toggleMode();
9033
9284
  };
@@ -9056,7 +9307,7 @@ function ModeToggleButton({ className, locale = "en" }) {
9056
9307
  }
9057
9308
 
9058
9309
  // src/renderer/components/AttachButton.tsx
9059
- var import_react30 = require("react");
9310
+ var import_react31 = require("react");
9060
9311
  var import_jsx_runtime19 = (
9061
9312
  // Attached state: filled icon
9062
9313
  require("react/jsx-runtime")
@@ -9064,7 +9315,7 @@ var import_jsx_runtime19 = (
9064
9315
  function AttachButton({ className, locale = "en" }) {
9065
9316
  const { isAttached, isAvailable, toggle } = useAttachState();
9066
9317
  const { isFloating } = useChatPanel();
9067
- const strings = (0, import_react30.useMemo)(() => resolveChatStrings(locale), [locale]);
9318
+ const strings = (0, import_react31.useMemo)(() => resolveChatStrings(locale), [locale]);
9068
9319
  if (!isFloating || !isAvailable) return null;
9069
9320
  const handleClick = async () => {
9070
9321
  await toggle();
@@ -9094,16 +9345,16 @@ function AttachButton({ className, locale = "en" }) {
9094
9345
  }
9095
9346
 
9096
9347
  // src/renderer/components/Resizer.tsx
9097
- var import_react31 = require("react");
9348
+ var import_react32 = require("react");
9098
9349
  var import_jsx_runtime20 = require("react/jsx-runtime");
9099
9350
  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)(() => {
9351
+ const rafRef = (0, import_react32.useRef)(null);
9352
+ const argsRef = (0, import_react32.useRef)(null);
9353
+ const fnRef = (0, import_react32.useRef)(fn);
9354
+ (0, import_react32.useLayoutEffect)(() => {
9104
9355
  fnRef.current = fn;
9105
9356
  });
9106
- const throttledFn = (0, import_react31.useCallback)((...args) => {
9357
+ const throttledFn = (0, import_react32.useCallback)((...args) => {
9107
9358
  argsRef.current = args;
9108
9359
  if (rafRef.current === null) {
9109
9360
  rafRef.current = requestAnimationFrame(() => {
@@ -9114,7 +9365,7 @@ function useRafThrottle(fn) {
9114
9365
  });
9115
9366
  }
9116
9367
  }, []);
9117
- (0, import_react31.useEffect)(() => {
9368
+ (0, import_react32.useEffect)(() => {
9118
9369
  return () => {
9119
9370
  if (rafRef.current !== null) {
9120
9371
  cancelAnimationFrame(rafRef.current);
@@ -9132,19 +9383,19 @@ function Resizer({
9132
9383
  className = ""
9133
9384
  }) {
9134
9385
  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)(() => {
9386
+ const [isHovered, setIsHovered] = (0, import_react32.useState)(false);
9387
+ const [isDragging, setIsDragging] = (0, import_react32.useState)(false);
9388
+ const startX = (0, import_react32.useRef)(0);
9389
+ const startWidth = (0, import_react32.useRef)(0);
9390
+ const widthRef = (0, import_react32.useRef)(width);
9391
+ (0, import_react32.useLayoutEffect)(() => {
9141
9392
  widthRef.current = width;
9142
9393
  }, [width]);
9143
9394
  const throttledSetWidth = useRafThrottle(setWidth);
9144
- const handleDoubleClick = (0, import_react31.useCallback)(() => {
9395
+ const handleDoubleClick = (0, import_react32.useCallback)(() => {
9145
9396
  setWidth(defaultWidth, true);
9146
9397
  }, [defaultWidth, setWidth]);
9147
- const handleMouseDown = (0, import_react31.useCallback)((e) => {
9398
+ const handleMouseDown = (0, import_react32.useCallback)((e) => {
9148
9399
  e.preventDefault();
9149
9400
  setIsDragging(true);
9150
9401
  startX.current = e.screenX;
@@ -9152,7 +9403,7 @@ function Resizer({
9152
9403
  document.body.style.cursor = "col-resize";
9153
9404
  document.body.style.userSelect = "none";
9154
9405
  }, []);
9155
- (0, import_react31.useEffect)(() => {
9406
+ (0, import_react32.useEffect)(() => {
9156
9407
  if (!isDragging) return;
9157
9408
  const handleMouseMove = (e) => {
9158
9409
  const deltaX = e.screenX - startX.current;
@@ -9229,15 +9480,15 @@ function Resizer({
9229
9480
  }
9230
9481
 
9231
9482
  // src/renderer/components/ResourceChip.tsx
9232
- var import_react32 = require("react");
9483
+ var import_react33 = require("react");
9233
9484
  var import_jsx_runtime21 = require("react/jsx-runtime");
9234
- var ResourceChip = (0, import_react32.memo)(function ResourceChip2({
9485
+ var ResourceChip = (0, import_react33.memo)(function ResourceChip2({
9235
9486
  resource,
9236
9487
  onRemove,
9237
9488
  disabled,
9238
9489
  locale: localeProp
9239
9490
  }) {
9240
- const i18nContext = (0, import_react32.useContext)(I18nContext);
9491
+ const i18nContext = (0, import_react33.useContext)(I18nContext);
9241
9492
  const locale = i18nContext?.locale ?? localeProp ?? "en";
9242
9493
  const strings = resolveChatStrings(locale);
9243
9494
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `resource-chip ${disabled ? "disabled" : ""}`, children: [
@@ -9256,7 +9507,7 @@ var ResourceChip = (0, import_react32.memo)(function ResourceChip2({
9256
9507
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("style", { children: resourceChipStyles })
9257
9508
  ] });
9258
9509
  });
9259
- var ResourceChipList = (0, import_react32.memo)(function ResourceChipList2({
9510
+ var ResourceChipList = (0, import_react33.memo)(function ResourceChipList2({
9260
9511
  resources,
9261
9512
  onRemove,
9262
9513
  disabled
@@ -9335,7 +9586,7 @@ var resourceChipStyles = `
9335
9586
  `;
9336
9587
 
9337
9588
  // src/renderer/hooks/useWindowBackgroundSync.ts
9338
- var import_react33 = require("react");
9589
+ var import_react34 = require("react");
9339
9590
  var cachedPlatform = null;
9340
9591
  var getPlatform = () => {
9341
9592
  if (cachedPlatform !== null) return cachedPlatform;
@@ -9353,7 +9604,7 @@ var setBackgroundColor = (color) => {
9353
9604
  }
9354
9605
  };
9355
9606
  function useWindowBackgroundSync(isDarkMode) {
9356
- (0, import_react33.useEffect)(() => {
9607
+ (0, import_react34.useEffect)(() => {
9357
9608
  if (getPlatform() !== "win32") return;
9358
9609
  const colors = getBaseColors(isDarkMode);
9359
9610
  setBackgroundColor(colors.bg);
@@ -9362,7 +9613,7 @@ function useWindowBackgroundSync(isDarkMode) {
9362
9613
 
9363
9614
  // src/renderer/components/FloatingChat.tsx
9364
9615
  var import_jsx_runtime22 = require("react/jsx-runtime");
9365
- var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9616
+ var FloatingChat = (0, import_react35.memo)(function FloatingChat2({
9366
9617
  messages,
9367
9618
  isLoading,
9368
9619
  isStreaming,
@@ -9385,8 +9636,8 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9385
9636
  header,
9386
9637
  footer
9387
9638
  }) {
9388
- const chatContainerRef = (0, import_react34.useRef)(null);
9389
- const chatInputRef = (0, import_react34.useRef)(null);
9639
+ const chatContainerRef = (0, import_react35.useRef)(null);
9640
+ const chatInputRef = (0, import_react35.useRef)(null);
9390
9641
  useChatStyles();
9391
9642
  useFocusPersistence({
9392
9643
  containerRef: chatContainerRef,
@@ -9398,18 +9649,18 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9398
9649
  const { themeClass, isDarkMode } = useResolvedTheme(themeMode);
9399
9650
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
9400
9651
  useWindowBackgroundSync(isDarkMode);
9401
- const rootStyle = (0, import_react34.useMemo)(() => ({
9652
+ const rootStyle = (0, import_react35.useMemo)(() => ({
9402
9653
  ...accentStyle || {},
9403
9654
  minHeight: "100vh",
9404
9655
  minWidth: "100vw"
9405
9656
  }), [accentStyle]);
9406
9657
  const resolvedLogo = logo ?? resolvedConfig?.logo;
9407
9658
  const resolvedLocale = resolvedConfig?.locale ?? locale;
9408
- const resolvedStrings = (0, import_react34.useMemo)(
9659
+ const resolvedStrings = (0, import_react35.useMemo)(
9409
9660
  () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
9410
9661
  [resolvedLocale, resolvedConfig?.strings]
9411
9662
  );
9412
- const headerConfig = (0, import_react34.useMemo)(
9663
+ const headerConfig = (0, import_react35.useMemo)(
9413
9664
  () => {
9414
9665
  if (!resolvedConfig) {
9415
9666
  return resolvedLogo ? { logo: resolvedLogo } : void 0;
@@ -9422,7 +9673,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9422
9673
  [resolvedConfig, resolvedLogo]
9423
9674
  );
9424
9675
  const { logoNode, showPin, showClose, isPinned, togglePin, resolvedOnClose } = useChatHeader(headerConfig);
9425
- const emptyLogoNode = (0, import_react34.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
9676
+ const emptyLogoNode = (0, import_react35.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
9426
9677
  const inputPlaceholder = placeholder ?? resolvedStrings.inputPlaceholder;
9427
9678
  const showHeader = !!(header || logoNode || showPin || showClose);
9428
9679
  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 +9718,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9467
9718
  ] })
9468
9719
  ] });
9469
9720
  const resolvedHeader = header ?? (showHeader ? defaultHeader : null);
9470
- const defaultRenderMessage = (0, import_react34.useCallback)(
9721
+ const defaultRenderMessage = (0, import_react35.useCallback)(
9471
9722
  (message) => {
9472
9723
  if (message.role === "tool") return null;
9473
9724
  const isUser = message.role === "user";
@@ -9481,7 +9732,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9481
9732
  },
9482
9733
  [renderContent]
9483
9734
  );
9484
- const defaultRenderHitl = (0, import_react34.useCallback)(
9735
+ const defaultRenderHitl = (0, import_react35.useCallback)(
9485
9736
  (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
9737
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "font-medium mb-2", children: interrupt.type === "approval_request" ? resolvedStrings.hitlApprovalRequired : resolvedStrings.hitlInputRequired }),
9487
9738
  interrupt.tool && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { className: "text-sm mb-2", children: [
@@ -9562,6 +9813,7 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9562
9813
  stopLabel: resolvedStrings.inputStop,
9563
9814
  isStreaming,
9564
9815
  isLoading,
9816
+ allowEmptySubmit: !!sessionResources && sessionResources.length > 0,
9565
9817
  disabled: !!pendingInterrupt || isLoading && !isStreaming,
9566
9818
  autoFocus: true
9567
9819
  }
@@ -9571,10 +9823,10 @@ var FloatingChat = (0, import_react34.memo)(function FloatingChat2({
9571
9823
  });
9572
9824
 
9573
9825
  // src/renderer/components/HistoryList.tsx
9574
- var import_react35 = require("react");
9826
+ var import_react36 = require("react");
9575
9827
  var import_jsx_runtime23 = require("react/jsx-runtime");
9576
9828
  function DeleteButton({ onClick, title, colors }) {
9577
- const [isHovered, setIsHovered] = (0, import_react35.useState)(false);
9829
+ const [isHovered, setIsHovered] = (0, import_react36.useState)(false);
9578
9830
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9579
9831
  "button",
9580
9832
  {
@@ -9618,7 +9870,7 @@ function DeleteButton({ onClick, title, colors }) {
9618
9870
  );
9619
9871
  }
9620
9872
  function LoadMoreButton({ onClick, colors, children }) {
9621
- const [isHovered, setIsHovered] = (0, import_react35.useState)(false);
9873
+ const [isHovered, setIsHovered] = (0, import_react36.useState)(false);
9622
9874
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
9623
9875
  "button",
9624
9876
  {
@@ -9668,7 +9920,7 @@ function formatRelativeTime(dateStr, strings) {
9668
9920
  return date.toLocaleDateString(void 0, { month: "short", day: "numeric" });
9669
9921
  }
9670
9922
  }
9671
- var HistoryList = (0, import_react35.memo)(function HistoryList2({
9923
+ var HistoryList = (0, import_react36.memo)(function HistoryList2({
9672
9924
  conversations,
9673
9925
  selectedId,
9674
9926
  isLoading,
@@ -9680,13 +9932,13 @@ var HistoryList = (0, import_react35.memo)(function HistoryList2({
9680
9932
  isDarkMode = false,
9681
9933
  strings = {}
9682
9934
  }) {
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)(() => {
9935
+ const [hoveredId, setHoveredId] = (0, import_react36.useState)(null);
9936
+ const loadMoreRef = (0, import_react36.useRef)(null);
9937
+ const isLoadingRef = (0, import_react36.useRef)(isLoading);
9938
+ (0, import_react36.useEffect)(() => {
9687
9939
  isLoadingRef.current = isLoading;
9688
9940
  }, [isLoading]);
9689
- (0, import_react35.useEffect)(() => {
9941
+ (0, import_react36.useEffect)(() => {
9690
9942
  if (!hasMore || loadError || !onLoadMore) return;
9691
9943
  const sentinel = loadMoreRef.current;
9692
9944
  if (!sentinel) return;
@@ -9783,10 +10035,10 @@ var HistoryList = (0, import_react35.memo)(function HistoryList2({
9783
10035
  });
9784
10036
 
9785
10037
  // src/renderer/components/HistoryModal.tsx
9786
- var import_react36 = require("react");
10038
+ var import_react37 = require("react");
9787
10039
  var import_jsx_runtime24 = require("react/jsx-runtime");
9788
10040
  var ANIMATION_DURATION = 120;
9789
- var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
10041
+ var HistoryModal = (0, import_react37.memo)(function HistoryModal2({
9790
10042
  isOpen,
9791
10043
  onClose,
9792
10044
  title,
@@ -9794,10 +10046,10 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9794
10046
  children,
9795
10047
  isDarkMode = false
9796
10048
  }) {
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)(() => {
10049
+ const modalRef = (0, import_react37.useRef)(null);
10050
+ const [isClosing, setIsClosing] = (0, import_react37.useState)(false);
10051
+ const [shouldRender, setShouldRender] = (0, import_react37.useState)(isOpen);
10052
+ (0, import_react37.useEffect)(() => {
9801
10053
  if (isOpen) {
9802
10054
  setShouldRender(true);
9803
10055
  setIsClosing(false);
@@ -9809,7 +10061,7 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9809
10061
  }, ANIMATION_DURATION);
9810
10062
  }
9811
10063
  }, [isOpen, shouldRender, isClosing]);
9812
- const handleClose = (0, import_react36.useCallback)(() => {
10064
+ const handleClose = (0, import_react37.useCallback)(() => {
9813
10065
  setIsClosing(true);
9814
10066
  setTimeout(() => {
9815
10067
  setShouldRender(false);
@@ -9817,7 +10069,7 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9817
10069
  onClose();
9818
10070
  }, ANIMATION_DURATION);
9819
10071
  }, [onClose]);
9820
- (0, import_react36.useEffect)(() => {
10072
+ (0, import_react37.useEffect)(() => {
9821
10073
  if (!shouldRender || isClosing) return;
9822
10074
  const handleKeyDown = (e) => {
9823
10075
  if (e.key === "Escape") {
@@ -9828,11 +10080,11 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9828
10080
  document.addEventListener("keydown", handleKeyDown);
9829
10081
  return () => document.removeEventListener("keydown", handleKeyDown);
9830
10082
  }, [shouldRender, isClosing, handleClose]);
9831
- (0, import_react36.useEffect)(() => {
10083
+ (0, import_react37.useEffect)(() => {
9832
10084
  if (!isOpen || !modalRef.current) return;
9833
10085
  modalRef.current.focus();
9834
10086
  }, [isOpen]);
9835
- const handleBackdropClick = (0, import_react36.useCallback)(
10087
+ const handleBackdropClick = (0, import_react37.useCallback)(
9836
10088
  (e) => {
9837
10089
  if (e.target === e.currentTarget && !isClosing) {
9838
10090
  handleClose();
@@ -9971,7 +10223,7 @@ var HistoryModal = (0, import_react36.memo)(function HistoryModal2({
9971
10223
  });
9972
10224
 
9973
10225
  // src/renderer/components/CompactChat.tsx
9974
- var import_react37 = require("react");
10226
+ var import_react38 = require("react");
9975
10227
  var import_react_dom = require("react-dom");
9976
10228
 
9977
10229
  // src/renderer/components/PanelControls.tsx
@@ -10011,7 +10263,7 @@ var PanelControls = {
10011
10263
 
10012
10264
  // src/renderer/components/CompactChat.tsx
10013
10265
  var import_jsx_runtime26 = require("react/jsx-runtime");
10014
- var CompactChat = (0, import_react37.memo)(function CompactChat2({
10266
+ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10015
10267
  adapter,
10016
10268
  config,
10017
10269
  logo,
@@ -10047,12 +10299,12 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10047
10299
  const { themeClass, isDarkMode: resolvedIsDarkMode } = useResolvedTheme(themeMode);
10048
10300
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
10049
10301
  const resolvedLogo = logo ?? resolvedConfig?.logo;
10050
- const baseStrings = (0, import_react37.useMemo)(
10302
+ const baseStrings = (0, import_react38.useMemo)(
10051
10303
  () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
10052
10304
  [resolvedConfig?.locale, resolvedConfig?.strings]
10053
10305
  );
10054
- const mergedStrings = (0, import_react37.useMemo)(() => ({ ...baseStrings, ...strings }), [baseStrings, strings]);
10055
- const headerConfig = (0, import_react37.useMemo)(
10306
+ const mergedStrings = (0, import_react38.useMemo)(() => ({ ...baseStrings, ...strings }), [baseStrings, strings]);
10307
+ const headerConfig = (0, import_react38.useMemo)(
10056
10308
  () => {
10057
10309
  if (!resolvedConfig) {
10058
10310
  return resolvedLogo ? { logo: resolvedLogo } : void 0;
@@ -10065,13 +10317,14 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10065
10317
  [resolvedConfig, resolvedLogo]
10066
10318
  );
10067
10319
  const { logoNode, showPin, showClose, isPinned, togglePin, resolvedOnClose } = useChatHeader(headerConfig);
10068
- const emptyLogoNode = (0, import_react37.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
10320
+ const emptyLogoNode = (0, import_react38.useMemo)(() => resolveLogoNode(resolvedLogo, "empty"), [resolvedLogo]);
10069
10321
  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);
10322
+ const chatContainerRef = (0, import_react38.useRef)(null);
10323
+ const chatInputRef = (0, import_react38.useRef)(null);
10324
+ const [showHistory, setShowHistory] = (0, import_react38.useState)(false);
10325
+ useWindowDragLock(showHistory);
10326
+ const [connectionAlert, setConnectionAlert] = (0, import_react38.useState)(null);
10327
+ const portalContainerRef = (0, import_react38.useRef)(inputPortalContainer ?? null);
10075
10328
  portalContainerRef.current = inputPortalContainer ?? null;
10076
10329
  const shouldRenderInputExternally = !!inputPortalContainer;
10077
10330
  const inputPlaceholder = placeholder ?? mergedStrings.inputPlaceholder;
@@ -10110,49 +10363,55 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10110
10363
  onError
10111
10364
  });
10112
10365
  const resourcePicker = useResourcePicker({ adapter });
10113
- const resourceProviders = (0, import_react37.useMemo)(
10366
+ const resourceProviders = (0, import_react38.useMemo)(
10114
10367
  () => resourcePicker.providers.filter((p) => p.hasGetList),
10115
10368
  [resourcePicker.providers]
10116
10369
  );
10117
- const handleHitlCancel = (0, import_react37.useCallback)(() => {
10370
+ const handleHitlCancel = (0, import_react38.useCallback)(() => {
10118
10371
  if (chat.pendingInterrupt?.type === "approval_request") {
10119
10372
  chat.rejectHitl();
10120
10373
  } else {
10121
10374
  chat.submitHitlInput({ cancelled: true });
10122
10375
  }
10123
10376
  }, [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)(() => {
10377
+ const handleSendWithResources = (0, import_react38.useCallback)(async (content) => {
10378
+ const resources = [...resourcePicker.attachedResources];
10379
+ const sent = await chat.trySendMessage(
10380
+ content,
10381
+ resources.length > 0 ? { attachedResources: resources } : void 0
10382
+ );
10383
+ if (sent) {
10384
+ resourcePicker.clearAttachedResources();
10385
+ }
10386
+ return sent;
10387
+ }, [chat.trySendMessage, resourcePicker.attachedResources, resourcePicker.clearAttachedResources]);
10388
+ (0, import_react38.useEffect)(() => {
10130
10389
  if (sendMessageRef) {
10131
10390
  sendMessageRef.current = chat.sendMessage;
10132
10391
  }
10133
10392
  }, [sendMessageRef, chat.sendMessage]);
10134
- (0, import_react37.useEffect)(() => {
10393
+ (0, import_react38.useEffect)(() => {
10135
10394
  if (newConversationRef) {
10136
10395
  newConversationRef.current = chat.newConversation;
10137
10396
  }
10138
10397
  }, [newConversationRef, chat.newConversation]);
10139
- (0, import_react37.useEffect)(() => {
10398
+ (0, import_react38.useEffect)(() => {
10140
10399
  if (parentFocusInputRef) {
10141
10400
  parentFocusInputRef.current = () => chatInputRef.current?.focus();
10142
10401
  }
10143
10402
  }, [parentFocusInputRef]);
10144
- (0, import_react37.useEffect)(() => {
10403
+ (0, import_react38.useEffect)(() => {
10145
10404
  if (parentSetTextRef) {
10146
10405
  parentSetTextRef.current = (text) => chatInputRef.current?.setValue(text);
10147
10406
  }
10148
10407
  }, [parentSetTextRef]);
10149
- (0, import_react37.useEffect)(() => {
10408
+ (0, import_react38.useEffect)(() => {
10150
10409
  if (!adapter.onFocusInput) return;
10151
10410
  return adapter.onFocusInput(() => {
10152
10411
  chatInputRef.current?.focus();
10153
10412
  });
10154
10413
  }, [adapter]);
10155
- (0, import_react37.useEffect)(() => {
10414
+ (0, import_react38.useEffect)(() => {
10156
10415
  if (onMessageReceived && chat.messages.length > 0) {
10157
10416
  const lastMessage = chat.messages[chat.messages.length - 1];
10158
10417
  if (lastMessage.role === "assistant" && !lastMessage.isStreaming) {
@@ -10160,12 +10419,12 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10160
10419
  }
10161
10420
  }
10162
10421
  }, [chat.messages, onMessageReceived]);
10163
- (0, import_react37.useEffect)(() => {
10422
+ (0, import_react38.useEffect)(() => {
10164
10423
  if (onLoadingChange) {
10165
10424
  onLoadingChange(chat.isLoading);
10166
10425
  }
10167
10426
  }, [chat.isLoading, onLoadingChange]);
10168
- (0, import_react37.useEffect)(() => {
10427
+ (0, import_react38.useEffect)(() => {
10169
10428
  if (onStateChange) {
10170
10429
  onStateChange({
10171
10430
  messages: chat.messages,
@@ -10173,24 +10432,24 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10173
10432
  });
10174
10433
  }
10175
10434
  }, [chat.messages, chat.conversationId, onStateChange]);
10176
- (0, import_react37.useEffect)(() => {
10435
+ (0, import_react38.useEffect)(() => {
10177
10436
  if (connection.isConnected) {
10178
10437
  resourcePicker.refreshProviders();
10179
10438
  }
10180
10439
  }, [connection.isConnected]);
10181
- (0, import_react37.useEffect)(() => {
10440
+ (0, import_react38.useEffect)(() => {
10182
10441
  if (showHistory && connection.isConnected) {
10183
10442
  conversations.loadConversations();
10184
10443
  }
10185
10444
  }, [showHistory, connection.isConnected]);
10186
- const handleSelectConversation = (0, import_react37.useCallback)(
10445
+ const handleSelectConversation = (0, import_react38.useCallback)(
10187
10446
  async (id) => {
10188
10447
  setShowHistory(false);
10189
10448
  await chat.loadConversation(id);
10190
10449
  },
10191
10450
  [chat]
10192
10451
  );
10193
- const handleDeleteConversation = (0, import_react37.useCallback)(
10452
+ const handleDeleteConversation = (0, import_react38.useCallback)(
10194
10453
  async (id) => {
10195
10454
  await conversations.deleteConversation(id);
10196
10455
  if (id === chat.conversationId) {
@@ -10199,14 +10458,14 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10199
10458
  },
10200
10459
  [conversations, chat]
10201
10460
  );
10202
- const handleNewChat = (0, import_react37.useCallback)(() => {
10461
+ const handleNewChat = (0, import_react38.useCallback)(() => {
10203
10462
  chat.newConversation();
10204
10463
  setShowHistory(false);
10205
10464
  setTimeout(() => {
10206
10465
  chatInputRef.current?.focus();
10207
10466
  }, 0);
10208
10467
  }, [chat]);
10209
- (0, import_react37.useEffect)(() => {
10468
+ (0, import_react38.useEffect)(() => {
10210
10469
  const handleKeyDown = (e) => {
10211
10470
  const isMac2 = navigator.platform.includes("Mac");
10212
10471
  const modifierKey = isMac2 ? e.metaKey : e.ctrlKey;
@@ -10220,20 +10479,19 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10220
10479
  }, [handleNewChat]);
10221
10480
  const isMac = typeof navigator !== "undefined" && navigator.platform.includes("Mac");
10222
10481
  const shortcutKey = isMac ? "\u2318N" : "Ctrl+N";
10223
- const defaultRenderMessage = (0, import_react37.useCallback)((message) => {
10482
+ const defaultRenderMessage = (0, import_react38.useCallback)((message) => {
10224
10483
  if (message.role === "tool") return null;
10225
10484
  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));
10485
+ const {
10486
+ displayBlocks,
10487
+ isEffectivelyComplete,
10488
+ showIntermediateSteps,
10489
+ showStreamingTimeline,
10490
+ showThinkingSection
10491
+ } = getAssistantDisplayState(message);
10492
+ const isToolCallsStreaming = message.isToolCallsStreaming ?? ((message.toolCalls?.some((tc) => tc.status === "running") ?? false) || (displayBlocks?.some((b) => b.type === "tool_call" && b.toolStatus === "running") ?? false));
10235
10493
  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 }),
10494
+ !isUser && showIntermediateSteps && displayBlocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(IntermediateSteps, { blocks: displayBlocks, strings: mergedStrings }),
10237
10495
  showThinkingSection && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10238
10496
  ThinkingSection,
10239
10497
  {
@@ -10241,18 +10499,18 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10241
10499
  currentThinking: message.currentThinking,
10242
10500
  isStreaming: message.isThinkingStreaming,
10243
10501
  isPaused: message.isThinkingPaused,
10244
- isComplete: message.isComplete,
10502
+ isComplete: isEffectivelyComplete,
10245
10503
  strings: mergedStrings
10246
10504
  }
10247
10505
  ),
10248
- showStreamingTimeline && message.blocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10506
+ showStreamingTimeline && displayBlocks && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10249
10507
  StreamingTimeline,
10250
10508
  {
10251
- blocks: message.blocks,
10509
+ blocks: displayBlocks,
10252
10510
  currentThinking: message.currentThinking,
10253
10511
  isThinkingStreaming: message.isThinkingStreaming,
10254
10512
  isToolCallsStreaming,
10255
- isComplete: message.isComplete,
10513
+ isComplete: isEffectivelyComplete,
10256
10514
  strings: mergedStrings
10257
10515
  }
10258
10516
  ),
@@ -10274,11 +10532,11 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10274
10532
  ] }) });
10275
10533
  }, [mergedStrings, linkHandler]);
10276
10534
  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)(() => {
10535
+ const baseColors = (0, import_react38.useMemo)(() => {
10278
10536
  if (!floating) return null;
10279
10537
  return getBaseColors(resolvedIsDarkMode);
10280
10538
  }, [floating, resolvedIsDarkMode]);
10281
- const containerStyle = (0, import_react37.useMemo)(() => {
10539
+ const containerStyle = (0, import_react38.useMemo)(() => {
10282
10540
  const baseStyle = floating && baseColors ? {
10283
10541
  ...accentStyle,
10284
10542
  // Override CSS variables with tinted colors for consistency with app
@@ -10306,7 +10564,7 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10306
10564
  "data-chat-font-size": resolvedConfig?.fontSize || "normal",
10307
10565
  children: [
10308
10566
  /* @__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 ? (
10567
+ !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
10568
  // Embedded mode: left-aligned buttons (collapse, float, history, new chat)
10311
10569
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-0.5", style: { WebkitAppRegion: "no-drag" }, children: [
10312
10570
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "chat-tooltip-wrapper", children: [
@@ -10481,6 +10739,7 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10481
10739
  (() => {
10482
10740
  if (hideInput && !shouldRenderInputExternally) return null;
10483
10741
  const disableInput = !!chat.pendingInterrupt || chat.isLoading && !chat.isStreaming;
10742
+ const allowEmptySubmit = resourcePicker.attachedResources.length > 0 || chat.sessionResources.length > 0;
10484
10743
  const addResourceButtonSlot = resourceProviders.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10485
10744
  AddResourceButton,
10486
10745
  {
@@ -10514,6 +10773,7 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10514
10773
  disabled: disableInput,
10515
10774
  isStreaming: chat.isStreaming,
10516
10775
  isLoading: chat.isLoading,
10776
+ allowEmptySubmit,
10517
10777
  autoFocus: true,
10518
10778
  leftSlot: addResourceButtonSlot
10519
10779
  }
@@ -10662,5 +10922,6 @@ var CompactChat = (0, import_react37.memo)(function CompactChat2({
10662
10922
  useResourcePicker,
10663
10923
  useStandaloneI18n,
10664
10924
  useStandaloneTheme,
10665
- useTheme
10925
+ useTheme,
10926
+ useWindowDragLock
10666
10927
  });