feat(stage-tamagotchi): perserve window pos & size

This commit is contained in:
Neko Ayaka
2025-10-07 00:36:26 +08:00
parent d7d8f2da4c
commit ecbc00972a
5 changed files with 119 additions and 2 deletions
+3
View File
@@ -63,9 +63,12 @@
"colorjs.io": "^0.5.2",
"culori": "^4.0.2",
"date-fns": "^4.1.0",
"defu": "^6.1.4",
"destr": "^2.0.5",
"dompurify": "^3.2.7",
"drizzle-kit": "^0.31.5",
"drizzle-orm": "^0.44.6",
"es-toolkit": "^1.39.10",
"jszip": "^3.10.1",
"listhen": "^1.9.0",
"localforage": "^1.10.0",
@@ -1,22 +1,42 @@
import type { BrowserWindowConstructorOptions, Rectangle } from 'electron'
import { dirname, join } from 'node:path'
import { env } from 'node:process'
import { fileURLToPath } from 'node:url'
import { is } from '@electron-toolkit/utils'
import { defu } from 'defu'
import { BrowserWindow, shell } from 'electron'
import { isMacOS } from 'std-env'
import icon from '../../../../resources/icon.png?asset'
import { transparentWindowConfig } from '../shared'
import { createConfig } from '../shared/persistence'
import { setupAppInvokeHandlers } from './eventa/index.electron'
import { setupWebInvokes } from './eventa/index.web'
interface AppConfig {
windows?: Array<Pick<BrowserWindowConstructorOptions, 'title' | 'x' | 'y' | 'width' | 'height'> & { tag: string }>
}
export function setup() {
const {
setup: setupConfig,
get: getConfig,
update: updateConfig,
} = createConfig<AppConfig>('app', 'config.json', { default: { windows: [] } })
setupConfig()
const mainWindowConfig = getConfig()?.windows?.find(w => w.title === 'AIRI' && w.tag === 'main')
const window = new BrowserWindow({
title: 'AIRI',
width: 450.0,
height: 600.0,
width: mainWindowConfig?.width ?? 450.0,
height: mainWindowConfig?.height ?? 600.0,
x: mainWindowConfig?.x,
y: mainWindowConfig?.y,
show: false,
icon,
webPreferences: {
@@ -26,6 +46,41 @@ export function setup() {
...transparentWindowConfig(),
})
function handleNewBounds(newBounds: Rectangle) {
const config = getConfig()!
if (!config.windows || !Array.isArray(config.windows)) {
config.windows = []
}
const existingConfigIndex = config.windows.findIndex(w => w.title === 'AIRI' && w.tag === 'main')
if (existingConfigIndex === -1) {
config.windows.push({
title: 'AIRI',
tag: 'main',
x: newBounds.x,
y: newBounds.y,
width: newBounds.width,
height: newBounds.height,
})
}
else {
const mainWindowConfig = defu(config.windows[existingConfigIndex], { title: 'AIRI', tag: 'main' })
mainWindowConfig.x = newBounds.x
mainWindowConfig.y = newBounds.y
mainWindowConfig.width = newBounds.width
mainWindowConfig.height = newBounds.height
config.windows[existingConfigIndex] = mainWindowConfig
}
updateConfig(config)
}
window.on('resize', () => handleNewBounds(window.getBounds()))
window.on('move', () => handleNewBounds(window.getBounds()))
window.setAlwaysOnTop(true)
if (isMacOS) {
window.setWindowButtonVisibility(false)
@@ -0,0 +1,49 @@
import { readFileSync } from 'node:fs'
import { writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { safeDestr } from 'destr'
import { app } from 'electron'
import { throttle } from 'es-toolkit'
function parseOrFallback<T>(config: string, fallback: T | undefined): T | undefined {
return safeDestr<T>(config) || fallback
}
const persistenceMap = new Map<string, any>()
export function createConfig<T>(namespace: string, filename: string, options?: { default?: T }) {
function configPath() {
const path = join(app.getPath('userData'), `${namespace}-${filename}`)
return path
}
function setup() {
const data = parseOrFallback<T>(readFileSync(configPath(), { encoding: 'utf-8' }), options?.default)
persistenceMap.set(`${namespace}-${filename}`, data)
}
const save = throttle(async () => {
try {
await writeFile(configPath(), JSON.stringify(persistenceMap.get(`${namespace}-${filename}`)))
}
catch (e) {
console.error('Failed to save config', e)
}
}, 250)
function update(newData: T) {
persistenceMap.set(`${namespace}-${filename}`, newData)
save()
}
function get(): T | undefined {
return persistenceMap.get(`${namespace}-${filename}`) as T | undefined
}
return {
setup,
get,
update,
}
}
+1
View File
@@ -68,6 +68,7 @@ words:
- defu
- demi
- demodel
- destr
- devlog
- devlogs
- directml
+9
View File
@@ -356,6 +356,12 @@ importers:
date-fns:
specifier: ^4.1.0
version: 4.1.0
defu:
specifier: ^6.1.4
version: 6.1.4
destr:
specifier: ^2.0.5
version: 2.0.5
dompurify:
specifier: ^3.2.7
version: 3.2.7
@@ -365,6 +371,9 @@ importers:
drizzle-orm:
specifier: ^0.44.6
version: 0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)
es-toolkit:
specifier: ^1.39.10
version: 1.39.10
jszip:
specifier: ^3.10.1
version: 3.10.1