wordflow-cli 0.1.5__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/PKG-INFO +11 -1
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/README.md +10 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/pyproject.toml +1 -1
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/anki_flashcard.py +10 -4
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/cli.py +14 -5
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/configuration.py +1 -1
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/translator.py +2 -2
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/workflows.py +43 -5
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow_cli.egg-info/PKG-INFO +11 -1
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/LICENSE +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/setup.cfg +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/__init__.py +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/clipboard.py +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/data/default_config.toml +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/data/logo.txt +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/data/logo2.txt +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/my_classes.py +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/notifications.py +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/text_to_speech.py +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow/wizard.py +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow_cli.egg-info/SOURCES.txt +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow_cli.egg-info/dependency_links.txt +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow_cli.egg-info/entry_points.txt +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow_cli.egg-info/requires.txt +0 -0
- {wordflow_cli-0.1.5 → wordflow_cli-0.1.6}/src/wordflow_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wordflow-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Automate Anki flashcard creation and text translation directly from your clipboard.
|
|
5
5
|
Author-email: Mathéo Tritschler <tritschler.matheo@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -154,12 +154,16 @@ Wordflow automatically routes audio to native speakers of the detected language.
|
|
|
154
154
|
## Instant Translation (-t)
|
|
155
155
|
|
|
156
156
|
Highlight any text on your screen and trigger the wordflow --translate command. Wordflow detects the language, translates it, and sends a desktop notification. Clicking the notification copies the translation to your clipboard.
|
|
157
|
+
|
|
157
158
|
## Cloze Card Creation (-c)
|
|
158
159
|
|
|
159
160
|
To create a flashcard, highlight the target word/sentence and trigger wordflow --cloze. Wordflow will parse all dictionary data, build the card according to your TOML layout, and push it directly to Anki.
|
|
160
161
|
|
|
161
162
|
Use wordflow --help to list all command-line arguments.
|
|
162
163
|
|
|
164
|
+
## Undo Last Card (-u / --undo)
|
|
165
|
+
|
|
166
|
+
If you accidentally create a card with a typo or the wrong highlighted text, simply trigger the `wordflow --undo` command. Wordflow remembers the ID of the last flashcard it generated and will instantly delete it from your Anki database.
|
|
163
167
|
# ⌨️ Creating Shortcuts
|
|
164
168
|
|
|
165
169
|
Wordflow is designed to be bound to your window manager's shortcuts, allowing you to seamlessly translate and capture vocabulary without losing focus on your current task.
|
|
@@ -177,6 +181,11 @@ Wordflow is designed to be bound to your window manager's shortcuts, allowing yo
|
|
|
177
181
|
hl.dispatch(hl.dsp.submap("reset"))
|
|
178
182
|
hl.dispatch(hl.dsp.exec_cmd("wordflow -c"))
|
|
179
183
|
end)
|
|
184
|
+
--u : Undo last card
|
|
185
|
+
hl.bind("U", function()
|
|
186
|
+
hl.dispatch(hl.dsp.submap("reset"))
|
|
187
|
+
hl.dispatch(hl.dsp.exec_cmd("wordflow -u"))
|
|
188
|
+
end)
|
|
180
189
|
|
|
181
190
|
hl.bind("catchall", hl.dsp.submap("reset"))
|
|
182
191
|
end)
|
|
@@ -184,3 +193,4 @@ Wordflow is designed to be bound to your window manager's shortcuts, allowing yo
|
|
|
184
193
|
## Sway / i3 Example
|
|
185
194
|
bindsym Mod4+t exec "wordflow -t"
|
|
186
195
|
bindsym Mod4+c exec "wordflow -c"
|
|
196
|
+
bindsym Mod4+u exec "wordflow -u"
|
|
@@ -133,12 +133,16 @@ Wordflow automatically routes audio to native speakers of the detected language.
|
|
|
133
133
|
## Instant Translation (-t)
|
|
134
134
|
|
|
135
135
|
Highlight any text on your screen and trigger the wordflow --translate command. Wordflow detects the language, translates it, and sends a desktop notification. Clicking the notification copies the translation to your clipboard.
|
|
136
|
+
|
|
136
137
|
## Cloze Card Creation (-c)
|
|
137
138
|
|
|
138
139
|
To create a flashcard, highlight the target word/sentence and trigger wordflow --cloze. Wordflow will parse all dictionary data, build the card according to your TOML layout, and push it directly to Anki.
|
|
139
140
|
|
|
140
141
|
Use wordflow --help to list all command-line arguments.
|
|
141
142
|
|
|
143
|
+
## Undo Last Card (-u / --undo)
|
|
144
|
+
|
|
145
|
+
If you accidentally create a card with a typo or the wrong highlighted text, simply trigger the `wordflow --undo` command. Wordflow remembers the ID of the last flashcard it generated and will instantly delete it from your Anki database.
|
|
142
146
|
# ⌨️ Creating Shortcuts
|
|
143
147
|
|
|
144
148
|
Wordflow is designed to be bound to your window manager's shortcuts, allowing you to seamlessly translate and capture vocabulary without losing focus on your current task.
|
|
@@ -156,6 +160,11 @@ Wordflow is designed to be bound to your window manager's shortcuts, allowing yo
|
|
|
156
160
|
hl.dispatch(hl.dsp.submap("reset"))
|
|
157
161
|
hl.dispatch(hl.dsp.exec_cmd("wordflow -c"))
|
|
158
162
|
end)
|
|
163
|
+
--u : Undo last card
|
|
164
|
+
hl.bind("U", function()
|
|
165
|
+
hl.dispatch(hl.dsp.submap("reset"))
|
|
166
|
+
hl.dispatch(hl.dsp.exec_cmd("wordflow -u"))
|
|
167
|
+
end)
|
|
159
168
|
|
|
160
169
|
hl.bind("catchall", hl.dsp.submap("reset"))
|
|
161
170
|
end)
|
|
@@ -163,3 +172,4 @@ Wordflow is designed to be bound to your window manager's shortcuts, allowing yo
|
|
|
163
172
|
## Sway / i3 Example
|
|
164
173
|
bindsym Mod4+t exec "wordflow -t"
|
|
165
174
|
bindsym Mod4+c exec "wordflow -c"
|
|
175
|
+
bindsym Mod4+u exec "wordflow -u"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wordflow-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
description = "Automate Anki flashcard creation and text translation directly from your clipboard."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -111,9 +111,10 @@ def setup_anki_model(url: str, model_name: str, fields: dict):
|
|
|
111
111
|
|
|
112
112
|
def make_cloze(
|
|
113
113
|
anki_config: AnkiConfig, sentence_data: TranslationData, word_data: TranslationData
|
|
114
|
-
):
|
|
114
|
+
) -> str:
|
|
115
115
|
"""
|
|
116
116
|
makes a cloze flashcard and sends it to anki through AnkiConnect
|
|
117
|
+
returns the id of the created card
|
|
117
118
|
"""
|
|
118
119
|
# before doing anything, checks that the word is in the sentence
|
|
119
120
|
if word_data.source_data.lower() not in sentence_data.source_data.lower():
|
|
@@ -142,14 +143,14 @@ def make_cloze(
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
try:
|
|
145
|
-
invoke(anki_config.url, "addNote", note=note)
|
|
146
|
+
note_id = invoke(anki_config.url, "addNote", note=note)
|
|
146
147
|
except AnkiConnectError as e:
|
|
147
148
|
if "duplicate" in str(e).lower():
|
|
148
149
|
raise DuplicateNoteError(
|
|
149
150
|
f"A note for '{word_data.source_data}' already exists."
|
|
150
151
|
)
|
|
151
|
-
raise e
|
|
152
|
-
return
|
|
152
|
+
raise AnkiConnectError(e)
|
|
153
|
+
return note_id
|
|
153
154
|
|
|
154
155
|
|
|
155
156
|
def add_audio_to_anki(url: str, text: str, lang_code: str, accent: str):
|
|
@@ -281,3 +282,8 @@ def process_fields(
|
|
|
281
282
|
|
|
282
283
|
print("fields sent to anki: ", formatted_anki_fields)
|
|
283
284
|
return formatted_anki_fields
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def delete_note(anki_url: str, note_id: int):
|
|
288
|
+
invoke(anki_url, "deleteNotes", notes=[note_id])
|
|
289
|
+
return True
|
|
@@ -4,7 +4,7 @@ import traceback
|
|
|
4
4
|
|
|
5
5
|
# local modules
|
|
6
6
|
from .configuration import create_config, load_config, print_config
|
|
7
|
-
from .workflows import translate_workflow, cloze_workflow
|
|
7
|
+
from .workflows import translate_workflow, cloze_workflow, undo_workflow
|
|
8
8
|
from .wizard import launch_wizard
|
|
9
9
|
from .my_classes import GlobalConfig, AnkiConfig
|
|
10
10
|
from .notifications import notify
|
|
@@ -36,6 +36,9 @@ def get_parser():
|
|
|
36
36
|
action="store_true",
|
|
37
37
|
help="Launch the interactive configuration wizard.",
|
|
38
38
|
)
|
|
39
|
+
group.add_argument(
|
|
40
|
+
"-u", "--undo", action="store_true", help="cancel last created card"
|
|
41
|
+
)
|
|
39
42
|
|
|
40
43
|
group.add_argument(
|
|
41
44
|
"--print_config",
|
|
@@ -77,8 +80,8 @@ def main():
|
|
|
77
80
|
|
|
78
81
|
args = parser.parse_args()
|
|
79
82
|
|
|
83
|
+
# handle correct operation
|
|
80
84
|
try:
|
|
81
|
-
# 2. Handle config and wizard
|
|
82
85
|
if args.wizard:
|
|
83
86
|
config = launch_wizard()
|
|
84
87
|
create_config(config)
|
|
@@ -87,17 +90,23 @@ def main():
|
|
|
87
90
|
print_config()
|
|
88
91
|
sys.exit(0)
|
|
89
92
|
|
|
90
|
-
#
|
|
93
|
+
# loads configuration
|
|
91
94
|
global_config, raw_anki_data = load_config(
|
|
92
95
|
args.source_language, args.target_language, args.notify
|
|
93
96
|
)
|
|
94
97
|
|
|
95
|
-
# 4. trigger workflow
|
|
96
98
|
if args.translate:
|
|
97
99
|
translate_workflow(global_config)
|
|
100
|
+
sys.exit(0)
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
if args.cloze:
|
|
100
103
|
cloze_workflow(global_config, raw_anki_data)
|
|
104
|
+
sys.exit(0)
|
|
105
|
+
|
|
106
|
+
if args.undo:
|
|
107
|
+
url = raw_anki_data.get("default", {}).get("url", "http://localhost:8765")
|
|
108
|
+
undo_workflow(url, global_config.enable_notifications)
|
|
109
|
+
sys.exit(0)
|
|
101
110
|
|
|
102
111
|
except KeyboardInterrupt:
|
|
103
112
|
notify(
|
|
@@ -138,7 +138,7 @@ def resolve_anki_config(
|
|
|
138
138
|
anki_override_fields = anki_override.get("fields", {})
|
|
139
139
|
|
|
140
140
|
anki_config = AnkiConfig(
|
|
141
|
-
url=anki_override.get("url", anki_defaults.get("url")),
|
|
141
|
+
url=anki_override.get("url", anki_defaults.get("url", "http://localhost:8765")),
|
|
142
142
|
deck=anki_override.get(
|
|
143
143
|
"deck", anki_defaults.get("deck", "Wordflow::{source_language}")
|
|
144
144
|
).replace("{source_language}", str(active_language)),
|
|
@@ -93,8 +93,8 @@ def translate(
|
|
|
93
93
|
examples=data.get("examples"),
|
|
94
94
|
)
|
|
95
95
|
# DEBUG
|
|
96
|
-
print("json data: ", response.json())
|
|
97
|
-
print("translation data: ", translation_data)
|
|
96
|
+
# print("json data: ", response.json())
|
|
97
|
+
# print("translation data: ", translation_data)
|
|
98
98
|
return translation_data
|
|
99
99
|
|
|
100
100
|
except requests.RequestException as e:
|
|
@@ -1,11 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
|
|
1
4
|
from .clipboard import get_text, ClipboardError, TimeOutError
|
|
2
5
|
from .translator import translate, TranslationError
|
|
3
6
|
from .notifications import notify
|
|
4
|
-
from .anki_flashcard import
|
|
7
|
+
from .anki_flashcard import (
|
|
8
|
+
AnkiConnectError,
|
|
9
|
+
DuplicateNoteError,
|
|
10
|
+
make_cloze,
|
|
11
|
+
delete_note,
|
|
12
|
+
)
|
|
5
13
|
from .my_classes import TranslationData, AnkiConfig, GlobalConfig
|
|
6
14
|
from .configuration import resolve_anki_config
|
|
7
15
|
|
|
8
16
|
|
|
17
|
+
CACHE_FILE = os.path.join(tempfile.gettempdir(), "wordflow_last_note.txt")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def undo_workflow(anki_url, enable_notifications):
|
|
21
|
+
"""tries to delete the last card created using wordflow"""
|
|
22
|
+
try:
|
|
23
|
+
with open(CACHE_FILE, "r") as f:
|
|
24
|
+
last_id = int(f.read().strip())
|
|
25
|
+
delete_note(anki_url, last_id)
|
|
26
|
+
|
|
27
|
+
# Clear the cache
|
|
28
|
+
os.remove(CACHE_FILE)
|
|
29
|
+
notify(
|
|
30
|
+
"Undo OK",
|
|
31
|
+
f"Successfully deleted the last created flashcard (ID: {last_id}).",
|
|
32
|
+
enable_notifications=enable_notifications,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
except (FileNotFoundError, ValueError):
|
|
36
|
+
print("No recent flashcard found to undo.")
|
|
37
|
+
|
|
38
|
+
|
|
9
39
|
def translate_workflow(global_config: GlobalConfig):
|
|
10
40
|
"""translates a word or sentence and outputs it in a clickable notification"""
|
|
11
41
|
try:
|
|
@@ -59,7 +89,10 @@ def cloze_workflow(
|
|
|
59
89
|
global_config: GlobalConfig,
|
|
60
90
|
raw_anki_data: dict,
|
|
61
91
|
):
|
|
62
|
-
"""
|
|
92
|
+
"""
|
|
93
|
+
creates an anki cloze flashcard from clipboard, automatically detecting correct language settings
|
|
94
|
+
writes the id of the resulting card in a temp file
|
|
95
|
+
"""
|
|
63
96
|
try:
|
|
64
97
|
# 1. Get sentence
|
|
65
98
|
original_sentence = get_text()
|
|
@@ -88,14 +121,19 @@ def cloze_workflow(
|
|
|
88
121
|
resolved_anki_config = resolve_anki_config(
|
|
89
122
|
raw_anki_data, sentence_translation_data.source_language
|
|
90
123
|
)
|
|
91
|
-
|
|
124
|
+
|
|
92
125
|
# 5. Make the cloze card and send it to anki
|
|
93
|
-
make_cloze(
|
|
126
|
+
note_id = make_cloze(
|
|
94
127
|
anki_config=resolved_anki_config,
|
|
95
128
|
sentence_data=sentence_translation_data,
|
|
96
129
|
word_data=word_translation_data,
|
|
97
130
|
)
|
|
98
|
-
|
|
131
|
+
|
|
132
|
+
# 6. write note id in temp file (for potential undo)
|
|
133
|
+
with open(CACHE_FILE, "w") as f:
|
|
134
|
+
f.write(str(note_id))
|
|
135
|
+
|
|
136
|
+
# 7. Notify success
|
|
99
137
|
notify(
|
|
100
138
|
"Anki Success",
|
|
101
139
|
f"Cloze card created for: '{word_to_cloze}' in {resolved_anki_config.deck}",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wordflow-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Automate Anki flashcard creation and text translation directly from your clipboard.
|
|
5
5
|
Author-email: Mathéo Tritschler <tritschler.matheo@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -154,12 +154,16 @@ Wordflow automatically routes audio to native speakers of the detected language.
|
|
|
154
154
|
## Instant Translation (-t)
|
|
155
155
|
|
|
156
156
|
Highlight any text on your screen and trigger the wordflow --translate command. Wordflow detects the language, translates it, and sends a desktop notification. Clicking the notification copies the translation to your clipboard.
|
|
157
|
+
|
|
157
158
|
## Cloze Card Creation (-c)
|
|
158
159
|
|
|
159
160
|
To create a flashcard, highlight the target word/sentence and trigger wordflow --cloze. Wordflow will parse all dictionary data, build the card according to your TOML layout, and push it directly to Anki.
|
|
160
161
|
|
|
161
162
|
Use wordflow --help to list all command-line arguments.
|
|
162
163
|
|
|
164
|
+
## Undo Last Card (-u / --undo)
|
|
165
|
+
|
|
166
|
+
If you accidentally create a card with a typo or the wrong highlighted text, simply trigger the `wordflow --undo` command. Wordflow remembers the ID of the last flashcard it generated and will instantly delete it from your Anki database.
|
|
163
167
|
# ⌨️ Creating Shortcuts
|
|
164
168
|
|
|
165
169
|
Wordflow is designed to be bound to your window manager's shortcuts, allowing you to seamlessly translate and capture vocabulary without losing focus on your current task.
|
|
@@ -177,6 +181,11 @@ Wordflow is designed to be bound to your window manager's shortcuts, allowing yo
|
|
|
177
181
|
hl.dispatch(hl.dsp.submap("reset"))
|
|
178
182
|
hl.dispatch(hl.dsp.exec_cmd("wordflow -c"))
|
|
179
183
|
end)
|
|
184
|
+
--u : Undo last card
|
|
185
|
+
hl.bind("U", function()
|
|
186
|
+
hl.dispatch(hl.dsp.submap("reset"))
|
|
187
|
+
hl.dispatch(hl.dsp.exec_cmd("wordflow -u"))
|
|
188
|
+
end)
|
|
180
189
|
|
|
181
190
|
hl.bind("catchall", hl.dsp.submap("reset"))
|
|
182
191
|
end)
|
|
@@ -184,3 +193,4 @@ Wordflow is designed to be bound to your window manager's shortcuts, allowing yo
|
|
|
184
193
|
## Sway / i3 Example
|
|
185
194
|
bindsym Mod4+t exec "wordflow -t"
|
|
186
195
|
bindsym Mod4+c exec "wordflow -c"
|
|
196
|
+
bindsym Mod4+u exec "wordflow -u"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|