Merge pull request #110 from bnema/fix/issue-107-go-install
fix(config): disable external bar commands by default
5 files · +30 -15
M README.md +1 -1
| @@ -20,7 +20,7 @@ What it is built around: | |||
| 20 | 20 | go install github.com/bnema/vev@latest | |
| 21 | 21 | ``` | |
| 22 | 22 | ||
| 23 | - | From a checkout, `make install` also installs the default status bar scripts. | |
| 23 | + | The default UI runs entirely from the `vev` binary. Optional external bar anchors can be configured in `~/.config/vev/config`. | |
| 24 | 24 | ||
| 25 | 25 | ## Usage | |
| 26 | 26 | ||
| @@ -20,7 +20,7 @@ What it is built around: | |||||
| 20 | go install github.com/bnema/vev@latest | 20 | go install github.com/bnema/vev@latest | ||
| 21 | ``` | 21 | ``` | ||
| 22 | 22 | ||||
| 23 | - | From a checkout, `make install` also installs the default status bar scripts. | 23 | + | The default UI runs entirely from the `vev` binary. Optional external bar anchors can be configured in `~/.config/vev/config`. |
| 24 | 24 | ||||
| 25 | ## Usage | 25 | ## Usage | ||
| 26 | 26 | ||||
M docs/configuration.md +4 -4
| @@ -34,9 +34,9 @@ switch-tab-1 = alt+1 | |||
| 34 | 34 | # Processes relaunched when a named session is restored. Empty disables relaunch. | |
| 35 | 35 | snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode | |
| 36 | 36 | ||
| 37 | - | # Right bar anchors: commands run on the daemon host. Empty disables an anchor. | |
| 38 | - | bar.top-right = vev-bar-top-right | |
| 39 | - | bar.bottom-right = vev-bar-bottom-right | |
| 37 | + | # Optional right bar anchors: commands run on the daemon host. Empty disables an anchor. | |
| 38 | + | bar.top-right = | |
| 39 | + | bar.bottom-right = | |
| 40 | 40 | bar.interval = 5s | |
| 41 | 41 | ||
| 42 | 42 | # Command palette codes: 2-3 letters or digits. | |
| @@ -102,4 +102,4 @@ The command runs through your shell. A changed command applies on the next launc | |||
| 102 | 102 | ||
| 103 | 103 | Anchor commands run on the daemon host every `bar.interval` (minimum 1s). vev reads the first line of stdout, strips ANSI codes, and keeps the last good value on failure. Scripts get `VEV_ANCHOR`, `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`. | |
| 104 | 104 | ||
| 105 | - | The default bottom-right script runs `git status --porcelain` on each refresh; raise the interval or replace it if that is too heavy for your repository. | |
| 105 | + | Both anchors are disabled by default, so `go install github.com/bnema/vev@latest` needs no companion scripts. Set either command to opt in; explicit commands are run unchanged. Checkout and release installs include `vev-bar-top-right` and `vev-bar-bottom-right` example scripts, which can be configured by name when they are on the daemon host's `PATH`. The bottom-right example runs `git status --porcelain` on each refresh; raise the interval or replace it if that is too heavy for your repository. | |
| @@ -34,9 +34,9 @@ switch-tab-1 = alt+1 | |||||
| 34 | # Processes relaunched when a named session is restored. Empty disables relaunch. | 34 | # Processes relaunched when a named session is restored. Empty disables relaunch. | ||
| 35 | snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode | 35 | snapshot.restore_processes = vi,vim,nvim,emacs,man,less,more,tail,top,htop,btop,claude,codex,pi,opencode | ||
| 36 | 36 | ||||
| 37 | - | # Right bar anchors: commands run on the daemon host. Empty disables an anchor. | 37 | + | # Optional right bar anchors: commands run on the daemon host. Empty disables an anchor. |
| 38 | - | bar.top-right = vev-bar-top-right | 38 | + | bar.top-right = |
| 39 | - | bar.bottom-right = vev-bar-bottom-right | 39 | + | bar.bottom-right = |
| 40 | bar.interval = 5s | 40 | bar.interval = 5s | ||
| 41 | 41 | ||||
| 42 | # Command palette codes: 2-3 letters or digits. | 42 | # Command palette codes: 2-3 letters or digits. | ||
| @@ -102,4 +102,4 @@ The command runs through your shell. A changed command applies on the next launc | |||||
| 102 | 102 | ||||
| 103 | Anchor commands run on the daemon host every `bar.interval` (minimum 1s). vev reads the first line of stdout, strips ANSI codes, and keeps the last good value on failure. Scripts get `VEV_ANCHOR`, `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`. | 103 | Anchor commands run on the daemon host every `bar.interval` (minimum 1s). vev reads the first line of stdout, strips ANSI codes, and keeps the last good value on failure. Scripts get `VEV_ANCHOR`, `VEV_SESSION`, `VEV_TAB`, `VEV_PANE`, `VEV_PANE_CWD`, and `VEV_COLS`. | ||
| 104 | 104 | ||||
| 105 | - | The default bottom-right script runs `git status --porcelain` on each refresh; raise the interval or replace it if that is too heavy for your repository. | 105 | + | Both anchors are disabled by default, so `go install github.com/bnema/vev@latest` needs no companion scripts. Set either command to opt in; explicit commands are run unchanged. Checkout and release installs include `vev-bar-top-right` and `vev-bar-bottom-right` example scripts, which can be configured by name when they are on the daemon host's `PATH`. The bottom-right example runs `git status --porcelain` on each refresh; raise the interval or replace it if that is too heavy for your repository. |
M internal/adapters/config/config_test.go +13 -7
| @@ -16,6 +16,12 @@ import ( | |||
| 16 | 16 | "github.com/stretchr/testify/require" | |
| 17 | 17 | ) | |
| 18 | 18 | ||
| 19 | + | func TestDefaultsDisableBarCommands(t *testing.T) { | |
| 20 | + | bar := domain.Defaults().Bar | |
| 21 | + | require.Empty(t, bar.TopRight) | |
| 22 | + | require.Empty(t, bar.BottomRight) | |
| 23 | + | } | |
| 24 | + | ||
| 19 | 25 | func TestParse(t *testing.T) { | |
| 20 | 26 | t.Parallel() | |
| 21 | 27 | ||
| @@ -217,7 +223,7 @@ func TestParse(t *testing.T) { | |||
| 217 | 223 | Theme: domain.ThemeAuto, | |
| 218 | 224 | Bar: domain.BarConfig{ | |
| 219 | 225 | TopRight: "echo foo #1", | |
| 220 | - | BottomRight: "vev-bar-bottom-right", | |
| 226 | + | BottomRight: "", | |
| 221 | 227 | Interval: 5 * time.Second, | |
| 222 | 228 | }, | |
| 223 | 229 | BindingEntries: []domain.ConfigEntry{}, | |
| @@ -230,8 +236,8 @@ func TestParse(t *testing.T) { | |||
| 230 | 236 | want: domain.Config{ | |
| 231 | 237 | Theme: domain.ThemeAuto, | |
| 232 | 238 | Bar: domain.BarConfig{ | |
| 233 | - | TopRight: "vev-bar-top-right", | |
| 234 | - | BottomRight: "vev-bar-bottom-right", | |
| 239 | + | TopRight: "", | |
| 240 | + | BottomRight: "", | |
| 235 | 241 | Interval: 5 * time.Second, | |
| 236 | 242 | }, | |
| 237 | 243 | BindingEntries: []domain.ConfigEntry{}, | |
| @@ -259,8 +265,8 @@ func TestParse(t *testing.T) { | |||
| 259 | 265 | want: domain.Config{ | |
| 260 | 266 | Theme: domain.ThemeAuto, | |
| 261 | 267 | Bar: domain.BarConfig{ | |
| 262 | - | TopRight: "vev-bar-top-right", | |
| 263 | - | BottomRight: "vev-bar-bottom-right", | |
| 268 | + | TopRight: "", | |
| 269 | + | BottomRight: "", | |
| 264 | 270 | Interval: 5 * time.Second, | |
| 265 | 271 | }, | |
| 266 | 272 | BindingEntries: []domain.ConfigEntry{}, | |
| @@ -274,8 +280,8 @@ func TestParse(t *testing.T) { | |||
| 274 | 280 | want: domain.Config{ | |
| 275 | 281 | Theme: domain.ThemeAuto, | |
| 276 | 282 | Bar: domain.BarConfig{ | |
| 277 | - | TopRight: "vev-bar-top-right", | |
| 278 | - | BottomRight: "vev-bar-bottom-right", | |
| 283 | + | TopRight: "", | |
| 284 | + | BottomRight: "", | |
| 279 | 285 | Interval: time.Second, | |
| 280 | 286 | }, | |
| 281 | 287 | BindingEntries: []domain.ConfigEntry{}, | |
| @@ -16,6 +16,12 @@ import ( | |||||
| 16 | "github.com/stretchr/testify/require" | 16 | "github.com/stretchr/testify/require" | ||
| 17 | ) | 17 | ) | ||
| 18 | 18 | ||||
| 19 | + | func TestDefaultsDisableBarCommands(t *testing.T) { | |||
| 20 | + | bar := domain.Defaults().Bar | |||
| 21 | + | require.Empty(t, bar.TopRight) | |||
| 22 | + | require.Empty(t, bar.BottomRight) | |||
| 23 | + | } | |||
| 24 | + | ||||
| 19 | func TestParse(t *testing.T) { | 25 | func TestParse(t *testing.T) { | ||
| 20 | t.Parallel() | 26 | t.Parallel() | ||
| 21 | 27 | ||||
| @@ -217,7 +223,7 @@ func TestParse(t *testing.T) { | |||||
| 217 | Theme: domain.ThemeAuto, | 223 | Theme: domain.ThemeAuto, | ||
| 218 | Bar: domain.BarConfig{ | 224 | Bar: domain.BarConfig{ | ||
| 219 | TopRight: "echo foo #1", | 225 | TopRight: "echo foo #1", | ||
| 220 | - | BottomRight: "vev-bar-bottom-right", | 226 | + | BottomRight: "", |
| 221 | Interval: 5 * time.Second, | 227 | Interval: 5 * time.Second, | ||
| 222 | }, | 228 | }, | ||
| 223 | BindingEntries: []domain.ConfigEntry{}, | 229 | BindingEntries: []domain.ConfigEntry{}, | ||
| @@ -230,8 +236,8 @@ func TestParse(t *testing.T) { | |||||
| 230 | want: domain.Config{ | 236 | want: domain.Config{ | ||
| 231 | Theme: domain.ThemeAuto, | 237 | Theme: domain.ThemeAuto, | ||
| 232 | Bar: domain.BarConfig{ | 238 | Bar: domain.BarConfig{ | ||
| 233 | - | TopRight: "vev-bar-top-right", | 239 | + | TopRight: "", |
| 234 | - | BottomRight: "vev-bar-bottom-right", | 240 | + | BottomRight: "", |
| 235 | Interval: 5 * time.Second, | 241 | Interval: 5 * time.Second, | ||
| 236 | }, | 242 | }, | ||
| 237 | BindingEntries: []domain.ConfigEntry{}, | 243 | BindingEntries: []domain.ConfigEntry{}, | ||
| @@ -259,8 +265,8 @@ func TestParse(t *testing.T) { | |||||
| 259 | want: domain.Config{ | 265 | want: domain.Config{ | ||
| 260 | Theme: domain.ThemeAuto, | 266 | Theme: domain.ThemeAuto, | ||
| 261 | Bar: domain.BarConfig{ | 267 | Bar: domain.BarConfig{ | ||
| 262 | - | TopRight: "vev-bar-top-right", | 268 | + | TopRight: "", |
| 263 | - | BottomRight: "vev-bar-bottom-right", | 269 | + | BottomRight: "", |
| 264 | Interval: 5 * time.Second, | 270 | Interval: 5 * time.Second, | ||
| 265 | }, | 271 | }, | ||
| 266 | BindingEntries: []domain.ConfigEntry{}, | 272 | BindingEntries: []domain.ConfigEntry{}, | ||
| @@ -274,8 +280,8 @@ func TestParse(t *testing.T) { | |||||
| 274 | want: domain.Config{ | 280 | want: domain.Config{ | ||
| 275 | Theme: domain.ThemeAuto, | 281 | Theme: domain.ThemeAuto, | ||
| 276 | Bar: domain.BarConfig{ | 282 | Bar: domain.BarConfig{ | ||
| 277 | - | TopRight: "vev-bar-top-right", | 283 | + | TopRight: "", |
| 278 | - | BottomRight: "vev-bar-bottom-right", | 284 | + | BottomRight: "", |
| 279 | Interval: time.Second, | 285 | Interval: time.Second, | ||
| 280 | }, | 286 | }, | ||
| 281 | BindingEntries: []domain.ConfigEntry{}, | 287 | BindingEntries: []domain.ConfigEntry{}, | ||
M internal/domain/config.go +1 -3
| @@ -105,9 +105,7 @@ func Defaults() Config { | |||
| 105 | 105 | Theme: ThemeAuto, | |
| 106 | 106 | ThemePalette: true, | |
| 107 | 107 | Bar: BarConfig{ | |
| 108 | - | TopRight: "vev-bar-top-right", | |
| 109 | - | BottomRight: "vev-bar-bottom-right", | |
| 110 | - | Interval: 5 * time.Second, | |
| 108 | + | Interval: 5 * time.Second, | |
| 111 | 109 | }, | |
| 112 | 110 | BindingEntries: []ConfigEntry{}, | |
| 113 | 111 | Codes: map[string]string{}, | |
| @@ -105,9 +105,7 @@ func Defaults() Config { | |||||
| 105 | Theme: ThemeAuto, | 105 | Theme: ThemeAuto, | ||
| 106 | ThemePalette: true, | 106 | ThemePalette: true, | ||
| 107 | Bar: BarConfig{ | 107 | Bar: BarConfig{ | ||
| 108 | - | TopRight: "vev-bar-top-right", | 108 | + | Interval: 5 * time.Second, |
| 109 | - | BottomRight: "vev-bar-bottom-right", | |||
| 110 | - | Interval: 5 * time.Second, | |||
| 111 | }, | 109 | }, | ||
| 112 | BindingEntries: []ConfigEntry{}, | 110 | BindingEntries: []ConfigEntry{}, | ||
| 113 | Codes: map[string]string{}, | 111 | Codes: map[string]string{}, | ||
M internal/usecase/daemon/bar_refresh_test.go +11 -0
| @@ -139,6 +139,17 @@ func TestBarScriptRefreshSkipsRunnerWhenCommandsDisabled(t *testing.T) { | |||
| 139 | 139 | require.Empty(t, state.bottomRight) | |
| 140 | 140 | } | |
| 141 | 141 | ||
| 142 | + | func TestDefaultBarConfigDoesNotInvokeRunner(t *testing.T) { | |
| 143 | + | r := &fakeBarRunner{} | |
| 144 | + | d := newBarRefreshTestDaemon(r, time.Second) | |
| 145 | + | d.barScripts.cfg = barConfigFromDomain(domain.Defaults().Bar) | |
| 146 | + | sess := newBarRefreshTestSession() | |
| 147 | + | sess.client = &attachedClient{size: domain.Size{Cols: 80, Rows: 24}} | |
| 148 | + | ||
| 149 | + | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | |
| 150 | + | require.Empty(t, r.calls) | |
| 151 | + | } | |
| 152 | + | ||
| 142 | 153 | func TestBarScriptForcedRefreshRespectsMinimumInterval(t *testing.T) { | |
| 143 | 154 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | |
| 144 | 155 | d := newBarRefreshTestDaemon(r, 5*time.Second) | |
| @@ -139,6 +139,17 @@ func TestBarScriptRefreshSkipsRunnerWhenCommandsDisabled(t *testing.T) { | |||||
| 139 | require.Empty(t, state.bottomRight) | 139 | require.Empty(t, state.bottomRight) | ||
| 140 | } | 140 | } | ||
| 141 | 141 | ||||
| 142 | + | func TestDefaultBarConfigDoesNotInvokeRunner(t *testing.T) { | |||
| 143 | + | r := &fakeBarRunner{} | |||
| 144 | + | d := newBarRefreshTestDaemon(r, time.Second) | |||
| 145 | + | d.barScripts.cfg = barConfigFromDomain(domain.Defaults().Bar) | |||
| 146 | + | sess := newBarRefreshTestSession() | |||
| 147 | + | sess.client = &attachedClient{size: domain.Size{Cols: 80, Rows: 24}} | |||
| 148 | + | ||||
| 149 | + | require.False(t, d.refreshBarScriptsIfDue(sess, time.Unix(0, 0), true)) | |||
| 150 | + | require.Empty(t, r.calls) | |||
| 151 | + | } | |||
| 152 | + | ||||
| 142 | func TestBarScriptForcedRefreshRespectsMinimumInterval(t *testing.T) { | 153 | func TestBarScriptForcedRefreshRespectsMinimumInterval(t *testing.T) { | ||
| 143 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | 154 | r := &fakeBarRunner{outs: []string{"top1", "bottom1", "top2", "bottom2"}} | ||
| 144 | d := newBarRefreshTestDaemon(r, 5*time.Second) | 155 | d := newBarRefreshTestDaemon(r, 5*time.Second) | ||