From 932ed022ab3858dcde36d379638de00940edb628 Mon Sep 17 00:00:00 2001 From: skift Date: Thu, 18 Dec 2025 18:30:00 +0800 Subject: [PATCH 1/2] bugfix: restore some bugs on notes --- frontend/package-lock.json | 12 +----------- frontend/src/app/notes/new/page.tsx | 18 +++++++++++++++++- frontend/src/app/notes/page.tsx | 18 +++++++++++++++++- frontend/src/app/smart-reading/page.tsx | 6 ++---- .../src/components/ai-agent/AgentChatPanel.tsx | 18 +++++++++++++++++- .../components/note-editor/NoteEditorPanel.tsx | 12 ++++++++++-- 6 files changed, 64 insertions(+), 20 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 16bf0c4..fd5c990 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1485,7 +1485,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1552,7 +1551,6 @@ "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/types": "8.46.0", @@ -2179,7 +2177,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3309,7 +3306,6 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3484,7 +3480,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7356,7 +7351,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7366,7 +7360,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -8546,8 +8539,7 @@ "version": "4.1.14", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", @@ -8621,7 +8613,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8798,7 +8789,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/frontend/src/app/notes/new/page.tsx b/frontend/src/app/notes/new/page.tsx index a3e64d5..c3f74f7 100644 --- a/frontend/src/app/notes/new/page.tsx +++ b/frontend/src/app/notes/new/page.tsx @@ -17,7 +17,8 @@ import { BACKEND_URL } from "@/lib/config"; export default function NewNotePage() { const router = useRouter(); - const token = getAccessToken(); + const [token, setToken] = useState(null); + const [hydrated, setHydrated] = useState(false); const [papers, setPapers] = useState([]); const [title, setTitle] = useState(""); const [content, setContent] = useState(""); @@ -30,6 +31,12 @@ export default function NewNotePage() { [], ); + // Defer token read to client to avoid SSR/client mismatch + useEffect(() => { + setToken(getAccessToken()); + setHydrated(true); + }, []); + useEffect(() => { if (!token) return; fetchUploadedPapers(token, { page: 1, pageSize: 50 }) @@ -61,6 +68,15 @@ export default function NewNotePage() { } } + // Prevent hydration mismatches: wait until client token is known + if (!hydrated) { + return ( + +
加载中…
+
+ ); + } + if (!token) { return ( diff --git a/frontend/src/app/notes/page.tsx b/frontend/src/app/notes/page.tsx index cf7236d..c138292 100644 --- a/frontend/src/app/notes/page.tsx +++ b/frontend/src/app/notes/page.tsx @@ -40,7 +40,8 @@ function truncate(text?: string | null, length = 140): string { export default function NotesPage() { const router = useRouter(); - const token = getAccessToken(); + const [token, setToken] = useState(null); + const [hydrated, setHydrated] = useState(false); const [notes, setNotes] = useState([]); const [papers, setPapers] = useState([]); const [loading, setLoading] = useState(true); @@ -56,6 +57,12 @@ export default function NotesPage() { [], ); + // Defer token read to client to avoid SSR/CSR mismatch + useEffect(() => { + setToken(getAccessToken()); + setHydrated(true); + }, []); + const paperLookup = useMemo(() => { return papers.reduce>((acc, item) => { acc[item.id] = item; @@ -168,6 +175,15 @@ export default function NotesPage() { const searchInputRef = useRef(null); + // Prevent hydration mismatch by waiting for client token + if (!hydrated) { + return ( + +
加载中…
+
+ ); + } + if (!token) { return ( diff --git a/frontend/src/app/smart-reading/page.tsx b/frontend/src/app/smart-reading/page.tsx index d5ec037..48fbf0a 100644 --- a/frontend/src/app/smart-reading/page.tsx +++ b/frontend/src/app/smart-reading/page.tsx @@ -66,7 +66,7 @@ export default function SmartReadingPage() { }, []); const agentChatPanelRef = useRef<{ setInputText: (text: string) => void } | null>(null); - const noteEditorPanelRef = useRef<{ appendContent: (text: string) => void } | null>(null); + const noteEditorPanelRef = useRef<{ appendContent: (text: string) => void; resetEditor: () => void } | null>(null); const splitContainerRef = useRef(null); const handleExtractText = useCallback((text: string) => { @@ -281,11 +281,9 @@ export default function SmartReadingPage() { setSelectedPdfUrl(null); setUploadedPaper(null); setUploadError(null); - setParseProgress(null); - setParseError(null); - setParsedDocument(null); setMineruResult(null); setIsParsing(false); + noteEditorPanelRef.current?.resetEditor?.(); }} className="flex items-center gap-2 rounded-lg border border-red-200 bg-red-50/80 px-3 py-2 font-medium text-red-700 transition hover:border-red-300 hover:bg-red-100 dark:border-red-800/60 dark:bg-red-900/30 dark:text-red-300 dark:hover:border-red-700 dark:hover:bg-red-900/50" > diff --git a/frontend/src/components/ai-agent/AgentChatPanel.tsx b/frontend/src/components/ai-agent/AgentChatPanel.tsx index 7c3ac78..7b6ed15 100644 --- a/frontend/src/components/ai-agent/AgentChatPanel.tsx +++ b/frontend/src/components/ai-agent/AgentChatPanel.tsx @@ -120,7 +120,23 @@ const AgentChatPanel = forwardRef< } } catch (error) { console.error("加载对话历史失败:", error); - // 如果加载失败,不影响新对话 + // 后端会在类别不匹配或不存在时返回 404;清理本地缓存以便重新创建对话 + const message = error instanceof Error ? error.message : ""; + if (message.includes("不存在") || message.toLowerCase().includes("not found")) { + if (paperId) { + localStorage.removeItem(`conversation_${paperId}`); + } + setConversationId(null); + setMessages([ + { + id: createId(), + role: "assistant", + content: "对话不存在,已为当前文档重置。可以继续提问,我会新建对话并保存历史。", + timestamp: new Date(), + }, + ]); + } + // 其他错误不影响新对话 } finally { setIsLoadingHistory(false); } diff --git a/frontend/src/components/note-editor/NoteEditorPanel.tsx b/frontend/src/components/note-editor/NoteEditorPanel.tsx index af02a7c..3ef83d6 100644 --- a/frontend/src/components/note-editor/NoteEditorPanel.tsx +++ b/frontend/src/components/note-editor/NoteEditorPanel.tsx @@ -25,7 +25,7 @@ interface NoteEditorPanelProps { } const NoteEditorPanel = forwardRef< - { appendContent: (text: string) => void }, + { appendContent: (text: string) => void; resetEditor: () => void }, NoteEditorPanelProps >(({ paperId, mineruResult }, ref) => { const [title, setTitle] = useState(""); @@ -41,7 +41,7 @@ const NoteEditorPanel = forwardRef< const autoSaveTimerRef = useRef | null>(null); const [isDarkMode, setIsDarkMode] = useState(false); - // 暴露appendContent方法给父组件 + // 暴露 appendContent / resetEditor 方法给父组件 useImperativeHandle(ref, () => ({ appendContent: (text: string) => { setContent((prev) => { @@ -49,6 +49,14 @@ const NoteEditorPanel = forwardRef< return prev + separator + text; }); }, + resetEditor: () => { + setTitle(""); + setContent(""); + setNoteId(null); + setLastSaved(null); + setHasAutoFilledTitle(false); + setRelatedNotes([]); + }, })); // 重新加载笔记列表 From aba5143b1ca5806ca47add4979eb51f13d5748cc Mon Sep 17 00:00:00 2001 From: skift Date: Thu, 18 Dec 2025 18:53:32 +0800 Subject: [PATCH 2/2] bugfix: auto summary only appear for the first time --- frontend/src/components/ai-agent/AgentChatPanel.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/frontend/src/components/ai-agent/AgentChatPanel.tsx b/frontend/src/components/ai-agent/AgentChatPanel.tsx index 7b6ed15..f198e04 100644 --- a/frontend/src/components/ai-agent/AgentChatPanel.tsx +++ b/frontend/src/components/ai-agent/AgentChatPanel.tsx @@ -69,6 +69,7 @@ const AgentChatPanel = forwardRef< const [showHeader, setShowHeader] = useState(true); const streamControllerRef = useRef(null); const autoSummaryHistoryRef = useRef>({}); + const savedConversationRef = useRef(false); // 暴露 setInputText 方法给父组件 useImperativeHandle(ref, () => ({ @@ -117,6 +118,7 @@ const AgentChatPanel = forwardRef< timestamp: new Date(msg.created_at), })); setMessages(historyMessages); + savedConversationRef.current = true; } } catch (error) { console.error("加载对话历史失败:", error); @@ -135,6 +137,7 @@ const AgentChatPanel = forwardRef< timestamp: new Date(), }, ]); + savedConversationRef.current = false; } // 其他错误不影响新对话 } finally { @@ -150,6 +153,7 @@ const AgentChatPanel = forwardRef< const convId = parseInt(savedConvId, 10); setConversationId(convId); loadConversationHistory(convId); + savedConversationRef.current = true; } else { // 没有保存的对话,显示欢迎消息 setMessages([ @@ -162,6 +166,7 @@ const AgentChatPanel = forwardRef< ]); setConversationId(null); setQuotedText(null); + savedConversationRef.current = false; } } else if (!mineruResult) { // 重置为初始状态 @@ -175,6 +180,7 @@ const AgentChatPanel = forwardRef< ]); setConversationId(null); setQuotedText(null); + savedConversationRef.current = false; } }, [paperId, mineruResult, loadConversationHistory]); @@ -302,6 +308,9 @@ const AgentChatPanel = forwardRef< if (!paperId || !mineruResult) { return; } + if (conversationId || savedConversationRef.current) { + return; + } const summaryIntro: AgentMessage = { id: createId(),