|
|
@@ -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)
|
|
|
-
|
|
|
- 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)
|
|
|
-}
|
|
|
+ registry.register(id, win)
|
|
|
|
|
|
-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
|
|
|
|