comicforge 0.0.1__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.
Files changed (76) hide show
  1. comicforge-0.0.1/.claude/skills/comicforge/SKILL.md +325 -0
  2. comicforge-0.0.1/.claude/skills/comicforge/reference.md +644 -0
  3. comicforge-0.0.1/.github/workflows/ci.yml +84 -0
  4. comicforge-0.0.1/.github/workflows/release.yml +69 -0
  5. comicforge-0.0.1/.gitignore +36 -0
  6. comicforge-0.0.1/.python-version +1 -0
  7. comicforge-0.0.1/CLAUDE.md +97 -0
  8. comicforge-0.0.1/PKG-INFO +136 -0
  9. comicforge-0.0.1/README.md +123 -0
  10. comicforge-0.0.1/comicforge/__init__.py +10 -0
  11. comicforge-0.0.1/comicforge/__main__.py +5 -0
  12. comicforge-0.0.1/comicforge/bubbles.py +133 -0
  13. comicforge-0.0.1/comicforge/cli.py +396 -0
  14. comicforge-0.0.1/comicforge/inspire.py +246 -0
  15. comicforge-0.0.1/comicforge/library.py +235 -0
  16. comicforge-0.0.1/comicforge/pixelart.py +80 -0
  17. comicforge-0.0.1/comicforge/render.py +560 -0
  18. comicforge-0.0.1/comicforge/scaffold.py +144 -0
  19. comicforge-0.0.1/comicforge/scene.py +112 -0
  20. comicforge-0.0.1/comicforge/validate.py +192 -0
  21. comicforge-0.0.1/docs/starting-a-project.md +70 -0
  22. comicforge-0.0.1/examples/README.md +43 -0
  23. comicforge-0.0.1/examples/README.md.old +22 -0
  24. comicforge-0.0.1/examples/pes/characters/bara/character.yaml +14 -0
  25. comicforge-0.0.1/examples/pes/characters/bara/face-happy.svg +10 -0
  26. comicforge-0.0.1/examples/pes/characters/bara/face-neutral.svg +11 -0
  27. comicforge-0.0.1/examples/pes/characters/bara/poses/sit/base.svg +25 -0
  28. comicforge-0.0.1/examples/pes/characters/bara/poses/sit/pose.yaml +4 -0
  29. comicforge-0.0.1/examples/pes/characters/bara/poses/walk/base.svg +28 -0
  30. comicforge-0.0.1/examples/pes/characters/bara/poses/walk/pose.yaml +5 -0
  31. comicforge-0.0.1/examples/pes/characters/tom/arms-crossed.svg +8 -0
  32. comicforge-0.0.1/examples/pes/characters/tom/arms-down.svg +8 -0
  33. comicforge-0.0.1/examples/pes/characters/tom/arms-hips.svg +8 -0
  34. comicforge-0.0.1/examples/pes/characters/tom/arms-point.svg +8 -0
  35. comicforge-0.0.1/examples/pes/characters/tom/arms-thumbsup.svg +12 -0
  36. comicforge-0.0.1/examples/pes/characters/tom/arms-wave.svg +8 -0
  37. comicforge-0.0.1/examples/pes/characters/tom/base.svg +31 -0
  38. comicforge-0.0.1/examples/pes/characters/tom/character.yaml +9 -0
  39. comicforge-0.0.1/examples/pes/characters/tom/face-angry.svg +9 -0
  40. comicforge-0.0.1/examples/pes/characters/tom/face-happy.svg +9 -0
  41. comicforge-0.0.1/examples/pes/characters/tom/face-laugh.svg +10 -0
  42. comicforge-0.0.1/examples/pes/characters/tom/face-neutral.svg +7 -0
  43. comicforge-0.0.1/examples/pes/characters/tom/face-sad.svg +10 -0
  44. comicforge-0.0.1/examples/pes/characters/tom/face-surprised.svg +11 -0
  45. comicforge-0.0.1/examples/pes/characters/tom/face-wink.svg +8 -0
  46. comicforge-0.0.1/examples/pes/pages/dvur-scene.yaml +21 -0
  47. comicforge-0.0.1/examples/pes/pages/kosticka.yaml +47 -0
  48. comicforge-0.0.1/examples/pes/pages/slepice.yaml +45 -0
  49. comicforge-0.0.1/examples/pes/pixel/bone.yaml +11 -0
  50. comicforge-0.0.1/examples/pes/pixel/heart.yaml +11 -0
  51. comicforge-0.0.1/examples/pes/pixel/star.yaml +11 -0
  52. comicforge-0.0.1/examples/pes/pixel/sun.yaml +13 -0
  53. comicforge-0.0.1/examples/pes/references.yaml +18 -0
  54. comicforge-0.0.1/examples/pes/scenes/dvur/base.svg +31 -0
  55. comicforge-0.0.1/examples/pes/scenes/dvur/scene.yaml +7 -0
  56. comicforge-0.0.1/examples/pes/scenes/dvur/weather-clear.svg +3 -0
  57. comicforge-0.0.1/examples/pes/scenes/dvur/weather-rain.svg +17 -0
  58. comicforge-0.0.1/examples/pes/scenes/pokoj/base.svg +16 -0
  59. comicforge-0.0.1/examples/pes/scenes/pokoj/scene.yaml +3 -0
  60. comicforge-0.0.1/examples/pes/theme.yaml +22 -0
  61. comicforge-0.0.1/poe_tasks.toml +60 -0
  62. comicforge-0.0.1/pyproject.toml +95 -0
  63. comicforge-0.0.1/skills/comicforge/SKILL.md +325 -0
  64. comicforge-0.0.1/skills/comicforge/reference.md +644 -0
  65. comicforge-0.0.1/tests/__init__.py +0 -0
  66. comicforge-0.0.1/tests/conftest.py +36 -0
  67. comicforge-0.0.1/tests/test_bubbles.py +32 -0
  68. comicforge-0.0.1/tests/test_cli.py +84 -0
  69. comicforge-0.0.1/tests/test_inspire.py +93 -0
  70. comicforge-0.0.1/tests/test_library.py +54 -0
  71. comicforge-0.0.1/tests/test_pixelart.py +47 -0
  72. comicforge-0.0.1/tests/test_render.py +117 -0
  73. comicforge-0.0.1/tests/test_scaffold.py +49 -0
  74. comicforge-0.0.1/tests/test_scene.py +34 -0
  75. comicforge-0.0.1/tests/test_validate.py +125 -0
  76. comicforge-0.0.1/uv.lock +817 -0
