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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
|
# ComicForge Architecture
This document describes the architecture of **ComicForge**, a Go CLI application that generates AI-powered comic books from vocabulary word lists or direct prompts.
## Table of Contents
1. [High-Level Overview](#high-level-overview)
2. [System Architecture](#system-architecture)
3. [Package Structure](#package-structure)
4. [Data Flow](#data-flow)
5. [Provider Architecture](#provider-architecture)
6. [Configuration System](#configuration-system)
7. [Comic Generation Pipeline](#comic-generation-pipeline)
8. [Key Design Patterns](#key-design-patterns)
---
## High-Level Overview
ComicForge is a command-line tool that orchestrates multiple AI providers to produce complete comic books. Given a vocabulary file containing words in a target language, it:
1. Generates a story incorporating those words
2. Creates a character bible for visual consistency
3. Renders comic pages (cover, story pages, gallery, back cover)
4. Assembles a PDF
5. Optionally generates cinematic audio narration
---
## System Architecture
```mermaid
graph TB
subgraph CLI["Command Layer"]
CMD["cmd/comicforge/main.go<br/>Minimal bootstrap"]
CLI_GO["cmd/comicforge/cli.go<br/>Cobra flags & wiring"]
end
subgraph Internal["Internal Packages"]
CFG["internal/config<br/>Configuration loading"]
PRV["internal/provider<br/>Provider interfaces"]
COM["internal/comic<br/>Generation pipeline"]
IMG["internal/image<br/>Image providers"]
TXT["internal/text<br/>Text providers"]
TTS["internal/tts<br/>TTS providers"]
VOC["internal/vocab<br/>Vocabulary parsing"]
HTP["internal/httpctx<br/>HTTP/GenAI clients"]
end
subgraph External["External Services"]
GEMINI["Google Gemini API<br/>(Text, Image, TTS)"]
FS["Local Filesystem<br/>(Output, PDFs, Audio)"]
end
CMD --> CLI_GO
CLI_GO --> CFG
CLI_GO --> COM
CLI_GO --> PRV
PRV --> TXT
PRV --> IMG
PRV --> TTS
COM --> TXT
COM --> IMG
COM --> TTS
COM --> VOC
TXT --> HTP
IMG --> HTP
TTS --> HTP
HTP --> GEMINI
COM --> FS
```
---
## Package Structure
```mermaid
graph LR
subgraph Entry["Entry Point"]
M[main.go]
C[cli.go]
end
subgraph Config["Configuration"]
CFG[config.go]
HOME[home.go]
end
subgraph Providers["Provider Layer"]
P[provider.go]
TR[text/registry.go]
IR[image/registry.go]
TTR[tts/registry.go]
TG[text/gemini.go]
IG[image/gemini.go]
TTG[tts/gemini.go]
end
subgraph Pipeline["Comic Pipeline"]
RUN[runner.go]
GEN[generator.go]
ART[artist.go]
NAR[narrator.go]
TYP[types.go]
end
subgraph Support["Supporting Packages"]
VOC[vocab/reader.go]
HTP[httpctx/httpctx.go]
CIR[apicircuit/apicircuit.go]
end
M --> C
C --> CFG
C --> RUN
C --> TR
C --> IR
C --> TTR
RUN --> GEN
RUN --> ART
RUN --> NAR
RUN --> VOC
GEN --> TG
ART --> IG
ART --> TG
NAR --> TTG
NAR --> TG
TR --> P
IR --> P
TTR --> P
TG --> HTP
IG --> HTP
TTG --> HTP
```
---
## Data Flow
### Vocabulary Mode (Full Comic)
```mermaid
sequenceDiagram
participant User
participant CLI as cli.go
participant Run as Runner
participant Gen as Generator
participant Art as Artist
participant Nar as Narrator
participant TP as TextProvider
participant IP as ImageProvider
participant TTS as TTSProvider
participant FS as Filesystem
User->>CLI: comicforge --vocab words.txt
CLI->>Run: NewRunner(cfg)
Run->>Gen: NewGenerator()
Run->>Art: NewArtist()
Run->>Nar: NewNarrator()
Run->>Gen: GenerateFull(entries)
Gen->>TP: GenerateText(storyPrompt)
TP-->>Gen: story + bible + title + panelScript
Gen-->>Run: GenerateResult
Run->>Art: DrawComicPages(story, bible, title, entries, panelScript)
loop For each page
Art->>Art: Render prompt template
Art->>IP: GenerateImageWithReferences(prompt, refs)
IP-->>Art: image.png
Art->>FS: Write page file
end
Art-->>Run: []pagePaths
Run->>FS: Save story.txt
Run->>FS: Save vocabulary.txt
Run->>FS: Save theme.txt
Run->>FS: Assemble PDF
opt Narration enabled
Run->>Nar: Narrate(storyText)
Nar->>TP: GenerateText(intro prompt)
Nar->>TTS: GenerateAudio(chunks)
Nar->>FS: Save narration.mp3
end
Run-->>CLI: success
CLI-->>User: Done
```
### Prompt Mode (Single Image)
```mermaid
sequenceDiagram
participant User
participant CLI as cli.go
participant Run as Runner
participant Art as Artist
participant TP as TextProvider
participant IP as ImageProvider
participant FS as Filesystem
User->>CLI: comicforge --prompt "dragon in space"
CLI->>Run: NewRunner(cfg)
Run->>Art: NewArtist()
Run->>Run: Generate title slug via TP
Run->>Art: generatePromptImage(prompt)
Art->>Art: Render manual prompt template
Art->>IP: GenerateImage(renderedPrompt)
IP-->>Art: image.png
Art->>FS: Write prompt.png
Run-->>CLI: success
CLI-->>User: Done
```
---
## Provider Architecture
ComicForge uses a **registry-based provider system** that abstracts over different AI backends. Currently, only **Google Gemini** is fully implemented; OpenAI is stubbed for future extension.
```mermaid
classDiagram
class TextProvider {
<<interface>>
+Name() string
+IsAvailable() error
+GenerateText(ctx, prompt) (string, error)
}
class ImageProvider {
<<interface>>
+Name() string
+IsAvailable() error
+GenerateImage(ctx, prompt, outputFile) error
}
class AspectRatioImageProvider {
<<interface>>
+GenerateImageWithAspectRatio(ctx, prompt, outputFile, aspectRatio) error
}
class TTSProvider {
<<interface>>
+Name() string
+IsAvailable() error
+GenerateAudio(ctx, text, outputFile) error
}
class GeminiTextProvider {
+client *genai.Client
+model string
+GenerateText(ctx, prompt) (string, error)
}
class GeminiImageProvider {
+client *genai.Client
+config *GeminiConfig
+GenerateImage(ctx, prompt, outputFile) error
+GenerateImageWithReferences(ctx, prompt, outputFile, refs) error
}
class GeminiTTSProvider {
+client *genai.Client
+model string
+voice string
+GenerateAudio(ctx, text, outputFile) error
}
class Registry~T, C~ {
+Register(name, factory)
+Resolve(name) Factory
+New(name, cfg) T
}
TextProvider <|.. GeminiTextProvider
ImageProvider <|.. GeminiImageProvider
ImageProvider <|-- AspectRatioImageProvider
AspectRatioImageProvider <|.. GeminiImageProvider
TTSProvider <|.. GeminiTTSProvider
Registry --> TextProvider
Registry --> ImageProvider
Registry --> TTSProvider
```
### Provider Registry Flow
```mermaid
graph LR
A[CLI Flags<br/>--text-provider gemini] --> B[Config.Normalize]
B --> C[Registry.Resolve"gemini"]
C --> D[Factory Function]
D --> E[GeminiProvider]
E --> F[IsAvailable Check]
F --> G[Inject into Runner]
```
---
## Configuration System
Configuration is loaded via **Viper** with a three-layer priority:
1. **Defaults** (code-defined)
2. **Config file** (`config.yaml`)
3. **Environment variables** (`COMICFORGE_*`)
4. **CLI flags** (highest priority)
```mermaid
graph TD
A[DefaultConfig] --> B[Viper SetDefaults]
C[config.yaml] --> D[Viper ReadInConfig]
E[COMICFORGE_API_GOOGLE_API_KEY] --> F[Viper AutomaticEnv]
G[CLI Flags<br/>--text-model, --style] --> H[Apply Overrides]
B --> I[Loaded Config]
D --> I
F --> I
H --> I
I --> J[Validate Providers]
J --> K[Return *Config]
```
The `Config` struct implements multiple small interfaces so each package only depends on what it needs:
```mermaid
graph TD
CFG[Config] --> TC[TextConfig]
CFG --> IC[ImageConfig]
CFG --> TTC[TTSConfig]
CFG --> TXTC[text.Config]
CFG --> IMGC[image.Config]
CFG --> TTSC[tts.Config]
TC --> TXT[internal/text]
IC --> IMG[internal/image]
TTC --> TTS[internal/tts]
```
---
## Comic Generation Pipeline
The pipeline is divided into three specialized stages:
```mermaid
graph LR
subgraph Stage1["Stage 1: Story Generation"]
direction TB
VOC[Vocabulary Words] --> GEN
GEN[Generator] --> STORY[Story Text]
GEN --> BIBLE[Character Bible]
GEN --> TITLE[Comic Title]
GEN --> PANEL[Panel Script]
end
subgraph Stage2["Stage 2: Visual Art"]
direction TB
STORY --> ART[Artist]
BIBLE --> ART
PANEL --> ART
ART --> COVER[Cover Page]
ART --> PAGES[Story Pages]
ART --> GALLERY[Gallery Pages]
ART --> BACK[Back Cover]
end
subgraph Stage3["Stage 3: Assembly"]
direction TB
COVER --> PDF[PDF Assembly]
PAGES --> PDF
GALLERY --> PDF
BACK --> PDF
STORY --> NAR[Narrator]
NAR --> AUDIO[MP3 Narration]
end
```
### Reference Image Chaining
To maintain visual consistency across comic pages, the Artist passes previous page images as **reference images** to the image provider:
```mermaid
graph LR
P1[Page 1] -->|ref| P2[Page 2]
P2 -->|ref| P3[Page 3]
P3 -->|ref| P4[...]
style P1 fill:#e1f5fe
style P2 fill:#e1f5fe
style P3 fill:#e1f5fe
```
The `appendRef` function keeps only the **first and most recent** reference images to avoid exceeding API limits.
---
## Key Design Patterns
### Dependency Injection
The CLI uses a `commandDeps` struct to inject provider factories, making the entire command layer testable without real API calls:
```go
type commandDeps struct {
loadConfig func(string) (*config.Config, error)
newTextProvider func(*config.Config) (provider.TextProvider, error)
newImageProvider func(*config.Config) (provider.ImageProvider, error)
newTTSProvider func(*config.Config, string) (provider.TTSProvider, error)
newRunner func(*comic.RunnerConfig) comic.StoryRunner
}
```
### Template-Driven Prompts
All LLM prompts are **Go text/template files** rendered with structured data. Templates live in `prompts/` and can be overridden at runtime via `--prompts-dir`:
```mermaid
graph LR
DATA[Template Data<br/>Language, Style, Bible] --> RENDER[config.RenderPrompt]
TPL[Template File<br/>story_full_prompt.md] --> RENDER
RENDER --> PROMPT[Final Prompt]
PROMPT --> LLM[Gemini API]
```
### Circuit Breaker
The `internal/apicircuit` package provides circuit breaker protection for API calls to prevent cascading failures when AI providers are unavailable.
### Defensive Defaults
Every component uses the **functional options / config struct** pattern with sensible defaults:
```mermaid
graph TD
A[User Config] --> B{Field Set?}
B -->|Yes| C[Use User Value]
B -->|No| D[Use Default Value]
C --> E[Initialized Component]
D --> E
```
---
## File Output Structure
When running with `--output .` and a comic titled "Space Adventure":
```
comics/
├── assets/
│ └── space-adventure/
│ ├── space-adventure_cover.png
│ ├── space-adventure_page_1.png
│ ├── space-adventure_page_2.png
│ ├── space-adventure_gallery_1.png
│ ├── space-adventure_back.png
│ ├── space-adventure_story.txt
│ ├── space-adventure_comic_vocabulary.txt
│ ├── space-adventure_theme.txt
│ └── space-adventure_narration.mp3
├── PDF/
│ └── space-adventure.pdf
└── gallery/
├── space-adventure_gallery_1.png
└── ...
```
---
## Technology Stack
| Component | Technology |
|-----------|------------|
| Language | Go 1.23+ |
| CLI Framework | Cobra |
| Configuration | Viper |
| AI Provider SDK | `google.golang.org/genai` |
| PDF Generation | Custom assembly via `internal/comic/pdf.go` |
| Audio Processing | `ffmpeg` (external dependency) |
| Testing | Go standard testing + test tables |
| Module Path | `codeberg.org/snonux/comicforge` |
---
## Future Extension Points
- **New AI Providers**: Implement `TextProvider`/`ImageProvider`/`TTSProvider` and register in the respective registries
- **New Style Modes**: Add to `StyleConfig` and update `pickStyle()` in `types.go`
- **Custom Output Formats**: The `assemblePDF` field in `Runner` is injectable for alternative exporters
- **Additional Languages**: Extend `localization.go` with new script/language mappings
|