diff --git a/demo/demo.gif b/demo/demo.gif new file mode 100644 index 0000000..b282d4a Binary files /dev/null and b/demo/demo.gif differ diff --git a/demo/demo.tape b/demo/demo.tape index f356cc2..9ae5df0 100644 --- a/demo/demo.tape +++ b/demo/demo.tape @@ -2,6 +2,10 @@ # Run `demo/setup.sh` first; it never touches your real Root or History. # # demo/setup.sh && vhs demo/demo.tape +# +# Use VHS v0.11.0 (go install github.com/charmbracelet/vhs@v0.11.0). +# v0.12.0 cancels its own context before rendering and silently writes +# no GIF. Output demo/demo.gif @@ -13,11 +17,12 @@ Set Padding 20 Set Theme "Catppuccin Mocha" Set TypingSpeed 60ms -Env XDG_CONFIG_HOME /tmp/cdd-demo/xdg/config -Env XDG_DATA_HOME /tmp/cdd-demo/xdg/data +Env XDG_CONFIG_HOME "/tmp/cdd-demo/xdg/config" +Env XDG_DATA_HOME "/tmp/cdd-demo/xdg/data" Hide Type "set -gx PATH /tmp/cdd-demo/bin $PATH" Enter +Type "set -g fish_autosuggestion_enabled 0" Enter Type "cdd init fish | source" Enter Type "cd /tmp/cdd-demo/root/lab/notes" Enter Type "clear" Enter diff --git a/demo/setup.sh b/demo/setup.sh index 3b363fa..1057b8f 100755 --- a/demo/setup.sh +++ b/demo/setup.sh @@ -99,6 +99,6 @@ Try it in a shell: export XDG_CONFIG_HOME=$CONFIG XDG_DATA_HOME=$DATA # bash/zsh $BIN/cdd -Record it: +Record it (VHS v0.11.0; v0.12.0 silently writes no GIF): vhs demo/demo.tape MSG diff --git a/internal/picker/view.go b/internal/picker/view.go index 7d86e46..296db8b 100644 --- a/internal/picker/view.go +++ b/internal/picker/view.go @@ -18,7 +18,7 @@ func (m Model) View() tea.View { return tea.NewView("") } if len(m.rows) == 0 { - return tea.NewView(m.emptyHistoryView()) + return fullScreen(m.emptyHistoryView()) } t := newTheme(m.dark) @@ -69,7 +69,17 @@ func (m Model) View() tea.View { b.WriteString("\n") b.WriteString(m.footerView(t, width, len(rows), lay)) - return tea.NewView(b.String()) + return fullScreen(b.String()) +} + +// fullScreen wraps content in a View drawn on the alternate screen. The +// frame always fills the terminal, and the alternate screen guarantees +// the shell's own scrollback comes back untouched when the Picker exits; +// inline rendering left stray lines above the prompt. +func fullScreen(content string) tea.View { + v := tea.NewView(content) + v.AltScreen = true + return v } // emptyHistoryView is shown when there are no rows at all: an empty diff --git a/internal/picker/view_test.go b/internal/picker/view_test.go index 3eca377..9442173 100644 --- a/internal/picker/view_test.go +++ b/internal/picker/view_test.go @@ -152,3 +152,18 @@ func TestModel_View_FrameMatchesTerminalHeight(t *testing.T) { } } } + +// TestModel_View_UsesAlternateScreen pins the Picker to the alternate +// screen so nothing is left above the shell prompt after a Jump or cancel. +func TestModel_View_UsesAlternateScreen(t *testing.T) { + m := picker.NewModel(manyRows(3), noopStatus, picker.Options{}) + next, _ := m.Update(tea.WindowSizeMsg{Width: 100, Height: 30}) + m = next.(picker.Model) + if !m.View().AltScreen { + t.Errorf("View().AltScreen = false, want true") + } + empty := picker.NewModel(nil, noopStatus, picker.Options{}) + if !empty.View().AltScreen { + t.Errorf("empty-History View().AltScreen = false, want true") + } +}