Add ability to restore previous layout.

This commit is contained in:
Greyson Parrelli
2026-08-27 10:55:04 -04:00
parent 5cb9bb117a
commit 0bf85b3b2a
6 changed files with 1029 additions and 33 deletions
+80
View File
@@ -441,6 +441,81 @@ GPU. Here, each frame walks the visible rows, groups cells into runs of
identical style, and hands each run to Pango. That is far more work per frame
in principle, but a terminal grid is small.
## Restoring a session
Quitting in the middle of something should not cost you the arrangement you were
in the middle of. Every exit writes a **session snapshot** — a photograph of the
window as it stood — and the next launch that finds one puts a banner at the foot
of the sidebar: *Restore session*, and how many tabs are in it. Click it and
those tabs come back; press the ✕ and it goes away. Doing nothing is the same as
dismissing it.
This is the counterpart to the [startup list](#startup-tabs), not a replacement
for it, and the difference is the whole point. A startup entry is a **recipe** you
maintain: open this layout against these values, written down once because you
want it every morning. A snapshot is a **photograph** nobody asked for, of
wherever the tabs had actually got to. A recipe cannot describe a shell that has
been `cd`-ed three directories deep, and a photograph is not something you would
ever sit down and edit — so the app keeps both.
What is in the photograph is each tab's split tree: the shape, the divider
ratios, every terminal's working directory, every web pane's URL, the repository
a review was bound to, and the name and emoji the row was wearing. What is
deliberately not in it is anything that was *running* — scrollback, shell
history, a half-typed command, the processes themselves. Restoring puts the
arrangement back and leaves the prompts empty. That is the honest version of the
feature: a snapshot that claimed to bring a build back would be lying about the
first thing you would check.
Restoring **replaces** the tabs the window opened for itself, rather than adding
to them — a window holding both your startup tabs and the session they were
standing in for is two of everything. That is also why the offer only stands
until the tab set changes: open or close a tab yourself and the banner goes,
because "the tabs the window opened" has stopped being a set anyone can point at,
and closing it would be closing your work. The banner is the launch-time gesture
it looks like.
Closing every tab by hand takes the snapshot with it. That route out is a
deliberate one — there is nothing left worth offering to put back, and a banner
at the next launch offering the session before that would be answering a question
nobody asked.
The file is `session.json`, and it lives in the **state** directory rather than
beside `layouts.json` and `settings.json`:
```
~/.local/state/playpen/session.json ($XDG_STATE_HOME/playpen/session.json)
```
Those two are files a person writes; this one is written behind your back on
every exit, and putting that much churn in a config directory — which plenty of
people keep in version control — would make every quit look like an edit. Its
per-tab `root` is the same split-tree grammar a saved layout's is, so the two can
never drift into dialects of one shape, with the difference that a snapshot is
taken *after* parameter substitution: every path in it is literal, because it is
the directory a shell was really sitting in.
```json
{
"version": 1,
"tabs": [
{ "name": "signal", "emoji": "🚀", "layout": "Project",
"root": {
"split": "horizontal", "ratio": 0.35,
"first": { "kind": "terminal", "cwd": "/home/you/src/signal" },
"second": { "kind": "review", "cwd": "/home/you/src/signal" }
} },
{ "name": "scratch",
"root": { "kind": "terminal", "cwd": "/home/you" } }
]
}
```
The `layout` a tab came from rides along even though it is not what reopens it.
It is there so that a restored window can still be captured by *Use current
tabs* — without it, restoring a session and then asking to keep those tabs at
launch would quietly write down a window of plain shells.
## Quitting
Closing the window asks first, and does so by default. A window here is not one
@@ -509,6 +584,11 @@ to closing the moment the window manager says so:
- **A startup list**: the tabs to open at launch, each a layout with its
parameters filled in, fillable from the tabs you have open now. See
[Startup tabs](#startup-tabs)
- **A session to pick up where you left off**: every exit photographs the
window, and the next launch offers it back from a banner at the foot of the
sidebar — click to reopen those tabs, or dismiss it and carry on. Unlike the
startup list this is the arrangement you actually had, directories and all.
See [Restoring a session](#restoring-a-session)
- **Pane status in the tab strip**, driven by OSC 9;4, so a tab can say whether
it is working, waiting on you, or finished and still unanswered. See
[Agent status](#agent-status)