@@ -0,0 +1,325 @@
1
+ ---
2
+ name: comicforge
3
+ description: >
4
+ Author comic pages and standalone illustrations as declarative YAML specs that
5
+ ComicForge renders to SVG / PNG / PDF. Use this skill whenever the user wants to
6
+ make a comic strip, comic page, cartoon panel, or single illustration with
7
+ ComicForge — placing characters (base SVG + stackable face/arms overlays), scene
8
+ backgrounds, speech/thought/shout bubbles, and pixel-art sprites in a row/panel
9
+ grid. Also triggers on adding a new character, scene, or sprite to a ComicForge
10
+ project, or on questions about the spec grammar, panel coordinates, or the
11
+ `comicforge` CLI (`init`, `render`, `scene`, `panel`, `validate`, `characters`,
12
+ `scenes`).
13
+ ---
14
+
15
+ # ComicForge — authoring comics from YAML
16
+
17
+ You write a **comic page as a YAML spec**; the engine renders it to SVG/PNG/PDF.
18
+ This file is the authoring contract. For deeper explanations (path resolution,
19
+ how to add characters/scenes, pixel-art format, Python API, full CLI reference)
20
+ see [`reference.md`](reference.md) in this skill directory.
21
+
22
+ ## Loop
23
+
24
+ All art belongs to a **project** (e.g. `examples/pes/`) — there is no shared
25
+ library. A project owns `characters/`, `scenes/`, `pixel/`, and its page specs
26
+ under `pages/`. Paths below assume you are working inside one project.
27
+
28
+ Starting from scratch? `cmf init <dir>` scaffolds those four asset dirs, a
29
+ renderable starter page, and a copy of this skill — a data-only project that
30
+ needs no Python (see [`reference.md`](reference.md)).
31
+
32
+ 1. Read the project's assets:
33
+ - `cmf characters --library examples/pes/characters` → JSON
34
+ of every character, its **slots**, **variants** per slot, and the defaults.
35
+ - `cmf scenes --scenes examples/pes/scenes` → JSON of every
36
+ scene background and its slots.
37
+ 2. Write a spec under `pages/` (see grammar below). Spec-level keys declare where
38
+ assets live, relative to the spec file (siblings are one level up):
39
+ ```yaml
40
+ library: "../characters"
41
+ scenes_dir: "../scenes"
42
+ pixel_dir: "../pixel"
43
+ ```
44
+ 3. Validate before rendering: `cmf validate mystrip.yaml` — checks every actor,
45
+ scene, pixel sprite, slot variant, pose, and bubble speaker against the
46
+ libraries without drawing anything, and lists *all* problems at once. Unlike
47
+ `render`, it flags mis-spelled keys (e.g. `fcae:`) that render silently
48
+ ignores. Exits non-zero when anything is wrong. Works on page and `scene` specs.
49
+ 4. Render:
50
+ - comic page: `cmf render mystrip.yaml -o out.pdf`
51
+ - single illustration: `cmf scene myscene.yaml -o out.png`
52
+ - one character on its own (to eyeball a pose/expression):
53
+ `cmf character bara sit happy --library examples/pes/characters`
54
+ — extra args are bare pose/variant names or `key=value` (`pose=walk`,
55
+ `face=happy`), rendered on a white background. Also writes a smaller
56
+ `…​.small.png` companion — read that one to save tokens.
57
+ - Omit `-o` and the file lands in the gitignored `output/` dir with a
58
+ timestamp (`output/<name>-<YYYYMMDD-HHMMSS>.png`), so renders accumulate and
59
+ you can compare how a page/character evolved. Pass `-o` to choose a path.
60
+ 5. Look at the output; adjust `x` / `y` / `scale` / `to` and re-render.
61
+
62
+ ## Coordinates
63
+
64
+ Every position inside a panel is a **fraction 0..1 of that panel**:
65
+ `x` = left→right, `y` = top→bottom. `(0.5, 0.5)` is the panel centre.
66
+ `scale` for actors/pixel art = height as a fraction of the panel height.
67
+
68
+ ## Spec grammar
69
+
70
+ ```yaml
71
+ title: "Optional page title" # bold caption strip at the top
72
+ type: page # page (default) | scene — see below
73
+ page: A4 # A4 | A5 | letter | [w_mm, h_mm]
74
+ px_per_mm: 4 # raster scale (vector PDF ignores it)
75
+ margin_mm: 14
76
+ gutter_mm: 6
77
+ library: "../characters" # path to character dir
78
+ scenes_dir: "../scenes" # path to scenes dir (omit if no scenes used)
79
+ pixel_dir: "../pixel" # path to pixel-art dir (omit if inline only)
80
+
81
+ rows: # page is a stack of rows…
82
+ - height: 1.0 # relative row height (default 1)
83
+ panels: # …each row is a left→right list of panels
84
+ - width: 1.0 # relative panel width (default 1)
85
+ bg: "#fbfaf6" # optional flat panel background
86
+ scene: dvur # optional scene background (see below)
87
+ actors: [ ... ] # characters (drawn back→front in list order)
88
+ pixel: [ ... ] # pixel-art sprites (drawn behind actors)
89
+ bubbles:[ ... ] # speech/thought/shout (drawn on top)
90
+ ```
91
+
92
+ ### actor
93
+
94
+ ```yaml
95
+ - char: tom # character name (from `comicforge characters`)
96
+ pose: walk # OPTIONAL: which body to draw; defaults to the character's default pose
97
+ face: happy # any slot -> a valid variant; omitted slots use defaults
98
+ arms: wave
99
+ x: 0.35 # centre (panel fraction)
100
+ y: 0.62
101
+ scale: 0.85 # height as fraction of panel height
102
+ flip: false # mirror horizontally (face the other way)
103
+ ```
104
+
105
+ Slots and poses are per character. `tom` is single-pose with `face`
106
+ (neutral/happy/surprised/sad/angry/laugh/wink) and `arms`
107
+ (down/wave/point/crossed/hips/thumbsup); `bara` has poses `sit`/`walk` and a
108
+ shared `face` (neutral/happy). Omit `pose` to get the default. Some slots are
109
+ shared across poses, some are pose-specific — the manifest shows both. Always
110
+ confirm against `comicforge characters --library <project>/characters` — never
111
+ guess a pose or variant that isn't listed.
112
+
113
+ ### scene (panel background)
114
+
115
+ A scene is a reusable illustrated background, scaled to *cover* the panel.
116
+
117
+ ```yaml
118
+ scene: dvur # just the name…
119
+ scene: {name: dvur, weather: rain} # …or pick scene slot variants
120
+ ```
121
+
122
+ Confirm scenes/slots with `comicforge scenes --scenes <project>/scenes`.
123
+ Seeds in `examples/pes/`: `dvur` (farmyard, slot `weather: clear|rain`),
124
+ `pokoj` (room, no slots). Both live in `examples/pes/scenes/`.
125
+
126
+ ## Standalone illustration (no comic grid)
127
+
128
+ Render one scene filling the whole canvas with actors/bubbles on top — good for
129
+ covers and single panels. Render with `comicforge scene file.yaml -o out.png`.
130
+
131
+ Set `type: scene` so the spec declares which command renders it: `render`
132
+ rejects a scene spec (and `scene` rejects a page spec) with a clear message
133
+ instead of a confusing crash, and `validate` checks the type matches the
134
+ structure. `type:` is optional — when omitted it's inferred (a top-level
135
+ `scene` with no `rows` == a scene) — but declare it on standalone illustrations.
136
+
137
+ ```yaml
138
+ title: "Optional"
139
+ type: scene
140
+ scene: {name: dvur, weather: clear}
141
+ scale: 3 # px per scene unit (canvas = scene viewbox × scale)
142
+ library: "../characters"
143
+ scenes_dir: "../scenes"
144
+ actors: [ ... ] # same actor grammar; x/y/scale are canvas fractions
145
+ pixel: [ ... ]
146
+ bubbles: [ ... ]
147
+ ```
148
+
149
+ ### bubble
150
+
151
+ ```yaml
152
+ - text: "Ahoj!"
153
+ kind: speech # speech | thought | shout
154
+ speaker: tom # OPTIONAL: auto-place above this actor + aim the tail at
155
+ # their head. Prefer this over manual x/y/to.
156
+ x: 0.5 # OPTIONAL bubble centre (panel fraction); else from speaker
157
+ y: 0.18 # OPTIONAL; omit and bubbles stack downward without overlap
158
+ to: [0.4, 0.5] # OPTIONAL tail target (panel fraction); else speaker's head
159
+ max_chars: 22 # wrap width (optional)
160
+ fs: 16 # font size px (optional)
161
+ ```
162
+
163
+ `thought` draws an ellipse with a trail of dots; `shout` draws a spiky burst.
164
+ The tail is a slim line dropping from the bubble underside; its tip is capped
165
+ short so it never overlaps the figure.
166
+
167
+ ### pixel art
168
+
169
+ ```yaml
170
+ # From the library (needs pixel_dir: in the spec):
171
+ - art: heart # library sprites: heart, sun, star, bone
172
+ x: 0.8 y: 0.25 scale: 0.18
173
+
174
+ # Inline (no pixel_dir needed):
175
+ - grid: ["....", ".RR.", ".RR.", "...."]
176
+ palette: {R: "#e8556d"}
177
+ x: 0.5 y: 0.5 scale: 0.3
178
+ ```
179
+
180
+ Library sprites live in `<project>/pixel/<name>.yaml`. Inline sprites work with
181
+ no `pixel_dir:` at all.
182
+
183
+ ## Adding a character (no code)
184
+
185
+ A character is an **identity** that owns one or more **poses** (different bodies:
186
+ sit, walk, …). Expressions (`face`, …) are authored once and *shared* across
187
+ poses. There are two on-disk shapes — use the simpler one until you need poses.
188
+
189
+ ### Single-pose (flat) — the simple case
190
+
191
+ Create `<project>/characters/<name>/`:
192
+
193
+ - `base.svg` — the body, drawn in a local `viewBox` (e.g. `0 0 200 320`).
194
+ - `<slot>-<variant>.svg` — overlays in the **same** viewBox/coords
195
+ (e.g. `face-happy.svg`, `arms-wave.svg`). They stack on top of the base.
196
+ - `character.yaml`:
197
+ ```yaml
198
+ name: <name>
199
+ label: <Display Name>
200
+ viewbox: [200, 320]
201
+ default: {face: neutral, arms: down}
202
+ slots: {arms: [down, wave, point], face: [neutral, happy, surprised, sad]}
203
+ ```
204
+
205
+ ### Multiple poses
206
+
207
+ When the **body** changes between poses (legs differ sit vs walk), give each pose
208
+ its own `base.svg`. Shared overlays (faces) live at the character root and are
209
+ re-registered onto each pose via an **anchor** — a reference point (typically the
210
+ head centre) the faces are drawn around.
211
+
212
+ ```
213
+ <project>/characters/bara/
214
+ character.yaml # identity + SHARED slots + default + pose list
215
+ face-happy.svg # SHARED overlays, drawn around the canonical anchor
216
+ face-neutral.svg
217
+ poses/
218
+ sit/ { pose.yaml, base.svg, [<slot>-<variant>.svg …] }
219
+ walk/ { pose.yaml, base.svg, [<slot>-<variant>.svg …] }
220
+ ```
221
+
222
+ ```yaml
223
+ # character.yaml
224
+ name: bara
225
+ label: Bára
226
+ anchor: [120, 72] # canonical point the shared faces are drawn around
227
+ slots: {face: [neutral, happy]} # SHARED across poses, re-anchored per pose
228
+ default: {pose: sit, face: neutral}
229
+ poses: [sit, walk]
230
+ ```
231
+
232
+ ```yaml
233
+ # poses/walk/pose.yaml
234
+ viewbox: [240, 180]
235
+ anchor: [132, 64] # where THIS pose's head-centre lands
236
+ slots: {arms: [trot]} # OPTIONAL pose-specific slots + overlays
237
+ default: {arms: trot}
238
+ ```
239
+
240
+ The engine shifts each shared overlay by `pose.anchor - character.anchor`, so one
241
+ `face-happy.svg` lands correctly on every pose. **Translate-only**: keep the head
242
+ the same *size* across poses — only its position changes. To add a pose, drop in a
243
+ new `poses/<name>/` (its own `base.svg` + `pose.yaml`) and list it under `poses:`.
244
+
245
+ > Tip: the seed art is hand/LLM-authored clean SVG, edited directly in the project.
246
+ > A visual model is useful as *inspiration* for a character or scene — generate a
247
+ > reference image for ideas, then author the crisp SVG by hand. Don't auto-vectorize:
248
+ > it breaks overlay/anchor registration and the editable slot structure.
249
+ > Use `comicforge inspire` (below) to paint those references.
250
+
251
+ ## Generating reference images (`comicforge inspire`)
252
+
253
+ Blend a project-wide **theme** (style, color scale, mood) with per-item
254
+ **descriptions** and let an image model paint reference art to author SVG from.
255
+ The output is inspiration only — it is *not* a shipped asset, and you should not
256
+ auto-vectorize it.
257
+
258
+ 1. `<project>/theme.yaml` — the central theme applied to every image:
259
+ ```yaml
260
+ style: > # the look, in words
261
+ Hand-drawn children's-book comic. Bold black ink outlines, flat cheerful fills.
262
+ palette: ["#f4d35e", "#ee964b", "#0d3b66"] # color scale fed to the model
263
+ mood: warm, playful, gentle
264
+ negative: No text, no letters, no words. # what to avoid (has a default)
265
+ aspect_ratio: "1:1"
266
+ # model: google/imagen-3 # optional Replicate model override
267
+ ```
268
+ 2. `<project>/references.yaml` — the things to depict (id → filename):
269
+ ```yaml
270
+ items:
271
+ - {id: tom, prompt: "A cheerful boy in a striped shirt, full body, neutral pose"}
272
+ - {id: dvur, prompt: "A sunny Czech farmyard with a wooden fence and chicken coop"}
273
+ ```
274
+ 3. Generate (writes `<project>/references/<id>.png` + `<id>.prompt.txt`):
275
+ ```bash
276
+ comicforge inspire <project>/references.yaml --review # + a review.html grid
277
+ comicforge inspire <project>/references.yaml --dry-run # compose prompts, no API call
278
+ comicforge inspire <project>/references.yaml --only tom,dvur --force
279
+ ```
280
+
281
+ `theme.yaml` and the output `references/` dir default to siblings of the spec
282
+ (override with `--theme` / `-o`). Live generation needs the optional extra
283
+ (`pip install "comicforge[inspire]"`) and a `REPLICATE_API_TOKEN` (env var or a
284
+ `.env` beside the spec). `--dry-run` needs neither — use it to iterate on prompts.
285
+
286
+ ## Adding a scene (no code)
287
+
288
+ Create a scene directory (e.g. `<project>/scenes/<name>/`) exactly like a character
289
+ but with `scene.yaml` instead of `character.yaml`. `base.svg` is the full
290
+ background (drawn in its own viewBox); optional `<slot>-<variant>.svg` overlays
291
+ add weather/props. The scene is scaled to cover whatever panel (or canvas) it is
292
+ placed in.
293
+
294
+ ```
295
+ <project>/scenes/<name>/
296
+ base.svg
297
+ <slot>-<variant>.svg (optional, one per variant)
298
+ scene.yaml
299
+ ```
300
+
301
+ Then point your spec at that directory via `scenes_dir:`.
302
+
303
+ ## Adding a pixel-art sprite
304
+
305
+ Create `<project>/pixel/<name>.yaml`:
306
+
307
+ ```yaml
308
+ palette:
309
+ R: "#e8556d"
310
+ grid:
311
+ - ".RR."
312
+ - "RRRR"
313
+ - ".RR."
314
+ ```
315
+
316
+ Then reference it with `{art: <name>}` in any panel whose spec has `pixel_dir:`
317
+ pointing at that `pixel/` directory.
318
+
319
+ ## Notes / limits (v0.1)
320
+
321
+ - Poses are pre-drawn bodies (a `base.svg` each), not an articulated skeleton;
322
+ shared expressions re-anchor onto them by translation only (no per-pose resize).
323
+ - One title strip; panels are a row/column grid (no free-form panel polygons yet).
324
+ - Text wrap is a width estimate — check long lines in the render.
325
+ - PDF is true vector (crisp at print); PNG is rasterized at `px_per_mm`.