anki_generator 1.1.0 → 1.4.0

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +59 -0
  3. data/.rubocop.yml +79 -0
  4. data/.ruby-version +1 -0
  5. data/.tool-versions +1 -0
  6. data/CHANGELOG.md +173 -0
  7. data/Gemfile +21 -0
  8. data/Makefile +20 -0
  9. data/README.md +160 -32
  10. data/Rakefile +174 -0
  11. data/anki_generator.gemspec +42 -0
  12. data/bin/anki_generator +3 -2
  13. data/docs/CI_SETUP.md +120 -0
  14. data/docs/architecture/current-v1.3.0.architecture.json +315 -0
  15. data/docs/architecture/current-v1.3.0.html +14990 -0
  16. data/docs/architecture/current-v1.3.0.visual-check.json +548 -0
  17. data/docs/architecture/phase3-proposed.architecture.json +310 -0
  18. data/docs/architecture/phase3-proposed.html +15001 -0
  19. data/docs/architecture/phase3-proposed.visual-check.json +548 -0
  20. data/docs/phase3-draft.md +86 -0
  21. data/examples/example_class.rb +13 -0
  22. data/examples/manual_cards.yaml +5 -0
  23. data/examples/study_prompt.txt +3 -0
  24. data/input/input.yaml.example +3 -0
  25. data/lib/anki_generator/anki_connect_client.rb +85 -0
  26. data/lib/anki_generator/apkg_schema.rb +257 -0
  27. data/lib/anki_generator/apkg_writer.rb +149 -0
  28. data/lib/anki_generator/card.rb +83 -0
  29. data/lib/anki_generator/cli.rb +183 -0
  30. data/lib/anki_generator/client_factory.rb +20 -0
  31. data/lib/anki_generator/commands/create_ai_template.rb +39 -0
  32. data/lib/anki_generator/commands/generate_deck.rb +43 -0
  33. data/lib/anki_generator/commands/generate_yaml.rb +63 -0
  34. data/lib/anki_generator/commands/import.rb +75 -0
  35. data/lib/anki_generator/commands/prompt_based.rb +74 -0
  36. data/lib/anki_generator/commands/prompt_to_deck.rb +84 -0
  37. data/lib/anki_generator/commands/push.rb +32 -0
  38. data/lib/anki_generator/commands/serve.rb +99 -0
  39. data/lib/anki_generator/commands/test_api.rb +33 -0
  40. data/lib/anki_generator/deck_builder.rb +184 -0
  41. data/lib/anki_generator/errors.rb +22 -0
  42. data/lib/anki_generator/file_processor.rb +154 -0
  43. data/lib/anki_generator/importers/csv.rb +52 -0
  44. data/lib/anki_generator/importers/markdown.rb +72 -0
  45. data/lib/anki_generator/prompt_builder.rb +77 -0
  46. data/lib/anki_generator/server.rb +98 -0
  47. data/lib/anki_generator/ui.rb +28 -0
  48. data/lib/anki_generator/version.rb +5 -0
  49. data/lib/anki_generator.rb +18 -114
  50. data/prompt.txt +5 -0
  51. metadata +100 -43
  52. data/lib/anki_cli.rb +0 -259
  53. data/lib/file_processor.rb +0 -156
  54. data/lib/openrouter_client.rb +0 -158
