Work>review and tool panes; panes re-sync their size; the Work pane follows the Board again
This commit is contained in:
86
tui/work.go
86
tui/work.go
@@ -44,20 +44,22 @@ type workModel struct {
|
||||
|
||||
tab int
|
||||
scroll int
|
||||
tools bool
|
||||
toolAt int
|
||||
readAt time.Time
|
||||
|
||||
showHelp bool
|
||||
width, height int
|
||||
}
|
||||
|
||||
var workTabs = []string{"runs", "change", "facts"}
|
||||
var workTabs = []string{"runs", "change", "facts", "review"}
|
||||
|
||||
func newWorkModel(c *paperclip.Client, folder, issueID string) workModel {
|
||||
return workModel{client: c, folder: folder, issue: issueID, explicit: issueID != ""}
|
||||
}
|
||||
|
||||
func (m workModel) Init() tea.Cmd {
|
||||
return tea.Batch(m.load(), tickEvery(5*time.Second))
|
||||
return tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize())
|
||||
}
|
||||
|
||||
func (m workModel) load() tea.Cmd {
|
||||
@@ -120,14 +122,38 @@ func (m workModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
|
||||
case tickMsg:
|
||||
return m, tea.Batch(m.load(), tickEvery(5*time.Second))
|
||||
return m, tea.Batch(m.load(), tickEvery(5*time.Second), tea.WindowSize())
|
||||
|
||||
case tea.KeyMsg:
|
||||
if m.tools {
|
||||
switch msg.String() {
|
||||
case "esc", "q", "t":
|
||||
m.tools = false
|
||||
return m, nil
|
||||
case "up", "k":
|
||||
m.toolAt = max(0, m.toolAt-1)
|
||||
return m, nil
|
||||
case "down", "j":
|
||||
m.toolAt = min(len(tools)-1, m.toolAt+1)
|
||||
return m, nil
|
||||
case "enter":
|
||||
t := tools[m.toolAt]
|
||||
m.tools = false
|
||||
if err := openTool(m.folder, t.name); err != nil {
|
||||
m.err = err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
if m.showHelp {
|
||||
m.showHelp = false
|
||||
return m, nil
|
||||
}
|
||||
switch msg.String() {
|
||||
case "t":
|
||||
m.tools = true
|
||||
return m, nil
|
||||
case "?":
|
||||
m.showHelp = true
|
||||
return m, nil
|
||||
@@ -225,10 +251,24 @@ func (m workModel) View() string {
|
||||
body = m.runsLines(m.width - 4)
|
||||
case 1:
|
||||
body = m.diffLines(m.width - 4)
|
||||
case 3:
|
||||
body = m.reviewLines(m.width - 4)
|
||||
default:
|
||||
body = m.factsLines(m.width - 4)
|
||||
}
|
||||
|
||||
if m.tools {
|
||||
entries := make([][2]string, 0, len(tools))
|
||||
for i, t := range tools {
|
||||
mark := " "
|
||||
if i == m.toolAt {
|
||||
mark = "▸ "
|
||||
}
|
||||
entries = append(entries, [2]string{mark + t.name, t.about})
|
||||
}
|
||||
return helpOverlay("open a tool pane here", entries, m.width, m.height)
|
||||
}
|
||||
|
||||
p := panel{
|
||||
title: "the work", width: m.width, height: m.height - 2,
|
||||
tabs: workTabs, active: m.tab, focused: true,
|
||||
@@ -249,6 +289,7 @@ func (m workModel) View() string {
|
||||
footer := hintLine([]string{
|
||||
keyHint("[ ]", "switch tab"),
|
||||
keyHint("↑↓ or j k", "scroll"),
|
||||
keyHint("t", "open a tool pane"),
|
||||
keyHint("r", "read again"),
|
||||
keyHint("R", "rebuild every pane"),
|
||||
keyHint("q", "leave"),
|
||||
@@ -355,6 +396,45 @@ func (m workModel) diffLines(width int) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// reviewLines shows the verdicts already recorded on the task. A review is
|
||||
// written as a comment, so the record lives with the work and survives us.
|
||||
func (m workModel) reviewLines(width int) []string {
|
||||
out := []string{"", dimStyle().Render(" verdicts recorded on this task")}
|
||||
if m.task.Identifier == "" {
|
||||
return append(out, mutedStyle2().Render(" no task chosen"))
|
||||
}
|
||||
cs, err := m.client.Comments(m.task.ID)
|
||||
if err != nil {
|
||||
return append(out, lipgloss.NewStyle().Foreground(lipgloss.Color(active.Error)).Render(" "+err.Error()))
|
||||
}
|
||||
found := 0
|
||||
for i := len(cs) - 1; i >= 0 && found < 3; i-- {
|
||||
body := cs[i].Body
|
||||
if !strings.HasPrefix(body, "## Review") &&
|
||||
!strings.HasPrefix(body, "## Merge refused") &&
|
||||
!strings.HasPrefix(body, "## Merged") &&
|
||||
!strings.HasPrefix(body, "## Merge undone") {
|
||||
continue
|
||||
}
|
||||
found++
|
||||
blocking := strings.Contains(body, "critical") || strings.Contains(body, "**high/")
|
||||
colour := active.Success
|
||||
if strings.HasPrefix(body, "## Merge refused") || blocking {
|
||||
colour = active.Error
|
||||
}
|
||||
for _, ln := range wrap(strings.ReplaceAll(body, "\n", " · "), width-2) {
|
||||
out = append(out, " "+lipgloss.NewStyle().Foreground(lipgloss.Color(colour)).Render(ln))
|
||||
}
|
||||
out = append(out, "")
|
||||
}
|
||||
if found == 0 {
|
||||
out = append(out, mutedStyle2().Render(" no review recorded for this task yet"))
|
||||
out = append(out, "")
|
||||
out = append(out, dimStyle().Render(" run one with: bin/review "+m.task.Identifier+" --post"))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m workModel) factsLines(width int) []string {
|
||||
var out []string
|
||||
out = append(out, "")
|
||||
|
||||
Reference in New Issue
Block a user