diff --git a/index.html b/index.html index 9c5dfda..c8cfa56 100644 --- a/index.html +++ b/index.html @@ -123,13 +123,13 @@

- - -
diff --git a/src/main.ts b/src/main.ts index 7175c3a..4e9c20b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3428,6 +3428,23 @@ function showLinkToast(text: string) { const filePreviewModal = $("#file-preview-modal"); const filePreviewTitle = $("#file-preview-title"); const filePreviewBody = $("#file-preview-body"); +// The panel stays in the DOM (translated off-screen) rather than +// display:none, so its CSS slide transition can animate — but that also +// keeps its buttons in the page's normal tab order while "closed". xterm.js +// deliberately leaves Shift+Tab uncancelled (so app-level Shift+Tab chords +// like Claude Code's plan-mode toggle still reach the pty), which means the +// browser's native reverse-tab-order focus navigation runs — and can land +// right here, scrolling the off-screen panel into view. tabindex="-1" while +// closed removes it from that path entirely; restored while open so the +// panel itself is still keyboard-navigable. +const filePreviewFocusables = [ + $("#file-preview-copy"), + $("#file-preview-edit"), + $("#file-preview-close"), +]; +function setFilePreviewFocusable(focusable: boolean) { + for (const el of filePreviewFocusables) el.tabIndex = focusable ? 0 : -1; +} // Tracks the live Three.js scene/renderer for the currently open model // preview (if any) so it can be torn down before the next preview replaces @@ -3440,6 +3457,7 @@ function disposeModelPreview() { function closeFilePreview() { filePreviewModal.classList.remove("open"); + setFilePreviewFocusable(false); disposeModelPreview(); setPreviewEditing(false); // Release state immediately rather than leaving the last-viewed file @@ -3569,6 +3587,7 @@ async function openFilePreview(raw: string, cwd: string | null) { filePreviewBody.className = "file-preview-body plain"; filePreviewBody.textContent = "…"; filePreviewModal.classList.add("open"); + setFilePreviewFocusable(true); // Move focus off the terminal and onto the panel: xterm's own keydown // handler treats Escape as a key it must own (cancels the browser event // unconditionally), which stops it from ever reaching our document-level