From 364fe4a286fac46be537bed810da98d1b4aefe8e Mon Sep 17 00:00:00 2001 From: nntrivi2001 Date: Thu, 29 Oct 2026 10:38:15 +0700 Subject: [PATCH] fix(files): an HTML file in the Changes panel shows as the page only on Preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes stays the default view and Whole file shows the source, as for any file. A Preview button — offered on the Changes diff too, where it was missing — opens the page; Raw, Changes or Whole file go back to the source. --- .../src/components/lanes/LaneChangesPanel.tsx | 27 ++++++++----- .../lanes/__tests__/LaneChangesPanel.test.tsx | 38 ++++++++++++++++++- docs/LANES.md | 5 ++- 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/client/src/components/lanes/LaneChangesPanel.tsx b/client/src/components/lanes/LaneChangesPanel.tsx index ebfa9059..8f87f57b 100644 --- a/client/src/components/lanes/LaneChangesPanel.tsx +++ b/client/src/components/lanes/LaneChangesPanel.tsx @@ -14,8 +14,9 @@ * is unsaved): Changes folds the unchanged lines away, Whole file * shows them; any other file opens on its read-only diff; a * whole markdown file can also be read rendered (Preview), an HTML - * file opens as the page itself (`HtmlFrame`, its relative CSS, - * scripts and images read like downloads), and an + * file shown as the page on Preview, from Changes too + * (`HtmlFrame`, its relative CSS, scripts and images read like + * downloads), and an * image file is shown as the picture (`GET …/git/raw`) * history — recent commits; opening one shows that commit's diff * browse — quick-open by path, or the repo's tracked file tree (the @@ -891,9 +892,9 @@ export function LaneChangesPanel({ const [whole, setWhole] = useState(false); // A whole markdown or HTML file shown rendered instead of as source; per // file, markdown opening as Settings → Appearance says - // (`lib/markdownView.ts`), an HTML page always as the page. + // (`lib/markdownView.ts`), an HTML page only once Preview is clicked. const [mdPreview, setMdPreview] = useState(readMarkdownPreview); - useEffect(() => setMdPreview(isHtml(openFile) || readMarkdownPreview()), [openFile]); + useEffect(() => setMdPreview(!isHtml(openFile) && readMarkdownPreview()), [openFile]); const [data, setData] = useState(null); // A folder that is not a repo (an empty new workspace, a parent of clones) // has no changes, review or history — only its files, which Files reads @@ -2562,6 +2563,9 @@ export function LaneChangesPanel({ aria-pressed={whole === w} onClick={() => { if (whole === w) return; + // Changes and Whole file show an HTML page's source; + // only Preview runs it. + if (isHtml(openFile)) setMdPreview(false); // Both views of a working file are the same editor: the // draft goes along, unsaved or not. if (editable && draft !== null) return setWhole(w); @@ -2993,16 +2997,21 @@ export function LaneChangesPanel({ {t("changes.annotate")} )} - {whole && text && !dirty && (isMarkdown(openFile) || isHtml(openFile)) && ( + {/* Offered on Changes too: from there it opens the rendered whole file. */} + {(!whole || text) && !dirty && (isMarkdown(openFile) || isHtml(openFile)) && ( )} {!local && range !== "commit" && openFile && ( diff --git a/client/src/components/lanes/__tests__/LaneChangesPanel.test.tsx b/client/src/components/lanes/__tests__/LaneChangesPanel.test.tsx index 57d67c98..e1d4a506 100644 --- a/client/src/components/lanes/__tests__/LaneChangesPanel.test.tsx +++ b/client/src/components/lanes/__tests__/LaneChangesPanel.test.tsx @@ -544,7 +544,7 @@ describe("LaneChangesPanel", () => { expect(screen.queryByTestId("lane-file-editor")).toBeNull(); }); - it("opens a whole HTML file as the page, its source after Raw", async () => { + it("opens a whole HTML file as its source, the page after Preview, the source after Raw", async () => { vi.mocked(api.lanes.gitTree).mockResolvedValue({ available: true, path: "", @@ -564,14 +564,48 @@ describe("LaneChangesPanel", () => { render(); const sidebar = await screen.findByTestId("lane-changes-sidebar"); await user.click(await within(sidebar).findByText("deck.html")); + expect(await screen.findByTestId("lane-file-editor")).toHaveValue("

deck

"); + expect(screen.queryByTestId("html-frame")).toBeNull(); + await user.click(screen.getByTestId("lane-file-preview")); const frame = await screen.findByTestId("html-frame"); expect(frame.getAttribute("srcdoc")).toContain("

deck

"); - expect(frame.getAttribute("sandbox")).not.toContain("allow-same-origin"); await user.click(screen.getByTestId("lane-file-preview")); expect(await screen.findByTestId("lane-file-editor")).toHaveValue("

deck

"); expect(screen.queryByTestId("html-frame")).toBeNull(); }); + it("opens a changed HTML file on its Changes, the page only after Preview", async () => { + vi.mocked(api.lanes.changes).mockResolvedValue( + changes({ + files: [ + { path: "deck.html", status: "??", untracked: true, added: 1, deleted: 0, binary: false }, + ], + }) + ); + vi.mocked(api.lanes.gitFile).mockResolvedValue({ + available: true, + path: "deck.html", + rev: "work", + binary: false, + truncated: false, + lines: ["

deck

"], + sha: "c".repeat(40), + }); + const user = userEvent.setup(); + render(); + await user.click(await screen.findByTitle("deck.html")); + await screen.findByRole("button", { name: "Changes", pressed: true }); + expect(screen.queryByTestId("html-frame")).toBeNull(); + await user.click(await screen.findByTestId("lane-file-preview")); + const frame = await screen.findByTestId("html-frame"); + expect(frame.getAttribute("srcdoc")).toContain("

deck

"); + expect(frame.getAttribute("sandbox")).not.toContain("allow-same-origin"); + // Back to Changes, then Whole file: the source again, not the page. + await user.click(screen.getByRole("button", { name: "Changes", pressed: false })); + await user.click(screen.getByRole("button", { name: "Whole file", pressed: false })); + await waitFor(() => expect(screen.queryByTestId("html-frame")).toBeNull()); + }); + it("shows an image file as the picture, with no edit control", async () => { vi.mocked(api.lanes.gitTree).mockResolvedValue({ available: true, diff --git a/docs/LANES.md b/docs/LANES.md index 87a3182b..2aaf57bc 100644 --- a/docs/LANES.md +++ b/docs/LANES.md @@ -678,8 +678,9 @@ reads the workspace's git surface in five modes: least one line tall (`MIN_ROW`), so an empty filler row keeps the two columns aligned. An image file (png, jpg, gif, webp, avif, bmp, ico, svg) is shown as the picture (`/git/raw`), an HTML file (`.html`/`.htm`) opens - as the page itself — **Raw** shows its source in the editor, **Preview** - brings the page back — and a conflicted file opens in the + like any file (Changes, or Whole file as source) with a **Preview** + button — on Changes too — that shows it as the page; **Raw** goes back to + its source — and a conflicted file opens in the conflict view. The page (here, in a Chat file preview, and on a double-click in the File Explorer, whose right-click menu keeps **Download**) runs in the chat artifact's sandbox (`HtmlFrame.tsx`: