feat(window): add windowId for extra windows

This commit is contained in:
SiriusXT 2025-12-29 14:32:53 +08:00
parent a4be86dbd8
commit 7740154bdc
7 changed files with 53 additions and 21 deletions

View File

@ -535,6 +535,7 @@ export type FilteredCommandNames<T extends CommandData> = keyof Pick<CommandMapp
export class AppContext extends Component {
isMainWindow: boolean;
windowId: string;
components: Component[];
beforeUnloadListeners: (WeakRef<BeforeUploadListener> | (() => boolean))[];
tabManager!: TabManager;
@ -543,10 +544,11 @@ export class AppContext extends Component {
lastSearchString?: string;
constructor(isMainWindow: boolean) {
constructor(isMainWindow: boolean, windowId: string) {
super();
this.isMainWindow = isMainWindow;
this.windowId = windowId;
// non-widget/layout components needed for the application
this.components = [];
this.beforeUnloadListeners = [];
@ -676,8 +678,7 @@ export class AppContext extends Component {
this.beforeUnloadListeners = this.beforeUnloadListeners.filter(l => l !== listener);
}
}
const appContext = new AppContext(window.glob.isMainWindow);
const appContext = new AppContext(window.glob.isMainWindow, window.glob.windowId);
// we should save all outstanding changes before the page/app is closed
$(window).on("beforeunload", () => {

View File

@ -142,14 +142,15 @@ export default class Entrypoints extends Component {
}
async openInWindowCommand({ notePath, hoistedNoteId, viewScope }: NoteCommandData) {
const extraWindowId = utils.randomString(4);
const extraWindowHash = linkService.calculateHash({ notePath, hoistedNoteId, viewScope });
if (utils.isElectron()) {
const { ipcRenderer } = utils.dynamicRequire("electron");
ipcRenderer.send("create-extra-window", { extraWindowHash });
ipcRenderer.send("create-extra-window", { extraWindowId, extraWindowHash });
} else {
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}?extraWindow=1${extraWindowHash}`;
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}?extraWindow=${extraWindowId}${extraWindowHash}`;
window.open(url, "", "width=1000,height=800");
}

View File

@ -34,6 +34,7 @@ interface CustomGlobals {
isProtectedSessionAvailable: boolean;
isDev: boolean;
isMainWindow: boolean;
windowId: string;
maxEntityChangeIdAtLoad: number;
maxEntityChangeSyncIdAtLoad: number;
assetPath: string;

View File

@ -6,6 +6,8 @@ import sqlInit from "@triliumnext/server/src/services/sql_init.js";
import windowService from "@triliumnext/server/src/services/window.js";
import tray from "@triliumnext/server/src/services/tray.js";
import options from "@triliumnext/server/src/services/options.js";
import { randomString } from "@triliumnext/server/src/services/utils.js";
import electronDebug from "electron-debug";
import electronDl from "electron-dl";
import { PRODUCT_NAME } from "./app-info";
@ -72,7 +74,8 @@ async function main() {
app.on("second-instance", (event, commandLine) => {
const lastFocusedWindow = windowService.getLastFocusedWindow();
if (commandLine.includes("--new-window")) {
windowService.createExtraWindow("");
const extraWindowId = randomString(4);
windowService.createExtraWindow(extraWindowId, "");
} else if (lastFocusedWindow) {
if (lastFocusedWindow.isMinimized()) {
lastFocusedWindow.restore();

View File

@ -12,6 +12,7 @@
isDev: <%= isDev %>,
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
isMainWindow: <%= isMainWindow %>,
windowId: "<%= windowId %>",
isProtectedSessionAvailable: <%= isProtectedSessionAvailable %>,
triliumVersion: "<%= triliumVersion %>",
assetPath: "<%= assetPath %>",

View File

@ -56,6 +56,7 @@ function index(req: Request, res: Response) {
appCssNoteIds: getAppCssNoteIds(),
isDev,
isMainWindow: view === "mobile" ? true : !req.query.extraWindow,
windowId: view !== "mobile" && req.query.extraWindow ? req.query.extraWindow : "main",
isProtectedSessionAvailable: protectedSessionService.isProtectedSessionAvailable(),
triliumVersion: packageJson.version,
assetPath,

View File

@ -16,28 +16,47 @@ import { RESOURCE_DIR } from "./resource_dir.js";
// Prevent the window being garbage collected
let mainWindow: BrowserWindow | null;
let setupWindow: BrowserWindow | null;
let allWindows: BrowserWindow[] = []; // // Used to store all windows, sorted by the order of focus.
function trackWindowFocus(win: BrowserWindow) {
interface WindowEntry {
window: BrowserWindow;
windowId: string; // custom window ID
}
let allWindowEntries: WindowEntry[] = [];
function trackWindowFocus(win: BrowserWindow, windowId: string) {
// We need to get the last focused window from allWindows. If the last window is closed, we return the previous window.
// Therefore, we need to push the window into the allWindows array every time it gets focused.
win.on("focus", () => {
allWindows = allWindows.filter(w => !w.isDestroyed() && w !== win);
allWindows.push(win);
allWindowEntries = allWindowEntries.filter(w => !w.window.isDestroyed() && w.window !== win);
allWindowEntries.push({ window: win, windowId: windowId });
if (!optionService.getOptionBool("disableTray")) {
electron.ipcMain.emit("reload-tray");
}
});
win.on("closed", () => {
allWindows = allWindows.filter(w => !w.isDestroyed());
cls.wrap(() => {
const savedWindows = JSON.parse(
optionService.getOption("openNoteContexts") || "[]"
);
const win = savedWindows.find(w => w.windowId === windowId);
if (win) {
win.closedAt = Date.now();
}
optionService.setOption("openNoteContexts", JSON.stringify(savedWindows));
})();
allWindowEntries = allWindowEntries.filter(w => !w.window.isDestroyed());
if (!optionService.getOptionBool("disableTray")) {
electron.ipcMain.emit("reload-tray");
}
});
}
async function createExtraWindow(extraWindowHash: string) {
async function createExtraWindow(extraWindowId: string, extraWindowHash: string) {
const spellcheckEnabled = optionService.getOptionBool("spellCheckEnabled");
const { BrowserWindow } = await import("electron");
@ -56,15 +75,15 @@ async function createExtraWindow(extraWindowHash: string) {
});
win.setMenuBarVisibility(false);
win.loadURL(`http://127.0.0.1:${port}/?extraWindow=1${extraWindowHash}`);
win.loadURL(`http://127.0.0.1:${port}/?extraWindow=${extraWindowId}${extraWindowHash}`);
configureWebContents(win.webContents, spellcheckEnabled);
trackWindowFocus(win);
trackWindowFocus(win, extraWindowId);
}
electron.ipcMain.on("create-extra-window", (event, arg) => {
createExtraWindow(arg.extraWindowHash);
createExtraWindow(arg.extraWindowId, arg.extraWindowHash);
});
interface PrintOpts {
@ -168,8 +187,8 @@ async function getBrowserWindowForPrinting(e: IpcMainEvent, notePath: string, ac
return browserWindow;
}
async function createMainWindow(app: App) {
if ("setUserTasks" in app) {
async function createMainWindow(app?: App) {
if (app && "setUserTasks" in app) {
app.setUserTasks([
{
program: process.execPath,
@ -219,7 +238,7 @@ async function createMainWindow(app: App) {
mainWindow.on("closed", () => (mainWindow = null));
configureWebContents(mainWindow.webContents, spellcheckEnabled);
trackWindowFocus(mainWindow);
trackWindowFocus(mainWindow, "main");
}
function getWindowExtraOpts() {
@ -381,11 +400,15 @@ function getMainWindow() {
}
function getLastFocusedWindow() {
return allWindows.length > 0 ? allWindows[allWindows.length - 1] : null;
return allWindowEntries.length > 0 ? allWindowEntries[allWindowEntries.length - 1]?.window : null;
}
function getAllWindows() {
return allWindows;
return allWindowEntries.map(e => e.window);
}
function getAllWindowIds(): string[] {
return allWindowEntries.map(e => e.windowId);
}
export default {
@ -396,5 +419,6 @@ export default {
registerGlobalShortcuts,
getMainWindow,
getLastFocusedWindow,
getAllWindows
getAllWindows,
getAllWindowIds
};