Workspaces named for what they are, capitalised; Reply gains a task panel; Project settings and limits; icon test row

This commit is contained in:
Sam
2026-11-10 16:12:54 +11:00
parent 09e32da046
commit 2640049f1f
15 changed files with 575 additions and 31 deletions

View File

@@ -22,6 +22,7 @@ import (
type replyThreadMsg struct {
task paperclip.Issue
comments []paperclip.Comment
names map[string]string
}
type replySentMsg struct{ task paperclip.Issue }
@@ -33,6 +34,7 @@ type replyModel struct {
explicit bool
task paperclip.Issue
names map[string]string
thread []paperclip.Comment
box string
inBox bool
@@ -75,7 +77,13 @@ func (m replyModel) load() tea.Cmd {
if err != nil {
return errMsg{err}
}
return replyThreadMsg{task: task, comments: cs}
names := map[string]string{}
if agents, err := c.Agents(); err == nil {
for _, a := range agents {
names[a.ID] = a.Name
}
}
return replyThreadMsg{task: task, comments: cs, names: names}
}
}
@@ -192,6 +200,7 @@ func (m replyModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case replyThreadMsg:
m.task = msg.task
m.names = msg.names
wasEmpty := len(m.thread)
m.thread = msg.comments
m.readAt = time.Now()
@@ -251,8 +260,10 @@ func (m replyModel) View() string {
}
composerHeight := max(5, min(9, m.height/4))
threadHeight := m.height - composerHeight - 2
headerHeight := 5
threadHeight := m.height - composerHeight - headerHeight - 2
headerPanel := panel{title: "the task", width: m.width, height: headerHeight}
thread := panel{
title: fmt.Sprintf("thread · %s", fmtCount(len(m.thread), "message", "messages")),
width: m.width, height: threadHeight, focused: !m.inBox,
@@ -305,12 +316,35 @@ func (m replyModel) View() string {
gap := max(2, m.width-lipgloss.Width(head)-lipgloss.Width(state)-1)
return strings.Join([]string{
head + strings.Repeat(" ", gap) + mutedStyle2().Render(state),
headerPanel.render(m.headerLines(m.width - 3)),
thread.render(lines[from:to]),
composer.render(boxLines),
footer,
}, "\n")
}
// headerLines is the panel the conversation needed: what task this is, without
// having to go back to the Board to remember.
func (m replyModel) headerLines(width int) []string {
if m.task.Identifier == "" {
return []string{"", mutedStyle2().Render("no task chosen yet")}
}
who := m.names[m.task.AssigneeAgent]
if who == "" {
who = "nobody yet"
}
status, _ := shortStatus(m.task.Status)
line1 := " " + lipgloss.NewStyle().Foreground(lipgloss.Color(active.Text)).Bold(true).
Render(m.task.Identifier) + " " + status + " " + mutedStyle2().Render("assigned to "+who)
out := []string{"", line1}
out = append(out, " "+truncateVisible(m.task.Title, max(8, width-2)))
if cmd := verifyFromDescription(m.task.Description); cmd != "" {
out = append(out, " "+dimStyle().Render("done when ")+
lipgloss.NewStyle().Foreground(lipgloss.Color(active.Info)).Render(cmd))
}
return out
}
func (m replyModel) composerLines(width, height int) []string {
text := m.box
if text == "" {