diff --git a/apps/stage-tamagotchi/src/main/libs/electron/persistence.ts b/apps/stage-tamagotchi/src/main/libs/electron/persistence.ts index bc9b42a9a..7ff56828d 100644 --- a/apps/stage-tamagotchi/src/main/libs/electron/persistence.ts +++ b/apps/stage-tamagotchi/src/main/libs/electron/persistence.ts @@ -1,7 +1,7 @@ import type { BaseIssue, BaseSchema, InferIssue, InferOutput } from 'valibot' import { existsSync, readFileSync } from 'node:fs' -import { writeFile } from 'node:fs/promises' +import { copyFile, rename, writeFile } from 'node:fs/promises' import { join } from 'node:path' import { safeDestr } from 'destr' @@ -74,7 +74,10 @@ export function createConfig( const save = throttle(async () => { try { - await writeFile(configPath(), JSON.stringify(persistenceMap.get(key))) + const path = configPath() + const tmpPath = `${path}.tmp` + await writeFile(tmpPath, JSON.stringify(persistenceMap.get(key))) + await rename(tmpPath, path) } catch (error) { console.error('Failed to save config', error) @@ -83,7 +86,11 @@ export function createConfig( const writeHealingConfig = async (value: InferOutput) => { try { - await writeFile(configPath(), JSON.stringify(value)) + const path = configPath() + if (existsSync(path)) { + await copyFile(path, `${path}.bak`).catch(err => console.warn('Failed to create backup for config:', path, err)) + } + await writeFile(path, JSON.stringify(value)) return true } catch (error) { diff --git a/packages/stage-ui-live2d/src/components/scenes/live2d/Model.vue b/packages/stage-ui-live2d/src/components/scenes/live2d/Model.vue index 600836a5b..721c1e2ec 100644 --- a/packages/stage-ui-live2d/src/components/scenes/live2d/Model.vue +++ b/packages/stage-ui-live2d/src/components/scenes/live2d/Model.vue @@ -61,6 +61,7 @@ const props = withDefaults(defineProps<{ const emits = defineEmits<{ (e: 'modelLoaded'): void + (e: 'error', error: Error): void }>() const componentState = defineModel<'pending' | 'loading' | 'mounted'>('state', { default: 'pending' }) @@ -369,6 +370,10 @@ async function loadModel() { emits('modelLoaded') } + catch (error) { + console.error('[Live2D] Failed to load model:', error) + emits('error', error instanceof Error ? error : new Error(String(error))) + } finally { modelLoading.value = false componentState.value = 'mounted' diff --git a/packages/stage-ui-live2d/src/utils/opfs-loader.ts b/packages/stage-ui-live2d/src/utils/opfs-loader.ts index da3f1cb85..b428deaa8 100644 --- a/packages/stage-ui-live2d/src/utils/opfs-loader.ts +++ b/packages/stage-ui-live2d/src/utils/opfs-loader.ts @@ -12,6 +12,18 @@ declare global { } export class OPFSCache { + static async clearAll(): Promise { + try { + const root = await navigator.storage.getDirectory() + for await (const entry of root.values()) { + await root.removeEntry(entry.name, { recursive: true }) + } + } + catch (e) { + console.error('[OPFS] Failed to clear cache:', e) + } + } + static async readDirectoryRecursive(dir: FileSystemDirectoryHandle, pathPrefix: string): Promise { const files: File[] = [] for await (const entry of dir.values()) { diff --git a/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue b/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue index a2e39e5e1..27296f45e 100644 --- a/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue +++ b/packages/stage-ui/src/components/scenarios/settings/model-settings/live2d.vue @@ -1,5 +1,6 @@