summaryrefslogtreecommitdiff
path: root/ui.go
diff options
context:
space:
mode:
authorDavid Schlachter <t480-debian-git@schlachter.ca>2026-01-11 22:47:52 -0500
committerDavid Schlachter <t480-debian-git@schlachter.ca>2026-01-11 22:47:52 -0500
commit18a0b77981bc1590f558341870f8d35f8aec23c9 (patch)
treed3229bc416e311df4579876bcf421b9d73bb8eda /ui.go
parentc3b21783adfb761f98f1201e484f083e0b936545 (diff)
Refactor a bit
Diffstat (limited to 'ui.go')
-rw-r--r--ui.go126
1 files changed, 48 insertions, 78 deletions
diff --git a/ui.go b/ui.go
index dada7c6..eb8d077 100644
--- a/ui.go
+++ b/ui.go
@@ -2,11 +2,9 @@ package main
import (
"database/sql"
- "errors"
"fmt"
"html"
"net/http"
- "regexp"
"strings"
"github.com/charmbracelet/bubbles/key"
@@ -18,18 +16,21 @@ import (
)
type model struct {
- wordInput textinput.Model
- err error
+ db *sql.DB
+ c *http.Client
+ p bluemonday.Policy
+
+ wordInput textinput.Model
+ definitionViewport viewport.Model
+
+ ankiDeck string
+ ankiModel string
+ apiURL string
+
currentWord string
currentDefinition string
- db *sql.DB
- c *http.Client
- wordAddStatus string
- p bluemonday.Policy
- vp viewport.Model
- ankiDeck string
- ankiModel string
- apiURL string
+ statusString string
+ err error
}
type (
@@ -39,14 +40,18 @@ type (
)
func initialModel(c *http.Client, db *sql.DB, apiURL, ankiDeck, ankiModel, firstWord string) model {
- ti := textinput.New()
- ti.Placeholder = ""
- ti.Focus()
- ti.CharLimit = 156
- ti.Width = 36
-
- vp := viewport.New(80, 30)
- vp.KeyMap = viewport.KeyMap{
+ input := textinput.New()
+ input.Placeholder = ""
+ input.Focus()
+ input.CharLimit = 156
+ input.Width = 36
+
+ if firstWord != "" {
+ input.SetValue(firstWord)
+ }
+
+ textbox := viewport.New(80, 30)
+ textbox.KeyMap = viewport.KeyMap{
PageDown: key.NewBinding(
key.WithKeys("pgdown", " ", "f"),
key.WithHelp("f/pgdn", "page down"),
@@ -57,22 +62,19 @@ func initialModel(c *http.Client, db *sql.DB, apiURL, ankiDeck, ankiModel, first
),
}
- if firstWord != "" {
- ti.SetValue(firstWord)
- }
-
return model{
- wordInput: ti,
- err: nil,
- db: db,
- c: c,
- wordAddStatus: "",
- p: *bluemonday.StrictPolicy(),
- vp: vp,
- ankiDeck: ankiDeck,
- ankiModel: ankiModel,
- apiURL: apiURL,
- currentWord: firstWord,
+ db: db,
+ c: c,
+ p: *bluemonday.StrictPolicy(),
+
+ wordInput: input,
+ definitionViewport: textbox,
+
+ ankiDeck: ankiDeck,
+ ankiModel: ankiModel,
+ apiURL: apiURL,
+
+ currentWord: firstWord,
}
}
@@ -84,37 +86,6 @@ func (m model) Init() tea.Cmd {
return tea.Batch(cmds...)
}
-func lookupWord(db *sql.DB, word string) tea.Cmd {
- return func() tea.Msg {
- word = strings.TrimSpace(word) // remove leading / trailing whitespace
- // Replace apostrophes (possible if copy-pasting) with single quotes
- word = strings.ReplaceAll(word, `’`, `'`)
-
- rows, err := db.Query(`select definition from words where word = ?`, word)
- if err != nil {
- if errors.Is(err, sql.ErrNoRows) {
- return definitionMsg("")
- }
- return errMsg(fmt.Errorf("looking up '%s': %s", word, err))
- }
- defer rows.Close()
- var combinedDefinition []string
- for rows.Next() {
- var definition string
- err = rows.Scan(&definition)
- if err != nil {
- return errMsg(fmt.Errorf("looking up '%s': %s", word, err))
- }
- combinedDefinition = append(combinedDefinition, definition)
- }
- if err := rows.Err(); err != nil {
- return errMsg(fmt.Errorf("looking up '%s': %s", word, err))
- }
-
- return definitionMsg(strings.Join(combinedDefinition, "\n\n"))
- }
-}
-
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
@@ -124,21 +95,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case definitionMsg:
m.currentDefinition = string(msg)
m.err = nil
- m.vp.SetContent(formatDefinitionForDisplay(m.p, m.currentDefinition, m.vp.Width))
+ m.definitionViewport.SetContent(formatDefinitionForDisplay(m.p, m.currentDefinition, m.definitionViewport.Width))
return m, nil
case wordAddedMsg:
- m.wordAddStatus = fmt.Sprintf("✅ Added '%s' to Anki", string(msg))
+ m.statusString = fmt.Sprintf("✅ Added '%s' to Anki", string(msg))
m.currentWord = ""
m.currentDefinition = ""
m.wordInput.SetValue("")
- m.vp.SetContent("")
+ m.definitionViewport.SetContent("")
m.err = nil
case tea.WindowSizeMsg:
// headerHeight is the height of everything above the definition window.
headerHeight := 11
- m.vp.Width = msg.Width
- m.vp.Height = msg.Height - headerHeight
- m.vp.SetContent(formatDefinitionForDisplay(m.p, m.currentDefinition, m.vp.Width))
+ m.wordInput.Width = msg.Width
+ m.definitionViewport.Width = msg.Width
+ m.definitionViewport.Height = msg.Height - headerHeight
+ m.definitionViewport.SetContent(formatDefinitionForDisplay(m.p, m.currentDefinition, m.definitionViewport.Width))
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC, tea.KeyCtrlD:
@@ -147,7 +119,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.currentWord = ""
m.currentDefinition = ""
m.wordInput.SetValue("")
- m.vp.SetContent("")
+ m.definitionViewport.SetContent("")
case tea.KeyEnter:
return m, addCard(m.c, m.apiURL, m.ankiDeck, m.ankiModel, m.currentWord, m.currentDefinition)
}
@@ -163,7 +135,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
var vpCmd tea.Cmd
- m.vp, vpCmd = m.vp.Update(msg)
+ m.definitionViewport, vpCmd = m.definitionViewport.Update(msg)
textPickerLongerCmds = append(textPickerLongerCmds, vpCmd)
m.wordInput, cmd = m.wordInput.Update(msg)
@@ -171,16 +143,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Batch(textPickerLongerCmds...)
}
-var whitespaceTrimmerRe = regexp.MustCompile(`^[ \t]*$`)
-
func (m model) View() string {
return fmt.Sprintf(
"\x1b[1;30;42mLook up a word:\x1b[0m\n\n%s\n\n\x1b[1;30;42mStatus:\x1b[0m %s\n\n%s\n\n%s\n%s",
m.wordInput.View(),
- formatStatus(m.err, m.wordAddStatus),
+ formatStatus(m.err, m.statusString),
"(Ctrl-C to quit, Esc to clear, Enter to add to Anki, PgUp/Down to scroll)",
"\x1b[1;30;42mCurrent definition:\x1b[0m\n",
- m.vp.View(),
+ m.definitionViewport.View(),
) + "\n"
}