@@ -0,0 +1,310 @@
1
+ {
2
+ "schema_version": 1,
3
+ "diagram_type": "architecture",
4
+ "meta": {
5
+ "title": "Anki Generator — Phase 3: Hosted Sync Service",
6
+ "output": "phase3-architecture.html",
7
+ "quality_profile": "showcase",
8
+ "locale": "en",
9
+ "views": [
10
+ {
11
+ "id": "sync-path",
12
+ "label": "Offline-first sync",
13
+ "focus": [
14
+ "android",
15
+ "ios",
16
+ "api",
17
+ "postgres"
18
+ ],
19
+ "note": "Apps cache decks locally and sync deltas over HTTPS; the API is the only server surface."
20
+ },
21
+ {
22
+ "id": "generation-path",
23
+ "label": "Async AI generation",
24
+ "focus": [
25
+ "api",
26
+ "queue",
27
+ "gensvc",
28
+ "llm",
29
+ "postgres",
30
+ "objectstore"
31
+ ],
32
+ "note": "Generation is a background job: the API enqueues, the worker reuses the gem and writes cards back."
33
+ }
34
+ ]
35
+ },
36
+ "components": [
37
+ {
38
+ "id": "android",
39
+ "type": "frontend",
40
+ "label": "Android app",
41
+ "sublabel": "offline cache · Kotlin",
42
+ "pos": [
43
+ 40,
44
+ 80
45
+ ],
46
+ "size": [
47
+ 150,
48
+ 64
49
+ ]
50
+ },
51
+ {
52
+ "id": "ios",
53
+ "type": "frontend",
54
+ "label": "iOS app",
55
+ "sublabel": "offline cache · Swift",
56
+ "pos": [
57
+ 40,
58
+ 220
59
+ ],
60
+ "size": [
61
+ 150,
62
+ 64
63
+ ]
64
+ },
65
+ {
66
+ "id": "web",
67
+ "type": "frontend",
68
+ "label": "Web client",
69
+ "sublabel": "hosted serve UI",
70
+ "pos": [
71
+ 40,
72
+ 360
73
+ ],
74
+ "size": [
75
+ 150,
76
+ 64
77
+ ]
78
+ },
79
+ {
80
+ "id": "api",
81
+ "type": "backend",
82
+ "label": "Sync API",
83
+ "sublabel": "Ruby · JSON / HTTPS",
84
+ "pos": [
85
+ 320,
86
+ 220
87
+ ],
88
+ "size": [
89
+ 150,
90
+ 70
91
+ ],
92
+ "tag": "auth: magic link / JWT"
93
+ },
94
+ {
95
+ "id": "postgres",
96
+ "type": "database",
97
+ "label": "PostgreSQL",
98
+ "sublabel": "users · decks · cards",
99
+ "pos": [
100
+ 580,
101
+ 220
102
+ ],
103
+ "size": [
104
+ 160,
105
+ 64
106
+ ]
107
+ },
108
+ {
109
+ "id": "queue",
110
+ "type": "messagebus",
111
+ "label": "Job queue",
112
+ "sublabel": "Solid Queue",
113
+ "pos": [
114
+ 320,
115
+ 396
116
+ ],
117
+ "size": [
118
+ 150,
119
+ 64
120
+ ]
121
+ },
122
+ {
123
+ "id": "gensvc",
124
+ "type": "backend",
125
+ "label": "Generation worker",
126
+ "sublabel": "reuses anki_generator gem",
127
+ "pos": [
128
+ 580,
129
+ 396
130
+ ],
131
+ "size": [
132
+ 170,
133
+ 64
134
+ ]
135
+ },
136
+ {
137
+ "id": "objectstore",
138
+ "type": "cloud",
139
+ "label": "Object storage",
140
+ "sublabel": ".apkg exports",
141
+ "pos": [
142
+ 860,
143
+ 82
144
+ ],
145
+ "size": [
146
+ 150,
147
+ 64
148
+ ]
149
+ },
150
+ {
151
+ "id": "llm",
152
+ "type": "cloud",
153
+ "label": "LLM providers",
154
+ "sublabel": "OpenRouter · Ollama",
155
+ "pos": [
156
+ 880,
157
+ 396
158
+ ],
159
+ "size": [
160
+ 150,
161
+ 64
162
+ ]
163
+ }
164
+ ],
165
+ "boundaries": [
166
+ {
167
+ "kind": "region",
168
+ "label": "Single small VPS / container host (cheap to run)",
169
+ "wraps": [
170
+ "api",
171
+ "postgres",
172
+ "queue",
173
+ "gensvc"
174
+ ]
175
+ },
176
+ {
177
+ "kind": "region",
178
+ "label": "User devices",
179
+ "wraps": [
180
+ "android",
181
+ "ios",
182
+ "web"
183
+ ]
184
+ }
185
+ ],
186
+ "connections": [
187
+ {
188
+ "id": "android-sync",
189
+ "from": "android",
190
+ "to": "api",
191
+ "label": "HTTPS sync",
192
+ "variant": "emphasis",
193
+ "toSide": "top",
194
+ "via": [
195
+ [
196
+ 255,
197
+ 112
198
+ ],
199
+ [
200
+ 255,
201
+ 150
202
+ ],
203
+ [
204
+ 395,
205
+ 150
206
+ ]
207
+ ]
208
+ },
209
+ {
210
+ "id": "ios-sync",
211
+ "from": "ios",
212
+ "to": "api",
213
+ "variant": "emphasis"
214
+ },
215
+ {
216
+ "id": "web-sync",
217
+ "from": "web",
218
+ "to": "api",
219
+ "fromSide": "top",
220
+ "toSide": "bottom"
221
+ },
222
+ {
223
+ "id": "api-sql",
224
+ "from": "api",
225
+ "to": "postgres",
226
+ "label": "SQL"
227
+ },
228
+ {
229
+ "id": "enqueue",
230
+ "from": "api",
231
+ "to": "queue",
232
+ "label": "enqueue",
233
+ "variant": "dashed",
234
+ "fromSide": "bottom",
235
+ "toSide": "top",
236
+ "labelAt": [
237
+ 395,
238
+ 340
239
+ ]
240
+ },
241
+ {
242
+ "id": "queue-to-worker",
243
+ "from": "queue",
244
+ "to": "gensvc"
245
+ },
246
+ {
247
+ "id": "worker-to-llm",
248
+ "from": "gensvc",
249
+ "to": "llm",
250
+ "label": "completions",
251
+ "variant": "dashed"
252
+ },
253
+ {
254
+ "id": "worker-writes",
255
+ "from": "gensvc",
256
+ "to": "postgres",
257
+ "label": "insert cards",
258
+ "fromSide": "top",
259
+ "toSide": "bottom"
260
+ },
261
+ {
262
+ "id": "export-store",
263
+ "from": "api",
264
+ "to": "objectstore",
265
+ "label": "store .apkg",
266
+ "variant": "dashed",
267
+ "fromSide": "top",
268
+ "toSide": "bottom",
269
+ "via": [
270
+ [
271
+ 395,
272
+ 175
273
+ ],
274
+ [
275
+ 935,
276
+ 175
277
+ ]
278
+ ]
279
+ }
280
+ ],
281
+ "cards": [
282
+ {
283
+ "dot": "cyan",
284
+ "title": "Clients: offline first",
285
+ "items": [
286
+ "Decks cached in SQLite (SQLDelight / GRDB) so study works with no network",
287
+ "Sync sends only deltas: card edits, review state, new decks",
288
+ "Generation requests return a job id; cards appear when the job finishes"
289
+ ]
290
+ },
291
+ {
292
+ "dot": "emerald",
293
+ "title": "Service: reuse the gem",
294
+ "items": [
295
+ "Sync API is a thin Ruby service over the existing AnkiGenerator::* objects",
296
+ "Solid Queue + one worker process run AI generation out of request path",
297
+ "Web client is today's serve UI with auth and the API as backend"
298
+ ]
299
+ },
300
+ {
301
+ "dot": "rose",
302
+ "title": "Cost: hobbyist scale",
303
+ "items": [
304
+ "One small VPS or free-tier container runs API + worker + queue",
305
+ "PostgreSQL: managed free tier or the same VPS",
306
+ "LLM cost stays pay-per-use via OpenRouter, or $0 with a local Ollama sidecar"
307
+ ]
308
+ }
309
+ ]
310
+ }