fix(editor): narrow findFilenameInput scope + dispatch input from setValue

Addresses two Minor follow-ups from the Task 4 code review:

- findFilenameInput previously included `body` in its closest() selector,
  meaning any "auto" textarea outside a modal would walk all the way up
  and pick up the files-editor modal's filename input from elsewhere in
  the document. Drop `body` so out-of-modal "auto" usage degrades
  cleanly to "plain".
- setValue now dispatches an `input` event on the textarea after
  writing, matching the onUpdate mirror. Task 10 wires the files-editor
  modal to call setValue when loading file content — without this fix,
  textarea-listening code (e.g. unsaved-changes indicators) wouldn't
  see programmatic loads. Now setValue and user typing produce the
  same observable side effects.

Plan source block updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-16 19:35:47 +02:00
parent e058b45ff2
commit 04f9a4d6a2
No known key found for this signature in database
2 changed files with 28 additions and 10 deletions

View file

@ -440,12 +440,15 @@ Key design decisions baked into the final file (both the initial skeleton and th
}; };
} }
// For "auto" language: look for a filename input near the textarea // For "auto" language: look for a filename input inside the nearest
// (the files-editor modal). Returns the <input> or null. // enclosing dialog or .modal. Returns the <input> or null.
// Intentionally does NOT walk up to <body>: an "auto" textarea
// outside a modal scope degrades to "plain" rather than picking up
// some unrelated .files-editor-filename elsewhere in the document.
function findFilenameInput(textarea) { function findFilenameInput(textarea) {
const modal = textarea.closest("dialog, .modal, body"); const scope = textarea.closest("dialog, .modal");
if (!modal) return null; if (!scope) return null;
return modal.querySelector(".files-editor-filename"); return scope.querySelector(".files-editor-filename");
} }
function mount(textarea) { function mount(textarea) {
@ -491,8 +494,14 @@ Key design decisions baked into the final file (both the initial skeleton and th
jar, jar,
language, language,
setValue: function (text) { setValue: function (text) {
// updateCode does not invoke the onUpdate mirror, so fire the
// same textarea sync + input event here for consistency. Any
// listener watching the textarea sees external setValue calls
// (e.g. files-overlay loading a file into the modal) the same
// way it sees user typing.
instance.jar.updateCode(text); instance.jar.updateCode(text);
textarea.value = text; textarea.value = text;
textarea.dispatchEvent(new Event("input", { bubbles: true }));
}, },
getValue: function () { getValue: function () {
return instance.jar.toString(); return instance.jar.toString();

View file

@ -37,12 +37,15 @@
}; };
} }
// For "auto" language: look for a filename input near the textarea // For "auto" language: look for a filename input inside the nearest
// (the files-editor modal). Returns the <input> or null. // enclosing dialog or .modal. Returns the <input> or null.
// Intentionally does NOT walk up to <body>: an "auto" textarea
// outside a modal scope degrades to "plain" rather than picking up
// some unrelated .files-editor-filename elsewhere in the document.
function findFilenameInput(textarea) { function findFilenameInput(textarea) {
const modal = textarea.closest("dialog, .modal, body"); const scope = textarea.closest("dialog, .modal");
if (!modal) return null; if (!scope) return null;
return modal.querySelector(".files-editor-filename"); return scope.querySelector(".files-editor-filename");
} }
function mount(textarea) { function mount(textarea) {
@ -88,8 +91,14 @@
jar, jar,
language, language,
setValue: function (text) { setValue: function (text) {
// updateCode does not invoke the onUpdate mirror, so fire the
// same textarea sync + input event here for consistency. Any
// listener watching the textarea sees external setValue calls
// (e.g. files-overlay loading a file into the modal) the same
// way it sees user typing.
instance.jar.updateCode(text); instance.jar.updateCode(text);
textarea.value = text; textarea.value = text;
textarea.dispatchEvent(new Event("input", { bubbles: true }));
}, },
getValue: function () { getValue: function () {
return instance.jar.toString(); return instance.jar.toString();