Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364import { on } from "svelte/events";
/** * Whether a visual should be running: on screen and in a visible tab. Create it in a * component's script and attach it to the visual's root: `{@attach live.watch}`. */export class Live { /** At least `threshold` of the element is in the viewport. */ inView = $state(false); tabHidden = $state(false);
readonly running = $derived(this.inView && !this.tabHidden);
constructor(readonly threshold = 0) {}
watch = (el: HTMLElement) => { this.tabHidden = document.hidden; const io = new IntersectionObserver(([e]) => (this.inView = e.isIntersecting), { threshold: this.threshold }); io.observe(el); const off = on(document, "visibilitychange", () => (this.tabHidden = document.hidden)); return () => { io.disconnect(); off(); }; };}
/** * A chain of timeouts that holds while `when()` is false. `wait(ms, fn)` queues the next * link; paused, the link is kept and runs `resume` ms after `when()` turns true again. One * link at a time — queueing a new one replaces it. Create it in a component's script (it owns * an effect, cleared with the component). */export class Chain { #next: (() => void) | null = null; #timer: ReturnType<typeof setTimeout> | undefined;
constructor( private when: () => boolean, private resume = 200 ) { $effect(() => { clearTimeout(this.#timer); if (this.when() && this.#next) this.#arm(this.resume, this.#next); return () => clearTimeout(this.#timer); }); }
wait(ms: number, fn: () => void) { clearTimeout(this.#timer); this.#next = fn; if (this.when()) this.#arm(ms, fn); }
#arm(ms: number, fn: () => void) { this.#timer = setTimeout(() => { this.#next = null; fn(); }, ms); }}