Repository navigation
Compare two or three files side by side - #494
Open
cerede2000 wants to merge 2 commits into
Open
cerede2000 wants to merge 2 commits into
cerede2000 wants to merge 2 commits into
Conversation
added 2 commits
October 3, 2026 12:15
Off by default, and turned on in Settings → Preferences → Browse in tabs. A tab is an address rather than a folder, so the strip holds anything the application can be at: a folder, a document, the text editor, a terminal, the trash, the settings. Each tab keeps what it was on — its listing, what was selected in it, the rename it was in the middle of, the place it was scrolled to. The clipboard is deliberately not per tab: copying in one and pasting in another is the point of having two. One rule opens anything behind: the platform modifier held with the gesture that opens the thing — the double click on a row, the single click on a favourite, a volume or a place on the home page, and Enter on whatever the keyboard is on. On a row that modifier with a single click still adds to the selection, which is worth more than a tab. The middle button says the same thing without a modifier, anywhere. What a tab holds stays alive while another is in front. A document is not opened again when you come back to it; the text editor comes back where you left it without reading the file again — what you typed and had not saved, the selection, and the line you were reading, put back before the editor is shown so the file does not appear at the top and jump. A folder tab comes back the same way, and is refreshed quietly behind what you were already looking at. A tab opened behind is got ready before you reach it: the folder listed, the document opened in its viewer, the text file read, the shell started. That is Settings → Preferences → Prepare tabs opened in the background, and it is capped at three prepared editors at once. While a tab is still working it shows a spinner where its icon goes, as a browser does — for a document, until it is really in the editor rather than until the panel exists. Back and Forward belong to the tab, not to the window: each keeps its own trail, so going back in one never takes you where another was looking, and the buttons grey out when that tab has nowhere to go. The rest of what the strip does: tabs are dragged along the row, duplicated, kept (pinned to the front as their icon alone, with no cross and immune to "close them all"), closed one at a time or all at once, and closed by double click for whoever turns that on. Files and folders dragged out of a listing can be dropped onto a tab, which moves them into the folder that tab is on — or copies them with the modifier held. The strip never scrolls: tabs share the room and stop at a number an administrator sets under Settings → Tabs. A terminal is a place now. What a terminal *is* came out of the drawer that showed it, so a page at an address of its own can show one too and there can be as many shells as there are tabs; the drawer opens per tab, and a new tab only on the gesture that asks for one. Closing a tab is the one gesture that destroys what a tab was holding, so a screen with something to lose can leave a question for its own tab. Asking it meant the application had to be able to ask at all: `window.confirm` answers by stopping the world, which a dialog inside the page cannot, so the question is the application's own dialog and everything from the guard to the cross on the tab waits for the answer. The browser's boxes are gone from the rest of the window with it — twenty-three of them, `confirm`, `prompt` and `alert`, replaced by that dialog and by the notifications this application already had. The browser journey comes with it, and with the bench it needs. Most of what tabs do is what a browser does to a page that is not on screen, and that is not a question a unit test can ask: a page unmounted the moment another tab comes forward, a menu clipped away by the strip's own overflow, a shell that dies when the layout holding it does. Each of those passed its unit suite and was broken in a browser. So `frontend/e2e/app` holds one journey through the whole application — set up, sign in, upload, share, delete and restore, read a version, edit, browse in tabs — run by a second Playwright project against the real server entry point serving the real build, in a throwaway install the journey also reads from disk to check what actually landed there. It runs under `npm run test:e2e` beside the specs already here, and takes about half a minute. Two readers in one folder, and the two things they were sharing that they should not have been. Where a reader is in a folder has always been remembered against the folder. With tabs that is no longer enough, because two tabs can be in one folder at once and be in different places in it — so a tab's place is kept against the tab as well. It was, and it was then read as a number: `tabPlace(key) || get(folder)`. A tab sitting at the top answers nought, nought is false, and the fallback is the folder's memory that every tab on it shares. Two tabs on one folder, the first scrolled to the bottom and left there, the second never moved from the top: the second came back down beside the first. The top is an answer, so the question is now whether this tab has a place here at all. The permission to be put back was shared the same way. It is granted for a folder when somebody walks back up into it — and consumed by whichever tab is drawn on that folder next, which need not be the tab that was promised it. One tab walking up out of a subfolder, or coming back out of a file, moved another reader. The permission names its tab now, and the rule that grants it moved out of the router's guard into `router/restorePermission.js` so that it can be asked directly, the way the other rules there are: it had no test, and what had no test is what went wrong. The Tabs page sits with the other preferences in the settings list rather than at the end of it.
Choose two or three text files in a listing, right-click, and Compare. They open at an address of their own, so a comparison lives in a tab, can be linked to, and is somewhere you leave and come back to rather than a dialog to finish. Every line of both files is there and aligned: two versions of a line sit on one row, and a gap shows where a side has nothing. Within a changed pair the part that actually differs is marked, which is what makes one character different in a long path readable. The alignment is written here rather than taken from a library because the answer has to be aligned and not merely listed: a patch says what changed, a comparison has to put line 40 of one file beside the line it corresponds to in the other. The differences are counted and numbered, and F8 and F7 walk them — alt with the arrows does the same, for a keyboard without those keys — coming round again at the end rather than stopping. Each difference can be taken across in either direction, a whole run at once, and the file written back keeps the line endings it had: a comparison that rewrote those would turn a one-line change into a change on every line. Three files align on the middle one, which is the arrangement that answers "who changed what" when two people have both edited a common version. Copies there go between neighbours, since left and right are not next to each other. Along the right: a bar the comparison draws for itself, because this platform draws its own over the content and fades it out when nothing is moving, and beside it a map of the differences — out of the way until the pointer nears the edge, pinned from the bar for whoever wants it there. The map is coloured the way the panes are and from the same single answer about what a line is: red where a line is only on the left, green where it is only on the right, amber where both have one and they differ. Search and replace looks in one file, another, or all of them at once, steps through what it finds, and replaces one or all — never into a side that is an earlier version. A line can be edited by hand: double-click it and type, with Enter breaking the line, backspace at the start joining it to the one above, the arrows walking from line to line, and a paste of several lines becoming several lines. The two sides can be put the other way round, address and tab name with them. A long file folds down to the differences with a little around them and says how much it folded, and that is always yours to unfold. In a tab, the tab is named after the files it compares, so three comparisons open are three tabs you can tell apart — and coming back to one reads neither file again: the lines, the difference you were on and anything taken across and not yet saved are all still there. A comparison with unsaved lines says so before you close its tab or the window. Also from the Versions panel: Compare with the file now puts an earlier version beside the file as it stands, the older one on the left, which is the order that reads as "what happened since". That side is read-only — there is nothing to write a version back to. Everything the text editor can open is comparable. COMPARE_FILE_EXTENSIONS adds whatever else an installation calls text, because that is a local question: somebody's .ino, somebody else's .tf.
This was referenced Oct 3, 2026
This branch has not been deployed
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.
This sits on top of the tabbed browsing branch (#493), so its diff against
mainshrinks to its own once that one lands.
Closes #487
Two or three files compared side by side, as WinMerge does. Choose two or three text
files in a listing, right-click, and Compare. They open at an address of their
own — so a comparison lives in a tab, can be linked to, and is somewhere you leave and
come back to rather than a dialog you have to finish. Nothing about it is modal.
What a reader gets
The alignment. Every line of every file, aligned: two versions of a line on one row,
a gap where a side has nothing. Within a changed pair the part that actually differs is
marked, which is what makes one character different in a long path readable.
Walking them. The differences are counted and numbered; F8 and F7 walk them,
alt with the arrows does the same for a keyboard without those keys, and they come
round again at the end — somebody on the last difference pressing "next" means "start
again", and a button that does nothing is a button they press twice to be sure.
Taking one side over the other, in either direction, a whole run at once. What is
written back keeps the line endings it had: a comparison that rewrote those would turn a
one-line change into a change on every line of the file.
Three files align on the middle one, which is the arrangement that answers "who
changed what" when two people have both edited a common version. Copies go between
neighbours, since left and right are not next to each other.
Finding your way down a long file. A scrollbar the comparison draws for itself — the
platform's is drawn over the content and fades out when nothing is moving, measured
rather than assumed — with the thumb grabbed where it is grabbed and a track that jumps.
Beside it, a map of the whole file with the part on screen drawn over it: out of the way
until the pointer nears the right-hand edge, pinned from the bar for whoever wants it
there, and coloured red where a line is only on the left, green where it is only
on the right, amber where both have one and they differ.
Search and replace in one file, the other, or all at once; steps through what it
finds and replaces one or all — never into a side that is an earlier version.
Editing by hand, because not every fix is one side or the other. Double-click a line
and type: Enter breaks the line at the caret, which is also how a line is added;
backspace at the start joins it to the line above, which is how one is removed;
delete at the end pulls the next one up; the arrows walk from line to line; a
paste of several lines becomes several lines.
And the rest. The two sides can be put the other way round, address and tab name with
them. A long file folds down to the differences and says how much it folded, always
yours to unfold. In a tab, the tab is named after the files it compares, and coming back
reads neither file again — the lines, the difference you were on and anything taken
across and not yet saved are all still there. A comparison with unsaved lines says so
before you close its tab or the window.
A file against its own past. From the Versions panel, Compare with the file now
puts an earlier version beside the file as it stands, the older one on the left, which
is the order that reads as "what happened since". That side is read-only: there is
nothing to write a version back to.
What holds it together
The lines are the state, the alignment is derived from them. Taking a block across is
then a three-line operation — replace those lines on that side — and every row, every
difference, every count and the position in the row of them is recomputed from that.
The alignment is written rather than borrowed. A patch says what changed; a
comparison has to put line 40 of one file beside the line it corresponds to in the
other, with a gap where there is nothing to put. Myers with a trace and a backtrack,
bounded so a pair of files with nothing in common cannot take the window with them.
One answer about what a line is. The panes and the map render the same five-valued
answer. They used to decide separately, and disagreed the moment a difference contained
a line from each side — a rewritten section is exactly that, and the map painted amber
over what the panes showed green.
The screen is a page, so what it holds lives outside it. A comparison is handed to
its tab and comes back with it, because the lines taken across and not yet saved exist
nowhere else.
Configuration
Everything the text editor can open is comparable.
COMPARE_FILE_EXTENSIONSaddswhatever else an installation calls text — a local question, somebody's
.ino,somebody else's
.tf. Comparison itself needs no flag.Checked
Built and booted on
mainwithmain's lockfile, every client call matched to a route,and the suites run in that tree: 3 278 frontend, 4 048 backend, plus the browser
journeys for what only a browser can answer — the scrollbar and the map, the colours,
walking the differences, taking a side across and reading the file back off disk.