diff options
| author | Paul Bütow <1224732+snonux@users.noreply.github.com> | 2025-06-21 23:16:30 +0300 |
|---|---|---|
| committer | Paul Bütow <1224732+snonux@users.noreply.github.com> | 2025-06-21 23:16:30 +0300 |
| commit | d9e09559ad92df8284ac526a8ea4de359a761497 (patch) | |
| tree | 88cc9f2193833efe0e1a53db3d91c3e6aba01d00 /internal | |
| parent | 449f343c7da6071599c5c8266f3f9b43e0b0a315 (diff) | |
Add blinking done effect and update tag hotkey
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/ui/table.go | 88 | ||||
| -rw-r--r-- | internal/ui/table_test.go | 8 |
2 files changed, 78 insertions, 18 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go index 3b1dacf..d0c62a0 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -72,6 +72,11 @@ type Model struct { undoStack []string + blinkID int + blinkRow int + blinkOn bool + blinkCount int + cellExpanded bool windowHeight int @@ -96,6 +101,11 @@ type Model struct { // editDoneMsg is emitted when the external editor process finishes. type editDoneMsg struct{ err error } +type blinkMsg struct{} + +const blinkInterval = 250 * time.Millisecond +const blinkCycles = 8 + // editCmd returns a command that edits the task and sends an // editDoneMsg once the process is complete. func editCmd(id int) tea.Cmd { @@ -103,6 +113,10 @@ func editCmd(id int) tea.Cmd { return tea.ExecProcess(c, func(err error) tea.Msg { return editDoneMsg{err: err} }) } +func blinkCmd() tea.Cmd { + return tea.Tick(blinkInterval, func(time.Time) tea.Msg { return blinkMsg{} }) +} + // New creates a new UI model with the provided rows. func New(filters []string) (Model, error) { m := Model{filters: filters} @@ -223,6 +237,29 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { _ = msg.err m.reload() return m, nil + case blinkMsg: + if m.blinkID != 0 { + m.blinkOn = !m.blinkOn + m.blinkCount++ + m.updateBlinkRow() + if m.blinkCount >= blinkCycles { + id := m.blinkID + 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 + } + } + task.Done(id) + m.reload() + return m, nil + } + return m, blinkCmd() + } + return m, nil case tea.KeyMsg: if m.annotating { switch msg.Type { @@ -460,14 +497,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if row := m.tbl.SelectedRow(); row != nil { idStr := ansi.Strip(row[0]) if id, err := strconv.Atoi(idStr); err == nil { - task.Done(id) - for _, tsk := range m.tasks { - if tsk.ID == id { - m.undoStack = append(m.undoStack, tsk.UUID) - break - } - } - m.reload() + m.blinkID = id + m.blinkRow = m.tbl.Cursor() + m.blinkOn = true + m.blinkCount = 0 + m.updateBlinkRow() + return m, blinkCmd() } } case "U": @@ -542,14 +577,17 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.updateTableHeight() return m, nil case "t": - m.theme = RandomTheme() - m.applyTheme() - m.reload() - return m, nil - case "T": - m.theme = m.defaultTheme - m.applyTheme() - m.reload() + if row := m.tbl.SelectedRow(); row != nil { + idStr := ansi.Strip(row[0]) + if id, err := strconv.Atoi(idStr); err == nil { + m.tagsID = id + m.tagsEditing = true + m.tagsInput.SetValue("") + m.tagsInput.Focus() + m.updateTableHeight() + return m, nil + } + } return m, nil case "/", "?": m.searching = true @@ -675,8 +713,7 @@ func (m Model) View() string { "A: replace annotations", "p: set priority", "f: change filter", - "t: randomize theme", - "T: reset theme", + "t: edit tags", "/, ?: search", "n/N: next/prev search match", "esc: close help/search", @@ -764,6 +801,9 @@ func (m Model) taskToRow(t task.Task) atable.Row { if t.Start != "" { style = style.Background(lipgloss.Color(m.theme.StartBG)) } + if t.ID == m.blinkID && m.blinkOn { + style = style.Reverse(true) + } age := "" if ts, err := time.Parse("20060102T150405Z", t.Entry); err == nil { @@ -896,6 +936,9 @@ func (m Model) taskToRowSearch(t task.Task, re *regexp.Regexp, styles atable.Sty if t.Start != "" { rowStyle = rowStyle.Background(lipgloss.Color(m.theme.StartBG)) } + if t.ID == m.blinkID && m.blinkOn { + rowStyle = rowStyle.Reverse(true) + } age := "" if ts, err := time.Parse("20060102T150405Z", t.Entry); err == nil { @@ -1007,6 +1050,15 @@ func (m *Model) updateSelectionHighlight(prevRow, newRow, prevCol, newCol int) { m.tbl.SetRows(rows) } +func (m *Model) updateBlinkRow() { + if m.blinkRow < 0 || m.blinkRow >= len(m.tasks) || m.tbl.Rows() == nil { + return + } + rows := m.tbl.Rows() + rows[m.blinkRow] = m.taskToRowSearch(m.tasks[m.blinkRow], m.searchRegex, m.tblStyles, -1) + m.tbl.SetRows(rows) +} + // updateTableHeight recalculates the table height based on the current window // size and which auxiliary views are open. func (m *Model) updateTableHeight() { diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index 89e1af6..3bf6f3e 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -164,6 +164,10 @@ func TestDoneHotkey(t *testing.T) { mv, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'D'}}) m = mv.(Model) + for i := 0; i < blinkCycles; i++ { + mv, _ = m.Update(blinkMsg{}) + m = mv.(Model) + } data, err := os.ReadFile(doneFile) if err != nil { @@ -209,6 +213,10 @@ func TestUndoHotkey(t *testing.T) { mv, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'D'}}) m = mv.(Model) + for i := 0; i < blinkCycles; i++ { + mv, _ = m.Update(blinkMsg{}) + m = mv.(Model) + } mv, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'U'}}) m = mv.(Model) |
