Add notifications.
This commit is contained in:
@@ -116,7 +116,7 @@ just the VT core.
|
||||
|
||||
```
|
||||
main.zig AdwApplication startup
|
||||
appearance.zig the colour scheme: preference -> libadwaita, CSS, palette
|
||||
appearance.zig the color scheme: preference -> libadwaita, CSS, palette
|
||||
Window.zig sidebar + GtkStack of views, tab management, reordering, shortcuts
|
||||
View.zig one tab's content: its panes, their layout, and drag handling
|
||||
Layout.zig the split tree: nodes, rearranging, GtkPaned materialization
|
||||
@@ -141,11 +141,12 @@ Session.zig libghostty-vt Terminal + parser, fed by the PTY
|
||||
Pty.zig openpt/fork/exec, controlling terminal setup
|
||||
script.zig runs a command for `$(...)` in a layout's directory
|
||||
key.zig GDK keyval -> libghostty-vt key mapping
|
||||
palette.zig every colour by name, its default per scheme, and the CSS for it
|
||||
notify.zig desktop notifications on a finish, and the mutes that stop them
|
||||
palette.zig every color by name, its default per scheme, and the CSS for it
|
||||
theme.zig the palette resolved for the terminal renderer, which reads numbers
|
||||
Settings.zig preferences: theme, palette overrides, startup tabs, JSON on disk
|
||||
SettingsDialog.zig the settings page, including the startup list editor
|
||||
PaletteEditor.zig the colour editor inside it: a swatch per palette entry
|
||||
PaletteEditor.zig the color editor inside it: a swatch per palette entry
|
||||
TabSettingsDialog.zig one tab's own settings, and the emoji picker
|
||||
emoji.zig generated: every emoji the picker offers, and the search over them
|
||||
```
|
||||
@@ -182,7 +183,7 @@ Running terminals keep their scrollback and processes straight through a move.
|
||||
**Drags rearrange live.** Each time the drop target changes, the move is
|
||||
applied for real, so the layout under the cursor is always the layout you will
|
||||
get — there is no separate drop indicator because the view itself is the
|
||||
preview. Cancelling a drag puts the pane back: only the dragged pane ever
|
||||
preview. Canceling 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.
|
||||
|
||||
@@ -225,7 +226,7 @@ save it, and replace the literal paths with `{{parameters}}`.
|
||||
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
|
||||
is written until you confirm, so canceling 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.
|
||||
|
||||
@@ -269,7 +270,7 @@ means `string`:
|
||||
{ "name": "path", "description": "Project directory", "type": "directory" }
|
||||
```
|
||||
|
||||
A type this build doesn't recognise is asked for in a text box rather than
|
||||
A type this build doesn't recognize is asked for in a text box rather than
|
||||
refusing the file — unlike a pane `kind`, which is a pane it cannot build. The
|
||||
startup list asks for its values the same way, so a directory parameter gets the
|
||||
same field there.
|
||||
@@ -451,7 +452,7 @@ to notice you are about to close the wrong window.
|
||||
|
||||
**Keep Working** is the default response, and Escape lands on it too: the guard
|
||||
should not itself be a keystroke to fumble. Pressing the compositor's close
|
||||
binding again while the question is up dismisses it, which counts as cancelling
|
||||
binding again while the question is up dismisses it, which counts as canceling
|
||||
— leaning on the key never costs the window.
|
||||
|
||||
Two closes don't ask, because both are already an answer: accepting the dialog,
|
||||
@@ -511,6 +512,11 @@ to closing the moment the window manager says so:
|
||||
- **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)
|
||||
- **A desktop notification when a pane finishes**, for the tabs you are not
|
||||
looking at — on by default, switchable from **Settings → Notifications**, and
|
||||
mutable one tab at a time from its right-click menu, for fifteen minutes, an
|
||||
hour, eight hours, or until you say otherwise. See
|
||||
[Notifications](#notifications)
|
||||
- **Renaming a tab**: `Ctrl+Shift+R`, double-click a tab row, or **Rename** in
|
||||
its right-click menu. A typed name pins the label; clearing it hands the
|
||||
label back to the panes
|
||||
@@ -531,11 +537,11 @@ to closing the moment the window manager says so:
|
||||
- **Light and dark schemes**, following the desktop by default and pinnable
|
||||
from the settings page, applying to open tabs immediately — terminal palette
|
||||
included. See [Theme](#theme)
|
||||
- **Every colour in the palette editable**, per scheme, from **Settings →
|
||||
Appearance → Customise**: the surfaces, the text on them, the accents, the
|
||||
status colours, and the terminal's own background, cursor, selection and 16
|
||||
ANSI colours. Each change repaints as you make it. See
|
||||
[Customising the palette](#customising-the-palette)
|
||||
- **Every color in the palette editable**, per scheme, from **Settings →
|
||||
Appearance → Customize**: the surfaces, the text on them, the accents, the
|
||||
status colors, and the terminal's own background, cursor, selection and 16
|
||||
ANSI colors. Each change repaints as you make it. See
|
||||
[Customizing the palette](#customizing-the-palette)
|
||||
|
||||
### Shortcuts
|
||||
|
||||
@@ -725,7 +731,7 @@ table, and add to its `SYNONYMS` when a glyph should answer to a word the data
|
||||
files don't know. Two things it leaves out. Skin-tone variants, because just over
|
||||
half of Unicode's 3944 sequences are the same gesture in five tones and a grid of
|
||||
them is longer to look through rather than more complete — every base glyph is
|
||||
there. And anything newer than emoji 16.0, because colour fonts trail Unicode by
|
||||
there. And anything newer than emoji 16.0, because color fonts trail Unicode by
|
||||
a year or two and a glyph the font has never heard of draws as a hex-digit box,
|
||||
which in a picker reads as a bug; `--max-version` raises the cutoff once fonts
|
||||
have caught up.
|
||||
@@ -923,11 +929,11 @@ So a tab can carry a dot:
|
||||
| red | stopped on an error |
|
||||
| none | idle |
|
||||
|
||||
Each colour is carried at three sizes, so it survives being glanced at rather
|
||||
Each color is carried at three sizes, so it survives being glanced at rather
|
||||
than read: an 11px dot on the row, a bar down the row's leading edge, and a wash
|
||||
behind the whole row. Working gets the bar without the wash — it is the resting
|
||||
state of a busy afternoon, and tinting half the sidebar all day would only teach
|
||||
you to stop looking. Inside a tab, a pane that is asking colours its own frame
|
||||
you to stop looking. Inside a tab, a pane that is asking colors its own frame
|
||||
and header the same way, which is what picks it out of a four-way split.
|
||||
|
||||
The green one is the point of the feature. A session that has gone back to idle
|
||||
@@ -959,6 +965,48 @@ so once the row has brought you to the tab, they say which pane inside it was
|
||||
the one asking; the row stays lit until every finished pane in it has been
|
||||
answered.
|
||||
|
||||
### Notifications
|
||||
|
||||
The dots answer "which of these needs me?" for a window that is in front of you.
|
||||
When it isn't — behind an editor, on another workspace, or on the other monitor —
|
||||
a pane finishing also posts a **desktop notification**, titled with the tab's
|
||||
name and carrying that pane's title as its body. That title is the useful half:
|
||||
the hook sets it from your prompt, so what the popup says is the task that just
|
||||
finished rather than "playpen".
|
||||
|
||||
It is on by default and **Settings → Notifications** switches it off, which
|
||||
leaves the sidebar dots as the only word you get:
|
||||
|
||||
```json
|
||||
{
|
||||
"notifications": false
|
||||
}
|
||||
```
|
||||
|
||||
Nothing is posted for a tab you were already looking at — the window has the
|
||||
focus and that tab is the one on screen — because the dot has already said it
|
||||
and a popup about the pane in front of you is the fastest way to make someone
|
||||
switch the whole feature off. Coming back to a tab also takes down whatever
|
||||
popup it left in the tray, the same visit that clears its row.
|
||||
|
||||
**One tab at a time can be muted** from its right-click menu, for 15 minutes, an
|
||||
hour, 8 hours, or until you unmute it. That is the answer for the one session
|
||||
that finishes every ninety seconds while the other three are worth hearing
|
||||
about; reopening the menu says how long the mute has left and offers the way out
|
||||
of it. A mute is session state and is not written to the settings file — a tab
|
||||
does not outlive the app, and neither should a decision about the next hour.
|
||||
Neither mute touches the dots. Muting is about being interrupted; the row is
|
||||
still how you find out what happened while you were away.
|
||||
|
||||
The notification is sent as a `GNotification`, so GIO picks whichever backend
|
||||
the session has and the popup gets Playpen's name and icon from the installed
|
||||
desktop entry — which means it looks right once `mise run install` has put that
|
||||
entry in place. Nothing is attached to a click: the app is deliberately
|
||||
non-unique, so no process owns `dev.greyson.playpen` on the session bus and a
|
||||
daemon calling an action back would find nobody home. Clicking closes the popup;
|
||||
getting back to the window is the compositor's job, and it already has a binding
|
||||
for that.
|
||||
|
||||
### How it gets there
|
||||
|
||||
Programs report state with **OSC 9;4** — the ConEmu progress protocol, the same
|
||||
@@ -1049,8 +1097,8 @@ light and a dark scheme. `Ctrl+,` or the gear at the foot of the sidebar opens
|
||||
**Settings**, whose Appearance section holds the choice: light, dark, or
|
||||
**system**, which is the default and follows the desktop.
|
||||
|
||||
Every colour it resolves to can be changed — see
|
||||
[Customising the palette](#customising-the-palette).
|
||||
Every color it resolves to can be changed — see
|
||||
[Customizing the palette](#customizing-the-palette).
|
||||
|
||||
The preference lives in `~/.config/playpen/settings.json` (or
|
||||
`$XDG_CONFIG_HOME`), beside `layouts.json`, alongside the
|
||||
@@ -1061,6 +1109,7 @@ The preference lives in `~/.config/playpen/settings.json` (or
|
||||
"version": 1,
|
||||
"theme": "system",
|
||||
"confirm_quit": true,
|
||||
"notifications": true,
|
||||
"colors": {},
|
||||
"startup": []
|
||||
}
|
||||
@@ -1069,20 +1118,20 @@ The preference lives in `~/.config/playpen/settings.json` (or
|
||||
Switching applies immediately to every open tab — nothing needs restarting, and
|
||||
a shell that has been running all day repaints along with everything else.
|
||||
|
||||
Three separate colour systems have to agree for that to be true, which is what
|
||||
Three separate color systems have to agree for that to be true, which is what
|
||||
`appearance.zig` exists to arrange:
|
||||
|
||||
- **libadwaita's style manager** colours the stock widgets — popovers, entries,
|
||||
- **libadwaita's style manager** colors the stock widgets — popovers, entries,
|
||||
dialog chrome. It is told to force a scheme, or left on `default` to follow
|
||||
the desktop.
|
||||
- **`style.css`** colours everything the app draws itself. It is written
|
||||
entirely against named colours; `palette.zig` writes the `@define-color`
|
||||
- **`style.css`** colors everything the app draws itself. It is written
|
||||
entirely against named colors; `palette.zig` writes the `@define-color`
|
||||
block that defines them for the scheme in force, and the two are loaded as a
|
||||
single `GtkCssProvider`. No rule in `style.css` may hardcode a colour — a
|
||||
single `GtkCssProvider`. No rule in `style.css` may hardcode a color — a
|
||||
literal hex is a rule that looks right in whichever scheme you happened to be
|
||||
testing in, and one the colour editor cannot reach.
|
||||
testing in, and one the color editor cannot reach.
|
||||
- **`theme.zig`** holds what Cairo draws the terminal grid from: the default
|
||||
background, foreground and cursor, plus the 16 ANSI colours. The style tree
|
||||
background, foreground and cursor, plus the 16 ANSI colors. The style tree
|
||||
is never consulted there, so a CSS reload alone would leave every terminal
|
||||
painted in the scheme it started in. It resolves the same table `style.css`
|
||||
is fed from, and caches the result — the renderer asks for the background
|
||||
@@ -1092,7 +1141,7 @@ Three separate colour systems have to agree for that to be true, which is what
|
||||
The ANSI palette is the part that is easy to skip and shouldn't be. The
|
||||
standard xterm yellow is `#cdcd00`, which on a white background is close to
|
||||
invisible — and prompts and build tools use it constantly, so a light scheme
|
||||
without a light palette is a light scheme you can't read. The 240 colours above
|
||||
without a light palette is a light scheme you can't read. The 240 colors above
|
||||
index 16 are fixed by spec and left alone; only the 16 named ones change.
|
||||
|
||||
They are swapped through libghostty-vt's `DynamicPalette.changeDefault`, which
|
||||
@@ -1106,20 +1155,20 @@ its `dark` property rather than the stored preference, and the preference only
|
||||
decides what the style manager is told. A desktop that switches at sunset takes
|
||||
this app with it, with no extra machinery and no second source of truth.
|
||||
|
||||
### Customising the palette
|
||||
### Customizing the palette
|
||||
|
||||
**Settings → Appearance → Customise** opens the palette itself: a swatch for
|
||||
every colour named above, in sections — surfaces, text, tab rows, accent,
|
||||
**Settings → Appearance → Customize** opens the palette itself: a swatch for
|
||||
every color named above, in sections — surfaces, text, tab rows, accent,
|
||||
status, then the terminal's own background, foreground, cursor and selection,
|
||||
and the 16 ANSI colours as a strip. Beside each row is the hex it currently
|
||||
and the 16 ANSI colors as a strip. Beside each row is the hex it currently
|
||||
resolves to, so a palette can be read off as text rather than only picked at,
|
||||
and a button that puts that one colour back. Each section heading has the same
|
||||
and a button that puts that one color back. Each section heading has the same
|
||||
button for the section, and the foot of the list has one for the whole scheme.
|
||||
|
||||
Three things about how it behaves, all following from the same decision — that
|
||||
the editor edits **the palette you can see**:
|
||||
|
||||
- **A change applies as you make it.** Every colour in the list paints something
|
||||
- **A change applies as you make it.** Every color in the list paints something
|
||||
behind the dialog, so the swatch is its own preview; there is no OK button
|
||||
because there is nothing pending to confirm.
|
||||
- **It edits the scheme in force.** Which is why the way to edit the other one is
|
||||
@@ -1128,9 +1177,9 @@ the editor edits **the palette you can see**:
|
||||
you the dark palette you built.
|
||||
- **The terminal follows the window.** Left alone, the terminal's background *is*
|
||||
the pane surface, its foreground *is* the body text, and its selection *is* the
|
||||
muted accent — so recolouring the app recolours the terminal drawn inside it,
|
||||
muted accent — so recoloring the app recolors the terminal drawn inside it,
|
||||
and the frame around a terminal keeps matching its contents. Setting one of
|
||||
those explicitly breaks the link for that colour and only that colour.
|
||||
those explicitly breaks the link for that color and only that color.
|
||||
|
||||
Only what has been changed is stored, per scheme, under `colors` in
|
||||
`settings.json`:
|
||||
@@ -1150,12 +1199,12 @@ short and hand-editable, it is what lets a row know whether it has been changed,
|
||||
and it means the shipped defaults can be retuned in a later version and still
|
||||
reach everyone who never touched them — rather than only the people who had
|
||||
never opened the editor. A name this build doesn't know, or a value that isn't
|
||||
`#rrggbb`, costs that one colour and is logged; the rest of the file is read
|
||||
`#rrggbb`, costs that one color and is logged; the rest of the file is read
|
||||
normally.
|
||||
|
||||
`palette.zig` is where the names, the defaults and the grouping live, which is
|
||||
why there is no longer a `palette-dark.css`. An editor needs to know what the
|
||||
colours are called, what they started as, and which ones belong together, and
|
||||
colors are called, what they started as, and which ones belong together, and
|
||||
none of that can be read back out of a stylesheet without parsing it — so the
|
||||
table answers all of it and the CSS is generated from the table.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user