@yushaw/sanqian-chat 0.2.42 → 0.2.44

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.
@@ -64,6 +64,7 @@ interface ChatUiStrings {
64
64
  embedWindow: string;
65
65
  collapseSidebar: string;
66
66
  history: string;
67
+ related: string;
67
68
  remove: string;
68
69
  }
69
70
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -64,6 +64,7 @@ interface ChatUiStrings {
64
64
  embedWindow: string;
65
65
  collapseSidebar: string;
66
66
  history: string;
67
+ related: string;
67
68
  remove: string;
68
69
  }
69
70
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -497,6 +497,7 @@ interface ChatUiStrings {
497
497
  embedWindow: string;
498
498
  collapseSidebar: string;
499
499
  history: string;
500
+ related: string;
500
501
  remove: string;
501
502
  }
502
503
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -497,6 +497,7 @@ interface ChatUiStrings {
497
497
  embedWindow: string;
498
498
  collapseSidebar: string;
499
499
  history: string;
500
+ related: string;
500
501
  remove: string;
501
502
  }
502
503
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -62,6 +62,7 @@ interface ChatUiStrings {
62
62
  embedWindow: string;
63
63
  collapseSidebar: string;
64
64
  history: string;
65
+ related: string;
65
66
  remove: string;
66
67
  }
67
68
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -62,6 +62,7 @@ interface ChatUiStrings {
62
62
  embedWindow: string;
63
63
  collapseSidebar: string;
64
64
  history: string;
65
+ related: string;
65
66
  remove: string;
66
67
  }
67
68
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -67,6 +67,7 @@ interface ChatUiStrings {
67
67
  embedWindow: string;
68
68
  collapseSidebar: string;
69
69
  history: string;
70
+ related: string;
70
71
  remove: string;
71
72
  }
72
73
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -1259,6 +1260,7 @@ interface HistoryListProps {
1259
1260
  today?: string;
1260
1261
  yesterday?: string;
1261
1262
  delete?: string;
1263
+ related?: string;
1262
1264
  };
1263
1265
  /** Whether a conversation should be highlighted (host-defined relation) */
1264
1266
  isConversationHighlighted?: (conversation: ConversationInfo) => boolean;
@@ -67,6 +67,7 @@ interface ChatUiStrings {
67
67
  embedWindow: string;
68
68
  collapseSidebar: string;
69
69
  history: string;
70
+ related: string;
70
71
  remove: string;
71
72
  }
72
73
  type ChatThemeMode = 'light' | 'dark' | 'auto';
@@ -1259,6 +1260,7 @@ interface HistoryListProps {
1259
1260
  today?: string;
1260
1261
  yesterday?: string;
1261
1262
  delete?: string;
1263
+ related?: string;
1262
1264
  };
1263
1265
  /** Whether a conversation should be highlighted (host-defined relation) */
1264
1266
  isConversationHighlighted?: (conversation: ConversationInfo) => boolean;
@@ -90,6 +90,7 @@ module.exports = __toCommonJS(renderer_exports);
90
90
  var import_react = require("react");
91
91
  var TYPEWRITER_DELAYS = { VERY_FAST: 2, FAST: 5, NORMAL: 10, SLOW: 20 };
92
92
  var TYPEWRITER_THRESHOLDS = { VERY_FAST: 100, FAST: 50, NORMAL: 20 };
