fix(web): reset markdown widgets when the previewed file changes - #9910
Conversation
#9677 keeps rendered Markdown mounted across text updates so streaming does not remount the whole message. The Files panel rendered every markdown file through the same unkeyed surface, so switching files carried the previous document's open details, wrapped fences, and pinned table widths into the next one. Key the rendered markdown surface by path, matching the sibling code views. In-file updates still reconcile in place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — A path-scoped React key resets markdown-specific UI state when switching files while preserving in-place updates for the currently previewed file. The one-file change is localized to the Files panel and introduces no broader application or configuration impact. You can add or adjust custom eligibility rules. Learn more. |
## What's Changed * chore: configure Knip workspace audits by @juliusmarminge in pingdotgg/t3code#9958 * refactor(web): prune unused UI and provider code by @juliusmarminge in pingdotgg/t3code#9959 * chore(mobile): remove obsolete widget wiring script by @juliusmarminge in pingdotgg/t3code#9960 * chore: remove redundant root tooling dependencies by @juliusmarminge in pingdotgg/t3code#9961 * ci: reject unused files and dependencies with Knip by @juliusmarminge in pingdotgg/t3code#9962 * test(contracts): keep driver default lookup private by @juliusmarminge in pingdotgg/t3code#9968 * test(server): remove Azure permissions constant snapshot by @juliusmarminge in pingdotgg/t3code#9973 * refactor(shared): remove unused viewport formatters by @juliusmarminge in pingdotgg/t3code#9970 * refactor(mobile): remove unused provider option summary by @juliusmarminge in pingdotgg/t3code#9971 * refactor(client-runtime): remove unused connection phase message by @juliusmarminge in pingdotgg/t3code#9972 * refactor(mobile): remove unused layout calculations by @juliusmarminge in pingdotgg/t3code#9974 * refactor(client-runtime): remove unused file position predicate by @juliusmarminge in pingdotgg/t3code#9976 * refactor(mobile): remove unused font size steppers by @juliusmarminge in pingdotgg/t3code#9975 * test(server): cover thread lookup through command invariants by @juliusmarminge in pingdotgg/t3code#9978 * test(server): remove provider equality wrapper fixture by @juliusmarminge in pingdotgg/t3code#9979 * test(server): assert the dispatched welcome thread model by @juliusmarminge in pingdotgg/t3code#9980 * refactor(desktop): remove unused keyring remediation text by @juliusmarminge in pingdotgg/t3code#9981 * refactor(desktop): remove test-only Electron error predicates by @juliusmarminge in pingdotgg/t3code#9982 * refactor(web): remove unused pull request state label by @juliusmarminge in pingdotgg/t3code#9984 * perf(web): keep timeline row reuse engaged while text streams by @juliusmarminge in pingdotgg/t3code#9909 * fix(web): reset markdown widgets when the previewed file changes by @juliusmarminge in pingdotgg/t3code#9910 * fix(mobile): keep highlighting review diffs after a long line by @juliusmarminge in pingdotgg/t3code#9911 * fix(marketing): align the endorsement carousel with its heading by @juliusmarminge in pingdotgg/t3code#9912 * fix(client): keep warm thread resumes live instead of flashing sync by @juliusmarminge in pingdotgg/t3code#9913 * test(server): remove authorization prompt snapshots by @juliusmarminge in pingdotgg/t3code#9985 * test(server): remove static OAuth page snapshots by @juliusmarminge in pingdotgg/t3code#9986 * test(server): remove provider label identity assertion by @juliusmarminge in pingdotgg/t3code#9987 * test(server): consolidate agent activity opt-in coverage by @juliusmarminge in pingdotgg/t3code#9988 * refactor(shared): remove unused preview URL predicate by @juliusmarminge in pingdotgg/t3code#9989 * refactor(shared): remove unused mention path serializer by @juliusmarminge in pingdotgg/t3code#9990 * refactor(shared): remove retired PATH capture parser by @juliusmarminge in pingdotgg/t3code#9991 * refactor(client-runtime): remove unused subagent selectors by @juliusmarminge in pingdotgg/t3code#9992 * refactor(web): test the live usage column builder by @juliusmarminge in pingdotgg/t3code#9993 * refactor(web): remove unused aspect ratio reconciler by @juliusmarminge in pingdotgg/t3code#9994 * refactor(web): remove obsolete cloud listing helpers by @juliusmarminge in pingdotgg/t3code#9995 * test(web): remove composer control style snapshots by @juliusmarminge in pingdotgg/t3code#9996 * test(web): keep the preview profile label helper private by @juliusmarminge in pingdotgg/t3code#9997 * test(server): cover raw OpenCode deltas through the adapter by @juliusmarminge in pingdotgg/t3code#9977 * refactor(web): remove obsolete pull request link opener by @juliusmarminge in pingdotgg/t3code#9983 * test(relay): keep the stage slug helper private by @juliusmarminge in pingdotgg/t3code#9998 * refactor(mobile): keep project selection helper private by @juliusmarminge in pingdotgg/t3code#9999 * refactor(mobile): keep review default ID helper private by @juliusmarminge in pingdotgg/t3code#10000 * refactor(mobile): remove unused native style constants by @juliusmarminge in pingdotgg/t3code#10001 * refactor(mobile): test terminal palettes through public theme API by @juliusmarminge in pingdotgg/t3code#10002 * refactor(mobile): remove unused file tree walkers by @juliusmarminge in pingdotgg/t3code#10003 * refactor(shared): keep persisted settings helpers private by @juliusmarminge in pingdotgg/t3code#10004 * test(mobile): remove mocked UUID shape assertions by @juliusmarminge in pingdotgg/t3code#10006 * refactor(mobile): test final connection status presentation by @juliusmarminge in pingdotgg/t3code#10007 * test(web): keep pull request menu items private by @juliusmarminge in pingdotgg/t3code#10016 * refactor(shared): test favicon selection through public API by @juliusmarminge in pingdotgg/t3code#10005 * refactor(server): remove test-only pricing normalizer by @juliusmarminge in pingdotgg/t3code#10017 * refactor(web): remove unused desktop update visibility helper by @juliusmarminge in pingdotgg/t3code#10014 * refactor(web): remove obsolete provider update helpers by @juliusmarminge in pingdotgg/t3code#10015 * refactor(web): remove unused terminal context preview formatter by @juliusmarminge in pingdotgg/t3code#10009 * refactor(web): test environment-scoped draft promotion by @juliusmarminge in pingdotgg/t3code#10010 * fix(web): retain wrapped row heights during edits by @juliusmarminge in pingdotgg/t3code#10018 * refactor(shared): remove unused Clerk hostname predicate by @juliusmarminge in pingdotgg/t3code#10008 * refactor(tailscale): keep package internals private by @juliusmarminge in pingdotgg/t3code#10011 * ci: reject unused tailscale exports with Knip by @juliusmarminge in pingdotgg/t3code#10012 * fix(web): keep chat media at a stable size while it loads by @juliusmarminge in pingdotgg/t3code#9938 * refactor(server): keep manifest age parsing private by @juliusmarminge in pingdotgg/t3code#10028 * refactor(mobile): remove unused awareness relay URL normalizer by @juliusmarminge in pingdotgg/t3code#10029 * refactor(server): remove unused startup heartbeat launcher by @juliusmarminge in pingdotgg/t3code#10030 * refactor(shared): keep search ranking comparator private by @juliusmarminge in pingdotgg/t3code#10031 * refactor(server): keep telemetry identity errors private by @juliusmarminge in pingdotgg/t3code#10032 * refactor(mobile): test composer persistence through the live decoder by @juliusmarminge in pingdotgg/t3code#10033 * refactor(web): remove unused sidebar selectors by @juliusmarminge in pingdotgg/t3code#10034 * refactor(server): keep Cursor fallback models private by @juliusmarminge in pingdotgg/t3code#10038 * refactor(web): remove unused xterm link range helpers by @juliusmarminge in pingdotgg/t3code#10040 * refactor(mobile): remove obsolete review list builder by @juliusmarminge in pingdotgg/t3code#10039 * test(server): remove duplicate VCS error constructor checks by @juliusmarminge in pingdotgg/t3code#10042 * refactor(mobile): keep appearance calculations private by @juliusmarminge in pingdotgg/t3code#10043 * refactor(web): remove unused sidebar menu action by @juliusmarminge in pingdotgg/t3code#10044 * refactor(web): test live Ghostty link resolution directly by @juliusmarminge in pingdotgg/t3code#10041 * test(server): exercise Codex prompts through public assembly by @juliusmarminge in pingdotgg/t3code#10045 * refactor(shared): remove unused elapsed-time adapter by @juliusmarminge in pingdotgg/t3code#10046 * refactor(web): remove unused preview thread reset helper by @juliusmarminge in pingdotgg/t3code#10049 * refactor(desktop): remove test-only error predicates by @juliusmarminge in pingdotgg/t3code#10047 * refactor(mobile): keep review reset hashing private by @juliusmarminge in pingdotgg/t3code#10048 * test(web): remove AppRoot element order snapshot by @juliusmarminge in pingdotgg/t3code#10052 * refactor(codex): keep app-server client internals private by @juliusmarminge in pingdotgg/t3code#10035 * ci: reject unused Codex client exports with Knip by @juliusmarminge in pingdotgg/t3code#10036 * refactor(server): simplify native telemetry error internals by @juliusmarminge in pingdotgg/t3code#10057 * refactor(mobile): remove write-only terminal font cache by @juliusmarminge in pingdotgg/t3code#10058 * refactor(web): remove obsolete HSL theme generator by @juliusmarminge in pingdotgg/t3code#10061 * refactor(mobile): remove obsolete native diff token stream by @juliusmarminge in pingdotgg/t3code#10062 * test(server): remove title prompt editorial snapshots by @juliusmarminge in pingdotgg/t3code#10063 * test(server): remove repeated runtime prompt interpolation cases by @juliusmarminge in pingdotgg/t3code#10059 * refactor(web): observe preview tests through the live registry by @juliusmarminge in pingdotgg/t3code#10064 * test(server): remove keybinding default assignment snapshot by @juliusmarminge in pingdotgg/t3code#10065 * refactor(mobile): remove obsolete whole-file review highlighters by @juliusmarminge in pingdotgg/t3code#10067 * test(server): cover CLI runner detection through command suggestions by @juliusmarminge in pingdotgg/t3code#10066 * refactor(mobile): remove unused cloud relay URL normalizer by @juliusmarminge in pingdotgg/t3code#10068 * refactor(web): test live keybinding resolvers directly by @juliusmarminge in pingdotgg/t3code#10069 * test(server): cover Grok skill parsing through discovery by @juliusmarminge in pingdotgg/t3code#10070 * test(web): remove mocked diff view prop snapshot by @juliusmarminge in pingdotgg/t3code#10073 * test(web): remove mocked annotation options snapshot by @juliusmarminge in pingdotgg/t3code#10074 * refactor(web): keep pending action labels private by @juliusmarminge in pingdotgg/t3code#10075 * refactor(mobile): remove unused cloud pending-status mapper by @juliusmarminge in pingdotgg/t3code#10071 * refactor(web): remove unused model picker hint helpers by @juliusmarminge in pingdotgg/t3code#10072 * fix(server): resume checkpointing after git init by @Yash-Singh1 in pingdotgg/t3code#10078 * feat(web): first-run welcome wizard with agent setup and project import by @t3dotgg in pingdotgg/t3code#5362 **Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260905.1285...v0.0.39-nightly.20260905.1286 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260905.1286
#9677 hoisted the react-markdown component map to a module constant so streaming text reconciles in place instead of remounting the whole message. The Files panel rendered every markdown file through the same unkeyed
RenderedMarkdownSurface, so a file switch now reconciled in place too: the previous document's open<details>, wrapped code fences, and pinned table header widths carried into the next file. Before #9677 the component map's memo depended ontext, which forced a clean remount on every file change.Key the rendered markdown surface by
relativePath, matching the sibling code-view branches in the same ternary. Updates to the same file (agent edits, workspace refreshes) still reconcile in place, and the chat timeline is unaffected because it keys rows by message id.Verification
Reproduced in the web app against a two-file fixture project: open
README.mdrendered, openCHANGELOG.md, expand its<details>, switch back toREADME.md.The
data-markdown-details-openattribute readtrueon return withmainandfalsewith this change. There is no cheap unit-level regression guard for akeyon this branch ofFilePreviewPanel(the existingChatMarkdowntests already cover in-place reconciliation with a stable key), so the proof is the browser check above.tsgo --noEmitinapps/webis clean.Found by an audit of the perf PRs merged on 2026-09-04. Created with Claude Fable 5 in Claude Code.
Note
Low Risk
Single React
keyon the file preview markdown branch; no auth, data, or API changes.Overview
Fixes stale markdown UI state when switching files in the Files panel rendered preview.
After in-place react-markdown reconciliation (#9677), an unkeyed
RenderedMarkdownSurfacekept the previous file’s widget state—open<details>, wrapped code fences, pinned table headers—when navigating to another markdown file. This PR addskey={relativePath}on that surface (aligned with other preview branches) so a path change forces a fresh mount while edits to the same file still reconcile in place.Reviewed by Cursor Bugbot for commit 476f8f1. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Reset
RenderedMarkdownSurfacewhen previewed file changes inFilePreviewPanelAdds a
keybased onrelativePathto theRenderedMarkdownSurfaceinstance so React remounts it on file switch. This prevents disclosure and wrap state from the previously displayed file from persisting into the newly selected document.Macroscope summarized 476f8f1.