diff --git a/apps/client/src/components/tab_manager.ts b/apps/client/src/components/tab_manager.ts index 8799a80c2..fdd918722 100644 --- a/apps/client/src/components/tab_manager.ts +++ b/apps/client/src/components/tab_manager.ts @@ -149,18 +149,31 @@ export default class TabManager extends Component { currentWin.contexts = filteredNoteContexts; } else { if (savedWindows?.length >= MAX_SAVED_WINDOWS) { - // Filter out a window to remove + // Filter out the oldest entry // 1) Never remove the "main" window - // 2) Remove the window with the oldest creation time - let windowToRemove: WindowState | null = null; - for (const win of savedWindows) { - if (win.windowId === "main") continue; // never remove main - if (!windowToRemove || win.createdAt < windowToRemove.createdAt) { - windowToRemove = win; + // 2) Prefer removing the oldest closed window (closedAt !== 0) + // 3) If no closed window exists, remove the window with the oldest created window + let oldestClosedIndex = -1; + let oldestClosedTime = Infinity; + let oldestCreatedIndex = -1; + let oldestCreatedTime = Infinity; + savedWindows.forEach((w: WindowState, i: number) => { + if (w.windowId === "main") return; + if (w.closedAt !== 0) { + if (w.closedAt < oldestClosedTime) { + oldestClosedTime = w.closedAt; + oldestClosedIndex = i; + } + } else { + if (w.createdAt < oldestCreatedTime) { + oldestCreatedTime = w.createdAt; + oldestCreatedIndex = i; + } } - } - if (windowToRemove) { - savedWindows.splice(savedWindows.indexOf(windowToRemove), 1); + }); + const indexToRemove = oldestClosedIndex !== -1 ? oldestClosedIndex : oldestCreatedIndex; + if (indexToRemove !== -1) { + savedWindows.splice(indexToRemove, 1); } } diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 8db9f91da..b96046209 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -127,7 +127,8 @@ async function onReady() { } }); } - + + await normalizeOpenNoteContexts(); tray.createTray(); } else { await windowService.createSetupWindow(); @@ -136,6 +137,30 @@ async function onReady() { await windowService.registerGlobalShortcuts(); } +/** + * Some windows may have closed abnormally, leaving closedAt as 0 in openNoteContexts. + * This function normalizes those timestamps to the current time for correct sorting/filtering. + */ +async function normalizeOpenNoteContexts() { + const savedWindows = options.getOptionJson("openNoteContexts") || []; + const now = Date.now(); + + let changed = false; + for (const win of savedWindows) { + if (win.windowId !== "main" && win.closedAt === 0) { + win.closedAt = now; + changed = true; + } + } + + if (changed) { + const { default: cls } = (await import("@triliumnext/server/src/services/cls.js")); + cls.wrap(() => { + options.setOption("openNoteContexts", JSON.stringify(savedWindows)); + })(); + } +} + function getElectronLocale() { const uiLocale = options.getOptionOrNull("locale"); const formattingLocale = options.getOptionOrNull("formattingLocale"); diff --git a/apps/server/src/services/tray.ts b/apps/server/src/services/tray.ts index 8f373a293..3b0831aff 100644 --- a/apps/server/src/services/tray.ts +++ b/apps/server/src/services/tray.ts @@ -201,10 +201,11 @@ function updateTrayMenu() { const openedWindowIds = windowService.getAllWindowIds(); const closedWindows = savedWindows .filter(win => !openedWindowIds.includes(win.windowId)) - .sort((a, b) => { return b.closedAt - a.closedAt; }); // sort by time in descending order + .sort((a, b) => { return a.closedAt - b.closedAt; }); // sort by time in ascending order const menuItems: Electron.MenuItemConstructorOptions[] = []; - for (const win of closedWindows) { + for (let i = closedWindows.length - 1; i >= 0; i--) { + const win = closedWindows[i]; const activeCtx = win.contexts.find(c => c.active === true); const activateNotePath = (activeCtx ?? win.contexts[0])?.notePath; const activateNoteId = activateNotePath?.split("/").pop() ?? null;