Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion internal/tui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ type App struct {
sessRefsCursor int // cursor within the References preview list
sessRefsResolved bool // whether the currently-previewed session's refs have been resolved
refsInFlight map[string]bool // session IDs with a resolve pass currently running (prevents re-targeting every tick)
openURL func(string) error // opens a URL in the browser; overridable in tests (defaults to `open`)

// Conversation preview state
sessConvEntries []mergedMsg // merged conversation messages
Expand Down Expand Up @@ -1807,6 +1808,13 @@ func (a *App) handleSessionKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
m, cmd, _ := a.drillIntoWorkflowAgent()
return m, cmd
}
// If References preview is focused, open the PR/Jira URL under the cursor
// in the browser (mirrors `o`; without this Enter would fall through to
// openConversation).
if sp.Focus && sp.Show && a.sessPreviewMode == sessPreviewRefs && len(a.sessPreviewRefs) > 0 {
m, cmd, _ := a.openRefUnderCursor()
return m, cmd
}
sess, ok := a.selectedSession()
if !ok {
return a, nil
Expand Down Expand Up @@ -2278,14 +2286,23 @@ func (a *App) openRefUnderCursor() (tea.Model, tea.Cmd, bool) {
a.copiedMsg = "No URL for this reference"
return a, nil, true
}
if err := exec.Command("open", ref.URL).Start(); err != nil {
if err := a.openInBrowser(ref.URL); err != nil {
a.copiedMsg = "Error: " + err.Error()
return a, nil, true
}
a.copiedMsg = "Opened " + ref.Label
return a, nil, true
}

// openInBrowser opens a URL in the default browser. The opener is overridable
// (a.openURL) so tests can intercept it instead of spawning `open`.
func (a *App) openInBrowser(url string) error {
if a.openURL != nil {
return a.openURL(url)
}
return exec.Command("open", url).Start()
}

// drillIntoWorkflowAgent opens the conversation view for the current session and
// navigates into the workflow agent under the cursor — its full transcript,
// which the workflow run summary only previews.
Expand Down
39 changes: 39 additions & 0 deletions internal/tui/refs_preview_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,42 @@ func TestRenderSessionRefs(t *testing.T) {
t.Error("focused render missing open hint")
}
}

// TestRefsPreviewEnterOpensURL guards the reported bug: with the References
// preview focused, Enter on a PR/Jira entry must open its URL in the browser —
// NOT fall through to km.Session.Open and open the conversation. `o` already
// worked; Enter was intercepted by the Session.Open case, which had no refs
// branch. Both Enter and `o` must open the URL and leave the view unchanged.
func TestRefsPreviewEnterOpensURL(t *testing.T) {
for _, key := range []tea.KeyMsg{
{Type: tea.KeyEnter},
{Type: tea.KeyRunes, Runes: []rune{'o'}},
} {
sessions := fakeSessions()
a := newTestApp(sessions)
a.sessionList.Select(0)

// Simulate a focused, populated References preview.
a.sessPreviewMode = sessPreviewRefs
a.sessSplit.Show = true
a.sessSplit.Focus = true
a.sessPreviewRefs = []session.SessionRef{
{Kind: session.RefPR, URL: "https://github.com/sendbird/ccx/pull/62",
Label: "sendbird/ccx#62", State: session.RefStateOpen, Resolved: true},
}
a.sessRefsCursor = 0

var opened string
a.openURL = func(u string) error { opened = u; return nil }

m, _ := a.Update(key)
got := m.(*App)

if opened != "https://github.com/sendbird/ccx/pull/62" {
t.Errorf("key %v: expected the ref URL to be opened, got %q", key, opened)
}
if got.state != viewSessions {
t.Errorf("key %v: view changed to %v — Enter leaked to openConversation", key, got.state)
}
}
}
Loading