|
1 | 1 | ## Tasks |
2 | 2 |
|
3 | 3 | ### P0 — Critical / Quick Wins |
| 4 | + |
4 | 5 | - [DONE] Export `RenderingError` from `__init__.py` so users can `from quickthumb import RenderingError` |
5 | 6 | - [DONE] Raise `ValidationError` when `canvas.background()` is called with no `color`, `gradient`, or `image` |
6 | 7 | - [DONE] Remove dead `_get_style_string` method in `canvas.py` (defined but never called) |
7 | 8 |
|
8 | 9 | ### P1 — High Impact Features |
| 10 | + |
9 | 11 | - [DONE] Rich text word-wrapping: auto-wrap `list[TextPart]` content when `max_width` is set (currently only plain string content wraps) |
10 | 12 | - [DONE] Long-word overflow: truncate or warn when a single word exceeds `max_width` in `_wrap_text` |
11 | 13 | - [DONE] Configurable font cache directory via `QUICKTHUMB_FONT_CACHE_DIR` env var (currently hardcoded to `/tmp`) |
12 | 14 |
|
13 | 15 | ### P1 — Planned Features (see SPEC.md) |
14 | 16 |
|
15 | 17 | #### CLI (`quickthumb[cli]`) |
| 18 | + |
16 | 19 | - [DONE] Create `quickthumb/cli.py` with `click`; add `quickthumb[cli]` optional extra in `pyproject.toml` |
17 | 20 | - [DONE] Implement `quickthumb render <spec.json>` with `-o`, `--format`, `--quality` flags |
18 | 21 | - [DONE] Implement `--var KEY=VALUE` template variable substitution in the `render` subcommand |
19 | | -- [TODO] Add `quickthumb watch <spec.json>` subcommand using `watchdog` (`quickthumb[cli,watch]`) |
| 22 | +- [DONE] Add `quickthumb watch <spec.json>` subcommand using `watchfiles` (`quickthumb[cli]`) |
20 | 23 | - [DONE] Wire up exit codes: 0 success, 1 `ValidationError`, 2 `RenderingError` |
21 | 24 |
|
22 | 25 | #### Template System |
| 26 | + |
23 | 27 | - [TODO] Implement `Canvas.from_template(spec_or_path, variables={})` with `$var` / `${var}` string substitution |
24 | 28 | - [TODO] Raise `ValidationError` on unresolved placeholders before JSON parsing |
25 | 29 | - [TODO] Add `Canvas.register_template(name, path)` and `Canvas.unregister_template(name)` registry |
26 | 30 | - [TODO] Create `quickthumb/templates/` directory with starter templates: `youtube-16x9`, `instagram-square`, `twitter-card`, `og-image` |
27 | 31 |
|
28 | 32 | #### Gradient / Image-Filled Text (Knockout Text) |
| 33 | + |
29 | 34 | - [TODO] Add `TextFillImage` model with `path` and `fit` fields and `type: Literal["image"]` discriminator |
30 | 35 | - [TODO] Add `fill` parameter to `TextLayer` accepting `LinearGradient`, `RadialGradient`, or `TextFillImage` |
31 | 36 | - [TODO] Add `fill` parameter to `TextPart` for per-segment fill overrides |
|
34 | 39 | - [TODO] Add JSON round-trip support using `type` discriminators (`linear_gradient`, `radial_gradient`, `image`) |
35 | 40 |
|
36 | 41 | #### Noise / Grain Effect |
| 42 | + |
37 | 43 | - [TODO] Add `Grain` effect model: `intensity`, `monochrome`, `blend_mode`, `opacity` fields with `type: "grain"` discriminator |
38 | 44 | - [TODO] Add `Grain` to `BackgroundEffect` and `ImageEffect` unions |
39 | 45 | - [TODO] Implement grain rendering using Pillow only (no NumPy) |
|
42 | 48 | - [TODO] Add JSON round-trip for both per-layer `Grain` effect and top-level `GrainLayer` |
43 | 49 |
|
44 | 50 | ### P2 — Docs & Discoverability |
| 51 | + |
45 | 52 | - [TODO] Homepage headline: make the AI/JSON workflow angle front and center (currently buried) |
46 | 53 | - [TODO] Add "Common LLM Mistakes" section to the JSON schema page (invalid hex, wrong position format, unsupported effects, etc.) |
47 | 54 | - [TODO] Add "Why not X" section: brief comparison vs raw Pillow and html2image to help developers justify the dependency |
48 | 55 | - [TODO] Add community entry point: link to GitHub issues/discussions for bug reports and questions |
49 | 56 |
|
50 | 57 | ### P1 — CLI Hardening (from code review) |
| 58 | + |
51 | 59 | - [DONE] CLI `render`: catch `FileNotFoundError` / `PermissionError` from `spec.read_text()` and exit with code 1 |
52 | 60 | - [DONE] CLI `render`: catch `json.JSONDecodeError` from `Canvas.from_json()` and exit with code 1 |
53 | 61 | - [DONE] `_substitute_vars`: raise `ValidationError` on unresolved `$VAR` / `${VAR}` placeholders (currently passes them through silently) |
|
58 | 66 | - [DONE] CLI `render`: validate `--format` is one of `PNG`, `JPEG`, `WEBP`; reject unknown values early |
59 | 67 |
|
60 | 68 | ### P2 — Font Cache Hardening (from code review) |
| 69 | + |
61 | 70 | - [TODO] Use `tempfile.gettempdir()` instead of hardcoded `"/tmp"` as the default font cache dir (fixes Windows compatibility) |
62 | 71 | - [TODO] Validate downloaded font content before writing to cache (currently writes arbitrary data from any URL) |
63 | 72 | - [DONE] Call `os.makedirs(cache_dir, exist_ok=True)` before writing cached font; `QUICKTHUMB_FONT_CACHE_DIR` pointing to a non-existent dir currently crashes with `FileNotFoundError` |
64 | 73 |
|
65 | 74 | ### P2 — CLI Polish (from code review) |
| 75 | + |
66 | 76 | - [TODO] Rename `format` parameter to `fmt` or `output_format` internally — currently shadows Python's built-in `format()` |
67 | 77 | - [TODO] Widen typer version pin from `>=0.24.1,<0.25.0` to `>=0.24.1,<1.0` to avoid unnecessary resolver conflicts |
68 | 78 |
|
69 | 79 | ### P3 — Lower Priority |
| 80 | + |
70 | 81 | - [TODO] Fix color tuple JSON round-trip: `BackgroundLayer.color` accepts RGB tuples but they break `to_json()` / `from_json()` |
71 | 82 | - [TODO] Font metadata reading: use `fonttools` to read font weight/style from file metadata instead of relying on filename parsing |
72 | 83 | - [TODO] Split `canvas.py` (currently 2466 lines) into smaller modules before it becomes a maintenance burden |
73 | 84 |
|
74 | 85 | ## Handoff Notes |
| 86 | + |
75 | 87 | - |
76 | 88 | - |
0 commit comments