Presentation: rotated frame breaks spotlight alignment #108
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_whiteboard#108
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Presenting a rotated frame is broken: the spotlight cutout is an axis-aligned rectangle that doesn't match the frame outline, and the fit-to-viewport math also ignores rotation, so the spotlight sits in the wrong place. Content outside the rotated frame leaks through the spotlight backdrop.
Steps to reproduce
Expected
The spotlight cutout matches the rotated frame outline; only the frame contents are visible inside it; everything else is masked by the dark backdrop.
Actual
The spotlight is an axis-aligned rectangle that doesn't align with the rotated frame. Other board content (kanban, calendar, drawings) leaks through the corners of the AABB.

Notes
frames.js::focusFrame: it usesbg.width()/bg.height()directly for the screen rect and fit math, without accounting forframe.rotation().#pres-spotlightdiv via CSStransformso the cutout matches the rotated frame.Implementation Spec for Issue #108
Objective
Make presentation respect frame rotation: the spotlight cutout must align with the rotated frame outline, and the fit-to-viewport math must use the rotated frame's bounding box, not its un-rotated dimensions.
Requirements
frames.jscarries rotation in the screen-rect payload, and the two templates apply CSStransform: rotate(...)to#pres-spotlight.Files to Modify
crates/hero_whiteboard_ui/static/web/js/whiteboard/frames.js—focusFrameusesgetClientRect({ relativeTo: stage })for the AABB-based fit and stores rotation on_focusedScreenRect.crates/hero_whiteboard_ui/templates/web/board.html—positionSpotlightapplies CSS rotation.crates/hero_whiteboard_ui/templates/web/board_view.html— same.Implementation Plan
Step 1: rotation-aware focusFrame
File:
crates/hero_whiteboard_ui/static/web/js/whiteboard/frames.jsReplace the manual
bg.width()/bg.height()-based fit with agetClientRect({ relativeTo: stage })AABB fit. The frame's pivot in screen pixels is still(stagePos.x + frame.x() * scale, stagePos.y + frame.y() * scale)because Konva groups rotate around their local origin (0,0), which sits at the group's(x, y)in stage coords.Pseudocode:
Notes:
aabbalready accounts for the group's rotation (Konva applies the transform when computinggetClientRectwithrelativeTo)._focusedScreenRect.width/heightstay as the un-rotated frame size in screen pixels — that's the size of the spotlight cutout before CSS rotation._focusedScreenRect.left/topis the rotation pivot in screen pixels; matches the un-rotated top-left whenrotation === 0, so the existing template code keeps working.rotation: rotationto the returned_emit()payload (which already passesscreenRectthrough).Dependencies: none.
Step 2: rotate the spotlight div
Files:
crates/hero_whiteboard_ui/templates/web/board.html,crates/hero_whiteboard_ui/templates/web/board_view.htmlUpdate
positionSpotlight(rect)in both templates:The existing CSS keeps
box-shadow: 0 0 0 9999px rgba(0,0,0,0.92)— after rotating the spotlight box around its top-left corner, the 9999px uniform shadow still covers the whole viewport, and the (now rotated) inner cutout matches the frame outline.Dependencies: Step 1.
Acceptance Criteria
cargo fmt,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace --libclean.Notes
(x, y)in stage coords. The screen-pixel pivot is(stagePos.x + frame.x() * scale, stagePos.y + frame.y() * scale). Settingtransform-origin: 0 0and CSSrotate(R deg)on the spotlight div with that left/top makes the cutout rotate around the same pivot.0 0 0 9999pxis uniform spread with no offset, so it remains a screen-filling backdrop after rotation.Test Results
cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets -- -D warnings— cleancargo check -p hero_whiteboard_ui— cleancargo test --workspace --lib— 0 failednode --check frames.js— cleanImplementation Summary
Make presentation respect frame rotation: spotlight cutout aligns with the rotated frame, fit-to-viewport uses the rotated AABB.
crates/hero_whiteboard_ui/static/web/js/whiteboard/frames.jsfocusFramenow usesframe.getClientRect({ skipShadow: true, skipStroke: true, relativeTo: stage })for the AABB-based fit. This already accounts for the group's rotation, so the math is identical to the previous code when rotation is zero. The screen rect emitted to the host template gains arotationfield — the un-rotated cutout size in pixels stays the same; the host applies CSS rotation.crates/hero_whiteboard_ui/templates/web/board.htmlandboard_view.htmlpositionSpotlightnow appliestransform: rotate(<deg>)withtransform-origin: 0 0. Because Konva groups rotate around their local (0,0) — which is the group's(x, y)in stage coords — setting the spotlight'sleft/topto the un-rotated top-left in screen pixels and rotating around that pivot makes the cutout align with the rotated frame. Thebox-shadow: 0 0 0 9999px rgba(0,0,0,0.92)is a uniform spread with no offset, so the backdrop still fills the screen after rotation.Files Changed
crates/hero_whiteboard_ui/static/web/js/whiteboard/frames.js—+15/-13crates/hero_whiteboard_ui/templates/web/board.html—+2crates/hero_whiteboard_ui/templates/web/board_view.html—+2Test Results
cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets -- -D warnings— cleancargo check -p hero_whiteboard_ui— cleancargo test --workspace --lib— 0 failednode --check frames.js— cleanManual smoke
?present=1— same behavior inboard_view.html.Notes
getClientRectwithrelativeTo: stagereturns the AABB after the group's local transform but before the stage's pan/scale, which is exactly what the existing fit math expected (the AABB lives in stage coords).