Feat: festes Avatar-VRM (das eine Modell) + Avatar-Picker entfernt

Avatar fest auf /avatar.vrm (frontend/public/avatar.vrm → dist). Galerie/Upload/URL raus;
AvatarPicker → reine VoiceControls (nur Stimme). vrmStore.ts gelöscht. avatar.vrm ist gitignored
(23 MB, lizenz-/redistributionssensibel) — liegt auf der Box, nicht in git.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-28 02:34:35 +02:00
parent b4a8f92cfa
commit 49c9e27502
7 changed files with 388 additions and 548 deletions
-35
View File
@@ -1,35 +0,0 @@
// Winziger IndexedDB-Wrapper, um ein hochgeladenes .vrm (ArrayBuffer) über Reloads hinweg zu
// behalten. localStorage scheidet aus (VRMs sind oft 540 MB). Ein einziger Record genügt.
const DB = "mc2-voice"
const STORE = "avatar"
const KEY = "uploaded-vrm"
function open(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const req = indexedDB.open(DB, 1)
req.onupgradeneeded = () => req.result.createObjectStore(STORE)
req.onsuccess = () => resolve(req.result)
req.onerror = () => reject(req.error)
})
}
export async function saveUploadedVrm(buf: ArrayBuffer): Promise<void> {
const db = await open()
await new Promise<void>((resolve, reject) => {
const tx = db.transaction(STORE, "readwrite")
tx.objectStore(STORE).put(buf, KEY)
tx.oncomplete = () => resolve()
tx.onerror = () => reject(tx.error)
})
}
export async function loadUploadedVrm(): Promise<ArrayBuffer | null> {
const db = await open()
return new Promise((resolve, reject) => {
const tx = db.transaction(STORE, "readonly")
const req = tx.objectStore(STORE).get(KEY)
req.onsuccess = () => resolve((req.result as ArrayBuffer) ?? null)
req.onerror = () => reject(req.error)
})
}