Work>work chooses its own task and merges from the interface; panes find their tools; shell follows the project folder
This commit is contained in:
119
tui/work.go
119
tui/work.go
@@ -19,6 +19,11 @@ import (
|
||||
"paperclip-harness/tui/paperclip"
|
||||
)
|
||||
|
||||
type pickTasksMsg struct {
|
||||
issues []paperclip.Issue
|
||||
err error
|
||||
}
|
||||
|
||||
type workMsg struct {
|
||||
task paperclip.Issue
|
||||
runs []paperclip.Run
|
||||
@@ -46,13 +51,19 @@ type workModel struct {
|
||||
scroll int
|
||||
tools bool
|
||||
toolAt int
|
||||
readAt time.Time
|
||||
|
||||
// choosing a task here, so the screen does not depend on the Board
|
||||
picking bool
|
||||
pickList []paperclip.Issue
|
||||
pickNames map[string]string
|
||||
pickAt int
|
||||
readAt time.Time
|
||||
|
||||
showHelp bool
|
||||
width, height int
|
||||
}
|
||||
|
||||
var workTabs = []string{"runs", "change", "facts", "review"}
|
||||
var workTabs = []string{"runs", "change", "facts", "review", "merge"}
|
||||
|
||||
func newWorkModel(c *paperclip.Client, folder, issueID string) workModel {
|
||||
return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""}
|
||||
@@ -125,6 +136,34 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize())
|
||||
|
||||
case tea.KeyMsg:
|
||||
if m.picking {
|
||||
switch msg.String() {
|
||||
case "esc", "q", "o":
|
||||
m.picking = false
|
||||
return m, nil
|
||||
case "up", "k":
|
||||
m.pickAt = max(0, m.pickAt-1)
|
||||
return m, nil
|
||||
case "down", "j":
|
||||
m.pickAt = min(len(m.pickList)-1, m.pickAt+1)
|
||||
return m, nil
|
||||
case "enter":
|
||||
if m.pickAt < len(m.pickList) {
|
||||
chosen := m.pickList[m.pickAt]
|
||||
m.picking = false
|
||||
if err := rememberTask(m.folder, chosen.Identifier); err != nil {
|
||||
m.err = err
|
||||
return m, nil
|
||||
}
|
||||
m.explicit = true
|
||||
m.issue = chosen.ID
|
||||
m.task = chosen
|
||||
return m, m.load()
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
if m.tools {
|
||||
switch msg.String() {
|
||||
case "esc", "q", "t":
|
||||
@@ -160,6 +199,39 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case "t":
|
||||
m.tools = true
|
||||
return m, nil
|
||||
case "o":
|
||||
c := m.client
|
||||
folder := m.folder
|
||||
return m, func() tea.Msg {
|
||||
cfg, err := readHarnessFile(folder)
|
||||
if err != nil {
|
||||
return errMsg{err}
|
||||
}
|
||||
issues, err := c.Issues()
|
||||
if err != nil {
|
||||
return errMsg{err}
|
||||
}
|
||||
var mine []paperclip.Issue
|
||||
for _, i := range issues {
|
||||
if i.ProjectID == cfg["paperclip.project"] {
|
||||
mine = append(mine, i)
|
||||
}
|
||||
}
|
||||
return pickTasksMsg{issues: mine}
|
||||
}
|
||||
case "m":
|
||||
if m.task.Identifier == "" {
|
||||
return m, nil
|
||||
}
|
||||
// The merge runs on its own and writes what it does to a log this
|
||||
// tab reads, so nobody has to type a command or leave the screen.
|
||||
if err := startMerge(m.folder, m.task.Identifier); err != nil {
|
||||
m.err = err
|
||||
return m, nil
|
||||
}
|
||||
m.tab = 4
|
||||
m.scroll = 1 << 30
|
||||
return m, m.load()
|
||||
case "?":
|
||||
m.showHelp = true
|
||||
return m, nil
|
||||
@@ -201,6 +273,15 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
case pickTasksMsg:
|
||||
m.pickList = msg.issues
|
||||
m.pickAt = 0
|
||||
m.picking = true
|
||||
if msg.err != nil {
|
||||
m.err = msg.err
|
||||
}
|
||||
return m, nil
|
||||
|
||||
case workMsg:
|
||||
m.task = msg.task
|
||||
m.runs = msg.runs
|
||||
@@ -259,10 +340,23 @@ func (m workModel) View() string {
|
||||
body = m.diffLines(m.width - 4)
|
||||
case 3:
|
||||
body = m.reviewLines(m.width - 4)
|
||||
case 4:
|
||||
body = m.mergeLines(m.width - 4)
|
||||
default:
|
||||
body = m.factsLines(m.width - 4)
|
||||
}
|
||||
|
||||
if m.picking {
|
||||
entries := make([][2]string, 0, len(m.pickList))
|
||||
for i, is := range m.pickList {
|
||||
mark := " "
|
||||
if i == m.pickAt {
|
||||
mark = "▸ "
|
||||
}
|
||||
entries = append(entries, [2]string{mark + is.Identifier, is.Status + " " + is.Title})
|
||||
}
|
||||
return helpOverlay("choose a task to look at", entries, m.width, m.height)
|
||||
}
|
||||
if m.tools {
|
||||
entries := make([][2]string, 0, len(tools))
|
||||
for i, t := range tools {
|
||||
@@ -296,6 +390,9 @@ func (m workModel) View() string {
|
||||
keyHint("[ ]", "switch tab"),
|
||||
keyHint("↑↓ or j k", "scroll"),
|
||||
keyHint("t", "open a tool pane"),
|
||||
keyHint("o", "choose a task"),
|
||||
keyHint("m", "merge this task"),
|
||||
keyHint("]", "tabs"),
|
||||
keyHint("r", "read again"),
|
||||
keyHint("R", "rebuild every pane"),
|
||||
keyHint("q", "leave"),
|
||||
@@ -441,6 +538,24 @@ func (m workModel) reviewLines(width int) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// mergeLines shows the last merge this screen started, read from the log the
|
||||
// merge writes. Pressing m again is safe: the merge takes a lock.
|
||||
func (m workModel) mergeLines(width int) []string {
|
||||
out := []string{"", dimStyle().Render(" " + mergeLogPath()), ""}
|
||||
for _, ln := range mergeLogTail(max(6, m.height-6)) {
|
||||
colour := active.Text
|
||||
switch {
|
||||
case strings.Contains(ln, "refused"), strings.Contains(ln, "stopped"):
|
||||
colour = active.Error
|
||||
case strings.Contains(ln, "merged into"), strings.Contains(ln, "finished"):
|
||||
colour = active.Success
|
||||
}
|
||||
out = append(out, " "+lipgloss.NewStyle().Foreground(lipgloss.Color(colour)).Render(truncateVisible(ln, width-2)))
|
||||
}
|
||||
out = append(out, "", dimStyle().Render(" m runs the checks and merges this task; the log above is what it did"))
|
||||
return out
|
||||
}
|
||||
|
||||
func (m workModel) factsLines(width int) []string {
|
||||
var out []string
|
||||
out = append(out, "")
|
||||
|
||||
Reference in New Issue
Block a user