diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted.png new file mode 100644 index 000000000..9e9671407 Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@1.25x.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@1.25x.png new file mode 100644 index 000000000..a3ee020b5 Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@1.25x.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@1.5x.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@1.5x.png new file mode 100644 index 000000000..f0074a3cb Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@1.5x.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@2x.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@2x.png new file mode 100644 index 000000000..ca5fa4f26 Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate-inverted@2x.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate.png new file mode 100644 index 000000000..e3adb5bd5 Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate@1.25x.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate@1.25x.png new file mode 100644 index 000000000..293850a3a Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate@1.25x.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate@1.5x.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate@1.5x.png new file mode 100644 index 000000000..257cf42f4 Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate@1.5x.png differ diff --git a/apps/desktop/src/assets/images/tray/closed-windowsTemplate@2x.png b/apps/desktop/src/assets/images/tray/closed-windowsTemplate@2x.png new file mode 100644 index 000000000..30f5b476b Binary files /dev/null and b/apps/desktop/src/assets/images/tray/closed-windowsTemplate@2x.png differ diff --git a/apps/server/src/assets/translations/en/server.json b/apps/server/src/assets/translations/en/server.json index 86e76df92..2070c895e 100644 --- a/apps/server/src/assets/translations/en/server.json +++ b/apps/server/src/assets/translations/en/server.json @@ -382,6 +382,8 @@ "tooltip": "Trilium Notes", "close": "Quit Trilium", "recents": "Recent notes", + "recently-closed-windows": "Recently closed windows", + "tabs-total": "total {{number}} tabs", "bookmarks": "Bookmarks", "today": "Open today's journal note", "new-note": "New note", diff --git a/apps/server/src/services/tray.ts b/apps/server/src/services/tray.ts index 504e81b21..008635794 100644 --- a/apps/server/src/services/tray.ts +++ b/apps/server/src/services/tray.ts @@ -196,6 +196,44 @@ function updateTrayMenu() { return menuItems; } + function buildClosedWindowsMenu() { + const savedOpenNoteContexts = JSON.parse(optionService.getOption("openNoteContexts") || "[]"); + const openedWindowIds = windowService.getAllWindowIds(); + const closedWindows = savedOpenNoteContexts + .filter(win => !openedWindowIds.includes(win.windowId)) + .sort((a, b) => { + // If closedAt is null, it indicates an abnormal closure and should be placed at the end + if (a.closedAt === null && b.closedAt === null) return 0; + if (a.closedAt === null) return 1; + if (b.closedAt === null) return -1; + // Otherwise, sort by time in descending order + return b.closedAt - a.closedAt; + }); + const menuItems: Electron.MenuItemConstructorOptions[] = []; + for (const win of closedWindows) { + const activeCtx = win.contexts.find(c => c.active === true); + const activateNotePath = (activeCtx ?? win.contexts[0])?.notePath; + const activateNoteId = activateNotePath?.split("/").pop() ?? null; + + // Get the title of the closed window + const rawTitle = activateNoteId ? becca_service.getNoteTitle(activateNoteId) : ""; + let winTitle = rawTitle.length > 20 ? `${rawTitle.slice(0, 17)}...` : rawTitle; + const mainTabCount = win.contexts.filter(ctx => ctx.mainNtxId === null).length; + if (mainTabCount > 1) { + const tabSuffix = t("tray.tabs-total", { number: mainTabCount }); + winTitle += ` (${tabSuffix})`; + } + + menuItems.push({ + label: winTitle, + type: "normal", + click: () => win.windowId !== "main" ? windowService.createExtraWindow(win.windowId, "") : windowService.createMainWindow() + }); + } + + return menuItems; + } + const windowVisibilityMenuItems: Electron.MenuItemConstructorOptions[] = []; // Only call getWindowTitle if windowVisibilityMap has more than one window @@ -258,6 +296,12 @@ function updateTrayMenu() { icon: getIconPath("recents"), submenu: buildRecentNotesMenu() }, + { + label: t("tray.recently-closed-windows"), + type: "submenu", + icon: getIconPath("closed-windows"), + submenu: buildClosedWindowsMenu() + }, { type: "separator" }, { label: t("tray.close"),