106 lines
6.5 KiB
Markdown
106 lines
6.5 KiB
Markdown
# 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 so `node` can run it). `load/toJSON` lossless
|
|
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, `seek` clamp, `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). `capturekey` window listener present.
|
|
Clips need `await 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 to `stage.entities()` via
|
|
`onChange`.
|
|
- `stagestub.js` — full §4.2 Stage as plain objects; records effects into
|
|
`stage.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
|
|
creating `web/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, then `load()` a 2-entity
|
|
scene → rows now appear, scene bar updates).
|
|
- SYNC1 #4 clipdrop: `scenegod:clipdrop {id,path,clipIndex}` → `addClipDrop`
|
|
drops a block at the playhead, `out = clip.duration` (from preload), auto
|
|
`fade:0.25` where 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.fade` so 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 a
|
|
`timeline.addEntity(desc)` (or the dock calls `stage.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.
|