Web Frame: URL editing uses window.prompt — replace with a proper modal #80
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#80
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?
Bug
Double-clicking a Web Frame on the board to change its URL pops a
window.prompt(...)dialog, which:Reproduction
Observed: a native browser
promptopens.Expected: an in-page modal styled like the app's other modals (rename board, new board, new workspace) opens with a URL input, Cancel and Save buttons, and Enter/Escape keyboard support.
Root cause
crates/hero_whiteboard_ui/static/web/js/whiteboard/webframe.js:255-281-- thepromptUrl(group)function:It's invoked from the webframe's
dblclick dbltaphandler atwebframe.js:80-83. There is no other caller -- it's a private helper inside the IIFE module.Affected files
crates/hero_whiteboard_ui/static/web/js/whiteboard/webframe.js-- replace thepromptcall with a modal flow; keep all the existing post-update side effects (label text, iframesrc,objStore.url, layer redraw, sync).crates/hero_whiteboard_ui/templates/web/board.html-- add the modal markup so it lives in the board template (web frames only exist on board pages, not on the home page).No server / SDK / openrpc / DB changes -- this is a pure UI refactor. The board's eventual sync still goes through
WhiteboardSync.onUpdatelike today.Expected behavior
Double-clicking a Web Frame opens an in-page modal that:
URLtext input pre-filled with the current URL.https://if no protocol is given (matches the existing logic atwebframe.js:260-262).alertpopup).var(--wb-surface),var(--wb-bg),var(--wb-border),var(--wb-text),var(--wb-text-muted),var(--wb-error)) so it matches dark/light mode like the existing rename/new-board modals.promptUrldoes: updatestate.url, label text, iframesrc,fallbacklinkhref,objStore[id].url, layer batchDraw, andWhiteboardSync.onUpdate(group)(the existing helper already calls it via the surrounding flow -- if not, the modal flow must trigger sync explicitly so the change propagates to other windows).Reference modals to match
crates/hero_whiteboard_ui/templates/web/home.html(the inline<div id="rename-modal">block).display:blocklabels,box-sizing:border-boxinputs, inline error region, Cancel + Create buttons, Enter/Escape keyboard handlers, andvar(--wb-...)theming.The Web Frame URL modal should follow the same conventions for visual consistency.
Acceptance criteria
https://(current behavior preserved).alert).window.promptorwindow.alertcalls remain in the Web Frame URL flow.webframe.jsandtemplates/web/board.html(no server/SDK/openrpc edits).Notes
promptUrl(group)entry point (just have it open the modal and pass the group), so the call fromwebframe.js:82(the dblclick handler) doesn't need to change -- this keeps the diff small and local.properties.js:83) in a follow-up; out of scope for this issue.Implementation Spec for Issue #80
Objective
Replace
window.promptin the Web Frame URL-edit flow with an in-page modal styled like the existing home-page modals (rename / new board / new workspace), preserving every existing downstream side effect (state, label, iframesrc, fallback link, objStore, layer redraw, sync).Requirements
https://rule the current code uses, updatesstate.url, label text, iframesrc, fallback<a>href,objStore[id].url, callsWhiteboardCanvas.getObjectLayer().batchDraw(), and callsWhiteboardSync.onUpdate(group)so the change syncs to other windows.alert).var(--wb-...)CSS variables so it matches dark/light themes.window.prompt/window.alertcalls remain in the Web Frame URL flow.crates/hero_whiteboard_ui/static/web/js/whiteboard/webframe.jsandcrates/hero_whiteboard_ui/templates/web/board.html.Files to Modify
crates/hero_whiteboard_ui/templates/web/board.html— add modal markup and a top-level keydown handler for Enter/Escape (matches the home page's pattern).crates/hero_whiteboard_ui/static/web/js/whiteboard/webframe.js— rewritepromptUrl(group)to drive the modal asynchronously (or via a queued callback). Keep the function name and its single internal caller (webframe.js:80-83) unchanged so the dblclick flow is preserved.No server / SDK / openrpc / DB changes.
Implementation Plan
Step 1: Add the modal markup + keydown handler to board.html
Files:
crates/hero_whiteboard_ui/templates/web/board.html<div id="webframe-url-modal">block inside{% block content %}styled like the home-page modals: fullscreen overlay, centered card withpadding: 24px; width: 400px; max-width: 90vw;. Inside the card:<h3>Edit Web Frame URL.<div>containing adisplay:block<label>URLand abox-sizing:border-box; width:100%<input id="webframe-url-input" type="text">.<div id="webframe-url-error">for empty/error messaging, styled withvar(--wb-error)and hidden by default.Cancel(closes modal) andSave(submits).{% block scripts %}, add a small<script>that wires:WhiteboardWebframe.openUrlModal(group)— exposed by webframe.js (Step 2). The modal is opened by webframe.js, not by board.html. The<script>block here only adds the keydown handler that listens for Enter/Escape while the modal is visible: Enter callswindow.__webframeUrlModalSubmit && window.__webframeUrlModalSubmit(), Escape callswindow.__webframeUrlModalCancel && window.__webframeUrlModalCancel().Dependencies: none.
Step 2: Rewire
promptUrlto drive the modal in webframe.jsFiles:
crates/hero_whiteboard_ui/static/web/js/whiteboard/webframe.jspromptUrl(group)with logic that:var state = group._wfState;.var modal = document.getElementById('webframe-url-modal');— if absent (e.g. embed/share view that doesn't render the modal), fall back toprompt(...)with the existing logic so the feature still works.webframe-url-inputwithstate.url, hideswebframe-url-error, shows the modal (display:flex), focuses+selects the input, and assignswindow.__webframeUrlModalSubmitandwindow.__webframeUrlModalCancelcallbacks.https://if no protocol. If unchanged fromstate.url, just close the modal. Otherwise updatestate.url, label text viatruncateUrl(newUrl, 50), iframesrc, fallback linkhref, andobjStore[id].url. CallWhiteboardCanvas.getObjectLayer().batchDraw()andWhiteboardSync.onUpdate(group). Close the modal.closeUrlModal()inside the IIFE that hides the modal and clears the global callbacks.onclickSave and Cancel buttons (declared in board.html) to call exported helpers via the global window callbacks. To avoid leaking globals, consider exposingWhiteboardWebframe.submitUrlModalandWhiteboardWebframe.cancelUrlModaland usingonclick="WhiteboardWebframe.submitUrlModal()"in board.html. (Either pattern is acceptable; pick the one that matches existing code style.)promptUrloutside the dblclick handler at line 80-83 (grep -rn promptUrl crates/hero_whiteboard_uiconfirms it).Dependencies: Step 1 (modal markup must exist for the modal path to take effect).
Acceptance Criteria
var(--wb-...).https://.alert).window.prompt/window.alertcalls remain in the Web Frame URL flow.webframe.jsandtemplates/web/board.html.Notes
prompt(...)when the modal element isn't present is a safety net for embed/share routes that may renderboard.htmldifferently (e.g.templates/web/embed.html). If grepping shows the embed view also rendersboard.html({% extends "web/board.html" %}-style), the modal will be present and the fallback never runs — but it's a cheap safety guard.home.html's recent modals:display:blocklabels,box-sizing:border-boxinputs, inlinevar(--wb-error)text region, Cancel + Save buttons (btn-sm+btn-sm btn-primary).truncateUrl, the iframe overlay positioning, or any other webframe internals.Test Results
cargo test --workspace --lib: 4 lib targets, 0 tests / 0 passed / 0 failed each.cargo clippy --workspace -- -D warnings: clean.cargo check --workspace: clean.node --check webframe.js: parses cleanly.This is a UI-only change (HTML template + JS). Manual verification recommended:
example.com(no protocol). Confirm the iframe navigates tohttps://example.comand the label updates.alert).Implementation Summary
Changes
crates/hero_whiteboard_ui/templates/web/board.html(+34 / -0)<div id="webframe-url-modal">block matching the home page's modal styling: themed viavar(--wb-...)CSS variables,display:blocklabel,box-sizing:border-boxinput, inline error region, Cancel + Save buttons.keydownhandler at the document level that routes Enter/Escape toWhiteboardWebframe.submitUrlModal/cancelUrlModalwhile the modal is visible.crates/hero_whiteboard_ui/static/web/js/whiteboard/webframe.js(+82 / -21)promptUrl(group)'s body. It now opens the in-page modal (when present) and pre-fills the input with the current URL.applyNewUrl(group, rawUrl)— handles trim, https:// auto-prepend, state update, label retext (truncateUrl(newUrl, 50)), iframesrc, fallback linkhref,objStore[id].url, layer batchDraw, andWhiteboardSync.onUpdate(group)to propagate to other windows.submitUrlModal(validates the input, shows inline error on empty, callsapplyNewUrl, closes the modal) andcancelUrlModal(just closes), exposed via the module's public surface for the template'sonclickand keydown handlers.prompt(...)when the modal element isn't rendered (defensive — covers any view that loadswebframe.jswithout including the modal markup).Why the fix is local to two files
The legacy
promptwas a private helper inside the webframe IIFE with a single internal caller (the dblclick handler atwebframe.js:80-83). The new module exports + the modal markup live where the user-facing flow happens. No server / SDK / openrpc / DB changes; no other JS module touched.Verification
cargo test --workspace --libpasses (4 lib targets, 0 tests).cargo clippy --workspace -- -D warnings: clean.node --check webframe.js: parses cleanly.Notes / caveats
applyNewUrlnow explicitly callsWhiteboardSync.onUpdate(group), where the legacypromptUrlrelied on neighbouring code paths. The early-return when the new URL equals the old URL preserves the legacy no-op behavior so this isn't a redundant sync.\uD83C\uDF10globe glyph used in the label is preserved verbatim from the original code.promptUrl(group)as the function called from the dblclick handler (webframe.js:80-83), so the call site needed no edit.