Skip to content

Browse in tabs, and let a tab keep what it was holding - #493

Open
cerede2000 wants to merge 1 commit into
nxzai:mainfrom
cerede2000:browse-in-tabs
Open

cerede2000 wants to merge 1 commit into
nxzai:mainfrom
cerede2000:browse-in-tabs

Conversation

@cerede2000

Copy link
Copy Markdown
Contributor

This sits on main as it stands, and the file comparison that follows sits on
this one.

Closes #486

Tabbed browsing, off by default and turned on under Settings → Preferences → Browse
in tabs
. With it off the application behaves exactly as it does today.

A tab is an address, not 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, where it was scrolled to. The clipboard is deliberately not per tab:
copying in one and pasting in another is the point of having two.

image image

What a reader gets

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 extends the selection, which is worth more than a
tab. The middle button says the same without a modifier, anywhere. Several entries
chosen open one tab each — the first in front, the rest behind — and stop when the row
is full.

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. It is re-checked quietly afterwards, and what somebody else wrote
takes over unless you have unsaved work of your own. A folder tab comes back the same
way and refreshes 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. Settings → Preferences →
Prepare tabs opened in the background turns it off; no more than 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. 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 strip. 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. A tab shows the icon of the
favourite it is inside. The strip never scrolls: tabs share the room and stop at a
number an administrator sets under Settings → Tabs (5, 10, 15 or 20; 10 unless
changed).

A terminal is a place. 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; a new tab only on the gesture that asks for
one.

What holds it together

A tab is an address, and the kinds are a table. One place says what addresses a tab
may hold, what each is called, what icon it wears and whether it is restored — rather
than every screen deciding for itself.

A page is unmounted the moment another tab comes forward. So anything that must
survive lives outside the page: the preview session, the editor's draft, the folder's
place. This is why coming back is not a reload, and it is the single idea most of this
batch is built on.

Closing asks, when there is something to lose. A screen leaves a question for its own
tab, and whoever closes the tab asks it. A folder leaves none — being asked about a
folder trains everybody to click through the question without reading it.

The application asks in its own voice. window.confirm answers by stopping the
world, which a dialog inside the page cannot, so the guard and everything from it to the
cross on the tab waits for the answer. The browser's boxes go with it: twenty-three
across the window (confirm, prompt, alert) replaced by that dialog and by the
notifications this application already had. What those drew was the browser's box, in
the browser's chrome, headed by the server's address and port, over a question about
somebody's file.

Settings

Where What Default
Preferences → Browse in tabs the whole mode off
Preferences → Close a tab by double-clicking it that gesture off
Preferences → Prepare tabs opened in the background getting a tab ready on
Settings → Tabs (administrator) how many tabs a row may hold 10

Checked

Built and booted on main with main's lockfile, every client call matched to a route,
and the suites run in that tree: 3 097 frontend, 4 048 backend, plus the browser
journeys that only a browser can answer — a tab opened behind being ready on arrival,
the spinner, the drop onto a tab, the per-tab trail.

Two readers in one folder, and the two things they were sharing

Pushed since this was opened, because both are faults this branch introduces and
neither needs a second pane to show itself.

Where a reader is in a folder has always been remembered against the folder. Two
tabs can now 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 — 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: the second came back down beside the first. The top is an answer, so the
question asked now is 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 was consumed by whichever tab was drawn on that
folder next — which need not be the tab it was promised to. One tab walking up out of a
subfolder, or coming back out of a file, moved another reader. It 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.

Also here: the Tabs page sits with the other preferences in the settings list rather
than at the end of it.

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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Browse in tabs: a tab per address, with what it was holding kept alive

1 participant