Spaces:
Running
Running
Download renderers/_shared/mount.js from smapoles/dynamicsystemsmodeling: direct link, hf CLI and curl.
- Browser
- Download file 5 kB
-
https://huggingface.co/spaces/smapoles/dynamicsystemsmodeling/resolve/main/renderers/_shared/mount.js
- Command line
-
hf download hf://spaces/smapoles/dynamicsystemsmodeling/renderers/_shared/mount.js
-
curl -L -o mount.js https://huggingface.co/spaces/smapoles/dynamicsystemsmodeling/resolve/main/renderers/_shared/mount.js
5 kB
| // renderers/_shared/mount.js — the {update, destroy} lifecycle skeleton | |
| // every renderer builds on (CONTRACT 3). Tracks disposables (event | |
| // listeners, observers, animation loops, WebGL renderers) so destroy() | |
| // reliably stops loops, disposes GL, and removes nodes — the lifecycle | |
| // IDIO_GRAPH_V2 lacked. | |
| // | |
| // Typical renderer usage: | |
| // import { createLifecycle } from '../_shared/mount.js'; | |
| // export function mount(el, payload) { | |
| // const lc = createLifecycle(el); | |
| // const { card, body } = createCard(el, payload.meta); // cell.js | |
| // ...build scene... | |
| // lc.trackThree(renderer); // WebGL cleanup | |
| // lc.raf((tMs) => { ...per-frame... }); // auto-cancelled loop | |
| // lc.observeResize(body, (w, h) => { ... }); // container-driven sizing | |
| // return lc.handle((fresh) => { ...re-render from fresh payload... }); | |
| // } | |
| /** | |
| * Create a lifecycle context bound to a mount host. | |
| * Everything registered through it is torn down (in reverse order) by | |
| * destroy(), which finally clears the host's children. | |
| */ | |
| export function createLifecycle(el) { | |
| const disposables = []; | |
| let destroyed = false; | |
| function runDisposable(d) { | |
| try { | |
| if (typeof d === 'function') d(); | |
| else if (d && typeof d.destroy === 'function') d.destroy(); | |
| else if (d && typeof d.dispose === 'function') d.dispose(); | |
| } catch (err) { | |
| console.error('[mount] disposable failed during destroy:', err); | |
| } | |
| } | |
| const lc = { | |
| el, | |
| get isDestroyed() { return destroyed; }, | |
| /** | |
| * Register anything needing teardown: a function, or an object with | |
| * .destroy() or .dispose() (three.js geometries/materials/textures, | |
| * observers, child mount handles). Returns the argument for chaining. | |
| */ | |
| track(d) { | |
| if (d != null) disposables.push(d); | |
| return d; | |
| }, | |
| /** addEventListener with automatic removal on destroy. */ | |
| listen(target, type, fn, opts) { | |
| target.addEventListener(type, fn, opts); | |
| lc.track(() => target.removeEventListener(type, fn, opts)); | |
| return fn; | |
| }, | |
| /** | |
| * Watch an element's content-box size (ResizeObserver), auto- | |
| * disconnected on destroy. fn(width, height, entry). No-ops (returns | |
| * null) where ResizeObserver is unavailable (jsdom smoke tests). | |
| */ | |
| observeResize(target, fn) { | |
| if (typeof ResizeObserver === 'undefined') return null; | |
| const ro = new ResizeObserver((entries) => { | |
| for (const entry of entries) { | |
| const box = entry.contentBoxSize?.[0]; | |
| const w = box ? box.inlineSize : entry.contentRect.width; | |
| const h = box ? box.blockSize : entry.contentRect.height; | |
| fn(w, h, entry); | |
| } | |
| }); | |
| ro.observe(target); | |
| lc.track(() => ro.disconnect()); | |
| return ro; | |
| }, | |
| /** | |
| * Start a requestAnimationFrame loop: fn(timestampMs) every frame | |
| * until stopped. Returns stop(); also auto-stopped on destroy. | |
| * (For three.js prefer renderer.setAnimationLoop + trackThree — | |
| * this is for canvas/SVG/DOM animation.) | |
| */ | |
| raf(fn) { | |
| let id = 0; | |
| let running = true; | |
| const loop = (t) => { | |
| if (!running) return; | |
| fn(t); | |
| id = requestAnimationFrame(loop); | |
| }; | |
| id = requestAnimationFrame(loop); | |
| const stop = () => { | |
| running = false; | |
| cancelAnimationFrame(id); | |
| }; | |
| lc.track(stop); | |
| return stop; | |
| }, | |
| /** setInterval with automatic clearInterval on destroy. Returns stop(). */ | |
| interval(fn, ms) { | |
| const id = setInterval(fn, ms); | |
| const stop = () => clearInterval(id); | |
| lc.track(stop); | |
| return stop; | |
| }, | |
| /** | |
| * Take ownership of a THREE.WebGLRenderer: on destroy the animation | |
| * loop is stopped, the GL context disposed and force-lost, and the | |
| * canvas removed from the DOM. | |
| */ | |
| trackThree(renderer) { | |
| lc.track(() => { | |
| try { | |
| renderer.setAnimationLoop?.(null); | |
| renderer.dispose(); | |
| renderer.forceContextLoss?.(); | |
| renderer.domElement?.remove(); | |
| } catch (err) { | |
| console.error('[mount] three renderer teardown failed:', err); | |
| } | |
| }); | |
| return renderer; | |
| }, | |
| /** Tear everything down (idempotent) and empty the mount host. */ | |
| destroy() { | |
| if (destroyed) return; | |
| destroyed = true; | |
| for (let i = disposables.length - 1; i >= 0; i -= 1) { | |
| runDisposable(disposables[i]); | |
| } | |
| disposables.length = 0; | |
| el.replaceChildren(); | |
| }, | |
| /** | |
| * Produce the CONTRACT-3 mount handle. `update` is the renderer's | |
| * re-render-from-fresh-payload function; it is guarded so calls after | |
| * destroy() are ignored. | |
| */ | |
| handle(update) { | |
| return { | |
| update(payload) { | |
| if (!destroyed && typeof update === 'function') update(payload); | |
| }, | |
| destroy() { lc.destroy(); }, | |
| }; | |
| }, | |
| }; | |
| return lc; | |
| } | |