Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ coverage.*
_designs/*
!_designs/00_STYLE_GUIDE.md
!_designs/00_WIREFRAME_SPEC.md
/dist/
10 changes: 10 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ builds:
archives:
- formats: [tar.gz]
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
# Listing `files` replaces goreleaser's defaults, so LICENSE and README
# have to be restated alongside the template.
files:
- LICENSE
- README.md
- contrib/tailtui.toml.tpl

# nfpm builds official .deb (Debian/Ubuntu) and .rpm (Fedora/RedHat) packages
nfpms:
Expand All @@ -35,6 +41,10 @@ nfpms:
contents:
- src: LICENSE
dst: /usr/share/licenses/tailtui/LICENSE
- src: contrib/tailtui.toml.tpl
dst: /usr/share/tailtui/tailtui.toml.tpl
- src: README.md
dst: /usr/share/doc/tailtui/README.md

checksum:
name_template: 'checksums.txt'
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ Before changing any interaction, consult the keybinding matrix and overlay specs
- **Phase 28 — CI workflow & release-config repair** — `.github/workflows/release.yml` was the **only** workflow and it fires solely on `v*` tags, so nothing verified a commit until a release was already being cut, and a broken `.goreleaser.yaml` could only surface as a failed release job *after* the tag existed. Added `.github/workflows/ci.yml`: runs on pushes to `main`, on every pull request, and via `workflow_dispatch`; `concurrency` cancels superseded in-flight runs; `contents` permission is read-only. Two jobs — **`test`** (a `gofmt -l` gate, a `go mod tidy` drift gate, `go build ./...`, `go vet ./...`, and **`go test -race ./...`**; the View layer is TTY-free so the suite runs headless, and `-race` covers the async `tea.Cmd` status/ping polling paths) and **`goreleaser-config`** (`goreleaser check`, so config errors fail on a PR instead of on a tag). Both resolve Go via `go-version-file: go.mod` rather than `stable`, so CI tracks the pinned toolchain instead of drifting on release day. **The new check immediately caught three real defects** in `.goreleaser.yaml`, every one of which would have failed the release job only after a tag was pushed: `nfpms.files` → **`nfpms.contents`** (`files` is a v1 key and is rejected outright by the v2 schema — the hard failure); `archives.format` → **`archives.formats`** (deprecated singular); and `snapshot.name_template` → **`snapshot.version_template`** (deprecated — note `goreleaser check` exits non-zero on deprecations, not just schema errors). Also dropped `-extldflags "{{.Env.LDFLAGS}}"` from the build ldflags — it required an `LDFLAGS` env var to be set at release time and is a no-op under `CGO_ENABLED=0` (no external linker to pass it to) — and translated the one non-English comment. **Test hermeticity is a requirement for CI to stay green**: every styles test uses `t.Setenv` for `HOME`/`TAILTUI_THEME`, and `TestInstalledOmarchyThemesMapCompletely` (which globs the machine's real Omarchy theme dir) **skips** when none is installed. Don't add a test that assumes Omarchy, a TTY, or a live `tailscale` daemon without an equivalent skip guard.
- **Phase 29 — Release v1.2.0 & version plumbing** — cut the release covering everything since the (never-published) `v1.1.0` tag: the 24.x sudo account flows, Phase 26 mock mode + VHS demo, the 26.2 privacy pass, Phase 27's Omarchy 4 theme support, and Phase 28's CI. **Version plumbing fix**: `.goreleaser.yaml` stamps `-X main.version={{.Version}}`, but `main.go` had **no `version` var** and `appVersion` was a `const` in `internal/tui/view.go` — so the ldflag silently did nothing (Go ignores `-X` on a missing symbol) and a tagged build would have shipped whatever was hardcoded. Now `main.go` declares `var version string` (empty for `go build`/`go run`) and calls **`tui.SetVersion(version)`** first thing in `main`; `appVersion` became a **var** (dev-build literal `v1.2.0`) and `SetVersion` ignores an empty value and normalizes a bare `1.2.0` → `v1.2.0`, since goreleaser's `{{.Version}}` drops the prefix. `appVersion` remains the single definition — set it there, never duplicate it. Added `internal/tui/version_test.go` (normalization table incl. prerelease + empty, plus a render test asserting the stamped version actually reaches the footer while staying flush). `.gitignore` was rewritten to cover the compiled `/tailtui` binary (previously **committed**, ~6.5 MB, removed from history in the same cycle), test/coverage output, and editor/OS noise, while preserving the existing `_designs/` allow-list. README gained a `ci` status badge under the tagline and a "What's New in v1.2.0" section above the v1.1.0 one. No layout / state / keybinding changes.
- **Phase 30 — tailTUI theme schema, Omarchy template & live theme reload** — closed the one gap Phase 27 could not: some mappings are genuinely undecidable in code. `osaka-jade` defines `yellow = #459451` (a green), so mapping `yellow`→Warning makes exit-node markers nearly indistinguishable from the online green — but preferring `orange` breaks `retro-82`, where orange *is* the accent. **(1) tailTUI's own schema**: a third wire struct `tailtuiTheme` with one key per `Theme` field, tried **before** both Omarchy schemas, and probed as `tailtui.toml` **ahead of** `colors.toml` inside each theme directory. Keys are disjoint from the Omarchy vocabularies so detection stays key-based; `mode`/`background` are shared and therefore excluded from `hasMarkers()`. **(2) Omarchy template**: `contrib/tailtui.toml.tpl`, installed by the user into `~/.config/omarchy/themed/`. Omarchy's `omarchy-theme-set-templates` renders every `*.tpl` there into the **active theme directory** (`$NEXT_THEME_DIR/<name>`, which becomes `~/.local/state/omarchy/current/theme/`) on every theme switch — i.e. straight into the directory the loader already probes, so no new path was needed. Templates get the full palette (semantic slots, `color0`–`color15`, `mode`, `bright_`/`dark_`/`light_` variants) plus `_strip`/`_rgb` modifiers and a `{{ mix a b 30% }}` blend. **(3) Live reload**: `styles.ThemeStamp()` (path+mtime) plus `checkThemeCmd`, batched onto the **existing** `refreshInterval` tick — no fsnotify, no new dependency — so switching the desktop theme re-colors a running tailTUI within one tick. The critical constraint: the command **returns** the parsed `Theme` and never calls `styles.Apply` itself, because `Apply` rewrites package-level vars `View` reads; applying happens in `Update`, on the Elm goroutine. The handler records the stamp even on no-change (so a file appearing/vanishing is caught) and rebuilds viewport-backed overlays via `resizeOverlay()`, whose content would otherwise keep stale ANSI colors. Zero-config behavior is untouched: with no template installed, `colors.toml` is read exactly as before. **Tests**: own-schema mapping, light mode, three-way detection non-ambiguity, `tailtui.toml`-over-`colors.toml` precedence, fallback when no template exists, `ThemeStamp` change tracking and empty state (`theme_test.go`); palette applied + stamp recorded + logged, unchanged-is-quiet, overlay rebuilt (forcing TrueColor, since a TTY-less test strips every SGR and both renders would otherwise compare equal), and `New()` stamp seeding (`theme_reload_test.go`).
- **Phase 31 — Release v1.3.0** — bumped `appVersion` `v1.2.0` → `v1.3.0` (still the single definition in `view.go`) for Phase 30's live theme switching + tailTUI theme schema. **Packaging gap closed**: the README told users to `cp contrib/tailtui.toml.tpl …`, which is wrong for anyone who installed from the AUR / `.deb` / `.rpm` — they have a binary package, not a checkout. The template is now installed to **`/usr/share/tailtui/tailtui.toml.tpl`** by the packaging, and the README gives both paths. Any future user-facing file added under `contrib/` needs the same treatment, or the docs silently break for package users. Also decoupled `version_test.go` from the real version string (it now asserts on an obviously-arbitrary literal), so a release bump no longer looks like it requires a test edit — the test exercises `SetVersion`'s normalization, not the current version. README gained a "What's New in v1.3.0" section; the theming docs and the Phase 30 entry shipped with the feature itself, which is the rule: **docs ride in the feature PR, and the release PR carries only version, changelog, and phase log**.
- **Upcoming (next major cycle — see the README Roadmap)** —
- **Tailscale Serve & Funnel management**: visual port forwarding to securely expose local services to the tailnet (`tailscale serve`) or the public internet (`tailscale funnel`), driven from keyboard overlays in the existing modal style.
- **Connection diagnostics**: deep-dive into peer connection health — DERP-relay vs. direct routing and the signals to debug a flaky link (likely from `tailscale status --json` endpoints + `tailscale ping`/`netcheck`).
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ for the opposite workflow:
drop you to the shell only when *they* need to (to paste an auth URL), then
restore the UI automatically.

## What's New in v1.3.0

- **Live theme switching.** Switch your desktop theme and a running `tailTUI`
re-colors itself within a few seconds — no restart, and it works with a modal
open. Implemented on the existing refresh tick, so there is no file-watcher
dependency and no extra background work.
- **A theme format of tailTUI's own.** A `tailtui.toml` with one key per UI role
(`primary`, `surface`, `warning`, …) now takes precedence over the raw Omarchy
palette. Write it by hand on any distro, or generate it from the bundled
Omarchy template.
- **You decide the ambiguous mappings.** Some palettes cannot be mapped
automatically — `osaka-jade` defines its `yellow` as a green, which leaves
exit-node markers nearly indistinguishable from the online color. A template
settles it in one line, per theme, instead of `tailTUI` guessing.

None of this is required: with no template installed, `tailTUI` reads the
theme's `colors.toml` and maps it itself, exactly as before.

## What's New in v1.2.0

- **Omarchy 4 ("Quattro") theme support.** Omarchy 4 moved the current-theme
Expand Down Expand Up @@ -230,6 +248,11 @@ running**, within a few seconds of you switching themes — no restart:

```bash
mkdir -p ~/.config/omarchy/themed

# installed from a package (AUR, .deb, .rpm)
cp /usr/share/tailtui/tailtui.toml.tpl ~/.config/omarchy/themed/

# or from a clone of this repository
cp contrib/tailtui.toml.tpl ~/.config/omarchy/themed/
```

Expand Down
10 changes: 5 additions & 5 deletions internal/tui/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ func TestSetVersion(t *testing.T) {
cases := []struct {
name, in, want string
}{
{"bare goreleaser version", "1.2.0", "v1.2.0"},
{"already prefixed", "v1.2.0", "v1.2.0"},
{"prerelease", "1.3.0-rc1", "v1.3.0-rc1"},
{"unstamped build keeps the literal", "", "v1.2.0"},
{"bare goreleaser version", "9.9.9", "v9.9.9"},
{"already prefixed", "v9.9.9", "v9.9.9"},
{"prerelease", "9.9.9-rc1", "v9.9.9-rc1"},
{"unstamped build keeps the literal", "", "v0.0.0-devbuild"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
restoreVersion(t)
appVersion = "v1.2.0" // the dev-build literal
appVersion = "v0.0.0-devbuild" // stands in for the dev-build literal
SetVersion(c.in)
if appVersion != c.want {
t.Errorf("SetVersion(%q) → %q, want %q", c.in, appVersion, c.want)
Expand Down
2 changes: 1 addition & 1 deletion internal/tui/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const appName = "tailTUI"
// appVersion is the version string shown in the footer. It is a var, not a
// const, so a release build can override it from the git tag via SetVersion
// (see main.go / .goreleaser.yaml); the literal here is the dev-build value.
var appVersion = "v1.2.0"
var appVersion = "v1.3.0"

// SetVersion overrides the footer version string, for builds that stamp the
// real tag in at link time. An empty version is ignored (keeping the dev-build
Expand Down
Loading