summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-28 09:28:35 +0200
committerPaul Buetow <paul@buetow.org>2026-04-07 09:24:18 +0300
commite2ef4d55f2da0959c832a530e68ea0f519bffdd1 (patch)
tree3cbf378f204a11cd2cfbb0cbbb31dbbd5e901d4b
parent1267209e5a2a734850cdef635182265c36e2edf9 (diff)
ui: render ultra mode screen (vo)
-rw-r--r--internal/ui/ultra.go158
1 files 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)