summaryrefslogtreecommitdiff
path: root/model_list.go
diff options
context:
space:
mode:
authoreltrac <hi@eltr.ac>2026-03-05 12:24:01 +0800
committereltrac <hi@eltr.ac>2026-03-05 12:24:01 +0800
commit5fd2372cd5b42689c305e82421832af5f8a42f10 (patch)
treee4b742d126e02f5c3f394f82b15b9c091ed3f474 /model_list.go
parentcba44c71d202f95a590f540e915ef46a940a2857 (diff)
feat: use viewport to show long listHEADmaster
Diffstat (limited to 'model_list.go')
-rw-r--r--model_list.go78
1 files changed, 68 insertions, 10 deletions
diff --git a/model_list.go b/model_list.go
index e916a03..f7a39db 100644
--- a/model_list.go
+++ b/model_list.go
@@ -8,6 +8,7 @@ import (
"strings"
"time"
+ "charm.land/bubbles/v2/viewport"
tea "charm.land/bubbletea/v2"
)
@@ -15,6 +16,8 @@ type model struct {
choices [][]string
cursor int
selected map[int]struct{}
+ ready bool
+ viewport viewport.Model
}
func listModel(arg string) model {
@@ -48,19 +51,28 @@ func (m model) Init() tea.Cmd {
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
+ var (
+ cmd tea.Cmd
+ cmds []tea.Cmd
+ )
+
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
- case "ctrl+c", "q":
+ case "ctrl+c", "q", "esc":
return m, tea.Quit
- case "up", "k":
+ case "k":
if m.cursor > 0 {
m.cursor--
+ m.viewport.SetContent(m.buildContent())
}
- case "down", "j":
+ return m, nil
+ case "j":
if m.cursor < len(m.choices)-1 {
m.cursor++
+ m.viewport.SetContent(m.buildContent())
}
+ return m, nil
case "enter":
choice := m.choices[m.cursor]
path := choice[0]
@@ -72,15 +84,44 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} else {
m.selected[m.cursor] = struct{}{}
}
+ m.viewport.SetContent(m.buildContent())
+ return m, nil
+ }
+
+ case tea.WindowSizeMsg:
+ headerHeight := 1
+ footerHeight := 1
+ verticalMarginHeight := headerHeight + footerHeight
+
+ if !m.ready {
+ m.viewport = viewport.New(viewport.WithWidth(msg.Width), viewport.WithHeight(msg.Height-verticalMarginHeight))
+ m.viewport.YPosition = headerHeight
+ m.viewport.SetContent(m.buildContent())
+ m.ready = true
+ } else {
+ m.viewport.SetWidth(msg.Width)
+ m.viewport.SetHeight(msg.Height - verticalMarginHeight)
}
+
case editorFinishedMsg:
return m, tea.Quit
}
- return m, nil
+
+ m.viewport, cmd = m.viewport.Update(msg)
+ cmds = append(cmds, cmd)
+
+ return m, tea.Batch(cmds...)
}
-func (m model) View() tea.View {
- s := "Search: press '/' to filter items (not implemented yet)\n\n"
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func (m model) buildContent() string {
+ var s strings.Builder
for i, choice := range m.choices {
cursor := " " // no cursor
@@ -96,14 +137,31 @@ func (m model) View() tea.View {
title := choice[2]
t, err := time.Parse(time.RFC3339, choice[3])
if err != nil {
-
+ continue
}
datetime := t.Format("2006-01-02 15:04")
- s += fmt.Sprintf("%s [%s] %s \t %s\n", cursor, checked, datetime, title)
+ s.WriteString(fmt.Sprintf("%s [%s] %s \t %s\n", cursor, checked, datetime, title))
}
- s += "\n[q] quit | [Enter] edit | [SPACE] select | [↑/k] up | [↓/j] down\n"
+ return s.String()
+}
+
+func (m model) headerView() string {
+ title := fmt.Sprintf("Hugo Content Manager (%d items)", len(m.choices))
+ line := strings.Repeat("─", max(0, m.viewport.Width()-len(title)))
+ return title + line
+}
- return tea.NewView(s)
+func (m model) footerView() string {
+ help := "[q] quit | [Enter] edit | [SPACE] select | [j/k] cursor | [↑/↓] scroll"
+ line := strings.Repeat("─", max(0, m.viewport.Width()-len(help)))
+ return line + help
+}
+
+func (m model) View() tea.View {
+ if !m.ready {
+ return tea.NewView("\n Initializing...")
+ }
+ return tea.NewView(fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.viewport.View(), m.footerView()))
}