6.5 KiB
6.5 KiB
Lane B log — append-only, newest at bottom
Format per session:
## <date> session N
DONE: …
DECISIONS: …
BLOCKED/REQUESTS: …
NEXT: …
2026-07-18 session 1
DONE: M1 complete. All files under scenegod/web/.
timeline.js— model + clock, no DOM, no three import (self-contained quat/euler/slerp math sonodecan run it).load/toJSONlossless round-trip (unknown entity fields preserved via structuredClone + a WeakMap for cached quats).evaluate(t): transform (pos/scale lerp, rot slerp→euler, easing incl. step-hold), params (num lerp, color sRGB lerp), clip blocks (set-time exact scrubbing, fade-out ramp), camera cuts (fires only on change). Clock:play()rAF deltas,seekclamp,step(frame)pure/deterministic for Lane C render. Mutators addKey/moveKey/deleteKey/ addClipBlock/moveClipBlock/trimClipBlock/addCut + undo stack (undo()wired; Ctrl+Z hooked in tlui).capturekeywindow listener present. Clips needawait tl.preload()before evaluate (prepareClip is async).tlui.js— canvas panel into#timeline. Ruler scrub, drag playhead, space=play/pause, dblclick lane=add key from stage state, drag key (snap to frame, Alt=free), right-click=delete, drag clip block/edge, Home/End, Ctrl+Z. Scene bar name/dur + Save/Load (POST/GET/scenes/{name}, localStorage fallback). Names column auto-syncs tostage.entities()viaonChange.stagestub.js— full §4.2 Stage as plain objects; records effects intostage.applied[]. Dev-only, delete at SYNC 1.timeline_test.mjs—node scenegod/web/timeline_test.mjs→ green (midpoint interp, step-ease hold, clip local time @3 frames, camera flip, lossless round-trip, unknown-field preservation).tldev.html— dev-only browser harness. Verified in browser: scrub moves entities (interp live in log), play runs to duration + auto-stops, camera cut flips camA→camB at t=3, keys/clips/cuts render. Delete at SYNC 1. DECISIONS:- Timeline imports NO three.js — inlined euler↔quat + slerp (~30 lines) so the node self-check needs zero deps (there is no node_modules/three in this stack). setTransform rot is euler (matches entityTransform format).
- One
<canvas>for all lanes (not canvas-per-lane); hit-testing via a hitbox list rebuilt each draw. Simpler, fast enough at this scale. - CSS injected from tlui.js via a
<style id=laneB-tl-css>rather than creatingweb/style.css— that file is Lane A's and didn't exist yet. Move into a/* === LANE B === */block at SYNC if John wants it centralized. (See REQUEST below.) - M1 crossfade = fade-OUT only; cross-block fade-IN is M2 "crossfade polish" per the milestone list. Marked with a ponytail comment in timeline.js. BLOCKED/REQUESTS:
- None blocking. Note for orchestrator/Lane A: my timeline styles live in
an injected
<style>in tlui.js. If you'd rather they sit in style.css, say so and I'll emit a/* === LANE B === */block for Lane A to paste (I won't edit style.css directly — ownership). - Lane A's files (index.html, stage.js, room3d.js, dock.js, style.css) are present untracked in the shared working dir but uncommitted — I staged ONLY my 5 files, left theirs alone. NEXT: Hold at M1 boundary. At SYNC 1 the stub swaps for the real Stage (one-line change in the page import) and tldev.html/stagestub.js get deleted. M2 (locked): clip crossfade polish, params/cut lane UI, snapping config, undo Ctrl+Z already stubbed, box-select multi-drag.
2026-07-18 session 2 (M2)
DONE: M2 per orchestrator update. SYNC findings fixed first, then polish.
- SYNC1 #3a/#3b: added
Timeline.onLoad(cb);load()fires it → TimelineUI rebuilds rows + refreshes name/duration fields. Verified with the exact repro (UI built against empty stage, thenload()a 2-entity scene → rows now appear, scene bar updates). - SYNC1 #4 clipdrop:
scenegod:clipdrop {id,path,clipIndex}→addClipDropdrops a block at the playhead,out = clip.duration(from preload), autofade:0.25where it abuts a neighbour. Verified via dispatched event. - Crossfade fade-IN (was fade-OUT only): a predecessor's fade pre-rolls the
successor's playback start earlier by
pred.fadeso local time is continuous across the boundary; weights ramp 1→0 / 0→1 and sum to 1 (asserted in test). - Params lanes (per-key hue), camera-cut lane add via dblclick on Cameras row (uses active cam / first camera), snap toggle in scene bar (Alt still = free), box-select + multi-drag + Delete of keys, selection highlight, fade wedge drawn on clip blocks, panel grows to content height (was fixed 200px → clipped the Cameras row once params rows were added).
- timeline_test.mjs extended: onLoad fires once, addClipDrop out=duration, crossfade weights sum to 1. All green. DECISIONS / BUG:
- tlui reads tracks from the TIMELINE, not the stage entity. Found mid-
verify:
load()structuredClones the scene, so timeline.scene.entities and stage entities are SEPARATE objects. Drawing from stage meant clipdrop/ addKey (which mutate the timeline copy) never appeared. Repointed all draw methods to_te(id) = tl.scene.entities.find(id). Rows now built from the timeline list merged with any stage-only (dock-added, not-yet-keyframed) entities. Per PLAN the timeline owns tracks — this is the correct source. - Grow-to-content height, no scroll yet (ponytail: add max-height+scroll only if a scene ever has enough entities to dominate the viewport). BLOCKED/REQUESTS:
- Gap for the orchestrator/Lane A to note: there's no bridge for a
dock-added stage entity to enter
timeline.scene.entities, so a freshly dropped character can't be keyframed until a scene load. tlui shows it as a row (empty tracks) but mutators need the entity present. If Lane A's dock should register new entities with the timeline, we need atimeline.addEntity(desc)(or the dock callsstage.captureState()→timeline.load). Flagging for SYNC 2 — not building it uninvited. - Verified with a throwaway harness (
_check.html) served on :8099; deleted, NOT committed (orchestrator removed the M1 tldev.html deliberately). NEXT: Hold at M2. Remaining M2 polish if a session 3 is granted: undo already wired to Ctrl+Z; could add snapping increment config + keyable-param add UI (currently params keys come from Lane A capture/inspector). Await SYNC 2.