fix(title bar): Auto-Reload and Mod+L agree in every mode (#692) - #696
Merged
Conversation
added 2 commits
August 22, 2026 01:34
Editing a file in Markpad while another program writes it is the case auto-reload exists for, and it was the one mode with no way to turn auto-reload on. The button was gated on `!isEditing`; its chord, Mod+L, is an `editorAction` in shortcuts.ts, so Monaco owns it and it fires in edit mode ONLY. Two places answered "where is Auto-Reload available?" and gave exactly complementary answers, so pressing Ctrl+L in the editor flipped the state with nothing on screen to show it had. The reload path itself already handled this: `loadMarkdown` skips the open-mode reset for a tab that already holds the file, so a reload keeps the editor, and a dirty buffer still goes to the conflict bar rather than being overwritten. Split view keeps its exclusion — its preview renders the buffer, not the file, which is why entering it turns live mode off. `visibleActionIds` moves out of TitleBar.svelte into titlebarToolbar.ts, whose `getConfiguredTitlebarToolbarIds` already consumed its output. The condition was wrong for as long as nothing could call it.
Extends the previous commit: the button now appears wherever there is a file on disk, and Mod+L reaches it from the preview too. Mod+L was only `editorAction: true`, so Monaco owned it and it did nothing in the preview — the exact inverse of where the button was drawn, while the shortcut panel advertised it in all three modes regardless. It gets the `documentCommands` half as well, the same pair Mod+E already carries. The Monaco action stays: Monaco's own Ctrl+L is `expandLineSelection`, so dropping it would make the chord select a line rather than do nothing. Split view stops killing live mode on the way in. That line arrived with the original split-view commit carrying no comment, and it reads as a consequence of the button being hidden there — nothing left to turn the state off with — rather than a decision that a split pane should not follow its file. A split pane is an editor: it has the same exposure to another program writing the file, and the reload path already keeps the split and still routes a dirty buffer to the conflict bar. The setting was also silently dropped and stayed dropped after leaving split.
PathGao
force-pushed
the
fix/auto-reload-in-edit-mode
branch
from
August 21, 2026 17:35
e788c48 to
d7c9557
Compare
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Closes #692, reported by @17Archangel: editing a
.mdfile in Markpad and in VS Code at the same time, saving in VS Code, and Markpad not picking the change up until the mode was toggled.The feature they wanted already exists — Auto-Reload. It had two surfaces that disagreed about which modes it existed in, and their answers were exact complements:
Mod+LWhether a user got the feature depended on which surface they happened to find first. The reporter was editing, so they found neither the button (hidden) nor a reason to try the chord.
Two failures fall out of that table beyond the report. Pressing Ctrl+L in the editor did work — it armed the watcher and started reloading the document — with nothing on screen to show the state had changed. And the shortcut panel prints
Mod+Lunconditionally (shortcutSectionsfilters by group only), so in the preview it advertised a chord that was bound to nothing.After this PR every cell in that table is yes.
Mechanism
The button.
!isEditing && !isSplitinTitleBar.sveltebecomes "there is a file on disk", which is the condition that actually matters: an external writer can surprise all three modes equally, and what varies between them is only which pane is on screen.The chord.
view-toggle-livecarriededitorAction: trueand nothing else, so it existed only as a Monaco action and only where Monaco does. It getsdocumentCommands: ['toggle-live-mode']as well — the same pairview-toggle-edit(Mod+E) already carries — plus the branch inviewerKeymap.tsand the case inrunViewerCommand. The Monaco action stays rather than being replaced: Monaco binds Ctrl+L toexpandLineSelectionby default, so dropping the action would make the chord select a line instead of doing nothing.Split view stops killing live mode on the way in. That was one uncommented line in
toggleSplitView, andgit blameputs it in the original split-view commit. It reads as a consequence of the button being hidden in split — nothing left to turn the state off with — rather than a decision that a split pane should not follow its file. A split pane is an editor: same exposure to another program writing the file, andloadMarkdownalready keeps the split through a reload and still routes a dirty buffer to the conflict bar. It also meant enabling Auto-Reload in the preview, entering split and leaving again silently lost the setting.Nothing downstream of the toggle needed a change. The watcher effect is
if (liveMode && currentFile), never gated on the mode;resolveExternalChangealready sends a dirty tab to the conflict bar instead of overwriting it; andloadMarkdownskips the open-mode reset for a tab that already holds the file (!existing), so a reload keeps whichever pane you were in and an editable pane takes theread_file_content_checkedbranch rather than the 5MB preview slice.Scope
visibleActionIdsmoves out ofTitleBar.svelteintotitlebarToolbar.tsasvisibleTitlebarActionIds(context). That module already owns which toolbar ids exist and already consumed this function's output throughgetConfiguredTitlebarToolbarIds, so this is the first half of a pipeline moving next to the second, not a new layer. It is also why the bug lasted: the condition sat in a$derived.byinside a component where no test could reach it. The moved body is otherwise the same statements in the same order, and the tests below pin every branch of it rather than only the one that changed.Considered and not done: leaving split unsupported and drawing the button disabled there instead. That keeps a rule to explain, needs a new tooltip string in 26 locales, and the rule itself did not survive being looked at — see the blame above.
Not changed:
liveModestill defaults to off. Turning it on by default puts Markpad's own auto-save and an external writer on the same file, and whether the self-write grace window absorbs that needs measuring rather than guessing.Tests
scripts/titlebarToolbar.test.ts, six cases over the extracted function: Auto-Reload offered in all three modes, absent with no file on disk, Find / editor toolbar / Sync Scroll / Edit each still answering to the mode they belong to, a non-Markdown file getting none of the Markdown actions, the home screen, and Reset Zoom.scripts/viewerKeymap.spec.ts, one case:Mod+Lresolves totoggle-live-modefrom the preview, from the editor with focus in it, and from split — andMod+Shift+L,Mod+Alt+Land a bareLresolve to nothing. The file's existing sweep already holds that every command the dispatcher can name has a case inrunViewerCommand.scripts/externalChangeReload.spec.ts, two cases:loadMarkdown(path)— the exact call thefile-changedlistener makes — against a clean tab withisEditingtrue leaves the tab in the editor with the disk content and not dirty; andtoggleSplitViewno longer namestoggleLiveMode.Each of the three was checked by breaking what it guards: restoring
!isEditingon the button, dropping the!existingguard indocumentSession, and putting the split kill back all turn the matching test red.Verification
npm run test:vitestalso reports 14 failures in this environment (session-restore and window-tag snapshots, allassert.deepEqualreference-identity complaints under node 26 + jsdom). Byte-identical on a cleanorigin/mastercheckout in the same environment, verified by stashing and re-running, so this PR neither causes nor fixes them.Not verified: the end-to-end gesture on Windows, where the report came from — button pressed in edit mode, file written by another editor, document refreshing in place. The wiring it depends on is platform-independent and the
notifywatcher was already carrying it in preview mode.