Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ <h2 id="empty-title"></h2>
<div class="file-preview-panel" id="file-preview-modal">
<div class="file-preview-head">
<span class="file-preview-title" id="file-preview-title"></span>
<button class="btn" id="file-preview-copy" data-i18n-title="copy">
<button class="btn" id="file-preview-copy" data-i18n-title="copy" tabindex="-1">
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</button>
<button class="btn" id="file-preview-edit" data-i18n-title="edit">
<button class="btn" id="file-preview-edit" data-i18n-title="edit" tabindex="-1">
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
</button>
<button class="btn" id="file-preview-close" data-i18n-title="close">
<button class="btn" id="file-preview-close" data-i18n-title="close" tabindex="-1">
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="m6.4 5 5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6 10.6 12 5 6.4z"/></svg>
</button>
</div>
Expand Down
19 changes: 19 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading