summaryrefslogtreecommitdiff
path: root/internal/ui/table.go
diff options
context:
space:
mode:
authorPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-22 17:12:01 +0300
committerGitHub <noreply@github.com>2025-06-22 17:12:01 +0300
commit8a74c22d7a0708c79cbba40ccf85f32f568326e6 (patch)
treea7881192a026b8d4b472e5af872db40f8b3b0728 /internal/ui/table.go
parent97ca49e85034cf6b57a6515407f8a73b440755ee (diff)
parent90f2d2efbf374f6ea82a46d2d701a099dbae1c89 (diff)
Merge pull request #83 from snonux/codex/fix-task-modification-bug-and-swap-hotkeys
Fix done marking bug and swap hotkeys
Diffstat (limited to 'internal/ui/table.go')
-rw-r--r--internal/ui/table.go139
1 files changed, 106 insertions, 33 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go
index 3d26f41..47ab784 100644
--- a/internal/ui/table.go
+++ b/internal/ui/table.go
@@ -61,6 +61,9 @@ type Model struct {
filterEditing bool
filterInput textinput.Model
+ addingTask bool
+ addInput textinput.Model
+
searching bool
searchInput textinput.Model
searchRegex *regexp.Regexp
@@ -78,10 +81,11 @@ type Model struct {
editID int
- blinkID int
- blinkRow int
- blinkOn bool
- blinkCount int
+ blinkID int
+ blinkRow int
+ blinkOn bool
+ blinkCount int
+ blinkMarkDone bool
cellExpanded bool
@@ -124,8 +128,9 @@ func blinkCmd() tea.Cmd {
return tea.Tick(blinkInterval, func(time.Time) tea.Msg { return blinkMsg{} })
}
-func (m *Model) startBlink(id int) tea.Cmd {
+func (m *Model) startBlink(id int, markDone bool) tea.Cmd {
m.blinkID = id
+ m.blinkMarkDone = markDone
m.blinkRow = -1
for i, tsk := range m.tasks {
if tsk.ID == id {
@@ -159,6 +164,9 @@ func New(filters []string) (Model, error) {
m.filterInput = textinput.New()
m.filterInput.Prompt = "filter: "
+ m.addInput = textinput.New()
+ m.addInput.Prompt = "add: "
+
m.defaultTheme = DefaultTheme()
m.theme = m.defaultTheme
@@ -264,7 +272,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Ignore any error and reload tasks once editing completes.
_ = msg.err
m.reload()
- cmd := m.startBlink(m.editID)
+ cmd := m.startBlink(m.editID, false)
m.editID = 0
return m, cmd
case blinkMsg:
@@ -274,16 +282,20 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.updateBlinkRow()
if m.blinkCount >= blinkCycles {
id := m.blinkID
+ mark := m.blinkMarkDone
m.blinkID = 0
m.blinkOn = false
m.blinkCount = 0
- for _, tsk := range m.tasks {
- if tsk.ID == id {
- m.undoStack = append(m.undoStack, tsk.UUID)
- break
+ m.blinkMarkDone = false
+ if mark {
+ for _, tsk := range m.tasks {
+ if tsk.ID == id {
+ m.undoStack = append(m.undoStack, tsk.UUID)
+ break
+ }
}
+ task.Done(id)
}
- task.Done(id)
m.reload()
return m, nil
}
@@ -303,7 +315,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.annotating = false
m.annotateInput.Blur()
m.reload()
- cmd := m.startBlink(m.annotateID)
+ cmd := m.startBlink(m.annotateID, false)
m.updateTableHeight()
return m, cmd
case tea.KeyEsc:
@@ -324,7 +336,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.descEditing = false
m.descInput.Blur()
m.reload()
- cmd := m.startBlink(m.descID)
+ cmd := m.startBlink(m.descID, false)
m.updateTableHeight()
return m, cmd
case tea.KeyEsc:
@@ -365,7 +377,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.tagsEditing = false
m.tagsInput.Blur()
m.reload()
- cmd := m.startBlink(m.tagsID)
+ cmd := m.startBlink(m.tagsID, false)
m.updateTableHeight()
return m, cmd
case tea.KeyEsc:
@@ -384,7 +396,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
task.SetDueDate(m.dueID, m.dueDate.Format("2006-01-02"))
m.dueEditing = false
m.reload()
- cmd := m.startBlink(m.dueID)
+ cmd := m.startBlink(m.dueID, false)
m.updateTableHeight()
return m, cmd
case tea.KeyEsc:
@@ -411,7 +423,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.recurEditing = false
m.recurInput.Blur()
m.reload()
- cmd := m.startBlink(m.recurID)
+ cmd := m.startBlink(m.recurID, false)
m.updateTableHeight()
return m, cmd
case tea.KeyEsc:
@@ -430,7 +442,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
task.SetPriority(m.priorityID, priorityOptions[m.priorityIndex])
m.prioritySelecting = false
m.reload()
- cmd := m.startBlink(m.priorityID)
+ cmd := m.startBlink(m.priorityID, false)
m.updateTableHeight()
return m, cmd
case tea.KeyEsc:
@@ -465,6 +477,46 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.filterInput, cmd = m.filterInput.Update(msg)
return m, cmd
}
+ if m.addingTask {
+ switch msg.Type {
+ case tea.KeyEnter:
+ oldIDs := make(map[int]struct{})
+ for _, tsk := range m.tasks {
+ oldIDs[tsk.ID] = struct{}{}
+ }
+ task.AddLine(m.addInput.Value())
+ m.addingTask = false
+ m.addInput.Blur()
+ m.reload()
+ var newID int
+ row := -1
+ for i, tsk := range m.tasks {
+ if _, ok := oldIDs[tsk.ID]; !ok {
+ newID = tsk.ID
+ row = i
+ break
+ }
+ }
+ m.updateTableHeight()
+ if row >= 0 {
+ prevRow := m.tbl.Cursor()
+ prevCol := m.tbl.ColumnCursor()
+ m.tbl.SetCursor(row)
+ m.tbl.SetColumnCursor(7)
+ m.updateSelectionHighlight(prevRow, m.tbl.Cursor(), prevCol, m.tbl.ColumnCursor())
+ return m, m.startBlink(newID, false)
+ }
+ return m, nil
+ case tea.KeyEsc:
+ m.addingTask = false
+ m.addInput.Blur()
+ m.updateTableHeight()
+ return m, nil
+ }
+ var cmd tea.Cmd
+ m.addInput, cmd = m.addInput.Update(msg)
+ return m, cmd
+ }
if m.searching {
switch msg.Type {
case tea.KeyEnter:
@@ -547,20 +599,15 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
task.Start(id)
}
m.reload()
- cmd := m.startBlink(id)
+ cmd := m.startBlink(id, false)
return m, cmd
}
}
- case "D":
+ case "d":
if row := m.tbl.SelectedRow(); row != nil {
idStr := ansi.Strip(row[1])
if id, err := strconv.Atoi(idStr); err == nil {
- m.blinkID = id
- m.blinkRow = m.tbl.Cursor()
- m.blinkOn = true
- m.blinkCount = 0
- m.updateBlinkRow()
- return m, blinkCmd()
+ return m, m.startBlink(id, true)
}
}
case "U":
@@ -576,10 +623,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
break
}
}
- cmd := m.startBlink(id)
+ cmd := m.startBlink(id, false)
return m, cmd
}
- case "d":
+ case "D":
if row := m.tbl.SelectedRow(); row != nil {
idStr := ansi.Strip(row[1])
if id, err := strconv.Atoi(idStr); err == nil {
@@ -598,7 +645,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
due := time.Now().AddDate(0, 0, days).Format("2006-01-02")
task.SetDueDate(id, due)
m.reload()
- cmd := m.startBlink(id)
+ cmd := m.startBlink(id, false)
return m, cmd
}
}
@@ -657,6 +704,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.filterInput.Focus()
m.updateTableHeight()
return m, nil
+ case "+":
+ m.addingTask = true
+ m.addInput.SetValue("")
+ m.addInput.Focus()
+ m.updateTableHeight()
+ return m, nil
case "t":
if row := m.tbl.SelectedRow(); row != nil {
idStr := ansi.Strip(row[1])
@@ -796,14 +849,15 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// View renders the table UI.
func (m Model) View() string {
if m.showHelp {
- return lipgloss.JoinVertical(lipgloss.Left,
+ lines := []string{
m.tbl.HelpView(),
"enter/i: edit or expand cell",
"E: edit task",
+ "+: add task",
"s: toggle start/stop",
- "D: mark task done",
+ "d: mark task done",
"U: undo done",
- "d: set due date",
+ "D: set due date",
"r: random due date",
"R: edit recurrence",
"a: annotate task",
@@ -818,7 +872,11 @@ func (m Model) View() string {
"esc: close help/search",
"q: quit",
"H: help", // show help toggle line
- )
+ }
+ for i, l := range lines {
+ lines[i] = centerLines(l, m.tbl.Width())
+ }
+ return lipgloss.JoinVertical(lipgloss.Top, lines...)
}
view := lipgloss.JoinVertical(lipgloss.Left,
m.topStatusLine(),
@@ -874,6 +932,12 @@ func (m Model) View() string {
m.filterInput.View(),
)
}
+ if m.addingTask {
+ view = lipgloss.JoinVertical(lipgloss.Left,
+ view,
+ m.addInput.View(),
+ )
+ }
if m.searching {
view = lipgloss.JoinVertical(lipgloss.Left,
view,
@@ -1187,7 +1251,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.filterEditing {
+ if m.annotating || m.dueEditing || m.prioritySelecting || m.searching || m.descEditing || m.tagsEditing || m.recurEditing || m.filterEditing || m.addingTask {
h--
}
if h < 1 {
@@ -1303,3 +1367,12 @@ func (m *Model) applyTheme() {
m.tblStyles.Highlight = m.tblStyles.Highlight.Background(lipgloss.Color(m.theme.RowBG)).Foreground(lipgloss.Color(m.theme.RowFG))
m.tbl.SetStyles(m.tblStyles)
}
+
+func centerLines(s string, width int) string {
+ lines := strings.Split(strings.TrimRight(s, "\n"), "\n")
+ style := lipgloss.NewStyle().Width(width).Align(lipgloss.Center)
+ for i, l := range lines {
+ lines[i] = style.Render(l)
+ }
+ return strings.Join(lines, "\n")
+}