fix(rflows): prevent Delete/Backspace from deleting nodes while editing

- Use composedPath()[0] instead of e.target in document keydown handler
  to pierce Shadow DOM event retargeting (inputs inside shadow DOM were
  seen as the host element, bypassing the INPUT tag guard)
- Block Delete/Backspace at document level when editor or inline config
  panel is open
- Add stopPropagation on editor panel inputs for defense-in-depth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-06 20:41:00 -08:00
parent cdcb287188
commit 9cdf246cc1
1 changed files with 8 additions and 4 deletions

View File

@ -1592,12 +1592,15 @@ class FolkFlowsApp extends HTMLElement {
// Keyboard
this._boundKeyDown = (e: KeyboardEvent) => {
// Skip if typing in any input/editable element (including modals outside shadow DOM)
const el = e.target as HTMLElement;
// Use composedPath to pierce Shadow DOM retargeting (e.target is the host element, not the input)
const el = (e.composedPath()[0] || e.target) as HTMLElement;
const tag = el.tagName;
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable) return;
// Also skip if a modal overlay is open (fund details, on-ramp, etc.)
// Also skip if a modal overlay is open or the editor panel is focused
if (document.getElementById("onramp-modal") || el.closest?.("[style*='z-index:99999']")) return;
if (this.editingNodeId || this.inlineEditNodeId) {
if (e.key === "Delete" || e.key === "Backspace") return;
}
if (e.key === "Escape") {
if (this.inlineEditNodeId) { this.exitInlineEdit(); return; }
@ -3722,9 +3725,10 @@ class FolkFlowsApp extends HTMLElement {
this.closeEditor();
});
// Input changes — live update
// Prevent keydown from propagating to canvas shortcuts (Delete, Backspace, etc.)
const inputs = panel.querySelectorAll(".editor-input, .editor-select");
inputs.forEach((input) => {
input.addEventListener("keydown", (e) => e.stopPropagation());
input.addEventListener("change", () => {
const field = (input as HTMLElement).dataset.field;
if (!field) return;