fix(palette): center command picker by default
- commit
- 88ed2defad9f7c063e20f6d6f541d9c2e74d2cdf
- author
- brice · · 2026-07-16 07:44 UTC
- parent
- 1beec74
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +15 -8
M docs/configuration.md +2 -2
| @@ -8,7 +8,7 @@ theme = auto | |||
| 8 | 8 | ||
| 9 | 9 | # Palette placement: auto, center, top-left, top, top-right, left, right, | |
| 10 | 10 | # bottom-left, bottom, or bottom-right. | |
| 11 | - | palette.anchor = auto | |
| 11 | + | palette.anchor = center | |
| 12 | 12 | ||
| 13 | 13 | # One prewarmed floating terminal per tab. An empty command uses the normal shell. | |
| 14 | 14 | floating.command = | |
| @@ -86,7 +86,7 @@ Set `copy.word-separators = ""` to use only Unicode whitespace as a separator. | |||
| 86 | 86 | ||
| 87 | 87 | ## Palette anchor | |
| 88 | 88 | ||
| 89 | - | `auto` uses a bottom shelf on narrow terminals and a bottom-right rail from 96 columns up. Reload moves an open palette without losing your query. | |
| 89 | + | The command palette is centered by default. Set `palette.anchor` to another anchor to reposition it, or use `auto` for a bottom shelf on narrow terminals and a bottom-right rail from 96 columns up. Reload moves an open palette without losing your query. | |
| 90 | 90 | ||
| 91 | 91 | ## Floating terminal | |
| 92 | 92 | ||
| @@ -8,7 +8,7 @@ theme = auto | |||||
| 8 | 8 | ||||
| 9 | # Palette placement: auto, center, top-left, top, top-right, left, right, | 9 | # Palette placement: auto, center, top-left, top, top-right, left, right, | ||
| 10 | # bottom-left, bottom, or bottom-right. | 10 | # bottom-left, bottom, or bottom-right. | ||
| 11 | - | palette.anchor = auto | 11 | + | palette.anchor = center |
| 12 | 12 | ||||
| 13 | # One prewarmed floating terminal per tab. An empty command uses the normal shell. | 13 | # One prewarmed floating terminal per tab. An empty command uses the normal shell. | ||
| 14 | floating.command = | 14 | floating.command = | ||
| @@ -86,7 +86,7 @@ Set `copy.word-separators = ""` to use only Unicode whitespace as a separator. | |||||
| 86 | 86 | ||||
| 87 | ## Palette anchor | 87 | ## Palette anchor | ||
| 88 | 88 | ||||
| 89 | - | `auto` uses a bottom shelf on narrow terminals and a bottom-right rail from 96 columns up. Reload moves an open palette without losing your query. | 89 | + | The command palette is centered by default. Set `palette.anchor` to another anchor to reposition it, or use `auto` for a bottom shelf on narrow terminals and a bottom-right rail from 96 columns up. Reload moves an open palette without losing your query. |
| 90 | 90 | ||||
| 91 | ## Floating terminal | 91 | ## Floating terminal | ||
| 92 | 92 | ||||
M internal/adapters/config/config_test.go +3 -0
| @@ -347,6 +347,9 @@ func TestParse(t *testing.T) { | |||
| 347 | 347 | if tt.want.Copy.WordSeparators == "" { | |
| 348 | 348 | tt.want.Copy = domain.Defaults().Copy | |
| 349 | 349 | } | |
| 350 | + | if !tt.want.Palette.AnchorSet { | |
| 351 | + | tt.want.Palette = domain.Defaults().Palette | |
| 352 | + | } | |
| 350 | 353 | got, warnings, err := Parse(strings.NewReader(tt.input)) | |
| 351 | 354 | if err != nil { | |
| 352 | 355 | t.Fatalf("Parse() error = %v", err) | |
| @@ -347,6 +347,9 @@ func TestParse(t *testing.T) { | |||||
| 347 | if tt.want.Copy.WordSeparators == "" { | 347 | if tt.want.Copy.WordSeparators == "" { | ||
| 348 | tt.want.Copy = domain.Defaults().Copy | 348 | tt.want.Copy = domain.Defaults().Copy | ||
| 349 | } | 349 | } | ||
| 350 | + | if !tt.want.Palette.AnchorSet { | |||
| 351 | + | tt.want.Palette = domain.Defaults().Palette | |||
| 352 | + | } | |||
| 350 | got, warnings, err := Parse(strings.NewReader(tt.input)) | 353 | got, warnings, err := Parse(strings.NewReader(tt.input)) | ||
| 351 | if err != nil { | 354 | if err != nil { | ||
| 352 | t.Fatalf("Parse() error = %v", err) | 355 | t.Fatalf("Parse() error = %v", err) | ||
M internal/adapters/config/palette_anchor_test.go +6 -6
| @@ -21,10 +21,10 @@ func TestParsePaletteAnchor(t *testing.T) { | |||
| 21 | 21 | want domain.PaletteConfig | |
| 22 | 22 | warnings []domain.Warning | |
| 23 | 23 | }{ | |
| 24 | - | {name: "default is auto", want: domain.PaletteConfig{}}, | |
| 24 | + | {name: "default is centered", want: domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}}, | |
| 25 | 25 | {name: "explicit auto is case insensitive", input: "palette.anchor = AuTo\n", want: domain.PaletteConfig{}}, | |
| 26 | 26 | {name: "concrete anchors are trimmed and case insensitive", input: "palette.anchor = CENTER\npalette.anchor = top-left\npalette.anchor = Top\npalette.anchor = TOP-RIGHT\npalette.anchor = left\npalette.anchor = RIGHT\npalette.anchor = bottom-left\npalette.anchor = Bottom\npalette.anchor = BOTTOM-RIGHT \n", want: domain.PaletteConfig{Anchor: domain.AnchorBottomRight, AnchorSet: true}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}, {Line: 3, Msg: "duplicate key \"palette.anchor\""}, {Line: 4, Msg: "duplicate key \"palette.anchor\""}, {Line: 5, Msg: "duplicate key \"palette.anchor\""}, {Line: 6, Msg: "duplicate key \"palette.anchor\""}, {Line: 7, Msg: "duplicate key \"palette.anchor\""}, {Line: 8, Msg: "duplicate key \"palette.anchor\""}, {Line: 9, Msg: "duplicate key \"palette.anchor\""}}}, | |
| 27 | - | {name: "invalid first preserves auto", input: "palette.anchor = diagonal\n", want: domain.PaletteConfig{}, warnings: []domain.Warning{{Line: 1, Msg: "invalid palette.anchor \"diagonal\""}}}, | |
| 27 | + | {name: "invalid first preserves centered default", input: "palette.anchor = diagonal\n", want: domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}, warnings: []domain.Warning{{Line: 1, Msg: "invalid palette.anchor \"diagonal\""}}}, | |
| 28 | 28 | {name: "valid then invalid preserves valid", input: "palette.anchor = top\npalette.anchor = diagonal\n", want: domain.PaletteConfig{Anchor: domain.AnchorTop, AnchorSet: true}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}, {Line: 2, Msg: "invalid palette.anchor \"diagonal\""}}}, | |
| 29 | 29 | {name: "explicit then auto clears to auto", input: "palette.anchor = left\npalette.anchor = auto\n", want: domain.PaletteConfig{}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}}}, | |
| 30 | 30 | } | |
| @@ -39,15 +39,15 @@ func TestParsePaletteAnchor(t *testing.T) { | |||
| 39 | 39 | } | |
| 40 | 40 | } | |
| 41 | 41 | ||
| 42 | - | func TestLoadMissingPaletteAnchorDefaultsToAuto(t *testing.T) { | |
| 42 | + | func TestLoadMissingPaletteAnchorDefaultsToCenter(t *testing.T) { | |
| 43 | 43 | t.Parallel() | |
| 44 | 44 | cfg, warnings, err := Load(filepath.Join(t.TempDir(), "missing")) | |
| 45 | 45 | require.NoError(t, err) | |
| 46 | 46 | require.Empty(t, warnings) | |
| 47 | - | require.Equal(t, domain.PaletteConfig{}, cfg.Palette) | |
| 47 | + | require.Equal(t, domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}, cfg.Palette) | |
| 48 | 48 | } | |
| 49 | 49 | ||
| 50 | - | func TestWatchPaletteAnchorRestoresAutoAfterFileRemoval(t *testing.T) { | |
| 50 | + | func TestWatchPaletteAnchorRestoresCenteredDefaultAfterFileRemoval(t *testing.T) { | |
| 51 | 51 | path := filepath.Join(t.TempDir(), "config") | |
| 52 | 52 | require.NoError(t, os.WriteFile(path, []byte("palette.anchor = top-right\n"), 0o600)) | |
| 53 | 53 | clk := newFakeClock(time.Unix(0, 0)) | |
| @@ -66,7 +66,7 @@ func TestWatchPaletteAnchorRestoresAutoAfterFileRemoval(t *testing.T) { | |||
| 66 | 66 | clk.waitForTimers(t, 1) | |
| 67 | 67 | clk.advance(2 * time.Second) | |
| 68 | 68 | got := <-changes | |
| 69 | - | require.Equal(t, domain.PaletteConfig{}, got.Palette) | |
| 69 | + | require.Equal(t, domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}, got.Palette) | |
| 70 | 70 | cancel() | |
| 71 | 71 | require.ErrorIs(t, <-done, context.Canceled) | |
| 72 | 72 | } | |
| @@ -21,10 +21,10 @@ func TestParsePaletteAnchor(t *testing.T) { | |||||
| 21 | want domain.PaletteConfig | 21 | want domain.PaletteConfig | ||
| 22 | warnings []domain.Warning | 22 | warnings []domain.Warning | ||
| 23 | }{ | 23 | }{ | ||
| 24 | - | {name: "default is auto", want: domain.PaletteConfig{}}, | 24 | + | {name: "default is centered", want: domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}}, |
| 25 | {name: "explicit auto is case insensitive", input: "palette.anchor = AuTo\n", want: domain.PaletteConfig{}}, | 25 | {name: "explicit auto is case insensitive", input: "palette.anchor = AuTo\n", want: domain.PaletteConfig{}}, | ||
| 26 | {name: "concrete anchors are trimmed and case insensitive", input: "palette.anchor = CENTER\npalette.anchor = top-left\npalette.anchor = Top\npalette.anchor = TOP-RIGHT\npalette.anchor = left\npalette.anchor = RIGHT\npalette.anchor = bottom-left\npalette.anchor = Bottom\npalette.anchor = BOTTOM-RIGHT \n", want: domain.PaletteConfig{Anchor: domain.AnchorBottomRight, AnchorSet: true}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}, {Line: 3, Msg: "duplicate key \"palette.anchor\""}, {Line: 4, Msg: "duplicate key \"palette.anchor\""}, {Line: 5, Msg: "duplicate key \"palette.anchor\""}, {Line: 6, Msg: "duplicate key \"palette.anchor\""}, {Line: 7, Msg: "duplicate key \"palette.anchor\""}, {Line: 8, Msg: "duplicate key \"palette.anchor\""}, {Line: 9, Msg: "duplicate key \"palette.anchor\""}}}, | 26 | {name: "concrete anchors are trimmed and case insensitive", input: "palette.anchor = CENTER\npalette.anchor = top-left\npalette.anchor = Top\npalette.anchor = TOP-RIGHT\npalette.anchor = left\npalette.anchor = RIGHT\npalette.anchor = bottom-left\npalette.anchor = Bottom\npalette.anchor = BOTTOM-RIGHT \n", want: domain.PaletteConfig{Anchor: domain.AnchorBottomRight, AnchorSet: true}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}, {Line: 3, Msg: "duplicate key \"palette.anchor\""}, {Line: 4, Msg: "duplicate key \"palette.anchor\""}, {Line: 5, Msg: "duplicate key \"palette.anchor\""}, {Line: 6, Msg: "duplicate key \"palette.anchor\""}, {Line: 7, Msg: "duplicate key \"palette.anchor\""}, {Line: 8, Msg: "duplicate key \"palette.anchor\""}, {Line: 9, Msg: "duplicate key \"palette.anchor\""}}}, | ||
| 27 | - | {name: "invalid first preserves auto", input: "palette.anchor = diagonal\n", want: domain.PaletteConfig{}, warnings: []domain.Warning{{Line: 1, Msg: "invalid palette.anchor \"diagonal\""}}}, | 27 | + | {name: "invalid first preserves centered default", input: "palette.anchor = diagonal\n", want: domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}, warnings: []domain.Warning{{Line: 1, Msg: "invalid palette.anchor \"diagonal\""}}}, |
| 28 | {name: "valid then invalid preserves valid", input: "palette.anchor = top\npalette.anchor = diagonal\n", want: domain.PaletteConfig{Anchor: domain.AnchorTop, AnchorSet: true}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}, {Line: 2, Msg: "invalid palette.anchor \"diagonal\""}}}, | 28 | {name: "valid then invalid preserves valid", input: "palette.anchor = top\npalette.anchor = diagonal\n", want: domain.PaletteConfig{Anchor: domain.AnchorTop, AnchorSet: true}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}, {Line: 2, Msg: "invalid palette.anchor \"diagonal\""}}}, | ||
| 29 | {name: "explicit then auto clears to auto", input: "palette.anchor = left\npalette.anchor = auto\n", want: domain.PaletteConfig{}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}}}, | 29 | {name: "explicit then auto clears to auto", input: "palette.anchor = left\npalette.anchor = auto\n", want: domain.PaletteConfig{}, warnings: []domain.Warning{{Line: 2, Msg: "duplicate key \"palette.anchor\""}}}, | ||
| 30 | } | 30 | } | ||
| @@ -39,15 +39,15 @@ func TestParsePaletteAnchor(t *testing.T) { | |||||
| 39 | } | 39 | } | ||
| 40 | } | 40 | } | ||
| 41 | 41 | ||||
| 42 | - | func TestLoadMissingPaletteAnchorDefaultsToAuto(t *testing.T) { | 42 | + | func TestLoadMissingPaletteAnchorDefaultsToCenter(t *testing.T) { |
| 43 | t.Parallel() | 43 | t.Parallel() | ||
| 44 | cfg, warnings, err := Load(filepath.Join(t.TempDir(), "missing")) | 44 | cfg, warnings, err := Load(filepath.Join(t.TempDir(), "missing")) | ||
| 45 | require.NoError(t, err) | 45 | require.NoError(t, err) | ||
| 46 | require.Empty(t, warnings) | 46 | require.Empty(t, warnings) | ||
| 47 | - | require.Equal(t, domain.PaletteConfig{}, cfg.Palette) | 47 | + | require.Equal(t, domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}, cfg.Palette) |
| 48 | } | 48 | } | ||
| 49 | 49 | ||||
| 50 | - | func TestWatchPaletteAnchorRestoresAutoAfterFileRemoval(t *testing.T) { | 50 | + | func TestWatchPaletteAnchorRestoresCenteredDefaultAfterFileRemoval(t *testing.T) { |
| 51 | path := filepath.Join(t.TempDir(), "config") | 51 | path := filepath.Join(t.TempDir(), "config") | ||
| 52 | require.NoError(t, os.WriteFile(path, []byte("palette.anchor = top-right\n"), 0o600)) | 52 | require.NoError(t, os.WriteFile(path, []byte("palette.anchor = top-right\n"), 0o600)) | ||
| 53 | clk := newFakeClock(time.Unix(0, 0)) | 53 | clk := newFakeClock(time.Unix(0, 0)) | ||
| @@ -66,7 +66,7 @@ func TestWatchPaletteAnchorRestoresAutoAfterFileRemoval(t *testing.T) { | |||||
| 66 | clk.waitForTimers(t, 1) | 66 | clk.waitForTimers(t, 1) | ||
| 67 | clk.advance(2 * time.Second) | 67 | clk.advance(2 * time.Second) | ||
| 68 | got := <-changes | 68 | got := <-changes | ||
| 69 | - | require.Equal(t, domain.PaletteConfig{}, got.Palette) | 69 | + | require.Equal(t, domain.PaletteConfig{Anchor: domain.AnchorCenter, AnchorSet: true}, got.Palette) |
| 70 | cancel() | 70 | cancel() | ||
| 71 | require.ErrorIs(t, <-done, context.Canceled) | 71 | require.ErrorIs(t, <-done, context.Canceled) | ||
| 72 | } | 72 | } | ||
M internal/domain/config.go +4 -0
| @@ -115,6 +115,10 @@ func Defaults() Config { | |||
| 115 | 115 | Copy: CopyConfig{ | |
| 116 | 116 | WordSeparators: DefaultWordSeparators, | |
| 117 | 117 | }, | |
| 118 | + | Palette: PaletteConfig{ | |
| 119 | + | Anchor: AnchorCenter, | |
| 120 | + | AnchorSet: true, | |
| 121 | + | }, | |
| 118 | 122 | Floating: FloatingConfig{ | |
| 119 | 123 | Width: 80, | |
| 120 | 124 | Height: 80, | |
| @@ -115,6 +115,10 @@ func Defaults() Config { | |||||
| 115 | Copy: CopyConfig{ | 115 | Copy: CopyConfig{ | ||
| 116 | WordSeparators: DefaultWordSeparators, | 116 | WordSeparators: DefaultWordSeparators, | ||
| 117 | }, | 117 | }, | ||
| 118 | + | Palette: PaletteConfig{ | |||
| 119 | + | Anchor: AnchorCenter, | |||
| 120 | + | AnchorSet: true, | |||
| 121 | + | }, | |||
| 118 | Floating: FloatingConfig{ | 122 | Floating: FloatingConfig{ | ||
| 119 | Width: 80, | 123 | Width: 80, | ||
| 120 | Height: 80, | 124 | Height: 80, | ||