From c4d060e651708b80a7b8b4e84a749426de2b0642 Mon Sep 17 00:00:00 2001 From: Jon Gallant <2163001+jongio@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:42:51 -0700 Subject: [PATCH] Copy preview file path with y Closes #196 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/panels/preview/preview.go | 3 ++- internal/panels/preview/selection.go | 13 +++++++++++++ internal/panels/preview/selection_test.go | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/internal/panels/preview/preview.go b/internal/panels/preview/preview.go index acd8498e..6e29f13c 100644 --- a/internal/panels/preview/preview.go +++ b/internal/panels/preview/preview.go @@ -508,6 +508,7 @@ func (p *Preview) Update(msg tea.Msg) (panels.Panel, tea.Cmd) { if p.hasSelection() { return p.copySelection() } + return p.copyFilePath() case "escape", "esc": if p.hasSelection() { p.clearSelection() @@ -631,7 +632,7 @@ func (p *Preview) KeyBindings() []panels.KeyBinding { {Key: "n", Description: "Toggle line numbers", Action: "toggle_line_numbers"}, {Key: "m", Description: "Toggle markdown render", Action: "toggle_markdown_render"}, {Key: "B", Description: "Toggle blame", Action: "toggle_blame"}, - {Key: "y/Ctrl+C", Description: "Copy selection", Action: "copy_selection"}, + {Key: "y/Ctrl+C", Description: "Copy selection or file path", Action: "copy_selection"}, } } diff --git a/internal/panels/preview/selection.go b/internal/panels/preview/selection.go index 513d78de..fdaaaa7f 100644 --- a/internal/panels/preview/selection.go +++ b/internal/panels/preview/selection.go @@ -448,6 +448,19 @@ func (p *Preview) copySelection() (panels.Panel, tea.Cmd) { } } +func (p *Preview) copyFilePath() (panels.Panel, tea.Cmd) { + if p.filePath == "" || p.ghMode { + return p, nil + } + path := p.filePath + return p, func() tea.Msg { + if err := panels.CopyToClipboard(context.Background(), path); err != nil { + return notify.ShowToastMsg{Message: "Copy failed: " + err.Error(), Level: notify.Error} + } + return notify.ShowToastMsg{Message: "Copied path", Level: notify.Info} + } +} + func pluralize(n int, word string) string { if n == 1 { return "1 " + word diff --git a/internal/panels/preview/selection_test.go b/internal/panels/preview/selection_test.go index 1354f4d9..4b716e05 100644 --- a/internal/panels/preview/selection_test.go +++ b/internal/panels/preview/selection_test.go @@ -296,6 +296,25 @@ func TestKeyY_NoSelection_Noop(t *testing.T) { assert.Nil(t, cmd) } +func TestKeyY_NoSelection_CopiesFilePath(t *testing.T) { + p := newTestPreview([]string{"hello"}) + p.filePath = `C:\repo\main.go` + + _, cmd := p.Update(keyMsg("y")) + + assert.NotNil(t, cmd, "should copy the file path when no text is selected") +} + +func TestKeyY_GitHubModeDoesNotCopyFilePath(t *testing.T) { + p := newTestPreview([]string{"issue"}) + p.filePath = `C:\repo\main.go` + p.ghMode = true + + _, cmd := p.Update(keyMsg("y")) + + assert.Nil(t, cmd) +} + func TestKeyEscape_ClearsSelection(t *testing.T) { p := newTestPreview([]string{"hello"}) p.selAnchor = &selPoint{Line: 0, Col: 0}