feat(stage-tamagotchi): perserve window pos & size
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ words:
|
||||
- defu
|
||||
- demi
|
||||
- demodel
|
||||
- destr
|
||||
- devlog
|
||||
- devlogs
|
||||
- directml
|
||||
|
||||
Generated
+9
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user