diff options
Diffstat (limited to 'internal/ui/table.go')
| -rw-r--r-- | internal/ui/table.go | 69 |
1 files changed, 64 insertions, 5 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go index 1cdb0c9..14b5c52 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -45,6 +45,16 @@ type helpSection struct { items []helpItem } +type undoRestore struct { + uuid string + status string +} + +type undoAction struct { + label string + restores []undoRestore +} + // blinkState holds row-level blink animation state for the task table. // A blink cycles the selected row's highlight on/off after a modification. type blinkState struct { @@ -111,6 +121,18 @@ type ultraModeState struct { ultraStartup bool } +// shellState holds the Taskwarrior command prompt and captured output panel. +type shellState struct { + shellActive bool + shellInput textinput.Model + shellHistory []string + shellOutputVisible bool + shellOutputTitle string + shellOutputViewport viewport.Model + shellCompletion task.CompletionSources + shellCompletionLoad bool +} + // editState holds inline field-editing state for the task table. // Each editing mode (annotate, desc, tags, …) is mutually exclusive; // clearEditingModes resets them all before activating a new one. @@ -168,6 +190,7 @@ type Model struct { ultraState // ultra mode task list and search state (see ultraState) detailEditState // detail-overlay external description editor state ultraModeState // ultra-mode lifecycle flags + shellState // Taskwarrior command prompt and output panel editState // inline field editing (see editState) cellExpanded bool @@ -191,7 +214,7 @@ type Model struct { filters []string tasks []task.Task - undoStack []string + undoStack []undoAction browserCmd string agentFilterHotkey string @@ -213,6 +236,16 @@ type descEditDoneMsg struct { tempFile string } +type shellDoneMsg struct { + result task.RunResult + err error + selectedID int +} + +type shellCompletionMsg struct { + sources task.CompletionSources +} + type blinkMsg struct{} type descriptionTempFile interface { @@ -307,6 +340,7 @@ func (m *Model) clearEditingModes() { m.filterEditing = false m.addingTask = false m.searching = false + m.shellActive = false m.prioritySelecting = false } @@ -335,7 +369,7 @@ func (m *Model) startBlink(id int, markDone bool) tea.Cmd { if markDone { for _, tsk := range m.tasks { if tsk.ID == id { - m.undoStack = append(m.undoStack, tsk.UUID) + m.pushUndoAction("done", []undoRestore{{uuid: tsk.UUID, status: "pending"}}) break } } @@ -394,6 +428,8 @@ func New(filters []string, browserCmd string) (Model, error) { m.addInput = textinput.New() m.addInput.Prompt = "add: " + m.shellInput = textinput.New() + m.shellInput.Prompt = "task " m.defaultTheme = DefaultTheme() m.theme = m.defaultTheme @@ -542,6 +578,10 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.handleEditDone(msg) case descEditDoneMsg: return m.handleDescEditDone(msg) + case shellDoneMsg: + return m.handleShellDone(msg) + case shellCompletionMsg: + return m.handleShellCompletion(msg) case blinkMsg: return m.handleBlinkMsg() case struct{ clearStatus bool }: @@ -552,6 +592,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.blinkID != 0 { return m.handleBlinkingState(msg) } + if m.shellOutputVisible { + return m.handleShellOutputMode(msg) + } // Check if we're in detail view if m.showTaskDetail { @@ -608,6 +651,7 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m *Model) handleWindowResize(msg tea.WindowSizeMsg) (tea.Model, tea.Cmd) { m.tbl.SetWidth(msg.Width) m.windowHeight = msg.Height + m.shellInput.SetWidth(msg.Width) m.computeColumnWidths() m.updateTableHeight() if m.showUltra { @@ -627,6 +671,14 @@ func (m *Model) handleWindowResize(msg tea.WindowSizeMsg) (tea.Model, tea.Cmd) { m.helpViewport.SetHeight(height) } } + if m.shellOutputVisible { + height := msg.Height - 2 + if height < 1 { + height = 1 + } + m.shellOutputViewport.SetWidth(msg.Width) + m.shellOutputViewport.SetHeight(height) + } return m, nil } @@ -667,7 +719,7 @@ func (m *Model) handleBlinkMsg() (tea.Model, tea.Cmd) { if mark { for _, tsk := range m.tasks { if tsk.ID == id { - m.undoStack = append(m.undoStack, tsk.UUID) + m.pushUndoAction("done", []undoRestore{{uuid: tsk.UUID, status: "pending"}}) break } } @@ -691,6 +743,8 @@ func (m Model) View() tea.View { content = m.renderHelpScreen() case m.showTaskDetail: content = m.renderDetailScreen() + case m.shellOutputVisible: + content = m.renderShellOutputScreen() case m.showUltra: content = m.renderUltraScreen() default: @@ -728,6 +782,8 @@ func (m Model) appendInlineInputOverlay(view string) string { overlay = m.addInput.View() case m.searching: overlay = m.searchInput.View() + case m.shellActive: + overlay = m.shellInput.View() } if overlay != "" { @@ -907,7 +963,8 @@ func (m Model) helpSections() []helpSection { {key: "+", desc: "add new task"}, {key: "e, E", desc: "edit entire task"}, {key: "d", desc: "mark task done"}, - {key: "U", desc: "undo last done"}, + {key: "D", desc: "delete task/recurring series"}, + {key: "U", desc: "undo last done/delete"}, {key: "s", desc: "start/stop task"}, }, }, @@ -931,6 +988,8 @@ func (m Model) helpSections() []helpSection { items: []helpItem{ {key: m.agentFilterHotkeyLabel(), desc: "toggle +agent/-agent filter"}, {key: "f", desc: "change filter"}, + {key: ":", desc: "run task command prompt"}, + {key: ";", desc: "run task command prompt for selected task"}, {key: "/, ?", desc: "search"}, {key: "n, N", desc: "next/previous match"}, {key: "space", desc: "refresh tasks"}, @@ -1247,7 +1306,7 @@ func (m *Model) updateTableHeight() { if m.cellExpanded { h-- } - if m.annotating || m.dueEditing || m.prioritySelecting || m.searching || m.descEditing || m.tagsEditing || m.recurEditing || m.projEditing || m.filterEditing || m.addingTask { + if m.annotating || m.dueEditing || m.prioritySelecting || m.searching || m.descEditing || m.tagsEditing || m.recurEditing || m.projEditing || m.filterEditing || m.addingTask || m.shellActive { h-- } if h < 1 { |
