From e2ef4d55f2da0959c832a530e68ea0f519bffdd1 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 28 Mar 2026 09:28:35 +0200 Subject: ui: render ultra mode screen (vo) --- internal/ui/ultra.go | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 2 deletions(-) diff --git a/internal/ui/ultra.go b/internal/ui/ultra.go index f9c5b42..ab94bfb 100644 --- a/internal/ui/ultra.go +++ b/internal/ui/ultra.go @@ -12,9 +12,127 @@ import ( "codeberg.org/snonux/tasksamurai/internal/task" ) -// renderUltraModus is still a placeholder until the full ultra-mode layout lands. func (m *Model) renderUltraModus() string { - return "Ultra Modus (TODO)" + tasks := m.ultraTaskList() + width := m.ultraRenderWidth() + + top := m.ultraStatusLine(m.ultraModeStatus(tasks), width) + bottom := m.ultraStatusLine(m.ultraCursorStatus(tasks), width) + overlay, overlayHeight := m.ultraSearchOverlay() + + var lines []string + lines = append(lines, top) + lines = append( + lines, + m.ultraRenderCards( + tasks, + width, + m.ultraVisibleCursor(tasks), + m.ultraVisibleStart(len(tasks)), + m.ultraCardBudget(top, bottom, overlayHeight), + )..., + ) + lines = append(lines, bottom) + if overlay != "" { + lines = append(lines, overlay) + } + return strings.Join(lines, "\n") +} + +func (m *Model) ultraRenderWidth() int { + width := m.tbl.Width() + if width <= 0 { + return 80 + } + return width +} + +func (m *Model) ultraSearchOverlay() (string, int) { + if !m.ultraSearching { + return "", 0 + } + + overlay := lipgloss.NewStyle(). + Foreground(lipgloss.Color("248")). + PaddingTop(1). + Render(m.ultraSearchInput.View()) + return overlay, lipgloss.Height(overlay) +} + +func (m *Model) ultraCardBudget(top, bottom string, overlayHeight int) int { + budget := m.windowHeight - lipgloss.Height(top) - lipgloss.Height(bottom) - overlayHeight + if budget < 0 { + return 0 + } + return budget +} + +func (m *Model) ultraVisibleStart(total int) int { + start := m.ultraOffset + if start < 0 { + return 0 + } + if start > total { + return total + } + return start +} + +func (m *Model) ultraTaskList() []task.Task { + if m.ultraFiltered == nil { + return m.tasks + } + + tasks := make([]task.Task, 0, len(m.ultraFiltered)) + for _, idx := range m.ultraFiltered { + if idx < 0 || idx >= len(m.tasks) { + continue + } + tasks = append(tasks, m.tasks[idx]) + } + return tasks +} + +// ultraVisibleCursor treats ultraCursor as a cursor within the visible ultra task list. +func (m *Model) ultraVisibleCursor(tasks []task.Task) int { + if len(tasks) == 0 { + return -1 + } + if m.ultraCursor < 0 { + return 0 + } + if m.ultraCursor >= len(tasks) { + return len(tasks) - 1 + } + return m.ultraCursor +} + +func (m Model) ultraStatusLine(text string, width int) string { + return lipgloss.NewStyle(). + Foreground(lipgloss.Color(m.theme.StatusFG)). + Background(lipgloss.Color(m.theme.StatusBG)). + Width(width). + Render(text) +} + +func (m *Model) ultraModeStatus(tasks []task.Task) string { + filter := "all" + if query := strings.TrimSpace(m.ultraSearchInput.Value()); query != "" { + filter = query + } else if m.ultraSearchRegex != nil { + filter = m.ultraSearchRegex.String() + } else if m.ultraFiltered != nil { + filter = "filtered" + } + return fmt.Sprintf("ULTRA MODE | filter: %s | %d tasks", filter, len(tasks)) +} + +func (m *Model) ultraCursorStatus(tasks []task.Task) string { + cursor := m.ultraVisibleCursor(tasks) + if cursor < 0 { + return fmt.Sprintf("0/%d", len(tasks)) + } + return fmt.Sprintf("%d/%d", cursor+1, len(tasks)) } // renderUltraCard assembles the card sections and applies the outer selection style. @@ -114,6 +232,42 @@ func (m *Model) renderUltraAnnotationsWithRegex(t task.Task, width int, re *rege return strings.Join(lines, "\n") } +func (m *Model) ultraRenderCards(tasks []task.Task, width, selected, start, cardBudget int) []string { + if start < 0 { + start = 0 + } + if start > len(tasks) { + start = len(tasks) + } + + var lines []string + used := 0 + for i := start; i < len(tasks); i++ { + card := m.renderUltraCard(tasks[i], width, i == selected, m.ultraSearchRegex) + if card == "" { + continue + } + + cardHeight := lipgloss.Height(card) + if len(lines) > 0 { + if used+1+cardHeight > cardBudget { + break + } + lines = append(lines, "") + used++ + } else if cardHeight > cardBudget { + break + } + + if used+cardHeight > cardBudget { + break + } + lines = append(lines, strings.Split(card, "\n")...) + used += cardHeight + } + return lines +} + func (m *Model) ultraStyledText(re *regexp.Regexp, style lipgloss.Style, text string) string { if re != nil && re.MatchString(text) { text = m.highlightMatches(text, re) -- cgit v1.2.3