1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
// This program looks up words fromm Wiktionary, and creates Anki flashcards
// from them.
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"time"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
_ "github.com/mattn/go-sqlite3"
"github.com/microcosm-cc/bluemonday"
)
const (
rawDictionary = "/home/david/work/french-wiktionary-flashcards/raw-wiktextract-data.jsonl"
dictionary = "/home/david/work/french-wiktionary-flashcards/raw-wiktextract-data.sqlite3"
apiURL = "http://localhost:8765"
deckName = "Français"
modelName = "Basic-830ae"
)
type model struct {
wordInput textinput.Model
err error
currentWord string
currentDefinition string
db *sql.DB
c *http.Client
wordAddStatus string
p bluemonday.Policy
}
type (
errMsg error
definitionMsg string
wordAddedMsg string
)
func initialModel(c *http.Client, db *sql.DB) model {
ti := textinput.New()
ti.Placeholder = ""
ti.Focus()
ti.CharLimit = 156
ti.Width = 36
return model{
wordInput: ti,
err: nil,
db: db,
c: c,
wordAddStatus: "(Press 'Enter' to add this word and its definition to Anki)",
p: *bluemonday.StrictPolicy(),
}
}
func (m model) Init() tea.Cmd {
return textinput.Blink
}
func lookupWord(db *sql.DB, word string) tea.Cmd {
return func() tea.Msg {
var definition string
row := db.QueryRow(`select definition from words where word = ? limit 1`, word)
err := row.Scan(&definition)
if err != nil {
return errMsg(fmt.Errorf("looking up '%s': %s", word, err))
}
return definitionMsg(definition)
}
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var textPickerLongerCmds []tea.Cmd
switch msg := msg.(type) {
case definitionMsg:
m.currentDefinition = string(msg)
return m, nil
case wordAddedMsg:
m.wordAddStatus = fmt.Sprintf("✅ Added '%s' to Anki", string(msg))
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC, tea.KeyEsc:
return m, tea.Quit
case tea.KeyEnter:
return m, addCard(m.c, m.currentWord, m.currentDefinition)
}
case errMsg:
m.err = msg
return m, nil
}
if m.wordInput.Value() != m.currentWord {
m.currentWord = m.wordInput.Value()
textPickerLongerCmds = append(textPickerLongerCmds, lookupWord(m.db, m.currentWord))
}
m.wordInput, cmd = m.wordInput.Update(msg)
textPickerLongerCmds = append(textPickerLongerCmds, cmd)
return m, tea.Batch(textPickerLongerCmds...)
}
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",
m.wordInput.View(),
m.wordAddStatus,
"(esc to quit)",
"Current definition:\n",
m.p.Sanitize(m.currentDefinition),
) + "\n"
}
func main() {
db, err := setupDatabase()
if err != nil {
log.Fatalf("setting up database: %s", err)
}
defer db.Close()
c := http.DefaultClient
c.Timeout = 5 * time.Second
p := tea.NewProgram(initialModel(c, db))
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
|