From 92f95e153f0eab8185f008ae9350fd2486e79971 Mon Sep 17 00:00:00 2001 From: gloria-zxr Date: Thu, 25 Dec 2025 20:44:12 +0800 Subject: [PATCH 01/40] refactor: improve styling and layout in SearchModal component - Adjusted spacing and alignment in the SearchModal for better visual consistency. - Updated button styles for improved usability and aesthetics. - Added a separator line to enhance the input area layout. --- src/ui/src/components/SearchModal.jsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ui/src/components/SearchModal.jsx b/src/ui/src/components/SearchModal.jsx index 4512ca8..3f11ecb 100644 --- a/src/ui/src/components/SearchModal.jsx +++ b/src/ui/src/components/SearchModal.jsx @@ -354,12 +354,12 @@ export function SearchModal({ isOpen, onClose, onSelectDoc, onSelectIdea }) { > {/* Search Input Area */}
-
+
)}
+
setQuery(e.target.value)} - onKeyDown={handleKeyDown} - placeholder={placeholderText} + onChange={(e) => setQuery(e.target.value)} + onKeyDown={handleKeyDown} + placeholder={placeholderText} className="flex-1 text-lg text-[#37352f] placeholder-gray-400 bg-transparent border-none focus:ring-0 p-0 outline-none leading-tight" autoComplete="off" autoCorrect="off" From af0f8275b6086b069c52728706b02760646e5503 Mon Sep 17 00:00:00 2001 From: gloria-zxr Date: Fri, 26 Dec 2025 14:55:54 +0800 Subject: [PATCH 02/40] feat: add error handling and retry functionality in AI reflection process --- src/ui/src/api.js | 12 ++++-- src/ui/src/components/IdeaTimeline.jsx | 38 ++++++++++++++++++- src/ui/src/components/IdeaTimeline.module.css | 2 +- src/ui/src/context/AIContext.jsx | 12 ++---- src/ui/src/i18n/locales/en.json | 1 + src/ui/src/i18n/locales/zh.json | 1 + 6 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/ui/src/api.js b/src/ui/src/api.js index 73c5e82..48f8cda 100644 --- a/src/ui/src/api.js +++ b/src/ui/src/api.js @@ -397,7 +397,11 @@ export async function streamAIChat(messages, onToken, onError) { if (error) { if (!resolved) { resolved = true; - onError?.(new Error(error)); + try { + onError?.(new Error(error)); + } catch (err) { + // ignore handler errors to avoid swallowing rejection + } if (unlisten) unlisten(); reject(new Error(error)); } @@ -464,8 +468,9 @@ export async function streamAIChat(messages, onToken, onError) { try { const data = JSON.parse(line.slice(6)); if (data.error) { - onError?.(new Error(data.error)); - return; + const err = new Error(data.error); + onError?.(err); + throw err; } if (data.content) { onToken?.(data.content); @@ -481,6 +486,7 @@ export async function streamAIChat(messages, onToken, onError) { } } catch (error) { onError?.(error); + throw error; } } diff --git a/src/ui/src/components/IdeaTimeline.jsx b/src/ui/src/components/IdeaTimeline.jsx index 68b0e05..89201eb 100644 --- a/src/ui/src/components/IdeaTimeline.jsx +++ b/src/ui/src/components/IdeaTimeline.jsx @@ -64,6 +64,7 @@ export default function IdeaTimeline({ // AI 反思的状态 const [reflectingThreadId, setReflectingThreadId] = useState(null); const [reflectionText, setReflectionText] = useState(''); + const [reflectionError, setReflectionError] = useState(''); const [isReflecting, setIsReflecting] = useState(false); const reflectionInputRef = useRef(null); const [animatedEntryId, setAnimatedEntryId] = useState(null); @@ -548,6 +549,7 @@ export default function IdeaTimeline({ setReflectingThreadId(threadId); setReflectionText(''); + setReflectionError(''); setIsReflecting(true); try { @@ -556,12 +558,13 @@ export default function IdeaTimeline({ }); } catch (err) { console.error('AI reflection failed:', err); - setReflectionText(t('idea.reflectError', 'AI 反思失败,请检查 AI 配置或稍后重试。')); + setReflectionError(err?.message || t('idea.reflectError', 'AI 反思失败,请检查 AI 配置或稍后重试。')); } finally { setIsReflecting(false); } }, [isAIAvailable, isReflecting, generateReflection, t]); + const handleSaveReflection = useCallback(async () => { if (!reflectionText.trim() || !reflectingThreadId) return; @@ -569,6 +572,7 @@ export default function IdeaTimeline({ await onAddAIReflection?.(reflectingThreadId, reflectionText); setReflectingThreadId(null); setReflectionText(''); + setReflectionError(''); } catch (err) { console.error('Failed to save AI reflection:', err); } @@ -577,6 +581,7 @@ export default function IdeaTimeline({ const handleCancelReflection = useCallback(() => { setReflectingThreadId(null); setReflectionText(''); + setReflectionError(''); }, []); const handleRequestDelete = useCallback((entryId, threadId) => { @@ -610,6 +615,11 @@ export default function IdeaTimeline({ return entries; }, [allEntriesGrouped]); + const handleRetryReflection = useCallback((threadId) => { + const entries = getThreadEntries(threadId); + handleStartReflect(threadId, entries); + }, [getThreadEntries, handleStartReflect]); + // 注册日期 ref const setDateRef = useCallback((dateKey, el) => { if (el) { @@ -1081,6 +1091,30 @@ export default function IdeaTimeline({ {t('idea.reflecting', 'AI 正在思考...')}
)} + {reflectionError && !isReflecting && ( +
+
+ + {reflectionError} +
+
+ + +
+
+ )} {/* 生成中:只读显示 + 光标动画 */} {reflectionText && isReflecting && (
@@ -1089,7 +1123,7 @@ export default function IdeaTimeline({
)} {/* 生成完成:可编辑 textarea */} - {!isReflecting && ( + {!isReflecting && !reflectionError && ( <>