4a579bdbc6
CI / test (pull_request) Successful in 4s
Every colour was a 256-colour ANSI index hardcoded in styles.go, so changing the palette meant editing the styles themselves. This puts a semantic token set between the two: styles name roles, a theme supplies the colours. internal/theme holds the twelve tokens, the two built-ins (gruvbox-dark, the new default, and gruvbox-light) and the loader for user themes in ~/.config/terdut-tui/themes/. A user file may 'extends:' a built-in and override only what it cares about, and may shadow a built-in name to tweak it in place. Unknown keys, malformed colours and incomplete themes are refused with a message naming what went wrong. Colours are truecolor hex now: lipgloss downsamples for 256- and 16-colour terminals and honours NO_COLOR, so themes carry no fallbacks of their own. An ANSI index is still accepted for anyone who would rather follow their terminal's own palette. The 21 package-level style vars become a Styles struct on the Model, which is what rule 3 asked for all along; the four free functions in view.go take one as their first argument. The embedded bubbles components are restyled from the same tokens — otherwise a theme would leave a pink selected row and grey help text behind. Note that the table's Cell style deliberately keeps no foreground: bubbles renders cells before wrapping the row in Selected, so a colour there cuts the selection highlight short.
196 lines
6.8 KiB
Markdown
196 lines
6.8 KiB
Markdown
# terdut-tui
|
||
|
||
A terminal user interface for [terdut-server](https://git.ryuvia.com/niklas/terdut-server). Communicates with the server over its REST API.
|
||
|
||
Written in Go using [Bubbletea](https://github.com/charmbracelet/bubbletea).
|
||
|
||
## Features
|
||
|
||
- **Incident queue** — open incidents with severity, status, assignee and age, auto-refreshing
|
||
- **Incident actions** — acknowledge, assign, snooze, note, resolve and archive
|
||
- **Timeline** — the full history of an incident, system events, pages and notes together
|
||
- **Alert feed** — the raw read-only alerts underneath, each linked to its incident
|
||
- **On-call schedule** — visual calendar of who is on duty, assign and remove entries
|
||
- **Statistics** — MTTA and MTTR, plus alert frequency by name, hour and day
|
||
- **User management** — add and remove users, manage API keys, set each user's ntfy topic
|
||
|
||
> Requires terdut-server **v0.4.0 or later**. Earlier servers have no incidents API;
|
||
> use terdut-tui v0.3.x with those.
|
||
|
||
## Alerts and incidents
|
||
|
||
The server keeps two objects and this client follows that split:
|
||
|
||
- An **alert** is Alertmanager's record — firing or resolved, and read-only here.
|
||
- An **incident** is the work item. It is what you acknowledge, assign, snooze,
|
||
discuss and resolve, and it is where all the actions live.
|
||
|
||
Incidents are correlated by the `groupKey` Alertmanager already computed from your
|
||
`group_by` configuration, so several alerts commonly share one incident.
|
||
|
||
Two behaviours worth knowing before you press a key:
|
||
|
||
- **Resolving is final.** The server treats a manual resolve as terminal: a later
|
||
occurrence opens a *new* incident rather than reopening this one, and if the alert
|
||
underneath never stops firing the incident stays closed. The TUI asks for
|
||
confirmation before doing it.
|
||
- **Snooze is the "not now" button.** It hides an incident from the default queue
|
||
without closing it, and expires on its own.
|
||
|
||
## Push notifications
|
||
|
||
When the server is configured for ntfy, an incident that opens pages whoever is
|
||
on call. Each user has their own topic, shown as a column in the Users section
|
||
and edited with `t`. A user with no topic falls back to the server's shared
|
||
fallback topic, which carries **no Acknowledge button** — the topic is shared, so
|
||
a button on it would let any subscriber acknowledge as somebody else.
|
||
|
||
Every delivery lands on the incident's timeline: `Notified <user> (triggered)`
|
||
when ntfy accepted the page, and `Notification to <user> failed` when it ran out
|
||
of retries. That second one is the one to look for when nobody's phone rang.
|
||
|
||
Editing topics needs terdut-server **v0.6.0 or later**; the timeline entries need
|
||
**v0.7.0 or later**. Against an older server the topic column stays empty and
|
||
editing one reports the server's 404.
|
||
|
||
## Installation
|
||
|
||
Download the latest release binary for your platform from the [releases page](https://git.ryuvia.com/niklas/terdut-tui/releases), or build from source:
|
||
|
||
```bash
|
||
go install git.ryuvia.com/niklas/terdut-tui@latest
|
||
```
|
||
|
||
## Configuration
|
||
|
||
Create `~/.config/terdut-tui/config.yaml`:
|
||
|
||
```yaml
|
||
server_url: https://terdut.example.com
|
||
api_key: <your-api-key>
|
||
refresh_interval: 30 # seconds, optional
|
||
theme: gruvbox-dark # optional, this is the default
|
||
```
|
||
|
||
The API key is generated in terdut-server. See the server documentation for how to bootstrap a user and issue an API key.
|
||
|
||
## Themes
|
||
|
||
Two themes ship with the client: `gruvbox-dark` (the default) and
|
||
`gruvbox-light`. Both colour foregrounds only — the terminal supplies the
|
||
background — so pick the one that matches the background you already run.
|
||
|
||
To make your own, drop a file in `~/.config/terdut-tui/themes/` and name it in
|
||
`theme:`. `extends` inherits a built-in, so a file only has to list what it
|
||
changes:
|
||
|
||
```yaml
|
||
# ~/.config/terdut-tui/themes/mine.yaml
|
||
extends: gruvbox-dark
|
||
primary: "#d3869b"
|
||
accent: "#fabd2f"
|
||
```
|
||
|
||
A file may shadow a built-in name — `themes/gruvbox-dark.yaml` is how you tweak
|
||
the default without renaming it.
|
||
|
||
Without `extends`, every token must be set. The twelve are:
|
||
|
||
| Token | Where it shows |
|
||
|---|---|
|
||
| `primary` | header, active tab, selected row, cursors |
|
||
| `on_primary` | text drawn *on* `primary` — the active tab and selected row |
|
||
| `text` | incident titles and other emphasis |
|
||
| `muted` | secondary text, dividers, footer, table headers |
|
||
| `accent` | status line, acknowledged incidents, the by-day chart |
|
||
| `firing` | firing alerts, triggered incidents, the by-hour chart |
|
||
| `resolved` | resolved alerts and incidents, the top-alerts chart |
|
||
| `error` | error banners |
|
||
| `sev_critical`, `sev_error`, `sev_warning`, `sev_info` | the `severity` label |
|
||
|
||
Values are hex (`#83a598` or `#abc`) or an ANSI palette index (`0`–`255`) if you
|
||
would rather follow your terminal's own colours. Colours are downsampled
|
||
automatically on 256- and 16-colour terminals, and `NO_COLOR` is honoured.
|
||
|
||
## Usage
|
||
|
||
```
|
||
terdut-tui start the TUI
|
||
terdut-tui --version print version
|
||
terdut-tui --self-update update to the latest release
|
||
```
|
||
|
||
### Keybindings
|
||
|
||
Global:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `j` / `↓` | Move down |
|
||
| `k` / `↑` | Move up |
|
||
| `tab` / `shift+tab` | Next / previous section |
|
||
| `enter` | Open detail |
|
||
| `esc` | Go back |
|
||
| `r` | Refresh |
|
||
| `f` | Cycle filter |
|
||
| `q` | Quit |
|
||
|
||
The sections, in `tab` order: Incidents · Alerts · Stats · Archived · Schedule · Users.
|
||
|
||
Incidents section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `f` | Cycle: open → triggered → acknowledged → resolved → snoozed |
|
||
| `x` | Archive (resolved incidents only) |
|
||
|
||
Incident detail:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `a` / `A` | Acknowledge / clear acknowledgement |
|
||
| `R` | Resolve — asks to confirm, and is final |
|
||
| `s` | Assign to a user |
|
||
| `z` / `Z` | Snooze for a duration / un-snooze |
|
||
| `c` | Add a note |
|
||
| `[` / `]` | Select a note |
|
||
| `d` | Delete the selected note (your own only) |
|
||
| `x` | Archive / un-archive |
|
||
|
||
Alerts section (read-only):
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `f` | Cycle: firing → resolved → all → archived |
|
||
| `i` | In detail: jump to the alert's incident |
|
||
|
||
Stats section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `j` / `k`, `pgup` / `pgdn` | Scroll |
|
||
|
||
Schedule section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `+` / `W` | Assign a day / a whole week |
|
||
| `d` | Remove the assignment |
|
||
| `←` / `→` | Shift the week window |
|
||
|
||
One person holds a given day. Assigning over days somebody else already has
|
||
asks first — naming them and how many days are being taken — and moves the whole
|
||
selection at once when you accept, so reassigning a week is one confirmation
|
||
rather than seven deletions. Taking somebody's shift needs terdut-server
|
||
**v0.8.0 or later**; against an older server the assignment is refused with
|
||
`date already assigned`.
|
||
|
||
Users section:
|
||
|
||
| Key | Action |
|
||
|-----|--------|
|
||
| `n` | Create a user |
|
||
| `t` | Edit the user's ntfy topic — submit empty to clear it |
|
||
| `d` | Delete a user |
|
||
| `k` | API keys for the selected user |
|