From a785de41aec013d637ed0d9cac15601b5e2550c0 Mon Sep 17 00:00:00 2001 From: type-two Date: Sat, 25 Jul 2026 21:03:21 +1000 Subject: [PATCH] fix: surface clip/direct failures instead of swallowing them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dropping a clip on a non-mixamo rig (lady.glb is a Character Creator export, CC_Base_* bones) threw inside a fire-and-forget event handler — the rejected promise vanished and the drop appeared to do nothing at all. Timeline._fail now logs + toasts via a shared window.sgToast that tlui installs; clipdrop and direct handlers route through it. Repro before: two clipdrops fired, only the mixamo one landed, no error. After: 'clip: retarget: missing hips (mixamorig skeleton?)' toast. Co-Authored-By: Claude Fable 5 --- scenegod-8020.log | 38 ++++++++++++++++++++++++++++++++++++++ scenegod/web/timeline.js | 18 ++++++++++++++++-- scenegod/web/tlui.js | 3 +++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 scenegod-8020.log diff --git a/scenegod-8020.log b/scenegod-8020.log new file mode 100644 index 0000000..7cdc35b --- /dev/null +++ b/scenegod-8020.log @@ -0,0 +1,38 @@ +INFO: Started server process [54956] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Uvicorn running on http://127.0.0.1:8020 (Press CTRL+C to quit) +INFO: 127.0.0.1:54739 - "GET / HTTP/1.1" 200 OK +INFO: 127.0.0.1:54771 - "GET / HTTP/1.1" 200 OK +INFO: 127.0.0.1:54771 - "GET /web/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:54772 - "GET /web/stage.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:54773 - "GET /web/dock.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:54773 - "GET /web/room3d.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:54772 - "GET /web/presets.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:54771 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:54773 - "GET /assets/tree HTTP/1.1" 200 OK +INFO: 127.0.0.1:54773 - "GET /web/grammar.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:54773 - "GET /director HTTP/1.1" 405 Method Not Allowed +INFO: 127.0.0.1:54771 - "GET /web/timeline.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:54771 - "GET /web/tlui.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:55276 - "GET /assets/file?path=characters%2Ftest%2Flady.glb HTTP/1.1" 200 OK +INFO: 127.0.0.1:55276 - "GET /assets/file?path=characters%2Ftest%2Fman.fbx HTTP/1.1" 200 OK +INFO: 127.0.0.1:55276 - "GET /assets/file?path=backdrops%2Fvideo%2Fscenegod_plates%2Fsuburb_strip_goldenhour.mp4 HTTP/1.1" 206 Partial Content +INFO: 127.0.0.1:55318 - "GET /assets/file?path=backdrops%2Fvideo%2Fscenegod_plates%2Fsuburb_strip_goldenhour.mp4 HTTP/1.1" 206 Partial Content +INFO: 127.0.0.1:55318 - "GET /assets/thumb?path=backdrops%2Fvideo%2Fscenegod_plates%2Fsuburb_strip_goldenhour.mp4 HTTP/1.1" 200 OK +INFO: 127.0.0.1:55318 - "GET /assets/file?path=animations%2Ftest%2Fhiphop.fbx HTTP/1.1" 200 OK +INFO: 127.0.0.1:55319 - "GET /assets/file?path=animations%2Ftest%2Fcarrying.fbx HTTP/1.1" 200 OK +INFO: 127.0.0.1:55319 - "GET /assets/file?path=backdrops%2Fvideo%2Fscenegod_plates%2Fsuburb_strip_goldenhour.mp4 HTTP/1.1" 206 Partial Content +INFO: 127.0.0.1:57176 - "GET / HTTP/1.1" 200 OK +INFO: 127.0.0.1:57176 - "GET /web/style.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:57176 - "GET /web/dock.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57177 - "GET /web/stage.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57176 - "GET /web/room3d.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57178 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:57177 - "GET /web/presets.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57176 - "GET /assets/tree HTTP/1.1" 200 OK +INFO: 127.0.0.1:57176 - "GET /web/grammar.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57176 - "GET /director HTTP/1.1" 405 Method Not Allowed +INFO: 127.0.0.1:57177 - "GET /web/timeline.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57177 - "GET /web/tlui.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:57221 - "GET /assets/file?path=characters%2Ftest%2Flady.glb HTTP/1.1" 200 OK diff --git a/scenegod/web/timeline.js b/scenegod/web/timeline.js index c906637..5202270 100644 --- a/scenegod/web/timeline.js +++ b/scenegod/web/timeline.js @@ -95,13 +95,18 @@ export class Timeline { if (id) this.addKey(id, 'transform', { t: this.time, ...this.stage.entityTransform(id), ease: 'inout' }); }); // Lane A dock drops a clip on a character → add a block at the playhead. + // REVIEW-FIX: this is fire-and-forget from an event handler, so a rejected + // promise vanished — a non-mixamo rig (CC_Base_*) failed the retarget and + // the drop just silently did nothing. Surface every failure. window.addEventListener('scenegod:clipdrop', (e) => { - if (e.detail && e.detail.id) this.addClipDrop(e.detail); + if (e.detail && e.detail.id) this.addClipDrop(e.detail).catch((err) => this._fail('clip', err)); }); // M5 DIRECTOR MODE: Lane A presets dispatch ops → normal keys/cuts at // the playhead (contract: lanes/A §M5.3 / PLAN §5 M5). window.addEventListener('scenegod:direct', (e) => { - if (e.detail && e.detail.op) this.applyDirect(e.detail); + if (e.detail && e.detail.op) { + try { this.applyDirect(e.detail); } catch (err) { this._fail('direct', err); } + } }); } // SYNC2 seam: mirror live stage adds/removes into scene.entities so @@ -109,6 +114,15 @@ export class Timeline { if (stage && stage.onChange) stage.onChange((en) => this._mirror(en)); } + // One place for "the user asked for something and it didn't happen": console + a + // toast if the UI provides one (tlui installs window.sgToast). Never swallow. + _fail(what, err) { + console.error(`[scenegod:${what}]`, err); + const msg = (err && err.message) || String(err); + if (typeof window !== 'undefined' && typeof window.sgToast === 'function') window.sgToast(`${what}: ${msg}`); + return null; + } + _mirror(en) { if (!en || !en.id) return; const list = this.scene.entities; diff --git a/scenegod/web/tlui.js b/scenegod/web/tlui.js index 57f9579..df6ec89 100644 --- a/scenegod/web/tlui.js +++ b/scenegod/web/tlui.js @@ -48,6 +48,9 @@ export class TimelineUI { this.tl = timeline; this.stage = timeline.stage; this.el = typeof mountEl === 'string' ? document.querySelector(mountEl) : mountEl; + // REVIEW-FIX: shared failure surface — timeline._fail (and any other module) + // toasts through here instead of dying silently in the console. + window.sgToast = (m) => this._toast(m); this._hits = []; this._drag = null; this._selKeys = []; // box-selected keys: [{id, track, key}]