diff options
| author | David Schlachter <t480-debian-git@schlachter.ca> | 2026-01-08 01:26:15 -0500 |
|---|---|---|
| committer | David Schlachter <t480-debian-git@schlachter.ca> | 2026-01-08 01:26:15 -0500 |
| commit | bfa6f9641a59e1c388b33d1bc1be2bbe9e4d3a86 (patch) | |
| tree | 5a2936eb159de765c7dd98e4aa8cb995fbd84b0a | |
| parent | 6d3623e3295ceacf7e1200d05790a9c662b69960 (diff) | |
Prettier view
| -rw-r--r-- | go.mod | 1 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | main.go | 30 |
3 files changed, 34 insertions, 1 deletions
@@ -8,6 +8,7 @@ require ( github.com/goccy/go-json v0.10.5 github.com/mattn/go-sqlite3 v1.14.33 github.com/microcosm-cc/bluemonday v1.0.27 + github.com/muesli/reflow v0.3.0 ) require ( @@ -30,6 +30,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= @@ -40,8 +41,11 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -7,12 +7,16 @@ import ( "fmt" "log" "net/http" + "regexp" + "strings" "time" "github.com/charmbracelet/bubbles/textinput" + "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" _ "github.com/mattn/go-sqlite3" "github.com/microcosm-cc/bluemonday" + "github.com/muesli/reflow/wordwrap" ) const ( @@ -33,6 +37,7 @@ type model struct { c *http.Client wordAddStatus string p bluemonday.Policy + vp viewport.Model } type ( @@ -47,6 +52,7 @@ func initialModel(c *http.Client, db *sql.DB) model { ti.Focus() ti.CharLimit = 156 ti.Width = 36 + vp := viewport.New(80, 30) return model{ wordInput: ti, @@ -55,6 +61,7 @@ func initialModel(c *http.Client, db *sql.DB) model { c: c, wordAddStatus: "(Press 'Enter' to add this word and its definition to Anki)", p: *bluemonday.StrictPolicy(), + vp: vp, } } @@ -82,6 +89,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case definitionMsg: m.currentDefinition = string(msg) + m.vp.SetContent(formatDefinitionForDisplay(m.p, m.currentDefinition)) return m, nil case wordAddedMsg: m.wordAddStatus = fmt.Sprintf("✅ Added '%s' to Anki", string(msg)) @@ -103,11 +111,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { textPickerLongerCmds = append(textPickerLongerCmds, lookupWord(m.db, m.currentWord)) } + var vpCmd tea.Cmd + m.vp, vpCmd = m.vp.Update(msg) + textPickerLongerCmds = append(textPickerLongerCmds, vpCmd) + m.wordInput, cmd = m.wordInput.Update(msg) textPickerLongerCmds = append(textPickerLongerCmds, cmd) return m, tea.Batch(textPickerLongerCmds...) } +var whitespaceTrimmerRe = regexp.MustCompile(`^[ \t]*$`) + func (m model) View() string { return fmt.Sprintf( "Look up a word:\n\n%s\n\n%s\n%s\n\n%s\n%s", @@ -115,10 +129,24 @@ func (m model) View() string { m.wordAddStatus, "(esc to quit)", "Current definition:\n", - m.p.Sanitize(m.currentDefinition), + m.vp.View(), ) + "\n" } +func formatDefinitionForDisplay(policy bluemonday.Policy, definition string) string { + return wordwrap.String( + strings.ReplaceAll( + whitespaceTrimmerRe.ReplaceAllLiteralString( + policy.Sanitize(definition), + "", + ), + "\n\n", + "\n", + ), + 72, + ) +} + func main() { db, err := setupDatabase() if err != nil { |
