Document element: cannot scroll long documents and element auto-resizes to fit content on edit #73
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_whiteboard#73
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
Two related issues on the Document element, the document renders at the full height of its
content instead of clipping to the element's bounds with an internal
scrollbar:
visible area, scrolling inside the element does not reveal the hidden
content. The only workaround is to resize the element taller.
automatically grows vertically to fit the new content length. The size
the user chose is not respected.
Combined, these make the document element behave like a non-scrollable,
content-sized container rather than a fixed-size scrollable region.
Implementation Spec for Issue #73
Objective
Make the Document whiteboard element behave as a fixed-size, scrollable container:
Requirements
_scrollYoffset on the group and a Konva scrollbar on the right edge.md-line-*) are offset vertically by-_scrollY; clipping is already done by the existingclipFunc._scrollYafter resize so no blank space appears.bg.width/bg.height.Files to Modify
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js— primary fix: drop auto-grow, add scroll state + scrollbar + wheel handling.No schema or server changes.
Implementation Plan
Step 1 — Drop auto-grow from
renderDocumentContentFiles:
objects.jsrenderDocumentContent, remove the branch that mutatesbg.height()toneededHeight. After this, the bg size only changes viaapplyTransform(user-initiated transformer-resize). TheskipAutoGrowoption name can be kept as a no-op for external callers, or removed.Dependencies: none.
Step 2 — Scroll state + scrollbar rendering in
renderDocumentContentFiles:
objects.jscreateDocument, initializegroup._scrollY = 0,group._contentHeight = 0.renderDocumentContent:group._contentHeight = contentHeight.md-line-*child a_baseY = child.y()sosetScrollYcan reposition without re-reading the markdown..scrollbar-track/.scrollbar-thumband rebuild them:listening: false, dim color, namescrollbar-track.dragBoundFunc; height =max(20, visibleH * visibleH / contentH); namescrollbar-thumb.maxScroll <= 0.dragmove: compute_scrollYfrom thumb y, callsetScrollY.Step 3 —
setScrollY/clampScrollhelpers on the groupFiles:
objects.jsgroup._setScrollY(y)andgroup._clampScroll()as closures (or plain functions called viagroupreference).setScrollY(y): clamp to[0, maxScroll], store ongroup._scrollY, iterate group children whosenamestarts withmd-line-and setchild.y(child._baseY - _scrollY), update thumb y, batchDraw.clampScroll(): re-runsetScrollY(_scrollY)— this naturally clips to the new max after a resize.Step 4 — Wheel interception
Files:
objects.jscreateDocument, addgroup.on('wheel', function(e) {...}):maxScroll <= 0, do not preventDefault (let stage pan).e.evt.preventDefault(); e.cancelBubble = true; group._setScrollY(_scrollY + e.evt.deltaY);.Step 5 — Re-clamp on resize
Files:
objects.jsapplyTransformfortype === 'document', afterrenderDocumentContent(node, { skipAutoGrow: true }), callnode._clampScroll && node._clampScroll().Acceptance Criteria
bg.width/bg.height._scrollYresets to 0 (intentional).Notes
Architecture: Konva-only. The document is a
Konva.Groupwith a.bgRect andmd-line-*children rendered byWhiteboardMarkdown.renderMarkdown. Clipping is already in place viagroup.clipFunc. A DOM textarea is used only during edit.Root cause 1 (no scroll): No scroll handling exists. Children are at absolute y-positions;
clipFunchides overflow but nothing offsets. Stage wheel panners trigger instead.Root cause 2 (auto-resize):
renderDocumentContentactively growsbg.height()to fit content whenever!skipAutoGrow;editMarkdownTextandrerenderDocumentboth call withoutskipAutoGrow. Dropping the auto-grow branch entirely fixes both edit and sync paths — user's size is already authoritative via the sync serializer.Scope: all changes in
objects.js. No schema changes.Test Results
JavaScript-only change in
objects.js; Rust workspace validated for regressions.cargo check --workspacecargo clippy --workspace -- -D warningscargo fmt --checkManual verification required: add a long markdown document, confirm a scrollbar appears on the right edge, scroll with wheel or by dragging the thumb, edit via the textarea or Properties panel and confirm the document does NOT grow vertically, resize via the transformer and confirm content still scrolls inside the new bounds.
Implementation Summary
Document elements now scroll internally when content overflows, and editing no longer auto-grows the element.
Files changed
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js(+111 / -16)Changes
renderDocumentContentno longer mutatesbg.height()to fit content. The user's chosen size (via transformer resize or creation default) is now authoritative. Every edit path (editMarkdownText,rerenderDocument, remote sync) flows through the same render without growing the bg.createDocumentinitializesgroup._scrollY = 0andgroup._contentHeight = 0, and attaches three helpers:_docMaxScroll(),_docSetScrollY(y),_docClampScroll()._docSetScrollYclamps, repositions everymd-line-*child by_baseY − _scrollY, updates the scrollbar thumb y, and batchDraws.group.on('wheel', ...)setse.cancelBubbleand consumese.evt.deltaYwhenmaxScroll > 0, so the stage pan/zoom is suppressed over documents. When content fits, the wheel passes through untouched.renderDocumentContentnow rebuilds a.scrollbar-track+.scrollbar-thumbon the right edge whenevermaxScroll > 0. The thumb is draggable with adragBoundFuncthat constrains it to the track;dragmoveconverts thumb position back into_scrollYvia the helper. Hover highlights the thumb.md-line-*is stamped with_baseY = c.y()so_docSetScrollYcan cheaply shift it to_baseY − _scrollYwithout re-running the markdown renderer.applyTransformfor documents now drops theskipAutoGrowoption (the flag became a no-op once auto-grow was removed) and callsnode._docClampScroll()after re-render so shrinking reveals hidden content and enlarging past the content pulls scroll back to 0.Architecture note
Konva-only — no HTML overlay. Clipping relies on the existing
clipFuncon the document group (which already clips rounded corners). Scroll offset is transient per-client view state; it is not serialized to the server or to history.Test results
cargo check --workspace: passcargo clippy --workspace -- -D warnings: passcargo fmt --check: passManual QA required: add a long document, scroll via wheel and thumb drag, edit to confirm no auto-grow, resize via transformer and confirm scroll re-clamps.
Pull request opened: #75
This PR implements the changes discussed in this issue.