Frame: double-click the title to rename inline (no property-panel detour) #88
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#88
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?
Problem
Frames have an editable title that today can only be changed via the right-side property panel (
properties.js:330— theprop-frame-titleinput). To rename a frame the user has to: select the frame, find the property panel, locate the Title input, type, click away. Every other titled / textual object on the board (sticky, text, shape, document, kanban column header, mindmap node, calendar's view-mode cycle) has an inline rename via double-click.There's even a stub helper for this —
editText(group, textNode, bgNode)atobjects.js:841, commented// Legacy editText for frames (non-markdown). It's not bound to any frame event.Expected behavior
<input>over the canvas, matching the pattern already used by other objects).frame.findOne('.label').text(value)and triggersWhiteboardSync.onUpdate(group)so the change syncs to other windows.Affected files
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js— add adblclick dbltaplistener on the label insidecreateFrame. The handler calls a small inline editor that returns the new value via Enter/blur or cancels via Escape. Reuse the existingeditTexthelper if it cleanly fits the pattern; otherwise inline a small variant.No other file needs to change. The sync side already handles label updates (the
applySyncUpdateframe branch atsync.js:574updateslabel.text(data.title)whendata.titleis sent, and the serialize side atsync.js:236already includesdata.title).Acceptance criteria
cargo check / clippy / fmt --check / testclean.Notes
objects.js:841 editText(group, textNode, bgNode)already exists but is unbound. If it does the right thing, just bind adblclick dbltapon the label and call it with(group, label, bg). If the helper is incomplete, a minimal pattern is the same one used byeditMarkdownText/editMindmapTitleetc. — create a<textarea>or<input>over the canvas at the label's screen position, focus it, listen for Enter/Escape/blur.Implementation Spec for Issue #88
Objective
Let users rename a frame inline by double-clicking its title label. Reuse the dead
editTexthelper inobjects.js(currently defined but never called), extending it minimally to commit on Enter and to callWhiteboardSync.onUpdate+WhiteboardHistoryso the change persists, syncs to other windows, and is undoable. Bind the listener on the label only (not the dashed body) so accidental double-clicks elsewhere don't open the editor.Requirements
<textarea>over the canvas, pre-filled with the current title.WhiteboardSync.onUpdate(group)so a second window on the same board sees the change.crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js.cargo check / clippy / fmt --check / testclean.Files to Modify
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.jseditText(group, textNode, bgNode)to (a) snapshot history before the edit, (b) push a commit history entry on commit, (c) callWhiteboardSync.onUpdate(group)on commit, (d) treat Enter as commit (blur).createFrame, attach adblclick dbltaplistener on the label that callseditText(group, label, null).No other file needs to change. The sync side already round-trips frame
data.title(sync.js:236serialize +sync.js:574apply).Implementation Plan
Step 1: Make
editTexthistory- and sync-aware; commit on EnterFiles:
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.jseditText, snapshot for undo:removeTextarea, aftertextNode.text(textarea.value)and the redraw, finalize history + sync: (Order matches the existing dragend pattern: snapshotBefore on start, commitUpdate + onUpdate on end.)keydownhandler to commit on Enter (without Shift) and to keep the existing Escape behavior (note: existing code blurs on Escape but does not revert — the spec asks Escape to cancel without changes; restore the original text first): The shift-Enter escape hatch is harmless — frame titles are typically single-line, but allowing a manual newline isn't worth blocking.Step 2: Attach
dblclick dbltapto the frame labelFiles:
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.jsIn
createFrame(around thegroup.add(label)line), add:Notes:
labelKonva.Text, not thegroupor thebgrect — so double-clicks on the dashed body don't trigger it. Other handlers (drag, transformer attach) target the group; they're unaffected.bgNode = nullsoeditTextdoesn't try to grow a non-existent bg around the label (frame's bg is the dashed rectangle below the label and shouldn't expand from a title rename).cancelBubble = trueprevents the dblclick from also reaching the stage's dblclick handlers (which today don't act on dblclick on the canvas, but future-proof).Dependencies: Step 1.
Acceptance Criteria
cargo check / clippy / fmt --check / testclean.Notes
editTextis currently defined but unused (objects.js:841-900); modifying it is safe.WhiteboardHistory.snapshotBefore/commitUpdateis the same pair the frame'sdragstart/dragendalready use, so undo is consistent across drag and rename.keydowne.stopPropagation()keeps it isolated from the page-level keydown handlers (presentation, modals, shortcuts) — that's preserved.Test Results
cargo test -p hero_whiteboard_server: 3 passed (covers the live-board / soft-delete / unique-name regression tests).cargo test --workspace --lib: clean.cargo clippy --workspace -- -D warnings: clean.cargo fmt --all -- --check: clean.cargo check --workspace: clean.node --check objects.js: parses cleanly.The one workspace integration test in
hero_whiteboard_examples(test_workspace_and_board_operations) is flaky under contention because it talks to a live socket and was observed to race (it passes when run alone). It is not affected by this change.Manual verification recommended:
Implementation Summary
1 file changed, +40 / -4 — all in
crates/hero_whiteboard_ui/static/web/js/whiteboard/objects.js.Changes
editText(group, textNode, bgNode):origTextand callsWhiteboardHistory.snapshotBefore(group.id())before the edit.WhiteboardHistory.commitUpdate(group.id())+WhiteboardSync.onUpdate(group)when the text actually changed (no-op blur doesn't pollute history or push redundant RPCs).origTextbefore blurring, so the cancel path commits no change and skips history/sync.createFramenow bindsdblclick dbltapon the label, callingeditText(group, label, null). The listener is on the label only (not the dashed body) so accidental dblclicks elsewhere don't open the editor.bgNode = nullso the dashed rect doesn't grow from a title rename.Verification
cargo test -p hero_whiteboard_server: 3 passed.cargo test --workspace --lib: clean.cargo clippy --workspace -- -D warnings: clean.cargo fmt --all -- --check: clean.cargo check --workspace: clean.node --check objects.js: parses cleanly.Notes / caveats
editTextwas previously dead code (defined and exported but never called). All the changes above only affect the new caller — frames — and don't change behavior for any other consumer because there is no other consumer.data.titleon frames (sync.jsserialize at line 236, apply at line 574); this change just teaches the local rename path to callWhiteboardSync.onUpdate(group)so the round-trip is triggered.prop-frame-title) is untouched and keeps working.