feat: copy thread / conversation
This commit is contained in:
24
tui.go
24
tui.go
@@ -148,6 +148,7 @@ type App struct {
|
||||
secondaryLoading bool
|
||||
err error
|
||||
lastRefresh time.Time
|
||||
notice string
|
||||
pendingZ bool
|
||||
searching bool
|
||||
searchQuery string
|
||||
@@ -175,6 +176,7 @@ type App struct {
|
||||
prEditUserIndex int
|
||||
prEditGeneration uint64
|
||||
cursorOutput *terminalCursorOutput
|
||||
clipboard textClipboard
|
||||
|
||||
foldResolved bool
|
||||
threadListWidthPercent int
|
||||
@@ -291,6 +293,7 @@ func NewAppWithSettings(
|
||||
viewerLabel: firstNonEmpty(settings.ViewerLabel, "login"),
|
||||
editorMode: settings.EditorMode,
|
||||
keybindings: settings.KeyBindings,
|
||||
clipboard: systemTextClipboard{},
|
||||
readState: state,
|
||||
drafts: settings.Drafts,
|
||||
mutations: settings.Mutations,
|
||||
@@ -981,6 +984,15 @@ func (m App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.recordHealth(issue.Component, healthWarning, issue.Message)
|
||||
}
|
||||
return m, m.difflet.setState(m.restingDiffletState())
|
||||
case threadCopiedMsg:
|
||||
if msg.err != nil {
|
||||
m.err = fmt.Errorf("copy review thread: %w", msg.err)
|
||||
m.notice = ""
|
||||
return m, m.difflet.setState(diffletRecoverableError)
|
||||
}
|
||||
m.err = nil
|
||||
m.notice = "thread copied"
|
||||
return m, m.difflet.setState(diffletSuccess)
|
||||
case draftFlushMsg:
|
||||
if msg.err != nil {
|
||||
m.recordHealth("draft persistence", healthWarning, msg.err.Error())
|
||||
@@ -1346,6 +1358,7 @@ func (m App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
rawKey := k
|
||||
m.notice = ""
|
||||
k = m.keybindings.canonicalMainKey(k, m.screen)
|
||||
if k == "ctrl+c" || k == "q" {
|
||||
return m, tea.Quit
|
||||
@@ -1466,6 +1479,10 @@ func (m App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
if m.screen == threadScreen {
|
||||
m.markCurrentThreadRead()
|
||||
}
|
||||
case "y":
|
||||
if m.screen == threadScreen {
|
||||
return m, m.copySelectedThread()
|
||||
}
|
||||
case "d":
|
||||
if m.screen == prScreen && len(m.prs) > 0 {
|
||||
return m, m.openSelectedPR(dashboardScreen)
|
||||
@@ -2677,6 +2694,7 @@ func (m App) helpBindings() []helpBinding {
|
||||
{keyLabel(m.keybindings.Threads.ClearFilter), "Clear the active thread filter and show every thread"},
|
||||
{combinedKeyLabel(m.keybindings.Threads.NextUnread, m.keybindings.Threads.PreviousUnread), "Next / previous new update"},
|
||||
{keyLabel(m.keybindings.Threads.MarkRead), "Mark the selected thread read"},
|
||||
{keyLabel(m.keybindings.Threads.Copy), "Copy the selected thread and local AI conversation for an LLM"},
|
||||
{keyLabel(m.keybindings.Threads.Reply), "Compose a reply to the selected thread"},
|
||||
{keyLabel(m.keybindings.Threads.Resolve), "Resolve or unresolve the selected thread"},
|
||||
{keyLabel(m.keybindings.Views.AI), "Open the local AI review or selected-thread discussion menu"},
|
||||
@@ -3782,10 +3800,11 @@ func (m App) viewThreads() string {
|
||||
body = lipgloss.JoinHorizontal(lipgloss.Top, left, " ", right)
|
||||
}
|
||||
help := fmt.Sprintf(
|
||||
"%s keys • %s focus • %s move/scroll • %s AI • %s reply • %s resolve • %s dashboard • %s back • %s quit",
|
||||
"%s keys • %s focus • %s move/scroll • %s copy • %s AI • %s reply • %s resolve • %s dashboard • %s back • %s quit",
|
||||
primaryKeyLabel(m.keybindings.General.Help),
|
||||
primaryCombinedKeyLabel(m.keybindings.Navigation.Left, m.keybindings.Navigation.Right),
|
||||
primaryCombinedKeyLabel(m.keybindings.Navigation.Down, m.keybindings.Navigation.Up),
|
||||
primaryKeyLabel(m.keybindings.Threads.Copy),
|
||||
primaryKeyLabel(m.keybindings.Views.AI),
|
||||
primaryKeyLabel(m.keybindings.Threads.Reply),
|
||||
primaryKeyLabel(m.keybindings.Threads.Resolve),
|
||||
@@ -4565,6 +4584,9 @@ func (m App) frame(lines []string, help string) string {
|
||||
if m.err != nil {
|
||||
status = badStyle.Render("error: " + truncate(m.err.Error(), max(20, m.width-8)))
|
||||
}
|
||||
if status == "" && m.notice != "" {
|
||||
status = okStyle.Render(m.notice)
|
||||
}
|
||||
// Keep an already-rendered screen byte-for-byte stable while a background
|
||||
// refresh starts. Changing only this footer on a full-height alternate
|
||||
// screen makes some terminals clear and repaint the entire frame.
|
||||
|
||||
Reference in New Issue
Block a user