fix(desktop): keep window tabs across app close (#34807)
This commit is contained in:
@@ -9,9 +9,10 @@ import { dirname, isAbsolute, join, relative, resolve } from "node:path"
|
||||
import { fileURLToPath, pathToFileURL } from "node:url"
|
||||
import type { TitlebarTheme } from "../preload/types"
|
||||
import { exportDebugLogs, write as writeLog } from "./logging"
|
||||
import { getStore } from "./store"
|
||||
import { getStore, removeStoreFile } from "./store"
|
||||
import { PINCH_ZOOM_ENABLED_KEY, WINDOW_IDS_KEY } from "./store-keys"
|
||||
import { createUnresponsiveSampler } from "./unresponsive"
|
||||
import { createWindowRegistry } from "./window-registry"
|
||||
|
||||
const root = dirname(fileURLToPath(import.meta.url))
|
||||
const rendererRoot = join(root, "../renderer")
|
||||
@@ -41,15 +42,21 @@ protocol.registerSchemesAsPrivileged([
|
||||
|
||||
let backgroundColor: string | undefined
|
||||
let relaunchHandler = () => {
|
||||
setAppQuitting()
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
let appQuitting = false
|
||||
let lastFocusedWindowID: string | undefined
|
||||
const titlebarThemes = new WeakMap<BrowserWindow, Partial<TitlebarTheme>>()
|
||||
const pinchZoomEnabled = new WeakMap<BrowserWindow, boolean>()
|
||||
const windowIDs = new WeakMap<BrowserWindow, string>()
|
||||
const windowsByID = new Map<string, BrowserWindow>()
|
||||
const registry = createWindowRegistry<BrowserWindow>({
|
||||
read: () => getStore().get(WINDOW_IDS_KEY),
|
||||
write: (ids) => getStore().set(WINDOW_IDS_KEY, ids),
|
||||
cleanup: (id) => {
|
||||
rmSync(join(app.getPath("userData"), windowStateFile(id)), { force: true })
|
||||
removeStoreFile(windowDataFile(id))
|
||||
},
|
||||
})
|
||||
const titlebarHeight = 40
|
||||
const maxZoomLevel = 10
|
||||
const minZoomLevel = 0.2
|
||||
@@ -58,8 +65,8 @@ export function setRelaunchHandler(handler: () => void) {
|
||||
relaunchHandler = handler
|
||||
}
|
||||
|
||||
export function setAppQuitting() {
|
||||
appQuitting = true
|
||||
export function setAppQuitting(quitting = true) {
|
||||
registry.setQuitting(quitting)
|
||||
}
|
||||
|
||||
export function setBackgroundColor(color: string) {
|
||||
@@ -128,14 +135,13 @@ export function getWindowID(win: BrowserWindow) {
|
||||
export function getLastFocusedWindow() {
|
||||
const focused = BrowserWindow.getFocusedWindow()
|
||||
if (focused) return focused
|
||||
if (!lastFocusedWindowID) return null
|
||||
const win = windowsByID.get(lastFocusedWindowID)
|
||||
const win = registry.lastFocused()
|
||||
if (!win || win.isDestroyed()) return null
|
||||
return win
|
||||
}
|
||||
|
||||
export function restoreMainWindows() {
|
||||
const ids = readWindowIDs()
|
||||
const ids = registry.persisted()
|
||||
return (ids.length ? ids : [randomUUID()]).map((id) => createMainWindow(id))
|
||||
}
|
||||
|
||||
@@ -213,44 +219,25 @@ export function createMainWindow(id: string = randomUUID()) {
|
||||
|
||||
function registerWindow(win: BrowserWindow, id: string) {
|
||||
windowIDs.set(win, id)
|
||||
windowsByID.set(id, win)
|
||||
persistWindowID(id)
|
||||
registry.register(id, win)
|
||||
|
||||
win.on("focus", () => {
|
||||
lastFocusedWindowID = id
|
||||
})
|
||||
win.on("closed", () => {
|
||||
windowsByID.delete(id)
|
||||
if (lastFocusedWindowID === id) lastFocusedWindowID = windowsByID.keys().next().value
|
||||
if (!appQuitting) removeWindowID(id)
|
||||
})
|
||||
}
|
||||
|
||||
function readWindowIDs() {
|
||||
const value = getStore().get(WINDOW_IDS_KEY)
|
||||
if (!Array.isArray(value)) return []
|
||||
return value.filter((id): id is string => typeof id === "string" && id.length > 0)
|
||||
}
|
||||
|
||||
function writeWindowIDs(ids: string[]) {
|
||||
getStore().set(WINDOW_IDS_KEY, [...new Set(ids)])
|
||||
}
|
||||
|
||||
function persistWindowID(id: string) {
|
||||
const ids = readWindowIDs()
|
||||
if (ids.includes(id)) return
|
||||
writeWindowIDs([...ids, id])
|
||||
}
|
||||
|
||||
function removeWindowID(id: string) {
|
||||
writeWindowIDs(readWindowIDs().filter((item) => item !== id))
|
||||
rmSync(join(app.getPath("userData"), windowStateFile(id)), { force: true })
|
||||
win.on("focus", () => registry.focused(id))
|
||||
// Windows never emits before-quit on OS shutdown/logoff, but each window
|
||||
// gets session-end before it closes; flag the quit so ids stay persisted.
|
||||
win.on("session-end", () => registry.setQuitting())
|
||||
win.on("closed", () => registry.closed(id))
|
||||
}
|
||||
|
||||
function windowStateFile(id: string) {
|
||||
return `window-state-${id.replace(/[^a-zA-Z0-9._-]/g, "-")}.json`
|
||||
}
|
||||
|
||||
// Mirrors windowStorage() in packages/app/src/utils/persist.ts, which names
|
||||
// the per-window renderer store this window persists its tabs into.
|
||||
function windowDataFile(id: string) {
|
||||
return `opencode.window.${id.replace(/[^a-zA-Z0-9._-]/g, "-")}.dat`
|
||||
}
|
||||
|
||||
export function registerRendererProtocol() {
|
||||
if (protocol.isProtocolHandled(rendererProtocol)) return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user