@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.
@@ -2,6 +2,7 @@
2
2
  import { useState, useCallback, useRef, useEffect } from "react";
3
3
  var TYPEWRITER_DELAYS = { VERY_FAST: 2, FAST: 5, NORMAL: 10, SLOW: 20 };
4
4
  var TYPEWRITER_THRESHOLDS = { VERY_FAST: 100, FAST: 50, NORMAL: 20 };
5
+ var MAX_DETACHED_SNAPSHOTS = 30;
5
6
  var findToolCallIndex = (toolCalls, toolId, toolName) => {
6
7
  if (!toolCalls || toolCalls.length === 0) return -1;
7
8
  if (toolId) {
@@ -19,6 +20,34 @@ var findLastBlock = (blocks, predicate) => {
19
20
  }
20
21
  return void 0;
21
22
  };
23
+ function cloneBlocks(blocks) {
24
+ return blocks?.map((block) => ({
25
+ ...block,
26
+ toolArgs: block.toolArgs ? { ...block.toolArgs } : block.toolArgs
27
+ }));
28
+ }
29
+ function cloneMessages(messages) {
30
+ return messages.map((message) => ({
31
+ ...message,
32
+ toolCalls: message.toolCalls?.map((toolCall) => ({
33
+ ...toolCall,
34
+ args: toolCall.args ? { ...toolCall.args } : toolCall.args
35
+ })),
36
+ blocks: cloneBlocks(message.blocks),
37
+ filePaths: message.filePaths ? [...message.filePaths] : message.filePaths,
38
+ attachedResources: message.attachedResources?.map((resource) => ({ ...resource }))
39
+ }));
40
+ }
41
+ function normalizeConversationMessages(messages) {
42
+ return messages.filter((message) => message.role !== "tool").map((message) => ({
43
+ ...message,
44
+ isStreaming: false,
45
+ isComplete: true
46
+ }));
47
+ }
48
+ function delay(ms) {
49
+ return new Promise((resolve) => setTimeout(resolve, ms));
50
+ }
22
51
  var CHAT_CAPABILITIES = {
23
52
  conversationSwitch: {
24
53
  supportsCancelActiveStream: true,
@@ -39,6 +68,7 @@ function useChat(options) {
39
68
  const isMountedRef = useRef(true);
40
69
  const messagesRef = useRef(messages);
41
70
  const conversationIdRef = useRef(conversationId);
71
+ const detachedConversationSnapshotsRef = useRef(/* @__PURE__ */ new Map());
42
72
  const currentRunIdRef = useRef(null);
43
73
  const pendingInterruptStreamIdRef = useRef(null);
44
74
  const currentAgentIdRef = useRef(null);
@@ -133,11 +163,49 @@ function useChat(options) {
133
163
  pendingCancelRef.current = false;
134
164
  pendingCancelFnRef.current = null;
135
165
  }, []);
166
+ const setDetachedConversationSnapshot = useCallback((id, snapshot) => {
167
+ if (!id || snapshot.length === 0) return;
168
+ const map = detachedConversationSnapshotsRef.current;
169
+ if (map.has(id)) {
170
+ map.delete(id);
171
+ }
172
+ map.set(id, cloneMessages(snapshot));
173
+ while (map.size > MAX_DETACHED_SNAPSHOTS) {
174
+ const oldestKey = map.keys().next().value;
175
+ if (!oldestKey) break;
176
+ map.delete(oldestKey);
177
+ }
178
+ }, []);
136
179
  const detachActiveStream = useCallback((detachContext) => {
137
180
  const context = activeStreamContextRef.current;
138
181
  if (!context || context.detached) {
139
182
  return;
140
183
  }
184
+ const detachedSnapshot = cloneMessages(normalizeConversationMessages(messagesRef.current));
185
+ const detachedAssistantMessageIndex = detachedSnapshot.findIndex(
186
+ (message) => message.id === context.assistantMessageId
187
+ );
188
+ if (detachedAssistantMessageIndex !== -1) {
189
+ const detachedAssistantMessage = detachedSnapshot[detachedAssistantMessageIndex];
190
+ const detachedContent = fullContentRef.current || displayedContentRef.current || detachedAssistantMessage.content;
191
+ detachedSnapshot[detachedAssistantMessageIndex] = {
192
+ ...detachedAssistantMessage,
193
+ content: detachedContent,
194
+ finalContent: detachedContent || detachedAssistantMessage.finalContent,
195
+ blocks: currentBlocksRef.current.length > 0 ? cloneBlocks(currentBlocksRef.current) : detachedAssistantMessage.blocks,
196
+ thinking: detachedAssistantMessage.thinking?.trimEnd(),
197
+ currentThinking: void 0,
198
+ isStreaming: false,
199
+ isThinkingStreaming: false,
200
+ isThinkingPaused: false,
201
+ isToolCallsStreaming: false,
202
+ isComplete: true
203
+ };
204
+ }
205
+ if (detachedSnapshot.length > 0) {
206
+ context.detachedSnapshot = detachedSnapshot;
207
+ setDetachedConversationSnapshot(context.conversationId, detachedSnapshot);
208
+ }
141
209
  context.detached = true;
142
210
  context.detachContext = detachContext;
143
211
  if (typewriterIntervalRef.current) {
@@ -153,19 +221,72 @@ function useChat(options) {
153
221
  pendingInterruptStreamIdRef.current = null;
154
222
  clearPendingCancel();
155
223
  suppressStreamRef.current = false;
156
- }, [clearPendingCancel, resetStreamBuffers]);
224
+ }, [clearPendingCancel, resetStreamBuffers, setDetachedConversationSnapshot]);
225
+ const refreshConversationIfVisible = useCallback(async (id) => {
226
+ if (!id || conversationIdRef.current !== id) return false;
227
+ try {
228
+ const detail = await adapter.getConversation(id);
229
+ if (!isMountedRef.current || conversationIdRef.current !== id) return false;
230
+ const normalizedMessages = normalizeConversationMessages(detail.messages);
231
+ if (normalizedMessages.length === 0) return false;
232
+ detachedConversationSnapshotsRef.current.delete(id);
233
+ setMessages(normalizedMessages);
234
+ setConversationTitle(detail.title ?? null);
235
+ return true;
236
+ } catch (refreshError) {
237
+ console.warn("[useChat] Failed to refresh conversation after detached stream completion:", refreshError);
238
+ return false;
239
+ }
240
+ }, [adapter]);
241
+ const refreshConversationIfVisibleWithRetry = useCallback((id, retryDelaysMs) => {
242
+ if (!id) return;
243
+ void (async () => {
244
+ for (const retryDelayMs of retryDelaysMs) {
245
+ if (retryDelayMs > 0) {
246
+ await delay(retryDelayMs);
247
+ }
248
+ if (!isMountedRef.current || conversationIdRef.current !== id) {
249
+ return;
250
+ }
251
+ const refreshed = await refreshConversationIfVisible(id);
252
+ if (refreshed) {
253
+ return;
254
+ }
255
+ }
256
+ })();
257
+ }, [refreshConversationIfVisible]);
157
258
  const handleStreamEvent = useCallback((event, streamContext) => {
158
259
  if (!isMountedRef.current) return;
159
260
  const isActiveStream = activeStreamContextRef.current?.token === streamContext.token && !streamContext.detached;
160
261
  if (!isActiveStream) {
161
262
  if (event.type === "done" && event.conversationId && !streamContext.didReportConversationChange) {
263
+ if (streamContext.conversationId && streamContext.conversationId !== event.conversationId) {
264
+ detachedConversationSnapshotsRef.current.delete(streamContext.conversationId);
265
+ }
266
+ streamContext.conversationId = event.conversationId;
162
267
  streamContext.didReportConversationChange = true;
268
+ if (streamContext.detachedSnapshot?.length) {
269
+ setDetachedConversationSnapshot(event.conversationId, streamContext.detachedSnapshot);
270
+ }
163
271
  onConversationChange?.(event.conversationId, event.title, {
164
272
  source: "background",
165
273
  streamToken: streamContext.token,
166
274
  detached: true,
167
275
  detachContext: streamContext.detachContext
168
276
  });
277
+ refreshConversationIfVisibleWithRetry(event.conversationId, [0, 300, 900]);
278
+ } else if (event.type === "done" && event.conversationId && streamContext.didReportConversationChange) {
279
+ if (streamContext.detachedSnapshot?.length) {
280
+ setDetachedConversationSnapshot(event.conversationId, streamContext.detachedSnapshot);
281
+ }
282
+ refreshConversationIfVisibleWithRetry(event.conversationId, [0, 300, 900]);
283
+ }
284
+ const isTerminalEvent = event.type === "done" || event.type === "cancelled" || event.type === "error";
285
+ if (isTerminalEvent) {
286
+ streamContext.detachedSnapshot = void 0;
287
+ }
288
+ if (isTerminalEvent && activeStreamContextRef.current?.token === streamContext.token) {
289
+ activeStreamContextRef.current = null;
169
290
  }
170
291
  return;
171
292
  }
@@ -250,9 +371,9 @@ function useChat(options) {
250
371
  return updated;
251
372
  });
252
373
  const qLen = tokenQueueRef.current.length;
253
- 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;
374
+ 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;
254
375
  if (typewriterIntervalRef.current !== null) {
255
- typewriterIntervalRef.current = setTimeout(tick, delay);
376
+ typewriterIntervalRef.current = setTimeout(tick, delay2);
256
377
  }
257
378
  } else {
258
379
  typewriterIntervalRef.current = null;
@@ -515,6 +636,7 @@ function useChat(options) {
515
636
  });
516
637
  resetStreamBuffers();
517
638
  if (event.conversationId) {
639
+ streamContext.conversationId = event.conversationId;
518
640
  streamContext.didReportConversationChange = true;
519
641
  setConversationId(event.conversationId);
520
642
  onConversationChange?.(event.conversationId, event.title, {
@@ -615,7 +737,7 @@ function useChat(options) {
615
737
  break;
616
738
  }
617
739
  }
618
- }, [clearPendingCancel, flushTypewriter, onConversationChange, onError, resetStreamBuffers]);
740
+ }, [clearPendingCancel, flushTypewriter, onConversationChange, onError, refreshConversationIfVisibleWithRetry, resetStreamBuffers, setDetachedConversationSnapshot]);
619
741
  const trySendMessage = useCallback(async (content, sendOptions) => {
620
742
  const trimmedContent = content.trim();
621
743
  const hasAttachedResources = (sendOptions?.attachedResources?.length ?? 0) > 0;
@@ -651,6 +773,7 @@ function useChat(options) {
651
773
  const streamContext = {
652
774
  token: crypto.randomUUID(),
653
775
  assistantMessageId: assistantMessage.id,
776
+ conversationId: conversationIdRef.current,
654
777
  detached: false,
655
778
  didReportConversationChange: false
656
779
  };
@@ -773,6 +896,7 @@ function useChat(options) {
773
896
  activeStreamContextRef.current = null;
774
897
  clearPendingCancel();
775
898
  suppressStreamRef.current = false;
899
+ detachedConversationSnapshotsRef.current.clear();
776
900
  }, [clearPendingCancel, resetStreamBuffers]);
777
901
  const loadConversation = useCallback(async (id, optionsArg) => {
778
902
  const cancelActiveStream = optionsArg?.cancelActiveStream ?? true;
@@ -789,11 +913,16 @@ function useChat(options) {
789
913
  setError(null);
790
914
  const detail = await adapter.getConversation(id);
791
915
  if (!isMountedRef.current) return;
792
- setMessages(detail.messages.filter((m) => m.role !== "tool").map((m) => ({
793
- ...m,
794
- isStreaming: false,
795
- isComplete: true
796
- })));
916
+ const normalizedMessages = normalizeConversationMessages(detail.messages);
917
+ const detachedSnapshot = detachedConversationSnapshotsRef.current.get(id);
918
+ const shouldUseDetachedSnapshot = normalizedMessages.length === 0 && !!detachedSnapshot?.length;
919
+ const nextMessages = shouldUseDetachedSnapshot ? cloneMessages(detachedSnapshot) : normalizedMessages;
920
+ if (!shouldUseDetachedSnapshot) {
921
+ detachedConversationSnapshotsRef.current.delete(id);
922
+ } else {
923
+ refreshConversationIfVisibleWithRetry(id, [250, 800, 1800]);
924
+ }
925
+ setMessages(nextMessages);
797
926
  setConversationId(detail.id);
798
927
  setConversationTitle(detail.title);
799
928
  currentAgentIdRef.current = detail.agentId ?? currentAgentIdRef.current;
@@ -804,7 +933,7 @@ function useChat(options) {
804
933
  } finally {
805
934
  if (isMountedRef.current) setIsLoading(false);
806
935
  }
807
- }, [adapter, detachActiveStream, onError, stopStreaming]);
936
+ }, [adapter, detachActiveStream, onError, refreshConversationIfVisibleWithRetry, stopStreaming]);
808
937
  const newConversation = useCallback((optionsArg) => {
809
938
  const cancelActiveStream = optionsArg?.cancelActiveStream ?? true;
810
939
  const activeStream = activeStreamContextRef.current;
@@ -1063,6 +1192,7 @@ var CHAT_UI_STRINGS = {
1063
1192
  embedWindow: "Embed",
1064
1193
  collapseSidebar: "Collapse",
1065
1194
  history: "History",
1195
+ related: "RELATED",
1066
1196
  // Resource
1067
1197
  remove: "Remove"
1068
1198
  },
@@ -1122,6 +1252,7 @@ var CHAT_UI_STRINGS = {
1122
1252
  embedWindow: "\u5D4C\u5165",
1123
1253
  collapseSidebar: "\u6536\u8D77",
1124
1254
  history: "\u5386\u53F2",
1255
+ related: "\u76F8\u5173",
1125
1256
  // Resource
1126
1257
  remove: "\u79FB\u9664"
1127
1258
  }
@@ -1273,13 +1404,13 @@ function useConnection(options) {
1273
1404
  const isFirstAttempt = status === "disconnected" && reconnectAttemptsRef.current === 0;
1274
1405
  const baseDelay = isFirstAttempt ? 0 : Math.min(500 * Math.pow(2, reconnectAttemptsRef.current), 5e3);
1275
1406
  const jitter = isFirstAttempt ? 0 : Math.random() * 500;
1276
- const delay = baseDelay + jitter;
1407
+ const delay2 = baseDelay + jitter;
1277
1408
  const timer = setTimeout(() => {
1278
1409
  if (!isMountedRef.current) return;
1279
1410
  reconnectAttemptsRef.current++;
1280
1411
  adapter.connect().catch(() => {
1281
1412
  });
1282
- }, delay);
1413
+ }, delay2);
1283
1414
  return () => clearTimeout(timer);
1284
1415
  }, [status, autoConnect, adapter]);
1285
1416
  useEffect3(() => {
@@ -1827,13 +1958,15 @@ code {
1827
1958
  border-radius: 6px !important;
1828
1959
  overflow: visible !important;
1829
1960
  background: transparent !important;
1830
- margin: 6px 0 !important;
1961
+ margin: 4px 0 !important;
1962
+ padding: 0 !important;
1963
+ gap: 0 !important;
1831
1964
  }
1832
1965
 
1833
1966
  [data-streamdown="code-block-header"] {
1834
1967
  position: absolute !important;
1835
- top: 10px !important;
1836
- left: 12px !important;
1968
+ top: 4px !important;
1969
+ left: 10px !important;
1837
1970
  right: auto !important;
1838
1971
  z-index: 10;
1839
1972
  padding: 0 !important;
@@ -1846,8 +1979,8 @@ code {
1846
1979
 
1847
1980
  [data-streamdown="code-block-actions"] {
1848
1981
  position: absolute !important;
1849
- top: 10px !important;
1850
- right: 8px !important;
1982
+ top: 4px !important;
1983
+ right: 6px !important;
1851
1984
  z-index: 11;
1852
1985
  display: flex !important;
1853
1986
  align-items: center;
@@ -1867,7 +2000,7 @@ code {
1867
2000
 
1868
2001
  [data-streamdown="code-block-header"] > span:first-child {
1869
2002
  display: inline-flex;
1870
- font-size: 11px;
2003
+ font-size: 10px;
1871
2004
  color: var(--color-muted);
1872
2005
  text-transform: lowercase;
1873
2006
  }
@@ -1882,7 +2015,7 @@ code {
1882
2015
  /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
1883
2016
  [data-streamdown="code-block-header"] button,
1884
2017
  [data-streamdown="code-block-actions"] button {
1885
- padding: 4px !important;
2018
+ padding: 2px !important;
1886
2019
  border: none !important;
1887
2020
  border-radius: 4px !important;
1888
2021
  background: var(--color-surface) !important;
@@ -1899,8 +2032,8 @@ code {
1899
2032
  [data-streamdown="code-block-header"] svg,
1900
2033
  [data-streamdown="code-block-actions"] svg {
1901
2034
  color: var(--color-muted) !important;
1902
- width: 12px !important;
1903
- height: 12px !important;
2035
+ width: 11px !important;
2036
+ height: 11px !important;
1904
2037
  }
1905
2038
 
1906
2039
  [data-streamdown="code-block-header"] button:hover svg,
@@ -1909,18 +2042,33 @@ code {
1909
2042
  }
1910
2043
 
1911
2044
  [data-streamdown="code-block-body"] {
1912
- padding: 8px 12px !important;
2045
+ padding: 18px 12px 8px !important;
1913
2046
  background: rgba(0, 0, 0, 0.02) !important;
1914
2047
  border-radius: 6px !important;
2048
+ border: none !important;
2049
+ box-shadow: none !important;
1915
2050
  font-size: 13px !important;
1916
2051
  line-height: 1.5 !important;
1917
2052
  white-space: pre !important;
1918
2053
  overflow-x: auto !important;
1919
2054
  }
1920
2055
 
2056
+ [data-streamdown="code-block-body"] pre {
2057
+ margin: 0 !important;
2058
+ padding: 0 !important;
2059
+ border: none !important;
2060
+ background: transparent !important;
2061
+ border-radius: 0 !important;
2062
+ }
2063
+
1921
2064
  [data-streamdown="code-block-body"] code {
1922
2065
  white-space: pre !important;
1923
2066
  display: block !important;
2067
+ color: var(--color-text);
2068
+ }
2069
+
2070
+ [data-streamdown="code-block-body"] code > span {
2071
+ display: block;
1924
2072
  }
1925
2073
 
1926
2074
  .dark [data-streamdown="code-block-body"],
@@ -1934,17 +2082,38 @@ code {
1934
2082
 
1935
2083
  [data-streamdown="code-block-body"],
1936
2084
  [data-streamdown="code-block-body"] code {
1937
- color: var(--color-text) !important;
1938
- opacity: 0.85;
2085
+ opacity: 1;
1939
2086
  }
1940
2087
 
1941
2088
  [data-streamdown="code-block-body"] code span[style] {
1942
2089
  background-color: transparent !important;
1943
2090
  }
1944
2091
 
1945
- .dark [data-streamdown="code-block-body"] code span[style],
1946
- :root.dark [data-streamdown="code-block-body"] code span[style] {
1947
- color: var(--shiki-dark) !important;
2092
+ [data-streamdown="code-block-body"] pre[style*="--sdm-bg"] {
2093
+ background-color: var(--sdm-bg) !important;
2094
+ }
2095
+
2096
+ .dark [data-streamdown="code-block-body"] pre[style*="--sdm-bg"],
2097
+ :root.dark [data-streamdown="code-block-body"] pre[style*="--sdm-bg"] {
2098
+ background-color: var(--shiki-dark-bg, var(--sdm-bg)) !important;
2099
+ }
2100
+
2101
+ [data-streamdown="code-block-body"] code span[style*="--sdm-c"] {
2102
+ color: var(--sdm-c) !important;
2103
+ }
2104
+
2105
+ .dark [data-streamdown="code-block-body"] code span[style*="--sdm-c"],
2106
+ :root.dark [data-streamdown="code-block-body"] code span[style*="--sdm-c"] {
2107
+ color: var(--shiki-dark, var(--sdm-c)) !important;
2108
+ }
2109
+
2110
+ [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"] {
2111
+ background-color: var(--sdm-tbg) !important;
2112
+ }
2113
+
2114
+ .dark [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"],
2115
+ :root.dark [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"] {
2116
+ background-color: var(--shiki-dark-bg, var(--sdm-tbg)) !important;
1948
2117
  }
1949
2118
 
1950
2119
  [data-streamdown="table-wrapper"].my-4 {
@@ -6945,6 +7114,7 @@ import { memo as memo6 } from "react";
6945
7114
  // src/renderer/renderers/MarkdownRenderer.tsx
6946
7115
  import { memo as memo3, useMemo as useMemo5, useCallback as useCallback11 } from "react";
6947
7116
  import { Streamdown, defaultRehypePlugins } from "streamdown";
7117
+ import { code as streamdownCode } from "@streamdown/code";
6948
7118
  import { harden } from "rehype-harden";
6949
7119
  import remarkGfm from "remark-gfm";
6950
7120
  import { jsx as jsx6 } from "react/jsx-runtime";
@@ -7003,6 +7173,7 @@ var MarkdownRenderer = memo3(function MarkdownRenderer2({
7003
7173
  const remarkPlugins = useMemo5(() => {
7004
7174
  return [remarkGfm];
7005
7175
  }, []);
7176
+ const streamdownPlugins = useMemo5(() => ({ code: streamdownCode }), []);
7006
7177
  const customComponents = useMemo5(() => {
7007
7178
  const comps = {};
7008
7179
  comps.p = ({ children }) => {
@@ -7055,6 +7226,7 @@ var MarkdownRenderer = memo3(function MarkdownRenderer2({
7055
7226
  {
7056
7227
  remarkPlugins,
7057
7228
  rehypePlugins,
7229
+ plugins: streamdownPlugins,
7058
7230
  components: customComponents,
7059
7231
  mode: isStreaming ? "streaming" : "static",
7060
7232
  isAnimating: isStreaming,
@@ -7790,7 +7962,7 @@ var SanqianChatMessage = memo6(function SanqianChatMessage2({ message }) {
7790
7962
  "div",
7791
7963
  {
7792
7964
  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"}`,
7793
- children: isUser ? /* @__PURE__ */ jsx9("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content ? /* @__PURE__ */ jsx9(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-3" }) : showStreamingPlaceholder ? /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 text-[var(--chat-muted)] italic min-h-[1.5rem]", children: [
7965
+ children: isUser ? /* @__PURE__ */ jsx9("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content ? /* @__PURE__ */ jsx9(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-0" }) : showStreamingPlaceholder ? /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 text-[var(--chat-muted)] italic min-h-[1.5rem]", children: [
7794
7966
  /* @__PURE__ */ jsxs4("div", { className: "flex gap-1 items-center", children: [
7795
7967
  /* @__PURE__ */ jsx9("span", { className: "w-1.5 h-1.5 bg-[var(--chat-accent)] rounded-full animate-bounce", style: { animationDelay: "0ms" } }),
7796
7968
  /* @__PURE__ */ jsx9("span", { className: "w-1.5 h-1.5 bg-[var(--chat-accent)] rounded-full animate-bounce", style: { animationDelay: "150ms" } }),
@@ -10001,7 +10173,7 @@ var FloatingChat = memo15(function FloatingChat2({
10001
10173
  "div",
10002
10174
  {
10003
10175
  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"}`,
10004
- children: isUser ? /* @__PURE__ */ jsx22("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content && (renderContent ? /* @__PURE__ */ jsx22("div", { className: "prose prose-chat dark:prose-invert max-w-none px-3", children: renderContent(message.content, message.isStreaming ?? false) }) : /* @__PURE__ */ jsx22(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-3" }))
10176
+ children: isUser ? /* @__PURE__ */ jsx22("span", { className: "whitespace-pre-wrap", children: message.content }) : message.content && (renderContent ? /* @__PURE__ */ jsx22("div", { className: "prose prose-chat dark:prose-invert max-w-none px-0", children: renderContent(message.content, message.isStreaming ?? false) }) : /* @__PURE__ */ jsx22(MarkdownRenderer, { content: message.content, isStreaming: message.isStreaming, className: "px-0" }))
10005
10177
  }
10006
10178
  ) }) }) });
10007
10179
  },
@@ -10239,7 +10411,7 @@ var HistoryList = memo16(function HistoryList2({
10239
10411
  deleteText: "#ef4444",
10240
10412
  loadingDot: isDarkMode ? "rgba(255, 255, 255, 0.3)" : "rgba(0, 0, 0, 0.2)"
10241
10413
  };
10242
- const loadingDots = /* @__PURE__ */ jsx23("div", { style: { display: "flex", justifyContent: "center", padding: "1.5rem 0" }, children: /* @__PURE__ */ jsx23("div", { style: { display: "flex", gap: "0.25rem" }, children: [0, 150, 300].map((delay) => /* @__PURE__ */ jsx23(
10414
+ const loadingDots = /* @__PURE__ */ jsx23("div", { style: { display: "flex", justifyContent: "center", padding: "1.5rem 0" }, children: /* @__PURE__ */ jsx23("div", { style: { display: "flex", gap: "0.25rem" }, children: [0, 150, 300].map((delay2) => /* @__PURE__ */ jsx23(
10243
10415
  "span",
10244
10416
  {
10245
10417
  style: {
@@ -10248,10 +10420,10 @@ var HistoryList = memo16(function HistoryList2({
10248
10420
  borderRadius: "50%",
10249
10421
  background: colors.loadingDot,
10250
10422
  animation: "bounce 1s infinite",
10251
- animationDelay: `${delay}ms`
10423
+ animationDelay: `${delay2}ms`
10252
10424
  }
10253
10425
  },
10254
- delay
10426
+ delay2
10255
10427
  )) }) });
10256
10428
  if (isLoading && conversations.length === 0) {
10257
10429
  return loadingDots;
@@ -10264,7 +10436,7 @@ var HistoryList = memo16(function HistoryList2({
10264
10436
  const isSelected = conv.id === selectedId;
10265
10437
  const isHovered = conv.id === hoveredId;
10266
10438
  const isHighlighted = isConversationHighlighted?.(conv) === true;
10267
- const resolvedHighlightLabel = isHighlighted ? (typeof highlightedLabel === "function" ? highlightedLabel(conv) : highlightedLabel) ?? "RELATED" : null;
10439
+ const resolvedHighlightLabel = isHighlighted ? (typeof highlightedLabel === "function" ? highlightedLabel(conv) : highlightedLabel) ?? strings.related ?? "RELATED" : null;
10268
10440
  return /* @__PURE__ */ jsxs17(
10269
10441
  "div",
10270
10442
  {
@@ -10915,7 +11087,7 @@ var CompactChat = memo18(function CompactChat2({
10915
11087
  content: message.content,
10916
11088
  isStreaming: message.isStreaming,
10917
11089
  linkHandler,
10918
- className: "px-3"
11090
+ className: "px-0"
10919
11091
  }
10920
11092
  )
10921
11093
  }
@@ -11115,7 +11287,8 @@ var CompactChat = memo18(function CompactChat2({
11115
11287
  loadMore: mergedStrings.loadMore,
11116
11288
  today: mergedStrings.today,
11117
11289
  yesterday: mergedStrings.yesterday,
11118
- delete: mergedStrings.delete
11290
+ delete: mergedStrings.delete,
11291
+ related: mergedStrings.related
11119
11292
  }
11120
11293
  }
11121
11294
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yushaw/sanqian-chat",
3
- "version": "0.2.42",
3
+ "version": "0.2.44",
4
4
  "description": "Floating chat window SDK for Sanqian AI Assistant",
5
5
  "main": "./dist/main/index.js",
6
6
  "types": "./dist/main/index.d.ts",
@@ -44,6 +44,7 @@
44
44
  "test:watch": "vitest"
45
45
  },
46
46
  "dependencies": {
47
+ "@streamdown/code": "^1.0.3",
47
48
  "@yushaw/sanqian-sdk": "^0.3.26",
48
49
  "react-virtuoso": "^4.15.0",
49
50
  "rehype-harden": "^1.1.6",
@@ -408,13 +408,15 @@ code {
408
408
  border-radius: 6px !important;
409
409
  overflow: visible !important;
410
410
  background: transparent !important;
411
- margin: 6px 0 !important;
411
+ margin: 4px 0 !important;
412
+ padding: 0 !important;
413
+ gap: 0 !important;
412
414
  }
413
415
 
414
416
  [data-streamdown="code-block-header"] {
415
417
  position: absolute !important;
416
- top: 10px !important;
417
- left: 12px !important;
418
+ top: 4px !important;
419
+ left: 10px !important;
418
420
  right: auto !important;
419
421
  z-index: 10;
420
422
  padding: 0 !important;
@@ -427,8 +429,8 @@ code {
427
429
 
428
430
  [data-streamdown="code-block-actions"] {
429
431
  position: absolute !important;
430
- top: 10px !important;
431
- right: 8px !important;
432
+ top: 4px !important;
433
+ right: 6px !important;
432
434
  z-index: 11;
433
435
  display: flex !important;
434
436
  align-items: center;
@@ -448,7 +450,7 @@ code {
448
450
 
449
451
  [data-streamdown="code-block-header"] > span:first-child {
450
452
  display: inline-flex;
451
- font-size: 11px;
453
+ font-size: 10px;
452
454
  color: var(--color-muted);
453
455
  text-transform: lowercase;
454
456
  }
@@ -463,7 +465,7 @@ code {
463
465
  /* Normalize action buttons for both streamdown v1 (header>div) and v2 (code-block-actions). */
464
466
  [data-streamdown="code-block-header"] button,
465
467
  [data-streamdown="code-block-actions"] button {
466
- padding: 4px !important;
468
+ padding: 2px !important;
467
469
  border: none !important;
468
470
  border-radius: 4px !important;
469
471
  background: var(--color-surface) !important;
@@ -480,8 +482,8 @@ code {
480
482
  [data-streamdown="code-block-header"] svg,
481
483
  [data-streamdown="code-block-actions"] svg {
482
484
  color: var(--color-muted) !important;
483
- width: 12px !important;
484
- height: 12px !important;
485
+ width: 11px !important;
486
+ height: 11px !important;
485
487
  }
486
488
 
487
489
  [data-streamdown="code-block-header"] button:hover svg,
@@ -490,18 +492,33 @@ code {
490
492
  }
491
493
 
492
494
  [data-streamdown="code-block-body"] {
493
- padding: 8px 12px !important;
495
+ padding: 18px 12px 8px !important;
494
496
  background: rgba(0, 0, 0, 0.02) !important;
495
497
  border-radius: 6px !important;
498
+ border: none !important;
499
+ box-shadow: none !important;
496
500
  font-size: 13px !important;
497
501
  line-height: 1.5 !important;
498
502
  white-space: pre !important;
499
503
  overflow-x: auto !important;
500
504
  }
501
505
 
506
+ [data-streamdown="code-block-body"] pre {
507
+ margin: 0 !important;
508
+ padding: 0 !important;
509
+ border: none !important;
510
+ background: transparent !important;
511
+ border-radius: 0 !important;
512
+ }
513
+
502
514
  [data-streamdown="code-block-body"] code {
503
515
  white-space: pre !important;
504
516
  display: block !important;
517
+ color: var(--color-text);
518
+ }
519
+
520
+ [data-streamdown="code-block-body"] code > span {
521
+ display: block;
505
522
  }
506
523
 
507
524
  .dark [data-streamdown="code-block-body"],
@@ -515,17 +532,38 @@ code {
515
532
 
516
533
  [data-streamdown="code-block-body"],
517
534
  [data-streamdown="code-block-body"] code {
518
- color: var(--color-text) !important;
519
- opacity: 0.85;
535
+ opacity: 1;
520
536
  }
521
537
 
522
538
  [data-streamdown="code-block-body"] code span[style] {
523
539
  background-color: transparent !important;
524
540
  }
525
541
 
526
- .dark [data-streamdown="code-block-body"] code span[style],
527
- :root.dark [data-streamdown="code-block-body"] code span[style] {
528
- color: var(--shiki-dark) !important;
542
+ [data-streamdown="code-block-body"] pre[style*="--sdm-bg"] {
543
+ background-color: var(--sdm-bg) !important;
544
+ }
545
+
546
+ .dark [data-streamdown="code-block-body"] pre[style*="--sdm-bg"],
547
+ :root.dark [data-streamdown="code-block-body"] pre[style*="--sdm-bg"] {
548
+ background-color: var(--shiki-dark-bg, var(--sdm-bg)) !important;
549
+ }
550
+
551
+ [data-streamdown="code-block-body"] code span[style*="--sdm-c"] {
552
+ color: var(--sdm-c) !important;
553
+ }
554
+
555
+ .dark [data-streamdown="code-block-body"] code span[style*="--sdm-c"],
556
+ :root.dark [data-streamdown="code-block-body"] code span[style*="--sdm-c"] {
557
+ color: var(--shiki-dark, var(--sdm-c)) !important;
558
+ }
559
+
560
+ [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"] {
561
+ background-color: var(--sdm-tbg) !important;
562
+ }
563
+
564
+ .dark [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"],
565
+ :root.dark [data-streamdown="code-block-body"] code span[style*="--sdm-tbg"] {
566
+ background-color: var(--shiki-dark-bg, var(--sdm-tbg)) !important;
529
567
  }
530
568
 
531
569
  [data-streamdown="table-wrapper"].my-4 {