mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 18:37:12 +02:00
fix(llm/sidebar): no longer properly persisting the chat
This commit is contained in:
@@ -104,7 +104,7 @@ export interface SavedData {
|
||||
|
||||
export function useEditorSpacedUpdate({ note, noteType, noteContext, getData, onContentChange, dataSaved, updateInterval }: {
|
||||
noteType: NoteType;
|
||||
note: FNote,
|
||||
note: FNote | null | undefined,
|
||||
noteContext: NoteContext | null | undefined,
|
||||
getData: () => Promise<SavedData | undefined> | SavedData | undefined,
|
||||
onContentChange: (newContent: string) => void,
|
||||
@@ -118,8 +118,8 @@ export function useEditorSpacedUpdate({ note, noteType, noteContext, getData, on
|
||||
return async () => {
|
||||
const data = await getData();
|
||||
|
||||
// for read only notes
|
||||
if (data === undefined || note.type !== noteType) return;
|
||||
// for read only notes, or if note is not yet available (e.g. lazy creation)
|
||||
if (data === undefined || !note || note.type !== noteType) return;
|
||||
|
||||
protected_session_holder.touchProtectedSessionIfNecessary(note);
|
||||
|
||||
@@ -138,7 +138,7 @@ export function useEditorSpacedUpdate({ note, noteType, noteContext, getData, on
|
||||
|
||||
// React to note/blob changes.
|
||||
useEffect(() => {
|
||||
if (!blob) return;
|
||||
if (!blob || !note) return;
|
||||
noteSavedDataStore.set(note.noteId, blob.content);
|
||||
spacedUpdate.allowUpdateWithoutChange(() => onContentChange(blob.content));
|
||||
}, [ blob ]);
|
||||
|
||||
@@ -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, useNote, useNoteProperty } from "../react/hooks.js";
|
||||
import { useActiveNoteContext, useEditorSpacedUpdate, useNote, useNoteProperty } 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";
|
||||
@@ -25,9 +25,8 @@ import RightPanelWidget from "./RightPanelWidget.js";
|
||||
*/
|
||||
export default function SidebarChat() {
|
||||
const [chatNoteId, setChatNoteId] = useState<string | null>(null);
|
||||
const [shouldSave, setShouldSave] = useState(false);
|
||||
const [recentChats, setRecentChats] = useState<RecentLlmChat[]>([]);
|
||||
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const spacedUpdateRef = useRef<{ scheduleUpdate: () => void }>(null);
|
||||
const historyDropdownRef = useRef<BootstrapDropdown | null>(null);
|
||||
|
||||
// Get the current active note context
|
||||
@@ -40,10 +39,40 @@ export default function SidebarChat() {
|
||||
// Use shared chat hook with sidebar-specific options
|
||||
const chat = useLlmChat(
|
||||
// onMessagesChange - trigger save
|
||||
() => setShouldSave(true),
|
||||
() => spacedUpdateRef.current?.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();
|
||||
}
|
||||
}
|
||||
});
|
||||
spacedUpdateRef.current = spacedUpdate;
|
||||
|
||||
// Update chat context when active note changes
|
||||
useEffect(() => {
|
||||
chat.setContextNoteId(activeNoteId ?? undefined);
|
||||
@@ -54,42 +83,6 @@ export default function SidebarChat() {
|
||||
chat.setChatNoteId(chatNoteId ?? undefined);
|
||||
}, [chatNoteId, chat.setChatNoteId]);
|
||||
|
||||
// Ref to access chat methods in effects without triggering re-runs
|
||||
const chatRef = useRef(chat);
|
||||
chatRef.current = chat;
|
||||
|
||||
// Handle debounced save when shouldSave is triggered
|
||||
useEffect(() => {
|
||||
if (!shouldSave || !chatNoteId) {
|
||||
setShouldSave(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setShouldSave(false);
|
||||
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
|
||||
saveTimeoutRef.current = setTimeout(async () => {
|
||||
const content = chat.getContent();
|
||||
try {
|
||||
await server.put(`notes/${chatNoteId}/data`, {
|
||||
content: JSON.stringify(content)
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to save chat:", err);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) {
|
||||
clearTimeout(saveTimeoutRef.current);
|
||||
saveTimeoutRef.current = undefined;
|
||||
}
|
||||
};
|
||||
}, [shouldSave, chatNoteId, chat]);
|
||||
|
||||
// Load the most recent chat on mount (runs once)
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -102,16 +95,6 @@ export default function SidebarChat() {
|
||||
|
||||
if (existingChat) {
|
||||
setChatNoteId(existingChat.noteId);
|
||||
// Load content inline to avoid dependency issues
|
||||
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);
|
||||
@@ -170,31 +153,38 @@ export default function SidebarChat() {
|
||||
}, [handleSubmit]);
|
||||
|
||||
const handleNewChat = useCallback(async () => {
|
||||
// Save any pending changes before switching
|
||||
await spacedUpdate.updateNowIfNecessary();
|
||||
|
||||
try {
|
||||
const note = await dateNoteService.createLlmChat();
|
||||
if (note) {
|
||||
setChatNoteId(note.noteId);
|
||||
chat.clearMessages();
|
||||
chatRef.current.clearMessages();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to create new chat:", err);
|
||||
}
|
||||
}, [chat]);
|
||||
}, [spacedUpdate]);
|
||||
|
||||
const handleSaveChat = useCallback(async () => {
|
||||
if (!chatNoteId) return;
|
||||
|
||||
// Save any pending changes before moving the chat
|
||||
await spacedUpdate.updateNowIfNecessary();
|
||||
|
||||
try {
|
||||
await server.post("special-notes/save-llm-chat", { llmChatNoteId: chatNoteId });
|
||||
// Create a new empty chat after saving
|
||||
const note = await dateNoteService.createLlmChat();
|
||||
if (note) {
|
||||
setChatNoteId(note.noteId);
|
||||
chat.clearMessages();
|
||||
chatRef.current.clearMessages();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to save chat to permanent location:", err);
|
||||
}
|
||||
}, [chatNoteId, chat]);
|
||||
}, [chatNoteId, spacedUpdate]);
|
||||
|
||||
const loadRecentChats = useCallback(async () => {
|
||||
try {
|
||||
@@ -210,17 +200,11 @@ export default function SidebarChat() {
|
||||
|
||||
if (noteId === chatNoteId) return;
|
||||
|
||||
try {
|
||||
const blob = await server.get<{ content: string }>(`notes/${noteId}/blob`);
|
||||
if (blob?.content) {
|
||||
const parsed: LlmChatContent = JSON.parse(blob.content);
|
||||
setChatNoteId(noteId);
|
||||
chat.loadFromContent(parsed);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load selected chat:", err);
|
||||
}
|
||||
}, [chatNoteId, chat]);
|
||||
// Save any pending changes before switching
|
||||
await spacedUpdate.updateNowIfNecessary();
|
||||
|
||||
setChatNoteId(noteId);
|
||||
}, [chatNoteId, spacedUpdate]);
|
||||
|
||||
return (
|
||||
<RightPanelWidget
|
||||
|
||||
Reference in New Issue
Block a user