Skip to content

Take the walk time off the map, and let the frame loop stop - #94

Merged
EnesYilmazcode merged 2 commits into
mainfrom
claude/open-issues-eymdb2-75
Sep 2, 2026
Merged

Take the walk time off the map, and let the frame loop stop#94
EnesYilmazcode merged 2 commits into
mainfrom
claude/open-issues-eymdb2-75

Conversation

@EnesYilmazcode

Copy link
Copy Markdown
Owner

Closes #75.

Both halves of #75, and nothing else.

1. The pill is gone

drawTarget took a label and js/app.js handed it `${state.selected.walk} min walk` to paint at the midpoint of the dashed line. The minutes come out of walkMinutes, which multiplies the straight-line metres by DETOUR = 1.3, so the pill stated the time to walk a path 30% longer than the line it was pinned to. The drawing and the label disagreed and nothing on screen said which to believe.

The label option is removed from drawTarget rather than left unused, so there is no seam for it to come back through: js/map.js now calls neither fillText nor measureText. The walk time still appears on the row and on the room screen, where nothing is drawn beside it to contradict it.

2. The loop stops

render() re-requested a frame on its first line whatever was on screen. #75 counted 290 callbacks in 2 seconds on a settled list with nothing selected, every one painting the same pixels.

render() now returns whether anything is still moving, and a new createFrameLoop(request, draw) in js/map.js asks for the next frame only when it says so. request is injected rather than read off window, which is what makes the loop testable under node. The flyover is the only draw that keeps itself going — and under prefers-reduced-motion even that returns false, because t is pinned to 0 and every frame computes the same view.

Everything else that changes what the map shows wakes the loop for a single frame. The call sites, enumerated:

site why
boot() the first frame
the campus.json .then render paints nothing while state.basemap is null
settle() flyover off, camera snapped to the origin
frame(r) selection; wakes before its guards, because a tap that does not move the camera still changes which footprint is lit, and it returns early after a hand pan or with no fix
reframe() the band changed with the screen
showAsk() restarts the flyover
showRoom() frame() below it runs only for a ranked row; a room opened from a link is not one
useOrigin() the dot, its accuracy ring and the end of the line
onPan / onZoom drag, pinch, wheel
resize surface() reallocates the backing store and the band moves with the height
MutationObserver on document.body style js/install.js writes --bar-h when the install rail mounts or is dismissed, which changes the band the map centres in; no event announces it, and --bar-h is the only inline style anything sets on the body

Every render input was walked to build that list: basemap, campus, view, settled, selected, buildings, origin, accuracy, originIsGuess, screen, the canvas size, and the rail height.

Tests

npm test: 673 tests, 669 pass, 0 fail, 4 skipped (main was 666 / 662 / 0 / 4).

  • The old "the walk label is dropped when the pill would cover the line" test becomes "the map draws no text, on any length of line", including the long-line case that used to draw the pill. Verified against origin/main's js/map.js: it draws fillText("1 min walk", 246.4, 219.0) there and fails, and draws nothing here.
  • A source check that neither half of the seam is left: no fillText/measureText in js/map.js, no label in the drawTarget call in js/app.js.
  • Six loop tests drive createFrameLoop through a fake requestAnimationFrame that queues callbacks and pumps them one display frame at a time. The one the issue is about: one wake on a settled draw costs one frame, and 120 pumped display frames after it cost none. Also that a moving draw keeps going and then stops, that a stopped loop restarts when something wakes it, that three wakes inside one frame buy one frame, and that a wake raised from inside the draw is not swallowed.
  • A source check that js/app.js asks for a frame in exactly one place, the request handed to createFrameLoop.

All six loop tests fail on origin/main — the module has no such export.

Also in this diff

sw.js's shell gzip figure. scripts/test/sw.test.mjs holds it to within 1% of the files, and the added comments and the loop put it 1.8% out. Recomputed over the changed shell: 85,856 bytes with gzip 1.12, against 85,719 from node's zlib at the same level, so the two tools sit 0.16% apart as that comment says they do.

Not done

  • docs/media/room.webp still shows the pill, and the README alt text for it still says "labelled 4 min walk". Re-shooting needs Chrome for scripts/shoot.mjs and this machine has none, so the screenshot and its alt text are untouched rather than made to disagree with each other. They need one node scripts/shoot.mjs run.
  • sw.js is not re-stamped. SHELL_CACHE still reads vacant-shell-74f888c, so this needs npm run stamp before it deploys. Left alone deliberately: several lanes are open at once and the last merge showed what happens when each re-stamps for its own branch.
  • No measurement of my own in a real browser, for the same missing-Chrome reason. The 290-in-2-seconds figure quoted in the code comments is Two of #51's done-whens came back: the map still prints a walk time, and the frame loop never idles #75's, attributed to it there; the frame counts in the tests are counted by the tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_017xiX95r6nnUUmhD3KwMnWM


Generated by Claude Code

Two of #51's done-whens that came back, caught by the #45 audit and filed
as #75.

The pill. drawTarget took a `label` and app.js handed it "4 min walk" at
the midpoint of the dashed line. The minutes come out of walkMinutes,
which multiplies the straight-line metres by DETOUR = 1.3, so the pill
answered for a path 30% longer than the line it was pinned to: the
drawing and the label disagreed and nothing on screen said which to
believe. The option is gone from drawTarget rather than left unused, so
there is no seam for it to come back through, and js/map.js now draws no
text at all. The number still appears on the row and on the room screen,
where nothing is drawn beside it to contradict it.

The loop. render() re-requested a frame on its first line whatever was on
screen; #75 counted 290 callbacks in 2 seconds on a settled list with
nothing selected, all painting the same pixels. render() now returns
whether anything is still moving, and createFrameLoop in js/map.js asks
for the next frame only when it says so. The flyover is the only thing
that keeps itself going. Everything else that changes what the map shows
wakes the loop for one frame: settle, frame, reframe, showAsk, showRoom,
useOrigin, the basemap arriving, boot, resize, and the pan and zoom
gestures. Two of those needed care. frame() wakes before its guards,
because a tap that does not move the camera still changes which footprint
is lit, and it returns early after a hand pan. --bar-h is watched with a
MutationObserver on the body, because the install rail changes the band
the map centres in and no event announces it.

Tests: the drawTarget label test becomes the case that it draws no text on
either length of line, plus a source check that neither half of the seam
is left. Six new tests drive the loop through a fake requestAnimationFrame
and count frames: one wake on a settled draw costs one frame and 120
display frames after it cost none, a moving draw keeps going, a stopped
loop restarts, three wakes inside one frame buy one frame, and a wake
raised from inside the draw is not swallowed. 673 tests, 0 fail.

The shell's gzip figure in sw.js is recomputed over the changed files:
85,856 bytes with gzip 1.12, against 85,719 from node's zlib at the same
level.

Not done: docs/media/room.webp still shows the pill, and the README alt
text still describes it. Re-shooting needs Chrome for scripts/shoot.mjs,
which this machine does not have.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xiX95r6nnUUmhD3KwMnWM
The comment said the loop was pumped 600 times; the loop is pumped 120,
which is the two seconds of 16.7 ms display frames the same comment
describes. 600 was a draft number that outlived the draft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xiX95r6nnUUmhD3KwMnWM
@EnesYilmazcode
EnesYilmazcode merged commit 67b3ff0 into main Sep 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two of #51's done-whens came back: the map still prints a walk time, and the frame loop never idles

2 participants