diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-29 12:59:39 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-29 12:59:39 +0300 |
| commit | e5f026af5979a4068055cf69ab34dea472f51950 (patch) | |
| tree | 2e5227cdc67785fc471a7058ec4179ad4551a367 /internal | |
| parent | 53d55ca359bfc5ea53759173c94051714dc0ff7d (diff) | |
fix: resolve text wrapping and task ID issues in TaskSamurai
- Add word wrapping for long descriptions and annotations in detail view
- Fix task ID showing as 0 after undo operation by improving task lookup
- Ensure restored tasks are properly fetched from Taskwarrior with correct IDs
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/ui/keyhandlers.go | 36 | ||||
| -rw-r--r-- | internal/ui/taskdetail.go | 73 |
2 files changed, 100 insertions, 9 deletions
diff --git a/internal/ui/keyhandlers.go b/internal/ui/keyhandlers.go index e806dbf..14f6abc 100644 --- a/internal/ui/keyhandlers.go +++ b/internal/ui/keyhandlers.go @@ -246,17 +246,51 @@ func (m *Model) handleUndo() (tea.Model, tea.Cmd) { return m, nil } - m.reload() + // Reload the task list to get the updated task with its new ID + if err := m.reload(); err != nil { + m.showError(err) + return m, nil + } // Find the task ID for blinking var id int + var found bool for _, tsk := range m.tasks { if tsk.UUID == uuid { id = tsk.ID + found = true break } } + // If task not found or has ID 0, try to get it directly from Taskwarrior + if !found || id == 0 { + // Use task export with UUID filter to get the specific task + filters := []string{uuid} + if m.filters != nil { + filters = append(filters, m.filters...) + } + filters = append(filters, "status:pending") + + tasks, err := task.Export(filters...) + if err == nil && len(tasks) > 0 { + id = tasks[0].ID + // Also update our local task list + for i, tsk := range m.tasks { + if tsk.UUID == uuid { + m.tasks[i].ID = id + break + } + } + } + } + + // If we still don't have a valid ID, don't try to blink + if id == 0 { + m.statusMsg = "Task restored" + return m, nil + } + return m, m.startBlink(id, false) } diff --git a/internal/ui/taskdetail.go b/internal/ui/taskdetail.go index c7a5b89..0fb8cb4 100644 --- a/internal/ui/taskdetail.go +++ b/internal/ui/taskdetail.go @@ -8,6 +8,36 @@ import ( "github.com/charmbracelet/lipgloss" ) +// wordWrap wraps text to fit within the specified width, breaking at word boundaries +func wordWrap(text string, width int) []string { + if width <= 0 { + return []string{text} + } + + var lines []string + words := strings.Fields(text) + if len(words) == 0 { + return []string{""} + } + + currentLine := words[0] + for i := 1; i < len(words); i++ { + word := words[i] + testLine := currentLine + " " + word + if len(testLine) > width { + lines = append(lines, currentLine) + currentLine = word + } else { + currentLine = testLine + } + } + if currentLine != "" { + lines = append(lines, currentLine) + } + + return lines +} + // Define field indices for navigation const ( fieldID = iota @@ -177,12 +207,24 @@ func (m *Model) renderTaskDetail() string { Italic(true) lines = append(lines, editingStyle.Render(" [Editing in external editor...]")) } else if t.Description != "" { - // Highlight search matches if searching + // Calculate available width for description (terminal width - margins) + availableWidth := m.tbl.Width() - 4 + if availableWidth < 20 { + availableWidth = 20 // Minimum width + } + + // Wrap the description text desc := t.Description - if m.detailSearchRegex != nil && m.detailSearchRegex.MatchString(desc) { - desc = m.highlightMatches(desc, m.detailSearchRegex) + wrappedLines := wordWrap(desc, availableWidth) + + // Apply search highlighting and render each wrapped line + for _, line := range wrappedLines { + displayLine := line + if m.detailSearchRegex != nil && m.detailSearchRegex.MatchString(line) { + displayLine = m.highlightMatches(line, m.detailSearchRegex) + } + lines = append(lines, descValueStyle.Render(displayLine)) } - lines = append(lines, descValueStyle.Render(desc)) } else { lines = append(lines, descValueStyle.Render("-")) } @@ -198,13 +240,28 @@ func (m *Model) renderTaskDetail() string { annValueStyle = annValueStyle.Background(lipgloss.Color(m.theme.SelectedBG)) } lines = append(lines, annLabelStyle.Render("Annotations:")) + // Calculate available width for annotations + availableWidth := m.tbl.Width() - 4 + if availableWidth < 20 { + availableWidth = 20 // Minimum width + } + for _, ann := range t.Annotations { annText := fmt.Sprintf("[%s] %s", m.formatTaskDate(ann.Entry), ann.Description) - // Highlight search matches - if m.detailSearchRegex != nil && m.detailSearchRegex.MatchString(annText) { - annText = m.highlightMatches(annText, m.detailSearchRegex) + wrappedAnnLines := wordWrap(annText, availableWidth) + + // Apply search highlighting and render each wrapped line + for i, line := range wrappedAnnLines { + displayLine := line + if m.detailSearchRegex != nil && m.detailSearchRegex.MatchString(line) { + displayLine = m.highlightMatches(line, m.detailSearchRegex) + } + // Add some indentation for continuation lines + if i > 0 { + displayLine = " " + displayLine + } + lines = append(lines, annValueStyle.Render(displayLine)) } - lines = append(lines, annValueStyle.Render(annText)) } } |
