fancy theme
This commit is contained in:
@@ -128,7 +128,7 @@ func (m model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
lines := m.detailLines()
|
||||
page := max(1, m.height-9)
|
||||
page := max(1, m.height-11)
|
||||
last := max(0, len(lines)-page)
|
||||
switch msg.String() {
|
||||
case "esc", "left":
|
||||
@@ -155,9 +155,9 @@ func (m model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case "down", "j":
|
||||
m.selected = min(max(0, len(matches)-1), m.selected+1)
|
||||
case "pgup":
|
||||
m.selected = max(0, m.selected-max(1, m.height-8))
|
||||
m.selected = max(0, m.selected-max(1, m.height-11))
|
||||
case "pgdown":
|
||||
m.selected = min(max(0, len(matches)-1), m.selected+max(1, m.height-8))
|
||||
m.selected = min(max(0, len(matches)-1), m.selected+max(1, m.height-11))
|
||||
case "home", "g":
|
||||
m.selected = 0
|
||||
case "end", "G":
|
||||
@@ -205,11 +205,18 @@ func (m model) detailLines() []string {
|
||||
}
|
||||
var lines []string
|
||||
for _, line := range strings.Split(strings.TrimRight(body, "\n"), "\n") {
|
||||
lines = append(lines, strings.Split(runewidth.Wrap(line, max(1, m.width-2)), "\n")...)
|
||||
lines = append(lines, strings.Split(runewidth.Wrap(line, m.contentWidth()), "\n")...)
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func (m model) contentWidth() int {
|
||||
if m.width < 50 || m.height < 14 || newTheme().plain {
|
||||
return max(1, min(m.width, 100))
|
||||
}
|
||||
return max(1, min(m.width, 100)-6) // border and horizontal padding
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
if m.confirm != "" {
|
||||
return m.confirmView()
|
||||
@@ -217,49 +224,86 @@ func (m model) View() string {
|
||||
if m.editor != nil {
|
||||
return m.editorView()
|
||||
}
|
||||
width := max(1, m.width)
|
||||
t := newTheme()
|
||||
width := m.contentWidth()
|
||||
matches := m.filtered()
|
||||
if m.detail && len(matches) > 0 {
|
||||
todo := m.items[matches[m.selected]]
|
||||
lines := m.detailLines()
|
||||
page := max(1, m.height-9)
|
||||
page := max(1, m.height-11)
|
||||
start := min(m.scroll, max(0, len(lines)-page))
|
||||
end := min(start+page, len(lines))
|
||||
header := fmt.Sprintf("TodoApp — %s [%s]\n%s • %s • %s\n\n", safe(todo.Title), todo.ID, safe(todo.Status), safe(strings.Join(todo.Tags, ", ")), todo.CreatedAt)
|
||||
return header + strings.Join(lines[start:end], "\n") + fmt.Sprintf("\n\n%d-%d/%d ↑↓/PgUp/PgDn scroll • e edit • x/o close/reopen • Esc back • q quit\n%s", start+1, end, len(lines), noticeLine(m.notice))
|
||||
header := t.heading("DETAIL / "+safe(todo.Title), todo.ID+" • "+safe(todo.CreatedAt), width)
|
||||
meta := t.status(todo.Status) + " " + t.muted.Render(safe(strings.Join(todo.Tags, ", ")))
|
||||
hint := fmt.Sprintf("%d-%d/%d ↑↓ scroll · e edit · x/o status · Esc back · q quit", start+1, end, len(lines))
|
||||
if width < 60 {
|
||||
hint = "↑↓ scroll · e edit · Esc back · q quit"
|
||||
}
|
||||
content := header + "\n" + meta + "\n\n" + strings.Join(lines[start:end], "\n") +
|
||||
fmt.Sprintf("\n\n%s\n", t.hint(hint, width))
|
||||
if m.notice != "" {
|
||||
content += t.alert(m.notice, width) + "\n"
|
||||
}
|
||||
return t.box(content, m.width, m.height)
|
||||
}
|
||||
var out strings.Builder
|
||||
fmt.Fprintf(&out, "TodoApp — %s\nTodos: %s\n", m.project, m.todosDir)
|
||||
fmt.Fprintln(&out, t.heading("TODOS", fmt.Sprintf("%d shown / %d total · %s", len(matches), len(m.items), safe(m.project)), width))
|
||||
if len(m.warnings) > 0 {
|
||||
fmt.Fprintf(&out, "Warning: %d unreadable todo(s): %s\n", len(m.warnings), safe(m.warnings[0].Error()))
|
||||
fmt.Fprintln(&out, t.alert(fmt.Sprintf("Warning: %d unreadable todo(s): %s", len(m.warnings), m.warnings[0]), width))
|
||||
}
|
||||
fmt.Fprintf(&out, "\n%d of %d todo(s)\n", len(matches), len(m.items))
|
||||
fmt.Fprintln(&out, t.muted.Render(runewidth.Truncate(m.todosDir, width, "…")))
|
||||
out.WriteString("\n")
|
||||
if len(matches) == 0 {
|
||||
if len(m.items) == 0 {
|
||||
out.WriteString("No todos yet. Pi stores todo items in this directory.\n")
|
||||
fmt.Fprintln(&out, t.muted.Render("No todos yet. Pi stores todo items in this directory."))
|
||||
} else {
|
||||
out.WriteString("No matching todos.\n")
|
||||
fmt.Fprintln(&out, t.muted.Render("No matching todos."))
|
||||
}
|
||||
} else {
|
||||
page := max(1, m.height-9)
|
||||
page := max(1, m.height-11)
|
||||
start := max(0, m.selected-page+1)
|
||||
end := min(start+page, len(matches))
|
||||
for i := start; i < end; i++ {
|
||||
todo := m.items[matches[i]]
|
||||
prefix := " "
|
||||
status := runewidth.Truncate(safe(todo.Status), max(1, min(16, width/3)), "…")
|
||||
tags := ""
|
||||
if width >= 60 {
|
||||
tags = runewidth.Truncate(safe(strings.Join(todo.Tags, ", ")), min(16, width/5), "…")
|
||||
}
|
||||
tagsWidth := 0
|
||||
if tags != "" {
|
||||
tagsWidth = 2 + runewidth.StringWidth(tags)
|
||||
}
|
||||
title := runewidth.Truncate(safe(todo.Title), max(1, width-16-runewidth.StringWidth(status)-tagsWidth), "…")
|
||||
line := todo.ID + " [" + status + "] " + title
|
||||
if tags != "" {
|
||||
line += " " + tags
|
||||
}
|
||||
if i == m.selected {
|
||||
prefix = "> "
|
||||
fmt.Fprintln(&out, t.row(line, true, width))
|
||||
} else {
|
||||
row := t.muted.Render(" "+todo.ID) + " " + t.status(status) + " " + title
|
||||
if tags != "" {
|
||||
row += " " + t.muted.Render(tags)
|
||||
}
|
||||
fmt.Fprintln(&out, row)
|
||||
}
|
||||
line := fmt.Sprintf("%s%s %s [%s] %s", prefix, todo.ID, safe(todo.Title), safe(todo.Status), safe(strings.Join(todo.Tags, ", ")))
|
||||
out.WriteString(runewidth.Truncate(line, width, "…") + "\n")
|
||||
}
|
||||
}
|
||||
search := "Search: " + safe(m.query)
|
||||
if m.search {
|
||||
fmt.Fprintf(&out, "\nSearch: %s_ (Enter/Esc finish, Ctrl+U clear)\n", safe(m.query))
|
||||
} else {
|
||||
fmt.Fprintf(&out, "\nSearch: %s (/ to edit, Esc clear)\n", safe(m.query))
|
||||
search += "▏"
|
||||
}
|
||||
out.WriteString("↑↓ navigate • Enter view • n new • e edit • x/o close/reopen • r refresh • q quit\n")
|
||||
out.WriteString(noticeLine(m.notice))
|
||||
return out.String()
|
||||
fmt.Fprintln(&out, "\n"+t.accent.Render(runewidth.Truncate(search, width, "…")))
|
||||
if m.notice != "" {
|
||||
fmt.Fprintln(&out, t.alert(m.notice, width))
|
||||
}
|
||||
hint := "↑↓ select · Enter view · / search · n new · e edit · x/o status · r refresh · q quit"
|
||||
if width < 40 {
|
||||
hint = "↑↓ select · Enter view · q quit"
|
||||
} else if width < 70 {
|
||||
hint = "↑↓ move · Enter view · / find · n new · e edit · q quit"
|
||||
}
|
||||
fmt.Fprintln(&out, t.hint(hint, width))
|
||||
return t.box(out.String(), m.width, m.height)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user