Work>review and tool panes; panes re-sync their size; the Work pane follows the Board again

This commit is contained in:
Sam
2026-11-10 18:06:07 +11:00
parent ec7e907b2e
commit 4a23f05d20
18 changed files with 884 additions and 25 deletions

View File

@@ -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, "")