93
+ var MAX_DETACHED_SNAPSHOTS = 30;
93
94
  var findToolCallIndex = (toolCalls, toolId, toolName) => {
94
95
  if (!toolCalls || toolCalls.length === 0) return -1;
95
96
  if (toolId) {
@@ -107,6 +108,34 @@ var findLastBlock = (blocks, predicate) => {
107
108
  }
108
109
  return void 0;
109
110
  };
111
+ function cloneBlocks(blocks) {
112
+ return blocks?.map((block) => ({
113
+ ...block,
114
+ toolArgs: block.toolArgs ? { ...block.toolArgs } : block.toolArgs
115
+ }));
116
+ }
117
+ function cloneMessages(messages) {
118
+ return messages.map((message) => ({
119
+ ...message,
120
+ toolCalls: message.toolCalls?.map((toolCall) => ({
121
+ ...toolCall,
122
+ args: toolCall.args ? { ...toolCall.args } : toolCall.args
123
+ })),
124
+ blocks: cloneBlocks(message.blocks),
125
+ filePaths: message.filePaths ? [...message.filePaths] : message.filePaths,
126
+ attachedResources: message.attachedResources?.map((resource) => ({ ...resource }))
127
+ }));
128
+ }
129
+ function normalizeConversationMessages(messages) {
130
+ return messages.filter((message) => message.role !== "tool").map((message) => ({
131
+ ...message,
132
+ isStreaming: false,
133
+ isComplete: true
134
+ }));
135
+ }
136
+ function delay(ms) {
137
+ return new Promise((resolve) => setTimeout(resolve, ms));
138
+ }
110
139
  var CHAT_CAPABILITIES = {
111
140
  conversationSwitch: {
112
141
  supportsCancelActiveStream: true,
@@ -127,6 +156,7 @@ function useChat(options) {
127
156
  const isMountedRef = (0, import_react.useRef)(true);
128
157
  const messagesRef = (0, import_react.useRef)(messages);
129
158
  const conversationIdRef = (0, import_react.useRef)(conversationId);
159
+ const detachedConversationSnapshotsRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
130
160
  const currentRunIdRef = (0, import_react.useRef)(null);
131
161
  const pendingInterruptStreamIdRef = (0, import_react.useRef)(null);
132
162
  const currentAgentIdRef = (0, import_react.useRef)(null);
@@ -221,11 +251,49 @@ function useChat(options) {
221
251
  pendingCancelRef.current = false;
222
252
  pendingCancelFnRef.current = null;
223
253
  }, []);
254
+ const setDetachedConversationSnapshot = (0, import_react.useCallback)((id, snapshot) => {
255
+ if (!id || snapshot.length === 0) return;
256
+ const map = detachedConversationSnapshotsRef.current;
257
+ if (map.has(id)) {
258
+ map.delete(id);
259
+ }
260
+ map.set(id, cloneMessages(snapshot));
261
+ while (map.size > MAX_DETACHED_SNAPSHOTS) {
262
+ const oldestKey = map.keys().next().value;
263
+ if (!oldestKey) break;
264
+ map.delete(oldestKey);
265
+ }
266
+ }, []);
224
267
  const detachActiveStream = (0, import_react.useCallback)((detachContext) => {
225
268
  const context = activeStreamContextRef.current;
226
269
  if (!context || context.detached) {
227
270
  return;
228
271
  }
272
+ const detachedSnapshot = cloneMessages(normalizeConversationMessages(messagesRef.current));
273
+ const detachedAssistantMessageIndex = detachedSnapshot.findIndex(
274
+ (message) => message.id === context.assistantMessageId
275
+ );
276
+ if (detachedAssistantMessageIndex !== -1) {
277
+ const detachedAssistantMessage = detachedSnapshot[detachedAssistantMessageIndex];
278
+ const detachedContent = fullContentRef.current || displayedContentRef.current || detachedAssistantMessage.content;
279
+ detachedSnapshot[detachedAssistantMessageIndex] = {
280
+ ...detachedAssistantMessage,
281
+ content: detachedContent,
282
+ finalContent: detachedContent || detachedAssistantMessage.finalContent,
283
+ blocks: currentBlocksRef.current.length > 0 ? cloneBlocks(currentBlocksRef.current) : detachedAssistantMessage.blocks,
284
+ thinking: detachedAssistantMessage.thinking?.trimEnd(),
285
+ currentThinking: void 0,
286
+ isStreaming: false,
287
+ isThinkingStreaming: false,
288
+ isThinkingPaused: false,
289
+ isToolCallsStreaming: false,
290
+ isComplete: true
291
+ };
292
+ }
293
+ if (detachedSnapshot.length > 0) {
294
+ context.detachedSnapshot = detachedSnapshot;
295
+ setDetachedConversationSnapshot(context.conversationId, detachedSnapshot);
296
+ }
229
297
  context.detached = true;
230
298
  context.detachContext = detachContext;
231
299
  if (typewriterIntervalRef.current) {
@@ -241,19 +309,72 @@ function useChat(options) {
241
309
  pendingInterruptStreamIdRef.current = null;
242
310
  clearPendingCancel();
243
311
  suppressStreamRef.current = false;
244
- }, [clearPendingCancel, resetStreamBuffers]);
312
+ }, [clearPendingCancel, resetStreamBuffers, setDetachedConversationSnapshot]);
313
+ const refreshConversationIfVisible = (0, import_react.useCallback)(async (id) => {
314
+ if (!id || conversationIdRef.current !== id) return false;
315
+ try {
316
+ const detail = await adapter.getConversation(id);
317
+ if (!isMountedRef.current || conversationIdRef.current !== id) return false;
318
+ const normalizedMessages = normalizeConversationMessages(detail.messages);
319
+ if (normalizedMessages.length === 0) return false;
320
+ detachedConversationSnapshotsRef.current.delete(id);
321
+ setMessages(normalizedMessages);
322
+ setConversationTitle(detail.title ?? null);
323
+ return true;
324
+ } catch (refreshError) {
325
+ console.warn("[useChat] Failed to refresh conversation after detached stream completion:", refreshError);
326
+ return false;
327
+ }
328
+ }, [adapter]);
329
+ const refreshConversationIfVisibleWithRetry = (0, import_react.useCallback)((id, retryDelaysMs) => {
330
+ if (!id) return;
331
+ void (async () => {
332
+ for (const retryDelayMs of retryDelaysMs) {
333
+ if (retryDelayMs > 0) {
334
+ await delay(retryDelayMs);
335
+ }
336
+ if (!isMountedRef.current || conversationIdRef.current !== id) {
337
+ return;
338
+ }
339
+ const refreshed = await refreshConversationIfVisible(id);
340
+ if (refreshed) {
341
+ return;
342
+ }
343
+ }
344
+ })();
345
+ }, [refreshConversationIfVisible]);
245
346
  const handleStreamEvent = (0, import_react.useCallback)((event, streamContext) => {
246
347
  if (!isMountedRef.current) return;
247
348
  const isActiveStream = activeStreamContextRef.current?.token === streamContext.token && !streamContext.detached;
248
349
  if (!isActiveStream) {
249
350
  if (event.type === "done" && event.conversationId && !streamContext.didReportConversationChange) {
351
+ if (streamContext.conversationId && streamContext.conversationId !== event.conversationId) {
352
+ detachedConversationSnapshotsRef.current.delete(streamContext.conversationId);
353
+ }
354
+ streamContext.conversationId = event.conversationId;
250
355
  streamContext.didReportConversationChange = true;
356
+ if (streamContext.detachedSnapshot?.length) {
357
+ setDetachedConversationSnapshot(event.conversationId, streamContext.detachedSnapshot);
358
+ }
251
359
  onConversationChange?.(event.conversationId, event.title, {
252
360
  source: "background",
253
361
  streamToken: streamContext.token,
254
362
  detached: true,
255
363
  detachContext: streamContext.detachContext
256
364
  });
365
+ refreshConversationIfVisibleWithRetry(event.conversationId, [0, 300, 900]);
366
+ } else if (event.type === "done" && event.conversationId && streamContext.didReportConversationChange) {
367
+ if (streamContext.detachedSnapshot?.length) {
368
+ setDetachedConversationSnapshot(event.conversationId, streamContext.detachedSnapshot);
369
+ }
370
+ refreshConversationIfVisibleWithRetry(event.conversationId, [0, 300, 900]);
371
+ }
372
+ const isTerminalEvent = event.type === "done" || event.type === "cancelled" || event.type === "error";
373
+ if (isTerminalEvent) {
374
+ streamContext.detachedSnapshot = void 0;
375
+ }
376
+ if (isTerminalEvent && activeStreamContextRef.current?.token === streamContext.token) {
377
+ activeStreamContextRef.current = null;
257
378
  }
258
379
  return;
259
380
  }
@@ -338,9 +459,9 @@ function useChat(options) {
338
459
  return updated;
339
460
  });
340
461
  const qLen = tokenQueueRef.current.length;
341
- const delay = qLen > TYPEWRITER_THRESHOLDS.VERY_FAST ? TYPEWRITER_DELAYS.VERY_FAST : qLen > TYPEWRITER_THRESHOLDS.FAST ? TYPEWRITER_DELAYS.FAST : qLen > TYPEWRITER_THRESHOLDS.NORMAL ? TYPEWRITER_DELAYS.NORMAL : TYPEWRITER_DELAYS.SLOW;
462
+ const delay2 = qLen > TYPEWRITER_THRESHOLDS.VERY_FAST ? TYPEWRITER_DELAYS.VERY_FAST : qLen > TYPEWRITER_THRESHOLDS.FAST ? TYPEWRITER_DELAYS.FAST : qLen > TYPEWRITER_THRESHOLDS.NORMAL ? TYPEWRITER_DELAYS.NORMAL : TYPEWRITER_DELAYS.SLOW;
342
463
  if (typewriterIntervalRef.current !== null) {
343
- typewriterIntervalRef.current = setTimeout(tick, delay);
464
+ typewriterIntervalRef.current = setTimeout(tick, delay2);
344
465
  }
345
466
  } else {
346
467
  typewriterIntervalRef.current = null;
@@ -603,6 +724,7 @@ function useChat(options) {
603
724
  });
604
725
  resetStreamBuffers();
605
726
  if (event.conversationId) {
727
+ streamContext.conversationId = event.conversationId;
606
728
  streamContext.didReportConversationChange = true;
607
729
  setConversationId(event.conversationId);
608
730
  onConversationChange?.(event.conversationId, event.title, {
@@ -703,7 +825,7 @@ function useChat(options) {
703
825
  break;
704
826
  }
705
827
  }
706
- }, [clearPendingCancel, flushTypewriter, onConversationChange, onError, resetStreamBuffers]);
828
+ }, [clearPendingCancel, flushTypewriter, onConversationChange, onError, refreshConversationIfVisibleWithRetry, resetStreamBuffers, setDetachedConversationSnapshot]);
707
829
  const trySendMessage = (0, import_react.useCallback)(async (content, sendOptions) => {
708
830
  const trimmedContent = content.trim();
709
831
  const hasAttachedResources = (sendOptions?.attachedResources?.length ?? 0) > 0;
@@ -739,6 +861,7 @@ function useChat(options) {
739
861
  const streamContext = {
740
862
  token: crypto.randomUUID(),
741
863
  assistantMessageId: assistantMessage.id,
864
+ conversationId: conversationIdRef.current,
742
865
  detached: false,
743
866
  didReportConversationChange: false
744
867
  };
@@ -861,6 +984,7 @@ function useChat(options) {
861
984
  activeStreamContextRef.current = null;
862
985
  clearPendingCancel();
863
986
  suppressStreamRef.current = false;
987
+ detachedConversationSnapshotsRef.current.clear();
864
988
  }, [clearPendingCancel, resetStreamBuffers]);
865
989
  const loadConversation = (0, import_react.useCallback)(async (id, optionsArg) => {
866
990
  const cancelActiveStream = optionsArg?.cancelActiveStream ?? true;
@@ -877,11 +1001,16 @@ function useChat(options) {
877
1001
  setError(null);
878
1002
  const detail = await adapter.getConversation(id);
879
1003
  if (!isMountedRef.current) return;
880
- setMessages(detail.messages.filter((m) => m.role !== "tool").map((m) => ({
881
- ...m,
882
- isStreaming: false,
883
- isComplete: true
884
- })));
1004
+ const normalizedMessages = normalizeConversationMessages(detail.messages);
1005
+ const detachedSnapshot = detachedConversationSnapshotsRef.current.get(id);
1006
+ const shouldUseDetachedSnapshot = normalizedMessages.length === 0 && !!detachedSnapshot?.length;
1007
+ const nextMessages = shouldUseDetachedSnapshot ? cloneMessages(detachedSnapshot) : normalizedMessages;
1008
+ if (!shouldUseDetachedSnapshot) {
1009
+ detachedConversationSnapshotsRef.current.delete(id);
1010
+ } else {
1011
+ refreshConversationIfVisibleWithRetry(id, [250, 800, 1800]);
1012
+ }
1013
+ setMessages(nextMessages);
885
1014
  setConversationId(detail.id);
886
1015
  setConversationTitle(detail.title);
887
1016
  currentAgentIdRef.current = detail.agentId ?? currentAgentIdRef.current;
@@ -892,7 +1021,7 @@ function useChat(options) {
892
1021
  } finally {
893
1022
  if (isMountedRef.current) setIsLoading(false);
894
1023
  }
895
- }, [adapter, detachActiveStream, onError, stopStreaming]);
1024
+ }, [adapter, detachActiveStream, onError, refreshConversationIfVisibleWithRetry, stopStreaming]);
896
1025
  const newConversation = (0, import_react.useCallback)((optionsArg) => {
897
1026
  const cancelActiveStream = optionsArg?.cancelActiveStream ?? true;
898
1027
  const activeStream = activeStreamContextRef.current;
@@ -1151,6 +1280,7 @@ var CHAT_UI_STRINGS = {
1151
1280
  embedWindow: "Embed",
1152
1281
  collapseSidebar: "Collapse",
1153
1282
  history: "History",
1283
+ related: "RELATED",
1154
1284
  // Resource
1155
1285
  remove: "Remove"
1156
1286
  },
@@ -1210,6 +1340,7 @@ var CHAT_UI_STRINGS = {
1210
1340
  embedWindow: "\u5D4C\u5165",
1211
1341
  collapseSidebar: "\u6536\u8D77",
1212
1342
  history: "\u5386\u53F2",
1343
+ related: "\u76F8\u5173",
1213
1344
  // Resource
1214
1345
  remove: "\u79FB\u9664"
1215
1346
  }
@@ -1361,13 +1492,13 @@ function useConnection(options) {
1361
1492
  const isFirstAttempt = status === "disconnected" && reconnectAttemptsRef.current === 0;
1362
1493
  const baseDelay = isFirstAttempt ? 0 : Math.min(500 * Math.pow(2, reconnectAttemptsRef.current), 5e3);
1363
1494
  const jitter = isFirstAttempt ? 0 : Math.random() * 500;
1364
- const delay = baseDelay + jitter;
1495
+ const delay2 = baseDelay + jitter;
1365
1496
  const timer = setTimeout(() => {
1366
1497
  if (!isMountedRef.current) return;
1367
1498
  reconnectAttemptsRef.current++;
1368
1499
  adapter.connect().catch(() => {
1369
1500
  });
1370
- }, delay);
1501
+ }, delay2);
1371
1502
  return () => clearTimeout(timer);
1372
1503
  }, [status, autoConnect, adapter]);
1373
1504
  (0, import_react4.useEffect)(() => {
@@ -1915,13 +2046,15 @@ code {
1915
2046
  border-radius: 6px !important;
1916
2047
  overflow: visible !important;
1917
2048
  background: transparent !important;
1918
- margin: 6px 0 !important;
2049
+ margin: 4px 0 !important;
2050
+ padding: 0 !important;
2051
+ gap: 0 !important;
1919
2052
  }
1920
2053
 
1921
2054
  [data-streamdown="code-block-header"] {
1922
2055
  position: absolute !important;
1923
- top: 10px !important;
1924
- left: 12px !important;
2056
+ top: 4px !important;
2057
+ left: 10px !important;
1925
2058
  right: auto !important;
1926
2059
  z-index: 10;
1927
2060
  padding: 0 !important;
@@ -1934,8 +2067,8 @@ code {
1934
2067
 
1935
2068
  [data-streamdown="code-block-actions"] {
1936
2069
  position: absolute !important;
1937
- top: 10px !important;
1938
- right: 8px !important;
2070
+ top: 4px !important;
2071
+ right: 6px !important;
1939
2072
  z-index: 11;
1940
2073
  display: flex !important;
1941
2074
  align-items: center;
@@ -1955,7 +2088,7 @@ code {
1955
2088
 
1956
2089
  [data-streamdown="code-block-header"] > span:first-child {
1957
2090
  display: inline-flex;
1958
- font-size: 11px;
2091
+ font-size: 10px;
1959
2092
  color: var(--color-muted);
1960
2093
  text-transform: lowercase;
1961
2094
  }
@@ -1970,7 +2103,7 @@ code {
1970
2103
  /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
1971
2104
  [data-streamdown="code-block-header"] button,
1972
2105
  [data-streamdown="code-block-actions"] button {
1973
- padding: 4px !important;
2106
+ padding: 2px !important;
1974
2107
  border: none !important;
1975
2108
  border-radius: 4px !important;
1976
2109
  background: var(--color-surface) !important;
@@ -1987,8 +2120,8 @@ code {
1987
2120
  [data-streamdown="code-block-header"] svg,
1988
2121
  [data-streamdown="code-block-actions"] svg {
1989
2122
  color: var(--color-muted) !important;
1990
- width: 12px !important;
1991
- height: 12px !important;
2123
+ width: 11px !important;
2124
+ height: 11px !important;
1992
2125
  }
1993
2126
 
1994
2127
  [data-streamdown="code-block-header"] button:hover svg,
@@ -1997,18 +2130,33 @@ code {
1997
2130
  }
1998
2131
 
1999
2132
  [data-streamdown="code-block-body"] {
2000
- padding: 8px 12px !important;
2133
+ padding: 18px 12px 8px !important;
2001
2134
  background: rgba(0, 0, 0, 0.02) !important;
2002
2135
  border-radius: 6px !important;
2136
+ border: none !important;
2137
+ box-shadow: none !important;
2003
2138
  font-size: 13px !important;
2004
2139
  line-height: 1.5 !important;
2005
2140
  white-space: pre !important;
2006
2141
  overflow-x: auto !important;
2007
2142
  }
2008
2143
 
2144
+ [data-streamdown="code-block-body"] pre {
2145
+ margin: 0 !important;
2146
+ padding: 0 !important;
2147
+ border: none !important;
2148
+ background: transparent !important;
2149
+ border-radius: 0 !important;
2150
+ }
2151
+
2009
2152
  [data-streamdown="code-block-body"] code {
2010
2153
  white-space: pre !important;
2011
2154
  display: block !important;
2155
+ color: var(--color-text);
2156
+ }
2157
+
2158
+ [data-streamdown="code-block-body"] code > span {
2159
+ display: block;
2012
2160
  }
2013
2161
 
2014
2162
  .dark [data-streamdown="code-block-body"],
@@ -2022,17 +2170,38 @@ code {
2022
2170
 
2023
2171
  [data-streamdown="code-block-body"],
2024
2172
  [data-streamdown="code-block-body"] code {
2025
- color: var(--color-text) !important;
2026
- opacity: 0.85;
2173
+ opacity: 1;
2027
2174
  }
2028
2175
 
2029
2176
  [data-streamdown="code-block-body"] code span[style] {
2030
2177
  background-color: transparent !important;
2031
2178
  }
2032
2179
 
2033
- .dark [data-streamdown="code-block-body"] code span[style],
2034
- :root.dark [data-streamdown="code-block-body"] code span[style] {
2035
- color: var(--shiki-dark) !important;
2180
+ [data-streamdown="code-block-body"] pre[style*="--sdm-bg"] {
2181
+ background-color: var(--sdm-bg) !important;
2182
+ }
2183
+
2184
+ .dark [data-streamdown="code-block-body"] pre[style*="--sdm-bg"],
2185
+ :root.dark [data-streamdown="code-block-body"] pre[style*="--sdm-bg"] {
2186
+ background-color: var(--shiki-dark-bg, var(--sdm-bg)) !important;
2187
+ }
2188
+
2189
+ [data-streamdown="code-block-body"] code span[style*="--sdm-c"] {
2190
+ color: var(--sdm-c) !important;
2191
+ }
2192
+
2193
+ .dark [data-streamdown="code-block-body"] code span[style*="--sdm-c"],
2194
+ :root.dark [data-streamdown="code-block-body"] code span[style*="--sdm-c"] {
2195
+ color: var(--shiki-dark, var(--sdm-c)) !important;
2196
+ }
2197
+
2198
+ [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"] {
2199
+ background-color: var(--sdm-tbg) !important;
2200
+ }
2201
+
2202
+ .dark [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"],
2203
+ :root.dark [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"] {
2204
+ background-color: var(--shiki-dark-bg, var(--sdm-tbg)) !important;
2036
2205
  }
2037
2206
 
2038
2207
  [data-streamdown="table-wrapper"].my-4 {
@@ -7025,6 +7194,7 @@ var import_react21 = require("react");
7025
7194
  // src/renderer/renderers/MarkdownRenderer.tsx
7026
7195
  var import_react18 = require("react");
7027
7196
  var import_streamdown = require("streamdown");
7197
+ var import_code = require("@streamdown/code");
7028
7198
  var import_rehype_harden = require("rehype-harden");
7029
7199
  var import_remark_gfm = __toESM(require("remark-gfm"));
7030
7200
  var import_jsx_runtime6 = require("react/jsx-runtime");
@@ -7083,6 +7253,7 @@ var MarkdownRenderer = (0, import_react18.memo)(function MarkdownRenderer2({
7083
7253
  const remarkPlugins = (0, import_react18.useMemo)(() => {
7084
7254
  return [import_remark_gfm.default];
7085
7255
  }, []);
7256
+ const streamdownPlugins = (0, import_react18.useMemo)(() => ({ code: import_code.code }), []);
7086
7257
  const customComponents = (0, import_react18.useMemo)(() => {
7087
7258
  const comps = {};
7088
7259
  comps.p = ({ children }) => {
@@ -7135,6 +7306,7 @@ var MarkdownRenderer = (0, import_react18.memo)(function MarkdownRenderer2({
7135
7306
  {
7136
7307
  remarkPlugins,
7137
7308
  rehypePlugins,
7309
+ plugins: streamdownPlugins,
7138
7310
  components: customComponents,
7139
7311
  mode: isStreaming ? "streaming" : "static",
7140
7312
  isAnimating: isStreaming,
@@ -7870,7 +8042,7 @@ var SanqianChatMessage = (0, import_react21.memo)(function SanqianChatMessage2({
7870
8042
  "div",
7871
8043
  {
7872
8044
  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"}`,
7873
- children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-3" }) : showStreamingPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 text-[var(--chat-muted)] italic min-h-[1.5rem]", children: [
8045
+ children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-0" }) : showStreamingPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2 text-[var(--chat-muted)] italic min-h-[1.5rem]", children: [
7874
8046
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex gap-1 items-center", children: [
7875
8047
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "w-1.5 h-1.5 bg-[var(--chat-accent)] rounded-full animate-bounce", style: { animationDelay: "0ms" } }),
7876
8048
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "w-1.5 h-1.5 bg-[var(--chat-accent)] rounded-full animate-bounce", style: { animationDelay: "150ms" } }),
@@ -10073,7 +10245,7 @@ var FloatingChat = (0, import_react35.memo)(function FloatingChat2({
10073
10245
  "div",
10074
10246
  {
10075
10247
  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"}`,
10076
- children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content && (renderContent ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "prose prose-chat dark:prose-invert max-w-none px-3", children: renderContent(message.content, message.isStreaming ?? false) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-3" }))
10248
+ children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content && (renderContent ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "prose prose-chat dark:prose-invert max-w-none px-0", children: renderContent(message.content, message.isStreaming ?? false) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-0" }))
10077
10249
  }
10078
10250
  ) }) }) });
10079
10251
  },
@@ -10311,7 +10483,7 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10311
10483
  deleteText: "#ef4444",
10312
10484
  loadingDot: isDarkMode ? "rgba(255, 255, 255, 0.3)" : "rgba(0, 0, 0, 0.2)"
10313
10485
  };
10314
- const loadingDots = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "1.5rem 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { display: "flex", gap: "0.25rem" }, children: [0, 150, 300].map((delay) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10486
+ const loadingDots = /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "1.5rem 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { display: "flex", gap: "0.25rem" }, children: [0, 150, 300].map((delay2) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
10315
10487
  "span",
10316
10488
  {
10317
10489
  style: {
@@ -10320,10 +10492,10 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10320
10492
  borderRadius: "50%",
10321
10493
  background: colors.loadingDot,
10322
10494
  animation: "bounce 1s infinite",
10323
- animationDelay: `${delay}ms`
10495
+ animationDelay: `${delay2}ms`
10324
10496
  }
10325
10497
  },
10326
- delay
10498
+ delay2
10327
10499
  )) }) });
10328
10500
  if (isLoading && conversations.length === 0) {
10329
10501
  return loadingDots;
@@ -10336,7 +10508,7 @@ var HistoryList = (0, import_react36.memo)(function HistoryList2({
10336
10508
  const isSelected = conv.id === selectedId;
10337
10509
  const isHovered = conv.id === hoveredId;
10338
10510
  const isHighlighted = isConversationHighlighted?.(conv) === true;
10339
- const resolvedHighlightLabel = isHighlighted ? (typeof highlightedLabel === "function" ? highlightedLabel(conv) : highlightedLabel) ?? "RELATED" : null;
10511
+ const resolvedHighlightLabel = isHighlighted ? (typeof highlightedLabel === "function" ? highlightedLabel(conv) : highlightedLabel) ?? strings.related ?? "RELATED" : null;
10340
10512
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
10341
10513
  "div",
10342
10514
  {
@@ -10987,7 +11159,7 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10987
11159
  content: message.content,
10988
11160
  isStreaming: message.isStreaming,
10989
11161
  linkHandler,
10990
- className: "px-3"
11162
+ className: "px-0"
10991
11163
  }
10992
11164
  )
10993
11165
  }
@@ -11187,7 +11359,8 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
11187
11359
  loadMore: mergedStrings.loadMore,
11188
11360
  today: mergedStrings.today,
11189
11361
  yesterday: mergedStrings.yesterday,
11190
- delete: mergedStrings.delete
11362
+ delete: mergedStrings.delete,
11363
+ related: mergedStrings.related
11191
11364
  }
11192
11365
  }
11193
11366
  )