Add custom layout creation.
This commit is contained in:
@@ -112,6 +112,9 @@ Pane.zig content plus its header, drag source, and drop target
|
||||
Terminal.zig GtkDrawingArea: Cairo/Pango renderer + input handling
|
||||
Browser.zig WebKitWebView plus a back/forward/reload/address bar
|
||||
webkit.zig hand-written bindings for the WebKitGTK calls we make
|
||||
Layouts.zig saved tab templates: model, JSON on disk, {{substitution}}
|
||||
OpenLayoutDialog.zig prompts for a layout's parameters
|
||||
SaveLayoutDialog.zig turns the current tab into a saved layout
|
||||
Session.zig libghostty-vt Terminal + parser, fed by the PTY
|
||||
Pty.zig openpt/fork/exec, controlling terminal setup
|
||||
key.zig GDK keyval -> libghostty-vt key mapping
|
||||
@@ -154,6 +157,88 @@ preview. Cancelling a drag puts the pane back: only the dragged pane ever
|
||||
moves, so the rest of the tree is unchanged and re-inserting it beside its
|
||||
original sibling restores the original shape.
|
||||
|
||||
## Layouts
|
||||
|
||||
A **layout** is a saved tab: an arrangement of panes, each with a directory and
|
||||
a script, that opens in one go. Layouts take **parameters**, so one layout
|
||||
serves any number of projects.
|
||||
|
||||
The layout button in the sidebar lists them. Picking one asks for its
|
||||
parameters — prefilled with their defaults — and opens a new tab. A layout with
|
||||
no parameters skips the prompt. The plain new-tab button is untouched: it still
|
||||
opens one shell, immediately.
|
||||
|
||||
**You author layouts by arranging a tab.** Split it, drag panes around, drag the
|
||||
dividers, then *Save tab as layout…*. That captures the tree, the split
|
||||
orientations and the ratios exactly as they are on screen, and asks only for
|
||||
what it can't infer: a name, the parameters, and each pane's script. There is
|
||||
deliberately no separate layout builder — the split tree already is one.
|
||||
|
||||
The save dialog prefills what it can read off the live tab: each terminal's
|
||||
current directory, straight out of `/proc/<pid>/cwd`, and each web pane's
|
||||
current page. So the usual flow is to get a tab set up the way you like,
|
||||
save it, and replace the literal paths with `{{parameters}}`.
|
||||
|
||||
**Editing** a saved layout opens the same dialog on the stored one, so its
|
||||
name, parameters and per-pane scripts can be changed without opening it.
|
||||
Renaming moves the layout rather than copying it, and renaming onto a name
|
||||
another layout already has is refused instead of quietly replacing it. Nothing
|
||||
is written until you confirm, so cancelling leaves the layout untouched. To
|
||||
change the *shape* of a layout, open it, rearrange the tab, and save over it
|
||||
under the same name — the same tools you used to build it in the first place.
|
||||
|
||||
Every `cwd`, `command` and `url` goes through `{{name}}` substitution, and a
|
||||
leading `~` is expanded afterwards. An unknown `{{name}}` is left as written
|
||||
rather than blanked, so a typo shows up in the pane instead of silently
|
||||
producing an empty path.
|
||||
|
||||
**Scripts are typed into the shell, not run instead of it.** A pane starts your
|
||||
login shell as usual, and the script is fed to it once it is ready. The shell
|
||||
is still there when the script finishes, with your environment loaded and the
|
||||
command in history. The wait matters: writing at spawn time loses the input,
|
||||
because shells that set up line editing discard whatever was buffered while
|
||||
they were initializing. The shell's first output is the signal that it is
|
||||
reading, so that is when the script goes in.
|
||||
|
||||
Layouts live in `~/.config/vtabs/layouts.json` (or `$XDG_CONFIG_HOME`), and the
|
||||
file is meant to be edited by hand as well — *Reload from disk* picks up
|
||||
changes. It is JSON because the app writes it too, and a format that
|
||||
round-trips without a hand-written emitter is worth more here than a prettier
|
||||
one.
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"layouts": [
|
||||
{
|
||||
"name": "Project",
|
||||
"parameters": [
|
||||
{ "name": "path", "description": "Project directory", "default": "~" }
|
||||
],
|
||||
"root": {
|
||||
"split": "horizontal",
|
||||
"ratio": 0.55,
|
||||
"first": {
|
||||
"kind": "terminal",
|
||||
"cwd": "{{path}}",
|
||||
"command": "git status"
|
||||
},
|
||||
"second": {
|
||||
"split": "vertical",
|
||||
"ratio": 0.5,
|
||||
"first": { "kind": "terminal", "cwd": "{{path}}", "command": "nvim ." },
|
||||
"second": { "kind": "web", "url": "https://github.com" }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
A node is a split if it has a `split` key and a leaf otherwise. Saves are
|
||||
atomic — written to a temporary and renamed — so an interrupted write leaves
|
||||
the previous layouts intact rather than a file that won't parse.
|
||||
|
||||
Two design choices worth calling out:
|
||||
|
||||
**No IO thread.** The PTY is read on the GLib main loop through a unix fd
|
||||
@@ -187,6 +272,9 @@ in principle, but a terminal grid is small.
|
||||
dragging around exactly like they do: back/forward/reload, an address bar
|
||||
that takes a URL or falls back to a search, a load-progress indicator in the
|
||||
entry, and the page title feeding the pane header and tab label
|
||||
- **Saved layouts**: whole tabs — panes, splits, ratios, per-pane directories
|
||||
and scripts — opened in one go, parameterised by `{{name}}`, authored by
|
||||
arranging a tab and saving it. See [Layouts](#layouts)
|
||||
|
||||
### Shortcuts
|
||||
|
||||
@@ -225,8 +313,9 @@ This is a proof of concept, and the following are deliberately absent:
|
||||
nothing else: no bookmarks, history, downloads, devtools, or find-in-page,
|
||||
and each pane uses WebKit's default context, so nothing is persisted between
|
||||
runs. Links that ask for a new window are ignored rather than opening a pane.
|
||||
- **Saved layouts.** A view's arrangement lives only as long as the tab.
|
||||
- **Kitty graphics, hyperlinks, tab reordering, config file.**
|
||||
- **Restoring a session.** Layouts save arrangements you open deliberately;
|
||||
nothing restores the tabs you happened to have open when the app closed.
|
||||
- **Kitty graphics, hyperlinks, tab reordering.**
|
||||
- **Custom terminfo.** `TERM` is reported as `xterm-256color` rather than
|
||||
`ghostty`, since we don't install a terminfo entry.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user