commit b36723cf9fc1d1832758bbbd9eba3847978535f1
parent a299b44fd8d9b7e045414cb087f0fa4e2527aa8c
Author: David Schlachter <t480-debian-git@schlachter.ca>
Date: Sun, 4 Oct 2026 01:46:31 -0400
Add option to auto-sync after adding card
Diffstat:
4 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
@@ -65,6 +65,10 @@ Usage of french-wiktionary-flashcards:
-apiURL string
Base URL to access the anki-connect plugin API. (default
"http://localhost:8765")
+ -autosync bool
+ If set to "true", automatically sync the collection to
+ Ankiweb after adding a new card. Useful for running on a
+ a headless server or a machine where Anki is always open.
-deck string
Name of the deck where new Anki cards will be created.
-dictionary string
diff --git a/main.go b/main.go
@@ -45,6 +45,11 @@ func main() {
"",
"Optional: first word to look up on program launch.",
)
+ autoSync := flag.Bool(
+ "autosync",
+ false,
+ "Whether to sync automatically with Ankiweb",
+ )
flag.Parse()
@@ -82,7 +87,7 @@ func main() {
c := http.DefaultClient
c.Timeout = 5 * time.Second
- p := tea.NewProgram(initialModel(c, db, *apiURL, *deckName, *modelName, *rawDict, *initialWord))
+ p := tea.NewProgram(initialModel(c, db, *apiURL, *deckName, *modelName, *rawDict, *initialWord, *autoSync))
if _, err := p.Run(); err != nil {
log.Fatalf("Unexpected error encountered while running program: %s", err)
}
diff --git a/sync.go b/sync.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+
+ tea "github.com/charmbracelet/bubbletea"
+)
+
+func syncWithAnkiWeb(c *http.Client, apiURL string) tea.Cmd {
+ return func() tea.Msg {
+ jsonBytes, err := json.Marshal(struct {
+ Action string `json:"action"`
+ Version int `json:"version"`
+ }{
+ "sync", apiVersion,
+ })
+ if err != nil {
+ return errMsg(fmt.Errorf("marshaling JSON: %s", err))
+ }
+
+ req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBytes))
+ req.Header.Set("Content-Type", "application/json")
+
+ resp, err := c.Do(req)
+ if err != nil {
+ return errMsg(fmt.Errorf("making request: %s", err))
+ }
+ defer resp.Body.Close()
+
+ body, _ := io.ReadAll(resp.Body)
+
+ var jsonResp struct {
+ Error string `json:"error"`
+ }
+
+ json.Unmarshal(body, &jsonResp)
+ if jsonResp.Error != "" {
+ return errMsg(fmt.Errorf("creating card: %s", jsonResp.Error))
+ }
+
+ return syncedMsg(true)
+ }
+}
diff --git a/ui.go b/ui.go
@@ -30,6 +30,7 @@ type model struct {
ankiModel string
apiURL string
rawDictionaryPath string
+ autoSync bool
dictionaryReady bool
@@ -44,6 +45,7 @@ type (
errMsg error
definitionMsg string
wordAddedMsg string
+ syncedMsg bool
isDictionaryEmptyMsg bool
populatingDictionaryMsg *dictionaryPopulator
)
@@ -52,6 +54,7 @@ func initialModel(
c *http.Client,
db *sql.DB,
apiURL, ankiDeck, ankiModel, rawDictionary, firstWord string,
+ autoSync bool,
) model {
input := textinput.New()
input.Placeholder = ""
@@ -94,6 +97,7 @@ func initialModel(
ankiModel: ankiModel,
apiURL: apiURL,
rawDictionaryPath: rawDictionary,
+ autoSync: autoSync,
currentWord: firstWord,
}
@@ -146,6 +150,17 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.wordInput.SetValue("")
m.definitionViewport.SetContent("")
m.err = nil
+ // If we're running on a headless server, or a machine where we always
+ // have Anki open, we can sync the collection automatically after adding
+ // a new card.
+ if m.autoSync {
+ return m, syncWithAnkiWeb(
+ m.c,
+ m.apiURL,
+ )
+ }
+ case syncedMsg:
+ m.statusString = "✅ Synced collection to AnkiWeb"
case tea.WindowSizeMsg:
m.importProgress.Width = msg.Width
m.wordInput.Width = msg.Width