feat(daemon): publish floating pane configuration
- commit
- aa3698c72bfb1d7400b643dbffefaa47865a9baa
- author
- brice · · 2026-07-09 23:24 UTC
- committer
- brice · · 2026-07-10 05:40 UTC
- parent
- edb3828
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +30 -0
M internal/usecase/daemon/config.go +9 -0
| @@ -27,6 +27,8 @@ func (d *Daemon) ApplyConfig(cfg domain.Config) { | |||
| 27 | 27 | d.bindings.Store(bindings) | |
| 28 | 28 | d.codeOverrides.Store(&overrides) | |
| 29 | 29 | d.restoreProcessAllowlist.Store(&allowlist) | |
| 30 | + | floating := cfg.Floating | |
| 31 | + | d.floatingConfig.Store(&floating) | |
| 30 | 32 | d.themeMode.Store(uint32(cfg.Theme)) | |
| 31 | 33 | if d.barScripts != nil { | |
| 32 | 34 | d.barScripts.mu.Lock() | |
| @@ -176,6 +178,13 @@ func (d *Daemon) logConfigWarning(w domain.Warning) { | |||
| 176 | 178 | d.log.Warn("config warning", "msg", w.Msg) | |
| 177 | 179 | } | |
| 178 | 180 | ||
| 181 | + | func (d *Daemon) currentFloatingConfig() domain.FloatingConfig { | |
| 182 | + | if cfg := d.floatingConfig.Load(); cfg != nil { | |
| 183 | + | return *cfg | |
| 184 | + | } | |
| 185 | + | return domain.Defaults().Floating | |
| 186 | + | } | |
| 187 | + | ||
| 179 | 188 | func (d *Daemon) codeOverrideSnapshot() map[string]string { | |
| 180 | 189 | overrides := d.codeOverrides.Load() | |
| 181 | 190 | if overrides == nil { | |
| @@ -27,6 +27,8 @@ func (d *Daemon) ApplyConfig(cfg domain.Config) { | |||||
| 27 | d.bindings.Store(bindings) | 27 | d.bindings.Store(bindings) | ||
| 28 | d.codeOverrides.Store(&overrides) | 28 | d.codeOverrides.Store(&overrides) | ||
| 29 | d.restoreProcessAllowlist.Store(&allowlist) | 29 | d.restoreProcessAllowlist.Store(&allowlist) | ||
| 30 | + | floating := cfg.Floating | |||
| 31 | + | d.floatingConfig.Store(&floating) | |||
| 30 | d.themeMode.Store(uint32(cfg.Theme)) | 32 | d.themeMode.Store(uint32(cfg.Theme)) | ||
| 31 | if d.barScripts != nil { | 33 | if d.barScripts != nil { | ||
| 32 | d.barScripts.mu.Lock() | 34 | d.barScripts.mu.Lock() | ||
| @@ -176,6 +178,13 @@ func (d *Daemon) logConfigWarning(w domain.Warning) { | |||||
| 176 | d.log.Warn("config warning", "msg", w.Msg) | 178 | d.log.Warn("config warning", "msg", w.Msg) | ||
| 177 | } | 179 | } | ||
| 178 | 180 | ||||
| 181 | + | func (d *Daemon) currentFloatingConfig() domain.FloatingConfig { | |||
| 182 | + | if cfg := d.floatingConfig.Load(); cfg != nil { | |||
| 183 | + | return *cfg | |||
| 184 | + | } | |||
| 185 | + | return domain.Defaults().Floating | |||
| 186 | + | } | |||
| 187 | + | ||||
| 179 | func (d *Daemon) codeOverrideSnapshot() map[string]string { | 188 | func (d *Daemon) codeOverrideSnapshot() map[string]string { | ||
| 180 | overrides := d.codeOverrides.Load() | 189 | overrides := d.codeOverrides.Load() | ||
| 181 | if overrides == nil { | 190 | if overrides == nil { | ||
M internal/usecase/daemon/config_test.go +18 -0
| @@ -49,6 +49,24 @@ func TestApplyConfigHotReloadSwapsBindingsAndCodes(t *testing.T) { | |||
| 49 | 49 | require.Equal(t, "NT", cmd.Code) | |
| 50 | 50 | } | |
| 51 | 51 | ||
| 52 | + | func TestApplyConfigPublishesImmutableFloatingSnapshot(t *testing.T) { | |
| 53 | + | d := newTestDaemon(t, nil, stubClock{}) | |
| 54 | + | require.Equal(t, domain.Defaults().Floating, d.currentFloatingConfig()) | |
| 55 | + | ||
| 56 | + | firstConfig := domain.Defaults() | |
| 57 | + | firstConfig.Floating = domain.FloatingConfig{Command: "btop", Width: 70, Height: 60} | |
| 58 | + | d.ApplyConfig(firstConfig) | |
| 59 | + | first := d.currentFloatingConfig() | |
| 60 | + | ||
| 61 | + | secondConfig := domain.Defaults() | |
| 62 | + | secondConfig.Floating = domain.FloatingConfig{Command: "lazygit", Width: 90, Height: 85} | |
| 63 | + | d.ApplyConfig(secondConfig) | |
| 64 | + | second := d.currentFloatingConfig() | |
| 65 | + | ||
| 66 | + | require.Equal(t, domain.FloatingConfig{Command: "btop", Width: 70, Height: 60}, first) | |
| 67 | + | require.Equal(t, domain.FloatingConfig{Command: "lazygit", Width: 90, Height: 85}, second) | |
| 68 | + | } | |
| 69 | + | ||
| 52 | 70 | func TestApplyConfigSnapshotRestoreProcesses(t *testing.T) { | |
| 53 | 71 | tests := []struct { | |
| 54 | 72 | name string | |
| @@ -49,6 +49,24 @@ func TestApplyConfigHotReloadSwapsBindingsAndCodes(t *testing.T) { | |||||
| 49 | require.Equal(t, "NT", cmd.Code) | 49 | require.Equal(t, "NT", cmd.Code) | ||
| 50 | } | 50 | } | ||
| 51 | 51 | ||||
| 52 | + | func TestApplyConfigPublishesImmutableFloatingSnapshot(t *testing.T) { | |||
| 53 | + | d := newTestDaemon(t, nil, stubClock{}) | |||
| 54 | + | require.Equal(t, domain.Defaults().Floating, d.currentFloatingConfig()) | |||
| 55 | + | ||||
| 56 | + | firstConfig := domain.Defaults() | |||
| 57 | + | firstConfig.Floating = domain.FloatingConfig{Command: "btop", Width: 70, Height: 60} | |||
| 58 | + | d.ApplyConfig(firstConfig) | |||
| 59 | + | first := d.currentFloatingConfig() | |||
| 60 | + | ||||
| 61 | + | secondConfig := domain.Defaults() | |||
| 62 | + | secondConfig.Floating = domain.FloatingConfig{Command: "lazygit", Width: 90, Height: 85} | |||
| 63 | + | d.ApplyConfig(secondConfig) | |||
| 64 | + | second := d.currentFloatingConfig() | |||
| 65 | + | ||||
| 66 | + | require.Equal(t, domain.FloatingConfig{Command: "btop", Width: 70, Height: 60}, first) | |||
| 67 | + | require.Equal(t, domain.FloatingConfig{Command: "lazygit", Width: 90, Height: 85}, second) | |||
| 68 | + | } | |||
| 69 | + | ||||
| 52 | func TestApplyConfigSnapshotRestoreProcesses(t *testing.T) { | 70 | func TestApplyConfigSnapshotRestoreProcesses(t *testing.T) { | ||
| 53 | tests := []struct { | 71 | tests := []struct { | ||
| 54 | name string | 72 | name string | ||
M internal/usecase/daemon/daemon.go +3 -0
| @@ -126,6 +126,7 @@ type Daemon struct { | |||
| 126 | 126 | bindings atomic.Pointer[keys.Bindings] | |
| 127 | 127 | codeOverrides atomic.Pointer[map[string]string] | |
| 128 | 128 | restoreProcessAllowlist atomic.Pointer[map[string]struct{}] | |
| 129 | + | floatingConfig atomic.Pointer[domain.FloatingConfig] | |
| 129 | 130 | themeMode atomic.Uint32 | |
| 130 | 131 | barScripts *barScriptState | |
| 131 | 132 | resumeParkGrace time.Duration | |
| @@ -306,6 +307,8 @@ func New(ptys ports.PTYFactory, clock ports.Clock, log *slog.Logger, opts ...Opt | |||
| 306 | 307 | running: make(map[domain.SessionID]bool), | |
| 307 | 308 | }, | |
| 308 | 309 | } | |
| 310 | + | defaultFloating := domain.Defaults().Floating | |
| 311 | + | d.floatingConfig.Store(&defaultFloating) | |
| 309 | 312 | for _, o := range opts { | |
| 310 | 313 | o(d) | |
| 311 | 314 | } | |
| @@ -126,6 +126,7 @@ type Daemon struct { | |||||
| 126 | bindings atomic.Pointer[keys.Bindings] | 126 | bindings atomic.Pointer[keys.Bindings] | ||
| 127 | codeOverrides atomic.Pointer[map[string]string] | 127 | codeOverrides atomic.Pointer[map[string]string] | ||
| 128 | restoreProcessAllowlist atomic.Pointer[map[string]struct{}] | 128 | restoreProcessAllowlist atomic.Pointer[map[string]struct{}] | ||
| 129 | + | floatingConfig atomic.Pointer[domain.FloatingConfig] | |||
| 129 | themeMode atomic.Uint32 | 130 | themeMode atomic.Uint32 | ||
| 130 | barScripts *barScriptState | 131 | barScripts *barScriptState | ||
| 131 | resumeParkGrace time.Duration | 132 | resumeParkGrace time.Duration | ||
| @@ -306,6 +307,8 @@ func New(ptys ports.PTYFactory, clock ports.Clock, log *slog.Logger, opts ...Opt | |||||
| 306 | running: make(map[domain.SessionID]bool), | 307 | running: make(map[domain.SessionID]bool), | ||
| 307 | }, | 308 | }, | ||
| 308 | } | 309 | } | ||
| 310 | + | defaultFloating := domain.Defaults().Floating | |||
| 311 | + | d.floatingConfig.Store(&defaultFloating) | |||
| 309 | for _, o := range opts { | 312 | for _, o := range opts { | ||
| 310 | o(d) | 313 | o(d) | ||
| 311 | } | 314 | } | ||