Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b646c8ade |
@@ -0,0 +1,23 @@
|
||||
# Session 0037.0 — Transcript
|
||||
|
||||
> App: human-experience-filter-art
|
||||
> Start: 2026-06-30T10-42 (PST)
|
||||
> Type: planning-and-executing
|
||||
> Posture: yolo
|
||||
> Claude-Session: aa7ac424-d797-4918-8837-2a904441d91e
|
||||
> Checkout: /Users/benstull/git/benstull.org/benstull/human-experience-filter-art/.claude/worktrees/session-0037-altitude-snap-on-release
|
||||
> Status: **PLACEHOLDER — claimed at session start; finalized at session end.**
|
||||
>
|
||||
> This file reserves session ID 0037 for human-experience-filter-art. The driver replaces this
|
||||
> body with the full transcript and renames the file to its final
|
||||
> SESSION-0037.0-TRANSCRIPT-2026-06-30T10-42--<end>.md form at session end.
|
||||
|
||||
## Launch prompt
|
||||
|
||||
_(launch prompt not captured at claim time)_
|
||||
|
||||
## Deferred decisions
|
||||
|
||||
_Autonomous-mode low-confidence calls the driver made and would have
|
||||
liked operator input on. Appended as the session runs; surfaced at
|
||||
finalize. Empty if none._
|
||||
@@ -106,5 +106,8 @@
|
||||
},
|
||||
"0036": {
|
||||
"title": ""
|
||||
},
|
||||
"0037": {
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,8 +276,7 @@ test("crossing a detent commits and locks the destination clip", async ({ page }
|
||||
const box = (await page.locator("#dial").boundingBox())!;
|
||||
const cx = box.x + box.width / 2, cy = box.y + box.height / 2;
|
||||
// Drag clockwise across one full detent (top -> right ~= 90deg = 1.25 detents),
|
||||
// crossing integer 1 (orbit) so it commits, then hold past it WHILE STILL DRAGGING
|
||||
// (the live blend holds wherever the knob is held; the snap happens only on release).
|
||||
// crossing integer 1 (orbit) so it commits, then hold past it (no auto-complete).
|
||||
await page.mouse.move(cx, cy - 30);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(cx + 30, cy + 6, { steps: 14 });
|
||||
@@ -294,30 +293,3 @@ test("crossing a detent commits and locks the destination clip", async ({ page }
|
||||
expect(st2.activeClipId).toBe(st.activeClipId);
|
||||
await page.mouse.up();
|
||||
});
|
||||
|
||||
test("releasing a partial drag snaps to the closest altitude (no held mid-blend)", async ({ page }) => {
|
||||
// The operator can grab the dial, turn it PART of a detent, and let go between two
|
||||
// altitudes. On release the knob must auto-scrub to the CLOSEST altitude and settle
|
||||
// there (integer position, frac 0) — landing on a discrete altitude exactly as if
|
||||
// they had clicked it, rather than holding the half-finished blend.
|
||||
await boot(page);
|
||||
const box = (await page.locator("#dial").boundingBox())!;
|
||||
const cx = box.x + box.width / 2, cy = box.y + box.height / 2;
|
||||
await page.mouse.move(cx, cy - 30);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(cx + 18, cy - 24, { steps: 8 }); // ~0.5 detent — parks BETWEEN altitudes
|
||||
// While held we are mid-blend: a fractional knob position.
|
||||
await page.waitForFunction(
|
||||
() => { const p = (window as any).__hefState().pos; return Math.abs(p - Math.round(p)) > 0.05; },
|
||||
null, { timeout: 5000 },
|
||||
);
|
||||
await page.mouse.up();
|
||||
// After release the position settles on an integer altitude (the snap).
|
||||
await page.waitForFunction(
|
||||
() => Number.isInteger((window as any).__hefState().pos),
|
||||
null, { timeout: 10000 },
|
||||
);
|
||||
const st = await page.evaluate(() => (window as any).__hefState());
|
||||
expect(Number.isInteger(st.pos)).toBe(true); // landed on an altitude, not held mid-blend
|
||||
expect(st.ringIndex).toBe(st.pos); // committed altitude == the position it landed on
|
||||
});
|
||||
|
||||
@@ -1076,11 +1076,7 @@ function onDialUp(e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Snap on release: a drag is a way of GRABBING the dial, not a free-standing
|
||||
// encoder detent — so when the operator lets go between altitudes, auto-scrub the
|
||||
// short way to the CLOSEST one and settle there (frac 0 locks). The result is the
|
||||
// same as a tap/label-click: a grab-and-release always lands on a discrete altitude.
|
||||
autoScrub(Math.round(pos));
|
||||
// No auto-complete: hold the blend wherever the knob stopped (continuous-encoder model).
|
||||
}
|
||||
|
||||
// Click a label → auto-scrub the SHORTEST signed way around the ring to that scale.
|
||||
|
||||
Reference in New Issue
Block a user