diff --git a/src/main/config.ts b/src/main/config.ts index b383ff0..68c893c 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -33,6 +33,7 @@ export interface AppConfig { game: { lastServer: string; socialTabBehaviour: 'New Window' | 'Same Window'; + rememberTabs: boolean; joinAsSpectator: boolean; rawInput: boolean; betterChat: boolean; @@ -108,6 +109,7 @@ export interface AppConfig { y: number | undefined; maximized: boolean; }; + savedTabs: string[]; } export const DEFAULT_KEYBINDS: AppConfig['keybinds'] = { @@ -145,6 +147,7 @@ export const config = new Store({ game: { lastServer: '', socialTabBehaviour: 'New Window', + rememberTabs: false, joinAsSpectator: false, rawInput: true, betterChat: true, @@ -210,5 +213,6 @@ export const config = new Store({ y: undefined, maximized: true, }, + savedTabs: [], }, }); diff --git a/src/main/index.ts b/src/main/index.ts index e108087..c947c18 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -463,6 +463,9 @@ async function launchApp(): Promise { win, ses, preloadPath, tabMode, isGameURL, () => config.get('tabWindow'), (state) => config.set('tabWindow', state), + () => config.get('savedTabs'), + (urls) => config.set('savedTabs', urls), + () => config.get('game.rememberTabs') ?? false, ); // Intercept in-page navigation (e.g. window.location = '/social.html') diff --git a/src/main/tab-manager.ts b/src/main/tab-manager.ts index f130966..fc7c991 100644 --- a/src/main/tab-manager.ts +++ b/src/main/tab-manager.ts @@ -42,7 +42,11 @@ export class TabManager { private recentlyClosed: { url: string; title: string }[] = []; private getTabWindowState: () => TabWindowState; private saveTabWindowState: (state: TabWindowState) => void; + private getSavedTabs: () => string[]; + private saveTabs: (urls: string[]) => void; + private isRememberEnabled: () => boolean; private tabSaveTimer: ReturnType | null = null; + private restoredTabs = false; constructor( win: BrowserWindow, @@ -52,6 +56,9 @@ export class TabManager { isGameURL: (url: string) => boolean, getTabWindowState: () => TabWindowState, saveTabWindowState: (state: TabWindowState) => void, + getSavedTabs: () => string[], + saveTabs: (urls: string[]) => void, + isRememberEnabled: () => boolean, ) { this.mainWin = win; this.ses = ses; @@ -60,6 +67,9 @@ export class TabManager { this.isGameURL = isGameURL; this.getTabWindowState = getTabWindowState; this.saveTabWindowState = saveTabWindowState; + this.getSavedTabs = getSavedTabs; + this.saveTabs = saveTabs; + this.isRememberEnabled = isRememberEnabled; // ── Tab bar view (shared between both modes) ── this.tabBarView = new WebContentsView({ @@ -185,8 +195,30 @@ export class TabManager { }); } - // ── Open a new tab ── + // ── Restore saved tabs on first open, then open the requested tab ── openTab(url: string): number { + if (!this.restoredTabs) { + this.restoredTabs = true; + const saved = this.isRememberEnabled() ? this.getSavedTabs() : []; + this.saveTabs([]); + if (saved.length > 0) { + for (const savedUrl of saved) { + this.openSingleTab(savedUrl); + } + // If the requested URL is already among the restored tabs, just activate it + const existing = this.tabs.find(t => t.url === url); + if (existing) { + this.switchToTab(existing.id); + this.showTabs(); + return existing.id; + } + } + } + return this.openSingleTab(url); + } + + // ── Open a single new tab ── + private openSingleTab(url: string): number { if (this.tabs.length >= MAX_TABS) { const existing = this.tabs.find(t => t.url === url); if (existing) { @@ -489,6 +521,12 @@ export class TabManager { } private destroyAllTabs(): void { + // Persist tab URLs so they can be restored later + if (this.tabs.length > 0 && this.isRememberEnabled()) { + this.saveTabs(this.tabs.map(t => t.url)); + this.restoredTabs = false; + } + for (const tab of this.tabs) { this.stopTitleWatcher(tab.id); if (this.activeTabId === tab.id) { diff --git a/src/preload/index.ts b/src/preload/index.ts index 8f64248..143d453 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -555,7 +555,7 @@ function buildGeneralSection( onChange: (v) => { perf.fpsUnlocked = v; ipcRenderer.invoke('set-config', 'performance', perf); }, })); - const gameDefaults = { lastServer: '', socialTabBehaviour: 'New Window' }; + const gameDefaults = { lastServer: '', socialTabBehaviour: 'New Window', rememberTabs: false }; const game = { ...gameDefaults, ...gameConf }; body.appendChild(createSelectRow({ @@ -566,6 +566,13 @@ function buildGeneralSection( onChange: (v) => { game.socialTabBehaviour = v; ipcRenderer.invoke('set-config', 'game', game); }, })); + body.appendChild(createToggleRow({ + label: 'Remember Tabs', + desc: 'Restore your open tabs when you reopen the social/hub window', + checked: game.rememberTabs, instant: true, + onChange: (v) => { game.rememberTabs = v; ipcRenderer.invoke('set-config', 'game', game); }, + })); + const uiDefaults = { showExitButton: true, deathscreenAnimation: false, hideMenuPopups: false }; const ui = { ...uiDefaults, ...uiConfRaw };