This commit is contained in:
th committed 2026-04-19 19:32:23 +02:00
1 parent 94e9846c13
commit e4aa0dce86
11 files changed
+2227 -32

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
import type { Scene } from '@/types/scene'
const FILE_VERSION = 1
const MAGIC = 'animatr-project'
export interface ProjectFile {
magic: typeof MAGIC
fileVersion: number
savedAt: string
scene: Scene
}
export function serializeProject(scene: Scene): string {
const payload: ProjectFile = {
magic: MAGIC,
fileVersion: FILE_VERSION,
savedAt: new Date().toISOString(),
scene,
}
return JSON.stringify(payload, null, 2)
}
export class ProjectFileError extends Error {
constructor(message: string) {
super(message)
this.name = 'ProjectFileError'
}
}
export function parseProject(text: string): Scene {
let data: unknown
try {
data = JSON.parse(text)
} catch {
throw new ProjectFileError('Datei ist kein gültiges JSON.')
}
if (!data || typeof data !== 'object') {
throw new ProjectFileError('Datei ist leer oder kein Objekt.')
}
const obj = data as Record<string, unknown>
if (obj.magic !== MAGIC) {
throw new ProjectFileError('Keine Animatr-Projektdatei (magic fehlt).')
}
const scene = obj.scene
if (!scene || typeof scene !== 'object') {
throw new ProjectFileError('Projektdatei enthält keine Szene.')
}
const s = scene as Record<string, unknown>
if (!s.canvas || typeof s.canvas !== 'object') {
throw new ProjectFileError('Szenendaten unvollständig: canvas fehlt.')
}
if (!Array.isArray(s.layers)) {
throw new ProjectFileError('Szenendaten unvollständig: layers fehlt.')
}
if (!Array.isArray(s.assets)) {
throw new ProjectFileError('Szenendaten unvollständig: assets fehlt.')
}
return scene as Scene
}
export function downloadProject(scene: Scene, filename = 'projekt.animatr.json') {
const source = serializeProject(scene)
const blob = new Blob([source], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
setTimeout(() => URL.revokeObjectURL(url), 1000)
}
export function pickProjectFile(): Promise<File | null> {
return new Promise((resolve) => {
const input = document.createElement('input')
input.type = 'file'
input.accept = '.json,application/json'
input.onchange = () => {
const f = input.files?.[0]
resolve(f ?? null)
}
input.oncancel = () => resolve(null)
input.click()
})
}
export async function readProjectFile(file: File): Promise<Scene> {
const text = await file.text()
return parseProject(text)
}