diff options
| -rw-r--r-- | lookup.go | 41 | ||||
| -rw-r--r-- | ui.go | 126 |
2 files changed, 89 insertions, 78 deletions
diff --git a/lookup.go b/lookup.go new file mode 100644 index 0000000..db72e65 --- /dev/null +++ b/lookup.go @@ -0,0 +1,41 @@ +package main + +import ( + "database/sql" + "errors" + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" +) + +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")) + } +} @@ -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" } |
