From a4262a5ef5984d2b0981b3796278deb0a3b1b4b8 Mon Sep 17 00:00:00 2001 From: Umais-Adeed Date: Wed, 11 Nov 2026 09:41:30 +0500 Subject: [PATCH 1/4] fix(web): files marked Viewed stay folded when a pull request's Code tab opens again The Code tab kept fold state only as local toggles, so closing and reopening the panel, or changing the commit scope, expanded every file while its Viewed tick stayed. A file still marked Viewed (and not stale) now folds by default, and the reader's toggles flip from that default. Fixes #13279 Co-Authored-By: Claude Opus 5.5 --- .../pullRequest/PullRequestCodeTab.tsx | 34 +++++++++++++++---- .../pullRequest/pullRequestDiff.logic.test.ts | 14 ++++++++ .../pullRequest/pullRequestDiff.logic.ts | 13 ++++--- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index e25725023ae24..d34fd327b589e 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -740,9 +740,17 @@ function PullRequestCodeTab({ ); + // A stale mark means the file changed since it was viewed, so it opens like any other file. + const foldsAsViewed = (path: string) => + filesViewedEnabled && isFileViewed(path) && !isFileViewedStale(path); const items: CodeViewDiffItem[] = annotatedFiles.map( ({ fileKey, path, fileDiff, annotations, annotationsVersion }) => { - const collapsed = isFileDiffCollapsed(fileKey, effectiveFoldOverride, toggledFiles); + const collapsed = isFileDiffCollapsed( + fileKey, + effectiveFoldOverride, + toggledFiles, + foldsAsViewed(path), + ); const viewedMark = filesViewedEnabled ? `e${isFileViewed(path) ? "v" : ""}${isFileViewedStale(path) ? "s" : ""}` : ""; @@ -802,7 +810,7 @@ function PullRequestCodeTab({ setToggledFiles((current) => range.reduce( (next, file) => - toggleFileDiffFoldForViewed(file.fileKey, viewed, effectiveFoldOverride, next), + toggleFileDiffFoldForViewed(file.fileKey, viewed, effectiveFoldOverride, next, viewed), current, ), ); @@ -820,7 +828,13 @@ function PullRequestCodeTab({ // Unfold only: a second reveal before the unfold renders must not fold the file again. if (item.collapsed === true) { setToggledFiles((current) => - toggleFileDiffFoldForViewed(item.id, false, effectiveFoldOverride, current), + toggleFileDiffFoldForViewed( + item.id, + false, + effectiveFoldOverride, + current, + foldsAsViewed(path), + ), ); } if (range) setSelectedLines({ id: item.id, range }); @@ -970,10 +984,18 @@ function PullRequestCodeTab({ onLineSelectionEnd: beginComment, onPostRender: codeViewReveal.flush, // Find may repeat requests before render; only unfold, never toggle. - onRevealSearchMatch: (item: CodeViewItem) => + onRevealSearchMatch: (item: CodeViewItem) => { + const path = annotatedFiles.find((file) => file.fileKey === item.id)?.path; setToggledFiles((current) => - toggleFileDiffFoldForViewed(item.id, false, effectiveFoldOverride, current), - ), + toggleFileDiffFoldForViewed( + item.id, + false, + effectiveFoldOverride, + current, + path !== undefined && foldsAsViewed(path), + ), + ); + }, }; const runThreadCommand = async ( diff --git a/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts b/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts index 552fa748f07f6..1675e15dff310 100644 --- a/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts +++ b/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts @@ -47,6 +47,12 @@ describe("isFileDiffCollapsed", () => { expect(isFileDiffCollapsed("b.ts", null, toggled)).toBe(true); expect(isFileDiffCollapsed("c.ts", null, toggled)).toBe(false); }); + + it("folds a file marked Viewed when the Code tab mounts again with no fold choices", () => { + expect(isFileDiffCollapsed("a.ts", "expanded", new Set(), true)).toBe(true); + expect(isFileDiffCollapsed("a.ts", "expanded", new Set(["a.ts"]), true)).toBe(false); + expect(isFileDiffCollapsed("a.ts", "expanded", new Set(), false)).toBe(false); + }); }); describe("toggleFileDiffFoldForViewed", () => { @@ -54,6 +60,14 @@ describe("toggleFileDiffFoldForViewed", () => { expect([...toggleFileDiffFoldForViewed("a.ts", true, "expanded", new Set())]).toEqual(["a.ts"]); expect(toggleFileDiffFoldForViewed("a.ts", false, "expanded", new Set()).size).toBe(0); }); + + it("measures a viewed file against its folded default", () => { + expect(toggleFileDiffFoldForViewed("a.ts", true, "expanded", new Set(), true).size).toBe(0); + // Unfolding a viewed file, as a reveal does, needs a toggle. + expect([...toggleFileDiffFoldForViewed("a.ts", false, "expanded", new Set(), true)]).toEqual([ + "a.ts", + ]); + }); }); describe("getFileViewedRange", () => { diff --git a/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts b/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts index f175c0151431a..4cbc01db01a1c 100644 --- a/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts +++ b/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts @@ -16,23 +16,28 @@ export function isLineInFileDiff( export type DiffFoldOverride = "expanded" | "folded" | null; -/** Keep fold choices relative to the toolbar so later pages inherit Fold all. */ +/** + * Keep fold choices relative to the toolbar so later pages inherit Fold all. A file still marked + * Viewed starts folded, so it reopens folded after the fold choices reset. + */ export function isFileDiffCollapsed( fileKey: string, foldOverride: DiffFoldOverride, toggledFileKeys: ReadonlySet, + viewed = false, ): boolean { - const foldedByDefault = foldOverride === "folded"; + const foldedByDefault = foldOverride === "folded" || viewed; return toggledFileKeys.has(fileKey) ? !foldedByDefault : foldedByDefault; } export function toggleFileDiffFoldForViewed( fileKey: string, - viewed: boolean, + collapsed: boolean, foldOverride: DiffFoldOverride, toggledFileKeys: ReadonlySet, + viewed = false, ): ReadonlySet { - if (isFileDiffCollapsed(fileKey, foldOverride, toggledFileKeys) === viewed) + if (isFileDiffCollapsed(fileKey, foldOverride, toggledFileKeys, viewed) === collapsed) return toggledFileKeys; const next = new Set(toggledFileKeys); if (next.has(fileKey)) next.delete(fileKey); From a5b7e6453c58bb3794f3e933a6c1784c1e2b63a0 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 11 Nov 2026 04:55:48 +0000 Subject: [PATCH 2/4] fix(web): Fold all and Expand all outrank Viewed marks in the Code tab Expand all left Viewed files folded, so with every file viewed the toolbar button did nothing. Viewed marks now set the fold default only until the toolbar picks one. AI-assisted; verified with the tests and tools listed above. --- .../components/pullRequest/PullRequestCodeTab.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index d34fd327b589e..b6aefae7cb57a 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -740,9 +740,9 @@ function PullRequestCodeTab({ ); - // A stale mark means the file changed since it was viewed, so it opens like any other file. + // A stale mark means the file changed since it was viewed; Fold all and Expand all outrank marks. const foldsAsViewed = (path: string) => - filesViewedEnabled && isFileViewed(path) && !isFileViewedStale(path); + foldOverride === null && filesViewedEnabled && isFileViewed(path) && !isFileViewedStale(path); const items: CodeViewDiffItem[] = annotatedFiles.map( ({ fileKey, path, fileDiff, annotations, annotationsVersion }) => { const collapsed = isFileDiffCollapsed( @@ -810,7 +810,13 @@ function PullRequestCodeTab({ setToggledFiles((current) => range.reduce( (next, file) => - toggleFileDiffFoldForViewed(file.fileKey, viewed, effectiveFoldOverride, next, viewed), + toggleFileDiffFoldForViewed( + file.fileKey, + viewed, + effectiveFoldOverride, + next, + viewed && foldOverride === null, + ), current, ), ); From a96349169f17d03b9b9243c30f4a81ce0a6b7d7d Mon Sep 17 00:00:00 2001 From: Umais-Adeed Date: Wed, 11 Nov 2026 10:00:45 +0500 Subject: [PATCH 3/4] fix(web): a reader's fold choice holds when a file's Viewed mark loads later Fold choices were toggles relative to a default that now includes the Viewed mark, so a mark that loaded or changed after a reader folded or unfolded a file flipped that choice. Store each choice as an absolute value; Fold all and Expand all still clear them. Co-Authored-By: Claude Opus 5.5 --- .../pullRequest/PullRequestCodeTab.tsx | 67 ++++++------------- .../pullRequest/pullRequestDiff.logic.test.ts | 35 +++++----- .../pullRequest/pullRequestDiff.logic.ts | 28 ++++---- 3 files changed, 51 insertions(+), 79 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index b6aefae7cb57a..fc7aea08600cb 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -123,8 +123,9 @@ import { getFileViewedRange, isFileDiffCollapsed, isLineInFileDiff, - toggleFileDiffFoldForViewed, + withFileDiffFold, type DiffFoldOverride, + type FileDiffFoldChoices, } from "./pullRequestDiff.logic"; import { PullRequestDiffStat, PullRequestMetaLine } from "./pullRequestPresentation"; import { PullRequestDiffUnavailableState } from "./PullRequestDiffUnavailableState"; @@ -385,7 +386,7 @@ function PullRequestCodeTab({ diffLayout: settings.diffLayout, wordWrap: settings.wordWrap, })); - const [toggledFiles, setToggledFiles] = useState>(() => new Set()); + const [foldChoices, setFoldChoices] = useState(() => new Map()); const [lastViewedFileKey, setLastViewedFileKey] = useState(null); const [visibleCommitCount, setVisibleCommitCount] = useState(COMMIT_PAGE_SIZE); const [foldOverride, setFoldOverride] = useState(null); @@ -430,7 +431,7 @@ function PullRequestCodeTab({ setLastViewedFileKey(null); setDraft(null); setSelectedLines(null); - setToggledFiles(new Set()); + setFoldChoices(new Map()); setFoldOverride(null); setVisibleCommitCount(COMMIT_PAGE_SIZE); setOrphansOpen(false); @@ -748,7 +749,7 @@ function PullRequestCodeTab({ const collapsed = isFileDiffCollapsed( fileKey, effectiveFoldOverride, - toggledFiles, + foldChoices, foldsAsViewed(path), ); const viewedMark = filesViewedEnabled @@ -791,13 +792,17 @@ function PullRequestCodeTab({ ); }; - const toggleFile = (fileKey: string) => - setToggledFiles((current) => { - const next = new Set(current); - if (next.has(fileKey)) next.delete(fileKey); - else next.add(fileKey); - return next; - }); + const toggleFile = (fileKey: string) => { + const path = annotatedFiles.find((file) => file.fileKey === fileKey)?.path; + const viewed = path !== undefined && foldsAsViewed(path); + setFoldChoices((current) => + withFileDiffFold( + current, + fileKey, + !isFileDiffCollapsed(fileKey, effectiveFoldOverride, current, viewed), + ), + ); + }; const setFileViewed = (fileKey: string, viewed: boolean, extendRange: boolean) => { const range = getFileViewedRange( @@ -807,18 +812,8 @@ function PullRequestCodeTab({ ); setLastViewedFileKey(fileKey); for (const file of range) setViewed(file.path, viewed); - setToggledFiles((current) => - range.reduce( - (next, file) => - toggleFileDiffFoldForViewed( - file.fileKey, - viewed, - effectiveFoldOverride, - next, - viewed && foldOverride === null, - ), - current, - ), + setFoldChoices((current) => + range.reduce((next, file) => withFileDiffFold(next, file.fileKey, viewed), current), ); }; const fileViewedToggle = { @@ -833,15 +828,7 @@ function PullRequestCodeTab({ if (item === undefined) return; // Unfold only: a second reveal before the unfold renders must not fold the file again. if (item.collapsed === true) { - setToggledFiles((current) => - toggleFileDiffFoldForViewed( - item.id, - false, - effectiveFoldOverride, - current, - foldsAsViewed(path), - ), - ); + setFoldChoices((current) => withFileDiffFold(current, item.id, false)); } if (range) setSelectedLines({ id: item.id, range }); codeViewReveal.request(item.id, range); @@ -849,7 +836,7 @@ function PullRequestCodeTab({ const toggleAllFiles = () => { setFoldOverride(areAllDiffFilesCollapsed(fileKeys, collapsedFileKeys) ? "expanded" : "folded"); - setToggledFiles(new Set()); + setFoldChoices(new Map()); }; const orderedCommits = // GitLab preserves UTC offsets, so compare instants. @@ -990,18 +977,8 @@ function PullRequestCodeTab({ onLineSelectionEnd: beginComment, onPostRender: codeViewReveal.flush, // Find may repeat requests before render; only unfold, never toggle. - onRevealSearchMatch: (item: CodeViewItem) => { - const path = annotatedFiles.find((file) => file.fileKey === item.id)?.path; - setToggledFiles((current) => - toggleFileDiffFoldForViewed( - item.id, - false, - effectiveFoldOverride, - current, - path !== undefined && foldsAsViewed(path), - ), - ); - }, + onRevealSearchMatch: (item: CodeViewItem) => + setFoldChoices((current) => withFileDiffFold(current, item.id, false)), }; const runThreadCommand = async ( diff --git a/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts b/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts index 1675e15dff310..47f309b3e32c0 100644 --- a/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts +++ b/apps/web/src/components/pullRequest/pullRequestDiff.logic.test.ts @@ -5,7 +5,7 @@ import { getFileViewedRange, isFileDiffCollapsed, isLineInFileDiff, - toggleFileDiffFoldForViewed, + withFileDiffFold, } from "./pullRequestDiff.logic"; function fileWithHunks( @@ -43,30 +43,29 @@ describe("isLineInFileDiff", () => { describe("isFileDiffCollapsed", () => { // regression: #9174 diffs now expand by default; a reader's fold must survive new slices it("keeps a file the reader folded closed as the next slice arrives", () => { - const toggled = new Set(["b.ts"]); - expect(isFileDiffCollapsed("b.ts", null, toggled)).toBe(true); - expect(isFileDiffCollapsed("c.ts", null, toggled)).toBe(false); + const choices = new Map([["b.ts", true]]); + expect(isFileDiffCollapsed("b.ts", "expanded", choices)).toBe(true); + expect(isFileDiffCollapsed("c.ts", "expanded", choices)).toBe(false); }); it("folds a file marked Viewed when the Code tab mounts again with no fold choices", () => { - expect(isFileDiffCollapsed("a.ts", "expanded", new Set(), true)).toBe(true); - expect(isFileDiffCollapsed("a.ts", "expanded", new Set(["a.ts"]), true)).toBe(false); - expect(isFileDiffCollapsed("a.ts", "expanded", new Set(), false)).toBe(false); + expect(isFileDiffCollapsed("a.ts", "expanded", new Map(), true)).toBe(true); + expect(isFileDiffCollapsed("a.ts", "expanded", new Map(), false)).toBe(false); }); -}); -describe("toggleFileDiffFoldForViewed", () => { - it("moves against whatever the toolbar last asked for", () => { - expect([...toggleFileDiffFoldForViewed("a.ts", true, "expanded", new Set())]).toEqual(["a.ts"]); - expect(toggleFileDiffFoldForViewed("a.ts", false, "expanded", new Set()).size).toBe(0); + it("keeps a reader's choice when the file's Viewed mark loads later", () => { + const choices = new Map([["a.ts", false]]); + expect(isFileDiffCollapsed("a.ts", "expanded", choices, false)).toBe(false); + expect(isFileDiffCollapsed("a.ts", "expanded", choices, true)).toBe(false); }); +}); - it("measures a viewed file against its folded default", () => { - expect(toggleFileDiffFoldForViewed("a.ts", true, "expanded", new Set(), true).size).toBe(0); - // Unfolding a viewed file, as a reveal does, needs a toggle. - expect([...toggleFileDiffFoldForViewed("a.ts", false, "expanded", new Set(), true)]).toEqual([ - "a.ts", - ]); +describe("withFileDiffFold", () => { + it("records an absolute choice and keeps the same map when nothing changes", () => { + const folded = withFileDiffFold(new Map(), "a.ts", true); + expect(folded.get("a.ts")).toBe(true); + expect(withFileDiffFold(folded, "a.ts", true)).toBe(folded); + expect(withFileDiffFold(folded, "a.ts", false).get("a.ts")).toBe(false); }); }); diff --git a/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts b/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts index 4cbc01db01a1c..da3844e90e1bb 100644 --- a/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts +++ b/apps/web/src/components/pullRequest/pullRequestDiff.logic.ts @@ -16,33 +16,29 @@ export function isLineInFileDiff( export type DiffFoldOverride = "expanded" | "folded" | null; +/** The reader's own fold choice per file. It is absolute, so a Viewed mark loading later keeps it. */ +export type FileDiffFoldChoices = ReadonlyMap; + /** - * Keep fold choices relative to the toolbar so later pages inherit Fold all. A file still marked - * Viewed starts folded, so it reopens folded after the fold choices reset. + * A reader's own choice wins. Otherwise the toolbar decides, and a file still marked Viewed + * starts folded. Fold all and Expand all clear the choices, so later pages follow the toolbar. */ export function isFileDiffCollapsed( fileKey: string, foldOverride: DiffFoldOverride, - toggledFileKeys: ReadonlySet, + choices: FileDiffFoldChoices, viewed = false, ): boolean { - const foldedByDefault = foldOverride === "folded" || viewed; - return toggledFileKeys.has(fileKey) ? !foldedByDefault : foldedByDefault; + return choices.get(fileKey) ?? (foldOverride === "folded" || viewed); } -export function toggleFileDiffFoldForViewed( +export function withFileDiffFold( + choices: FileDiffFoldChoices, fileKey: string, collapsed: boolean, - foldOverride: DiffFoldOverride, - toggledFileKeys: ReadonlySet, - viewed = false, -): ReadonlySet { - if (isFileDiffCollapsed(fileKey, foldOverride, toggledFileKeys, viewed) === collapsed) - return toggledFileKeys; - const next = new Set(toggledFileKeys); - if (next.has(fileKey)) next.delete(fileKey); - else next.add(fileKey); - return next; +): FileDiffFoldChoices { + if (choices.get(fileKey) === collapsed) return choices; + return new Map(choices).set(fileKey, collapsed); } export function getFileViewedRange( From 9c174f7f355af036631a089e30946acf37827814 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 11 Nov 2026 05:21:24 +0000 Subject: [PATCH 4/4] fix(web): a revealed file stays open when its Viewed mark loads later Revealing a file that was already open recorded no fold choice, so a Viewed answer that arrived afterwards folded it and hid the revealed line. Record the unfold on every reveal. AI-assisted; verified with the tests and tools listed above. --- .../PullRequestCodeTab.browser.test.tsx | 59 ++++++++++++++++++- .../pullRequest/PullRequestCodeTab.tsx | 6 +- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx index 96ef692c4aa13..b06f3d117b9ba 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.browser.test.tsx @@ -56,12 +56,43 @@ const { diffAtom, pagedDiffAtom } = await vi.hoisted(async () => { }; }); -const mocks = vi.hoisted(() => ({ paged: false, diffCommits: [] as Array })); +const mocks = vi.hoisted(() => ({ + paged: false, + diffCommits: [] as Array, + viewedPaths: null as ReadonlySet | null, + viewedListeners: new Set<() => void>(), +})); + +function deliverViewedPaths(paths: ReadonlySet | null) { + mocks.viewedPaths = paths; + for (const listener of mocks.viewedListeners) listener(); +} vi.mock(import("~/state/use-atom-command"), async (original) => ({ ...(await original()), useAtomCommand: (() => vi.fn()) as never, })); +vi.mock(import("./usePullRequestFilesViewed"), async () => { + const { useSyncExternalStore } = await import("react"); + const subscribe = (listener: () => void) => { + mocks.viewedListeners.add(listener); + return () => mocks.viewedListeners.delete(listener); + }; + return { + usePullRequestFilesViewed: () => { + const viewedPaths = useSyncExternalStore(subscribe, () => mocks.viewedPaths); + return { + enabled: viewedPaths !== null, + isViewed: (path: string) => viewedPaths?.has(path) === true, + isStale: () => false, + setViewed: () => {}, + truncated: false, + error: null, + refresh: () => {}, + }; + }, + }; +}); vi.mock(import("~/state/pullRequests"), async (original) => { const actual = await original(); return { @@ -136,6 +167,8 @@ function setup({ paged }: { paged: boolean }) { __setClientSettingsForTests(DEFAULT_CLIENT_SETTINGS); mocks.paged = paged; mocks.diffCommits = []; + mocks.viewedPaths = null; + mocks.viewedListeners.clear(); } const tab = ( @@ -174,6 +207,30 @@ it("keeps the diff after a refresh that brings back the same page", async () => await expect.element(screen.getByText("1 file", { exact: true })).toBeVisible(); }); +// regression: #13279 a Viewed answer that lands after a link reveals a file must not fold it +it("keeps a revealed file open when its Viewed mark loads afterwards", async () => { + setup({ paged: false }); + __setClientSettingsForTests({ ...DEFAULT_CLIENT_SETTINGS, diffFilesCollapsed: false }); + using _preferences = { + [Symbol.dispose]() { + __setClientSettingsForTests(DEFAULT_CLIENT_SETTINGS); + }, + }; + mocks.viewedPaths = new Set(); + const container = document.createElement("div"); + container.className = "flex h-[700px] w-[900px]"; + await using screen = await render(tab(0, { path: "a.ts", nonce: 1 }), { container }); + await expect.element(screen.getByText("three()", { exact: true })).toBeVisible(); + + deliverViewedPaths(new Set(["a.ts"])); + await expect.element(screen.getByRole("checkbox", { name: "Viewed" })).toBeChecked(); + + await expect + .element(screen.getByRole("button", { name: "Collapse diff" })) + .toHaveAttribute("aria-expanded", "true"); + await expect.element(screen.getByText("three()", { exact: true })).toBeVisible(); +}); + function pagedContainer() { const container = document.createElement("div"); container.className = "flex h-[500px] w-[900px]"; diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index fc7aea08600cb..68525756e2d99 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -826,10 +826,8 @@ function PullRequestCodeTab({ const revealFile = (path: string, range?: SelectedLineRange) => { const item = items.find((candidate) => resolveFileDiffPath(candidate.fileDiff) === path); if (item === undefined) return; - // Unfold only: a second reveal before the unfold renders must not fold the file again. - if (item.collapsed === true) { - setFoldChoices((current) => withFileDiffFold(current, item.id, false)); - } + // Record the unfold even when already open, so a Viewed mark that loads later cannot fold it. + setFoldChoices((current) => withFileDiffFold(current, item.id, false)); if (range) setSelectedLines({ id: item.id, range }); codeViewReveal.request(item.id, range); };