mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 13:16:16 +02:00
fix(llm): sidebar chat lost when saving to note
This commit is contained in:
@@ -10,7 +10,7 @@ import { formatDateTime } from "../../utils/formatters";
|
||||
import ActionButton from "../react/ActionButton.js";
|
||||
import Dropdown from "../react/Dropdown.js";
|
||||
import { FormListItem } from "../react/FormList.js";
|
||||
import { useActiveNoteContext, useEditorSpacedUpdate, useNote, useNoteProperty } from "../react/hooks.js";
|
||||
import { useActiveNoteContext, useNote, useNoteProperty, useSpacedUpdate } from "../react/hooks.js";
|
||||
import NoItems from "../react/NoItems.js";
|
||||
import ChatInputBar from "../type_widgets/llm_chat/ChatInputBar.js";
|
||||
import ChatMessage from "../type_widgets/llm_chat/ChatMessage.js";
|
||||
@@ -22,11 +22,15 @@ import RightPanelWidget from "./RightPanelWidget.js";
|
||||
* Sidebar chat widget that appears in the right panel.
|
||||
* Uses a hidden LLM chat note for persistence across all notes.
|
||||
* The same chat persists when switching between notes.
|
||||
*
|
||||
* Unlike the LlmChat type widget which receives a valid FNote from the
|
||||
* framework, the sidebar creates notes lazily. We use useSpacedUpdate with
|
||||
* a direct server.put (using the string noteId) instead of useEditorSpacedUpdate
|
||||
* (which requires an FNote and silently no-ops when it's null).
|
||||
*/
|
||||
export default function SidebarChat() {
|
||||
const [chatNoteId, setChatNoteId] = useState<string | null>(null);
|
||||
const [recentChats, setRecentChats] = useState<RecentLlmChat[]>([]);
|
||||
const spacedUpdateRef = useRef<{ scheduleUpdate: () => void }>(null);
|
||||
const historyDropdownRef = useRef<BootstrapDropdown | null>(null);
|
||||
|
||||
// Get the current active note context
|
||||
@@ -36,42 +40,35 @@ export default function SidebarChat() {
|
||||
const chatNote = useNote(chatNoteId);
|
||||
const chatTitle = useNoteProperty(chatNote, "title") || t("sidebar_chat.title");
|
||||
|
||||
// Refs for stable access in the spaced update callback
|
||||
const chatNoteIdRef = useRef(chatNoteId);
|
||||
chatNoteIdRef.current = chatNoteId;
|
||||
|
||||
// Use shared chat hook with sidebar-specific options
|
||||
const chat = useLlmChat(
|
||||
// onMessagesChange - trigger save
|
||||
() => spacedUpdateRef.current?.scheduleUpdate(),
|
||||
() => spacedUpdate.scheduleUpdate(),
|
||||
{ defaultEnableNoteTools: true, supportsExtendedThinking: true }
|
||||
);
|
||||
|
||||
// Ref to access chat methods in callbacks without triggering re-runs
|
||||
const chatRef = useRef(chat);
|
||||
chatRef.current = chat;
|
||||
|
||||
// Persistence via useEditorSpacedUpdate (same mechanism as the LlmChat type widget).
|
||||
// When chatNote is null (before lazy creation), saves are no-ops.
|
||||
const spacedUpdate = useEditorSpacedUpdate({
|
||||
note: chatNote,
|
||||
noteType: "llmChat",
|
||||
noteContext: null,
|
||||
getData: () => {
|
||||
const content = chatRef.current.getContent();
|
||||
return { content: JSON.stringify(content) };
|
||||
},
|
||||
onContentChange: (content) => {
|
||||
if (!content) {
|
||||
chatRef.current.clearMessages();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed: LlmChatContent = JSON.parse(content);
|
||||
chatRef.current.loadFromContent(parsed);
|
||||
} catch (e) {
|
||||
console.error("Failed to parse LLM chat content:", e);
|
||||
chatRef.current.clearMessages();
|
||||
}
|
||||
// Save directly via server.put using the string noteId.
|
||||
// This avoids the FNote dependency that useEditorSpacedUpdate requires.
|
||||
const spacedUpdate = useSpacedUpdate(async () => {
|
||||
const noteId = chatNoteIdRef.current;
|
||||
if (!noteId) return;
|
||||
|
||||
const content = chatRef.current.getContent();
|
||||
try {
|
||||
await server.put(`notes/${noteId}/data`, {
|
||||
content: JSON.stringify(content)
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to save chat:", err);
|
||||
}
|
||||
});
|
||||
spacedUpdateRef.current = spacedUpdate;
|
||||
|
||||
// Update chat context when active note changes
|
||||
useEffect(() => {
|
||||
@@ -95,8 +92,17 @@ export default function SidebarChat() {
|
||||
|
||||
if (existingChat) {
|
||||
setChatNoteId(existingChat.noteId);
|
||||
// Load content
|
||||
try {
|
||||
const blob = await server.get<{ content: string }>(`notes/${existingChat.noteId}/blob`);
|
||||
if (!cancelled && blob?.content) {
|
||||
const parsed: LlmChatContent = JSON.parse(blob.content);
|
||||
chatRef.current.loadFromContent(parsed);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load chat content:", err);
|
||||
}
|
||||
} else {
|
||||
// No existing chat - will create on first message
|
||||
setChatNoteId(null);
|
||||
chatRef.current.clearMessages();
|
||||
}
|
||||
@@ -203,7 +209,17 @@ export default function SidebarChat() {
|
||||
// Save any pending changes before switching
|
||||
await spacedUpdate.updateNowIfNecessary();
|
||||
|
||||
setChatNoteId(noteId);
|
||||
// Load the selected chat's content
|
||||
try {
|
||||
const blob = await server.get<{ content: string }>(`notes/${noteId}/blob`);
|
||||
if (blob?.content) {
|
||||
const parsed: LlmChatContent = JSON.parse(blob.content);
|
||||
setChatNoteId(noteId);
|
||||
chatRef.current.loadFromContent(parsed);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load selected chat:", err);
|
||||
}
|
||||
}, [chatNoteId, spacedUpdate]);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user