Skip to content

fix(web): remember the rendered-markdown choice across threads - #4853

Merged
juliusmarminge merged 3 commits into
pingdotgg:mainfrom
Sy-D:fix/sticky-markdown-view-mode
Jul 29, 2026
Merged

fix(web): remember the rendered-markdown choice across threads#4853
juliusmarminge merged 3 commits into
pingdotgg:mainfrom
Sy-D:fix/sticky-markdown-view-mode

Conversation

@Sy-D

@Sy-D Sy-D commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What Changed

The rendered-markdown choice in the file preview panel is now a persisted preference instead of panel state tied to one file path.

Why

Reported in #4645. The state was:

const [markdownView, setMarkdownView] = useState<{ path: string | null; revealRequestId: number | null }>(...)
const renderMarkdown = isMarkdown && markdownView.path === relativePath && ...

It said "rendered is on for this file", so leaving the thread tore the panel down and returning showed source again. The reporter describes it as a preference — "once I've said I want to read markdown rendered, that's my preference until I toggle it back" — so it now persists the way this same panel already persists its explorer state (FILE_EXPLORER_STORAGE_KEY / initialExplorerOpen), rather than introducing a new mechanism or touching the settings schema.

A line reveal still wins over the preference, because a line only exists in the source. It no longer clears the preference on the way past, so toggling once returns you to rendered.

Notes For Review

A limitation worth knowing, which this PR does not change. rightPanelStore keeps revealLine / revealRequestId as durable surface state and rehydrates them. So if the last thing you did with a file surface was jump to a line, returning to it still shows source: the stored reveal is indistinguishable, at mount, from a fresh one, and treating it as already handled would break the case the reveal exists for — clicking a line link while the panel is closed.

That is not a regression. Before this change, returning showed source in every case; now it shows source only in that one. Making it complete means letting the store consume a reveal once it has been applied, which changes shared surface semantics and its persistence, so it felt like a separate change rather than something to fold in here.

Validation

Driven in a running client, on a markdown file whose own content links back to itself so the reveal path can be exercised:

step expected observed
Toggle rendered preference stored t3code.renderMarkdown = true, view rendered
Click a README.md:9 link while rendered source, line revealed toggle flipped to source; preference still true
Toggle back rendered rendered
Leave the thread and return, no pending reveal rendered rendered — verified by the reporter

The last row is the reported scenario. I could not reproduce it myself: the file explorer in my seeded fixture workspace lists no files, so I had no way to open a markdown file without going through a line link. It was checked by the reporter on a real project instead, and I am flagging that rather than implying I measured it.

  • vp run --filter @t3tools/web test — 1663 passed
  • vp run --filter @t3tools/web typecheck — no errors
  • vp lint / vp fmt --check on the changed file — clean

Closes #4645.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • No visual change beyond which of the two existing views is shown

Note

Low Risk
UI preference persistence in the file preview panel only; no auth, data, or API changes.

Overview
Fixes #4645 by persisting the file preview rendered markdown toggle in localStorage (t3code.renderMarkdown) via useLocalStorage, matching how explorer open/close is already stored—not per-file panel state that reset when switching threads.

Line reveals still force source view (the line only exists there). A new handledReveal state pairs path with revealRequestId so a reveal handled on one file does not block reveals on another; toggling rendered again records the current reveal as handled without clearing the stored preference.

Reviewed by Cursor Bugbot for commit 3238bcb. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Persist rendered-markdown toggle choice across threads in FilePreviewPanel

  • Replaces the local markdownView state with a useLocalStorage-backed preference keyed at t3code.renderMarkdown, so the toggle choice survives navigation between threads and files.
  • When a line reveal is active for a file and hasn't been marked as handled for that path/requestId, the panel forces source view regardless of the stored preference.
  • Toggling to rendered markdown records the current file path and requestId as handled, preventing the reveal logic from overriding the preference again for the same request.

Macroscope summarized 3238bcb.

The choice lived in panel state keyed to one file path, so leaving the thread
tore it down and the panel came back showing source. It is a reading
preference, not a property of a file: it now persists the way the panel
already persists its explorer state.

A line reveal still wins, since a line only exists in the source, and it no
longer clears the preference on its way past.

Closes pingdotgg#4645.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 69531c48-b272-4ff3-be92-50f2be9e8509

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 29, 2026
Comment thread apps/web/src/components/files/FilePreviewPanel.tsx Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dae51be. Configure here.

Comment thread apps/web/src/components/files/FilePreviewPanel.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved 3238bcb

This PR makes a simple change to persist the markdown rendering preference to localStorage using an existing hook. The change is self-contained with no security or schema implications, and the open review comment is just a clarifying question (the hook already exists).

You can customize Macroscope's approvability policy. Learn more.

Review feedback, and a regression I introduced: each file surface counts its
reveals from one, so after dismissing a reveal on one file the first line link
to another file carried the same id, was taken for already handled, and never
left the rendered view.

Pair the id with the path again, the way the state it replaced did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Sy-D

Sy-D commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Right on both counts, and it was a regression I introduced — the state I replaced paired the path with the request id, and I dropped the path. Fixed in a533f3a.

Confirmed the precondition rather than taking it on faith. rightPanelStore derives the id per surface:

const surfaceId = `file:${relativePath}`;
const surface = fileSurface(relativePath, normalizeRevealLine(line), (existing?.revealRequestId ?? 0) + 1);

so every file starts counting at one. Reproduced with two markdown files that link to each other by line, and read the persisted store afterwards:

README.md: revealLine=9 requestId=1
NOTES.md:  revealLine=5 requestId=1

Both at 1, exactly the collision described. Walked through it in a running client:

step expected observed
Click README.md:9 source, line revealed source
Toggle back to rendered rendered, reveal dismissed rendered
Click NOTES.md:5 from there source — different file, its own first reveal source

Without the pairing the last row would have stayed rendered, since the stored id 1 matched.

handledReveal now carries { path, requestId } and the comparison checks both.

vp run --filter @t3tools/web test — 1663 passed. Typecheck and lint clean.

macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jul 29, 2026
Comment on lines +685 to +687
// Reading markdown rendered is a preference, not a property of one file. Keeping
// it on the panel meant a thread switch dropped it and forced source back.
const [renderMarkdownPreferred, setRenderMarkdownPreferred] = useState(initialRenderMarkdown);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we not have a useLocalStorage hook yet??

@Sy-D Sy-D Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do see comment below @juliusmarminge fixed it and why this approach has been chosen previously.

The hook exists and does this better: it reads, writes, validates and keeps
instances in sync, so the hand-rolled initializer and the try/catch around the
write both go away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Sy-D

Sy-D commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

oh yea we do and its used by editorPreferences, providerUpdateDismissal and a couple of others. Switched to it in 3238bcb.

I had followed the neighbouring pattern in this file instead: FILE_EXPLORER_STORAGE_KEY with initialExplorerOpen() and a manual setLocalStorageItem in the toggle, a few lines above where this landed. Matching the file seemed safer than reaching past it, but the hook is more sleek than using multiple try catch..

PS: the explorer state right above still uses the hand-rolled version. Happy to convert it too, but it is unrelated to this fix so I left it alone rather than widening the diff.

@macroscopeapp
macroscopeapp Bot dismissed their stale review July 29, 2026 17:36

Dismissing prior approval to re-evaluate 3238bcb

@juliusmarminge
juliusmarminge enabled auto-merge (squash) July 29, 2026 17:57
@juliusmarminge
juliusmarminge merged commit e698796 into pingdotgg:main Jul 29, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Rendered-markdown view for a file in the right sidebar resets to source when you switch threads

2 participants