fancy theme

This commit is contained in:
2026-11-04 17:18:09 +01:00
parent e6ca4aef8f
commit d0860c26ad
11 changed files with 351 additions and 44 deletions
+56 -11
View File
@@ -248,12 +248,17 @@ func (m *model) handleConfirm(key tea.KeyMsg) {
func (m model) editorView() string {
e := m.editor
t := newTheme()
width := m.contentWidth()
mode := "New todo"
if !e.creating {
mode = "Edit " + e.original.ID
}
var out strings.Builder
fmt.Fprintf(&out, "TodoApp — %s\n\n", mode)
fmt.Fprintln(&out, t.heading("EDITOR", mode, width))
if m.height >= 14 {
out.WriteString("\n")
}
for i, label := range [2]string{"Title", "Tags (comma-separated)"} {
prefix := " "
if i == e.field {
@@ -264,16 +269,30 @@ func (m model) editorView() string {
chars := []rune(value)
value = string(chars[:e.cursor]) + "▏" + string(chars[e.cursor:])
}
fmt.Fprintln(&out, runewidth.Truncate(prefix+label+": "+safe(value), max(1, m.width), "…"))
labelText := prefix + label + ": "
line := labelText + visibleCaretLine(safe(value), max(1, width-runewidth.StringWidth(labelText)))
line = runewidth.Truncate(line, width, "…")
if i == e.field {
fmt.Fprintln(&out, t.accent.Render(line))
} else {
fmt.Fprintln(&out, t.muted.Render(line))
}
}
if m.height >= 14 {
out.WriteString("\n")
}
if width < 38 {
out.WriteString(t.accent.Render("Body · Enter newline") + "\n")
} else {
out.WriteString(t.accent.Render("Body") + t.muted.Render(" Markdown · Enter adds a line") + "\n")
}
out.WriteString("\nBody (Markdown; Enter adds a line):\n")
body := e.fields[2]
if e.field == 2 {
chars := []rune(body)
body = string(chars[:e.cursor]) + "▏" + string(chars[e.cursor:])
}
lines := strings.Split(safe(body), "\n")
page := max(1, m.height-11)
page := max(1, m.height-13)
cursorLine := 0
if e.field == 2 {
cursorLine = strings.Count(string([]rune(e.fields[2])[:e.cursor]), "\n")
@@ -283,24 +302,50 @@ func (m model) editorView() string {
start = max(0, len(lines)-page)
}
for _, line := range lines[start:min(start+page, len(lines))] {
fmt.Fprintln(&out, runewidth.Truncate(line, max(1, m.width), "…"))
fmt.Fprintln(&out, visibleCaretLine(line, width))
}
if len(lines) > page {
fmt.Fprintf(&out, "(%d-%d of %d body lines)\n", start+1, min(start+page, len(lines)), len(lines))
if len(lines) > page && m.height >= 14 {
fmt.Fprintln(&out, t.muted.Render(fmt.Sprintf("%d-%d / %d body lines", start+1, min(start+page, len(lines)), len(lines))))
}
out.WriteString(noticeLine(m.notice))
out.WriteString("\nTab/Shift+Tab fields • ←→ cursor • Ctrl+S save • Esc cancel\n")
return out.String()
if m.notice != "" {
fmt.Fprintln(&out, t.alert(m.notice, width))
}
if m.height >= 14 {
out.WriteString("\n")
}
hint := "Tab/Shift+Tab fields · ←→ cursor · Ctrl+S save · Esc cancel"
if width < 60 {
hint = "Tab fields · Ctrl+S save · Esc cancel"
}
if width < 40 {
hint = "Tab · Ctrl+S save · Esc cancel"
}
out.WriteString(t.hint(hint, width) + "\n")
return t.box(out.String(), m.width, m.height)
}
func (m model) confirmView() string {
t := newTheme()
question := "Discard unsaved edits?"
if m.confirm == "close" {
question = "Close this todo?"
} else if m.confirm == "reopen" {
question = "Reopen this todo?"
}
return fmt.Sprintf("TodoApp\n\n%s\n\ny confirm • n/Esc cancel\n", question)
content := t.heading("CONFIRM", "This action changes the current todo", m.contentWidth()) + "\n\n" +
t.warning.Render(question) + "\n\n" + t.accent.Render("[y] confirm") + " " + t.muted.Render("[n] / Esc cancel") + "\n"
return t.box(content, m.width, m.height)
}
// Keep the insertion cursor visible even when editing a line wider than the terminal.
func visibleCaretLine(line string, width int) string {
if index := strings.Index(line, "▏"); index >= 0 {
before := runewidth.StringWidth(line[:index])
if before >= width-2 {
line = runewidth.TruncateLeft(line, max(0, before-width+4), "…")
}
}
return runewidth.Truncate(line, max(1, width), "…")
}
func noticeLine(text string) string {