feat(tui-go): idea board viewer

OverlayIdeas (opened with I, global/cross-session) lists the idea board in the
artifact-viewer shell: ↑↓ to move, x/d to remove (sends DiscardIdea, optimistic
drop), esc to close. Pull-based via ListIdeas -> idea.list.
This commit is contained in:
2026-06-14 14:36:13 +04:00
parent 04e931c860
commit 02e963d6cc
8 changed files with 250 additions and 1 deletions
+35
View File
@@ -176,6 +176,8 @@ func (m Model) handleNormalKey(k tea.KeyMsg) (tea.Model, tea.Cmd) {
m.overlay = OverlayToolPalette
case "v":
m.openArtifacts()
case "I":
m.openIdeas()
case "S":
m.openStats()
case "g":
@@ -442,6 +444,28 @@ func (m Model) handleOverlayKey(k tea.KeyMsg) (tea.Model, tea.Cmd) {
if runeIs(k, "S") {
m.overlay = OverlayNone
}
case OverlayIdeas:
switch {
case k.Type == tea.KeyUp || runeIs(k, "k"):
if m.ideasIndex > 0 {
m.ideasIndex--
}
case k.Type == tea.KeyDown || runeIs(k, "j"):
if m.ideasIndex < len(m.ideas)-1 {
m.ideasIndex++
}
case runeIs(k, "x") || runeIs(k, "d"):
if m.ideasIndex >= 0 && m.ideasIndex < len(m.ideas) {
m.client.Send(protocol.DiscardIdea(m.ideas[m.ideasIndex].IdeaID))
// Optimistic removal; the server's fresh idea.list reply reconciles.
m.ideas = append(m.ideas[:m.ideasIndex], m.ideas[m.ideasIndex+1:]...)
if m.ideasIndex >= len(m.ideas) && m.ideasIndex > 0 {
m.ideasIndex--
}
}
case runeIs(k, "I"):
m.overlay = OverlayNone
}
}
return m, nil
}
@@ -463,6 +487,17 @@ func (m *Model) openArtifacts() {
m.client.Send(protocol.ListArtifacts(m.selectedID))
}
// openIdeas opens the cross-session idea board and requests it from the server. The board
// is global (not session-scoped), so it opens from anywhere — including the idle list.
func (m *Model) openIdeas() {
m.overlay = OverlayIdeas
m.ideasIndex = 0
if m.ideas == nil {
m.ideasLoading = true
}
m.client.Send(protocol.ListIdeas())
}
// openStats opens the session-stats pane for the selected session and requests its
// metrics from the server. No-op when no session is selected.
func (m *Model) openStats() {