merge: fix +Dev inline-namer blur (sherlock's preventDefault diagnosis)

Primary fix: e.preventDefault() on the pointerdown for both armed-tool
branches in onCanvasPointerDown. Without it, the browser's default
mousedown action blurs the freshly-focused input in promptInline
(the SVG click target isn't focusable), and the blur handler calls
done(null) before m can type.

Secondary fix: clear activeNamer before fo.remove() in done(), to
prevent a re-entrant pageerror when Enter triggers blur synchronously.
This commit is contained in:
mAi
2026-05-15 23:18:57 +02:00

View File

@@ -465,11 +465,21 @@ function onCanvasPointerDown(e) {
// of an existing frame or device — fires the tool. The +Dev tool needs
// this so m can drop a device inside a frame; without it the frame's
// own pointerdown handler would steal the click and start a drag.
//
// e.preventDefault() suppresses the compatibility mousedown's default
// focus-shift. Without it, the freshly-focused inline-namer input gets
// blurred ~6ms later by the browser's "focus nearest focusable ancestor
// or blur active" behaviour (SVG rects are not focusable), and the
// blur handler tears the namer down before m can type. Root cause +
// verified fix from sherlock's Playwright shift; see docs/sherlock-+dev-bug.md
// for the full trace.
if (state.tool === "frame") {
e.preventDefault();
startFrameRubberBand(e, p);
return;
}
if (state.tool === "device") {
e.preventDefault();
placeDeviceAt(p);
return;
}
@@ -582,7 +592,14 @@ function promptInline(placeholder, cx, cy) {
const input = fo.querySelector("input");
input.focus();
const done = (val) => {
if (activeNamer === fo) { fo.remove(); activeNamer = null; }
// Clear the flag *before* removing the node. Enter-key triggers a
// synchronous blur on the input, which re-enters done() — and if
// fo.remove() ran first, the second call hits a
// "node no longer a child" pageerror. Reordering makes the second
// re-entry a no-op (activeNamer is already null).
if (activeNamer !== fo) return;
activeNamer = null;
fo.remove();
resolve(val);
};
input.addEventListener("keydown", (e) => {