Compare commits

..

No commits in common. "f22d331c31130e6a843fb68a124ce56fd87c4c5a" and "ae1cae967be649f31d83e0e104031d2e06240577" have entirely different histories.

187 changed files with 0 additions and 18475 deletions

View File

@ -1,49 +0,0 @@
name: odin-ci
on:
push:
paths:
- 'odin/**'
- '.github/workflows/odin-ci.yml'
pull_request:
paths:
- 'odin/**'
- '.github/workflows/odin-ci.yml'
workflow_dispatch:
jobs:
build-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: odin
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- name: Setup Odin
uses: laytan/setup-odin@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Build
run: ./build.sh
- name: Test
run: |
CLAY_DIR="$(pwd)/vendor/clay/bindings/odin/clay-odin"
BUILD_DIR="$(pwd)/build"
odin test tests -collection:clay="$CLAY_DIR" -extra-linker-flags:"-L$BUILD_DIR -losdialog"
- name: Package
run: ./scripts/package.sh
- name: Upload package artifact
uses: actions/upload-artifact@v4
with:
name: comic-odin-package
path: |
odin/dist/*.tar.gz
odin/dist/*.sha256

6
.gitmodules vendored
View File

@ -1,6 +0,0 @@
[submodule "odin/vendor/clay"]
path = odin/vendor/clay
url = https://github.com/nicbarker/clay.git
[submodule "odin/vendor/osdialog"]
path = odin/vendor/osdialog
url = https://github.com/AndrewBelt/osdialog.git

View File

@ -1,88 +0,0 @@
# comic-odin / comicsroll
Dual-language repo: **React/TypeScript frontend** (`src/`) + **Odin native desktop app** (`odin/`).
## Build & Test — Odin (`odin/`)
```bash
# Build debug binary
./odin/build.sh # builds C deps + odin src/app
# Or directly:
odin build odin/src/app -target:linux_amd64 -collection:clay=$(pwd)/odin/vendor/clay/bindings/odin/clay-odin
# Run all tests
odin test odin/tests -target:linux_amd64 -collection:clay=$(pwd)/odin/vendor/clay/bindings/odin/clay-odin
# Run single test
odin test odin/tests -target:linux_amd64 -collection:clay=$(pwd)/odin/vendor/clay/bindings/odin/clay-odin -define:ODIN_TEST_NAMES=tests.<test_proc_name>
# Run binary
./bin/comic_odin gui # Raylib GUI
./bin/comic_odin tui # Terminal TUI
```
## Build & Test — React Frontend (`src/`)
```bash
npm run dev # vite dev server
npm run build # tsc -b && vite build
npm run lint # eslint . (strictTypeChecked, no any)
npm run typecheck # tsc --noEmit
npm run test # vitest run (jsdom, globals)
npm run test:ui # vitest --ui
```
## Architecture
### React (Vite 8, React 19, TypeScript 6, Tailwind 3, Zustand 5)
- **No URL routing** — manual `useState` + zustand `workflow.currentStep` drives component switching in `App.tsx`.
- **Single zustand store** (`src/store/comicStore.ts`) with `persist` middleware.
- **Two API services**: DeepSeek (via OpenAI SDK for script gen) and fal.ai (via `@fal-ai/client` for images).
- **`@` path alias** → `./src`.
- **`verbatimModuleSyntax: true`** — type-only imports need `import type`.
- **`noUnusedLocals`, `noUnusedParameters` both `true`**.
- `framer-motion`, `fabric`, `react-router-dom` installed but unused.
- No test files exist yet (vitest configured and ready).
### Odin (Raylib + Clay layout engine + osdialog)
- **Single binary**, entry: `odin/src/app/main.odin``run_cli_from_process_args()``.Gui``gui.run_gui_app()`
- **8 source packages**: `app` (entry), `core` (domain), `shared` (config/errors), `adapters` (DeepSeek/fal/export/storage), `ui` (controller/screens/jobs), `gui` (Raylib+Clay UI), `osdialog` (file dialogs).
- Clay imported as `import clay "clay:."` via collection `-collection:clay=vendor/clay/bindings/odin/clay-odin`.
- **ols.json** defines the `clay` collection path.
### Workflow state machine
```
Story_Input → Generating_Script → Script_Review → Character_Setup →
Generating_Panels → Layout → Speech_Bubbles → Complete
```
Defined in `core/workflow.odin` (`can_transition()`). In the React app, zustand's `workflow.currentStep` mirrors this.
## Clay Layout Gotchas (Critical)
- **`SizingPercent()` expects 01, not 0100.** `SizingPercent(55)` = 5500% (off-screen). Use `SizingPercent(0.55)`.
- **`SizingFit({})` cards + `SizingGrow({})` children = invisible.** A Fit-height card computes its height from children's *initial* CloseElement sizes. Grow children have initial height 0, so the card stays tiny. If a card contains a Grow-height child (scroll list, wireframe), override the card height to `SizingGrow({})`.
- **`backgroundColor` on image elements generates a covering RECTANGLE.** Remove `backgroundColor` from image elements to let the IMAGE command be visible.
- **Texture pointers** (`rawptr(tex_ptr)`) require `reserve(&app.panel_textures, N)` after map creation to avoid pointer invalidation on reallocation.
- **Clay v0.14** is vendored at `vendor/clay/`. The Odin binding is at `vendor/clay/bindings/odin/clay-odin/clay.odin`.
## Raygui Integration
- `rl.GuiTextBox` overlays on top of Clay layout. After Clay renders, get element bounds via `clay.GetElementData(id)`.
- `GuiTextBox` returns `true` on click — NOT on text change. Compare buffer length after call to detect edits.
- `rl.EndScissorMode()` must be called before `rl.GuiTextBox` and after to clear stale scissor state.
## Memory
- **Odin**: `persistent_pool` (256KB) in `gui/runtime.odin` for strings that must survive temp allocator resets.
- **Odin**: All `delete()`'d string defaults in `new_initial_state()` must use `strings.clone()` to avoid `free(): invalid pointer` crashes.
- **React**: Zustand store persisted to localStorage (`comic-creator-storage`). `partialize` excludes ephemeral state.
## API Keys (.env)
```
DEEPSEEK_API_KEY=sk-...
FAL_API_KEY=...
VITE_DEEPSEEK_API_KEY=sk-...
VITE_FAL_KEY=...
```

BIN
app

Binary file not shown.

View File

@ -1,2 +0,0 @@
DEEPSEEK_API_KEY=sk-c6e67b9d125448f593f202a5891eb123
FAL_API_KEY=d6eda9df-62ca-4934-8a61-4e7e659411e2:731fc05a520e6aeb1f3b68d74d0515aa

5
odin/.gitignore vendored
View File

@ -1,5 +0,0 @@
bin/
*.ll
*.o
*.obj
*.pdb

View File

@ -1,38 +0,0 @@
# Changelog
All notable changes to comic-odin will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Added
- Bubble editing MVP (Milestone 37): Add/delete/auto-place bubbles, type selector, persistence
- Layout validation badges (Milestone 36C): Coverage %, missing bindings, bounds violations
- Layout constants extraction (Milestone 34E): `shared/layout.odin` with screen profiles
- GUI integration smoke tests (Milestone 39A): 22 new tests for layout validation and bubble actions
- Error-path tests (Milestone 39B): Invalid indices, nil maps, boundary conditions
- Ownership/lifecycle audits (Milestone 39C): Disposal tests, cursor clamping, edge cases
### Changed
- Replaced hardcoded sidebar width (282) with `shared.LAYOUT.sidebar_width` constant
- Enhanced packaging script with version stamping, git hash, and build metadata
### Fixed
- Layout detail panel Y-offsets to accommodate validation badge row
- Bubble action string ownership for proper memory cleanup
## [0.1.0] - 2025-XX-XX
### Added
- Initial port skeleton: domain types, workflow state machine, adapters
- CLI runtime with TUI mode (Milestones 6-8)
- Native GUI with Raylib (Milestone 25)
- Script generation (local + DeepSeek) (Milestones 9-10, 34F)
- Panel generation and layout (Milestones 10-13)
- Export pipeline (PDF/PNG/CBZ) (Milestone 11)
- Offline quick-local pipeline (Milestones 14-16)
- TUI guided workflow commands (Milestones 19-24)
- GUI visual redesign pass (Milestones 30-33)
- Script inspector with page navigation (Milestone 34A-D)
- Panels detail surface (Milestone 35A-D)

View File

@ -1,82 +0,0 @@
# comic-odin
Native desktop application for comic creation, powered by Odin and Raylib.
## Features
- **Story & Script**: Generate comic scripts locally or via DeepSeek AI
- **Panel Generation**: Create panel images locally or via fal.ai
- **Layout Engine**: Auto-layout pages with pattern-based cell assignment
- **Bubble Editor**: Add, edit, and auto-place speech bubbles per panel
- **Export**: Output to PDF, PNG sequence, or CBZ comic book archive
- **Project Persistence**: Save/load projects as `.comic.json`
- **Native GUI**: Full Raylib-based desktop interface with dark theme
- **CLI/TUI**: Terminal-based interactive mode for headless workflows
## Quick Start
```bash
cd odin
./build.sh
./bin/comic_odin gui # Launch native GUI
./bin/comic_odin tui # Launch terminal UI
./bin/comic_odin status # Quick status check
```
## Building
Requires [Odin](https://odin-lang.org/) and [Raylib](https://www.raylib.com/).
```bash
# Build debug binary
./build.sh
# Run tests
odin test tests
# Package release artifact
VERSION=0.2.0 ./scripts/package.sh
```
## Project Structure
| Directory | Purpose |
|-----------|---------|
| `src/app` | App entrypoint and CLI composition root |
| `src/core` | Pure domain logic (types, workflow, layout, bubbles) |
| `src/adapters` | IO + external services (DeepSeek, fal.ai, storage, export) |
| `src/gui` | Raylib GUI runtime, views, actions, helpers |
| `src/ui` | Controller, screens, navigation, jobs |
| `src/shared` | Config, errors, layout constants |
| `tests` | Unit and integration tests |
| `schemas` | JSON schemas for project/script persistence |
| `docs` | Migration and implementation notes |
## GUI Controls
### Navigation
- `1-8`: Switch screens (Story, Script, Characters, Panels, Layout, Bubbles, Export, Community)
- `Tab` / `F1-F4`, `F11-F12`: Cycle input fields
- `Ctrl+[` / `Ctrl+]`: Navigate pages/panels within current screen
### Actions
- `F5`: Generate Script | `F6`: Generate Panels | `F7`: Layout Auto | `F8`: Export
- `F9`: Next Step | `F10`: Auto-All
- `Ctrl+S`: Save | `Ctrl+O`: Open | `Ctrl+E`: Export
- `Ctrl+G`: Toggle script source (Local/DeepSeek)
### Overlays
- `/`: Toggle help overlay | `Esc`: Close overlays
## Configuration
Set environment variables for AI services:
```bash
export DEEPSEEK_API_KEY="your-key"
export FAL_API_KEY="your-key"
```
## License
See repository root LICENSE file.

BIN
odin/app

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 417 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 KiB

View File

@ -1,17 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CLAY_DIR="$SCRIPT_DIR/vendor/clay/bindings/odin/clay-odin"
BUILD_DIR="$SCRIPT_DIR/build"
# Build C dependencies
"$SCRIPT_DIR/build_osdialog.sh"
mkdir -p bin
odin build src/app \
-out:bin/comic_odin \
-debug \
-collection:clay="$CLAY_DIR" \
-extra-linker-flags:"-L$BUILD_DIR -losdialog"

Binary file not shown.

View File

@ -1,35 +0,0 @@
#!/bin/bash
# Build osdialog static library for Linux (zenity backend)
set -e
OSDIALOG_DIR="$(dirname "$0")/vendor/osdialog"
OUTPUT_DIR="$(dirname "$0")/build"
mkdir -p "$OUTPUT_DIR"
LIB_OUT="$OUTPUT_DIR/libosdialog.a"
# Only rebuild if sources changed
SOURCES=("$OSDIALOG_DIR/osdialog.c" "$OSDIALOG_DIR/osdialog_zenity.c")
NEED_REBUILD=false
for src in "${SOURCES[@]}"; do
if [ "$src" -nt "$LIB_OUT" ] 2>/dev/null; then
NEED_REBUILD=true
break
fi
done
if [ "$NEED_REBUILD" = false ] && [ -f "$LIB_OUT" ]; then
echo "osdialog already built. Skipping."
exit 0
fi
echo "Building osdialog (zenity backend)..."
for src in "${SOURCES[@]}"; do
obj="${OUTPUT_DIR}/$(basename "${src%.c}.o")"
echo " CC $src"
gcc -c -O2 -std=c99 -I"$OSDIALOG_DIR" "$src" -o "$obj"
done
ar rcs "$LIB_OUT" "$OUTPUT_DIR"/osdialog.o "$OUTPUT_DIR"/osdialog_zenity.o
echo " AR $LIB_OUT"
echo "osdialog build complete."

View File

@ -1 +0,0 @@
comic/odin ui  ? ✗ ./build.sh && ./bin/comic_odin gui

Binary file not shown.

Binary file not shown.

View File

@ -1,138 +0,0 @@
# comic-odin GUI User Guide
## Getting Started
### Launching
```bash
cd odin
./bin/comic_odin gui
```
The application launches in borderless fullscreen on your primary monitor.
### First Run Workflow
1. Enter a **Story Idea** (or leave default)
2. Click **Generate Script Local** (or press `F5`)
3. Click **Generate Panels Local** (or press `F6`)
4. Click **Layout Pages** (or press `F7`)
5. Navigate to **Bubbles** screen to add speech bubbles
6. Click **Export** (or press `F8`) to produce your comic
## Screens
### 1. Story
Enter your comic concept: idea, genre, and target audience. These fields seed script generation.
### 2. Script
View generated script pages. Navigate pages with `< Pg` / `Pg >` buttons or `Ctrl+[` / `Ctrl+]`.
**Script Source Toggle**: Switch between `Local` (deterministic) and `DeepSeek` (AI-generated) modes. DeepSeek requires `DEEPSEEK_API_KEY` environment variable.
### 3. Characters
Character editor is scaffolded. Characters are auto-populated from script generation.
### 4. Panels
View generated panel images and their metadata (dimensions, seed, source). Navigate with `< Pn` / `Pn >` or `Ctrl+[` / `Ctrl+]`. Use **Regenerate** to re-roll a specific panel.
### 5. Layout
View layout wireframe previews with validation badges:
- **Cov**: Page coverage percentage (green if 80-105%)
- **Bind**: Missing panel bindings (green if 0)
- **Bounds**: Cells outside valid range (green if 0)
Navigate pages with `< Ly` / `Ly >` or `Ctrl+[` / `Ctrl+]`. Use **Regen** to cycle the layout pattern.
### 6. Bubbles
Speech bubble editor. Select a panel using `< Pn` / `Pn >` or `Ctrl+[` / `Ctrl+]`, then:
- **Add**: Create a new bubble (Normal type, placeholder text)
- **Auto Place**: Generate bubbles from script dialogue automatically
- **Type selector**: Change bubble type (Normal/Thought/Shout/Whisper/Narration/SFX)
- **x button**: Delete the selected bubble
Use mouse wheel to cycle through panels on the current page.
### 7. Export
Choose format (PDF/PNG/CBZ), set the export path, and click Export. The path extension auto-matches the selected format.
### 8. Community
Placeholder for future sharing/collaboration features.
## Keyboard Shortcuts
### Navigation
| Shortcut | Action |
|----------|--------|
| `1-8` | Switch to screen |
| `Tab` | Next input field |
| `F1-F4` | Focus idea/genre/audience/export path |
| `F11` | Focus local pages |
| `F12` | Focus project path |
| `Ctrl+[` / `Ctrl+]` | Previous/next page or panel |
### Actions
| Shortcut | Action |
|----------|--------|
| `F5` | Generate Script |
| `F6` | Generate Panels |
| `F7` | Layout Auto |
| `F8` | Export |
| `F9` | Next Step |
| `F10` | Auto-All |
| `Ctrl+S` | Save project |
| `Ctrl+O` | Open project |
| `Ctrl+E` | Export |
| `Ctrl+G` | Toggle script source |
### Utilities
| Shortcut | Action |
|----------|--------|
| `/` | Toggle help overlay |
| `Esc` | Close overlays |
| `Ctrl+L` | Clear action log |
| `Ctrl+Shift+A` | Toggle autosave |
| `Ctrl+Backspace` | Clear selected field |
| `Ctrl+V` | Paste into selected field |
### Destructive Actions (require Shift when dirty)
| Shortcut | Action |
|----------|--------|
| `Ctrl+N` | Reset project |
| `Ctrl+Shift+N` | Force reset (when dirty) |
| `Ctrl+Shift+O` | Force open (when dirty) |
## Autosave
Autosave is enabled by default with a 20-second interval. Adjust via:
- **Autosave** button to toggle on/off
- **Interval(s)** field to set seconds
- **15/30/60** preset buttons
- `Ctrl+-` / `Ctrl+=` to decrease/increase by 5s
## Path Management
### Quick-Fix Buttons
- **Fix Exp Ext**: Normalize export path extension to match format
- **Fix Proj Ext**: Normalize project path to `.comic.json`
- **Proj From Exp**: Derive project path from export directory
- **Exp From Proj**: Derive export path from project directory
### Status Indicators
- **P** (red): Project path needs normalization → click to fix
- **E** (red): Export path needs normalization → click to fix
- **PE** (red): Fix both paths at once
## Troubleshooting
### "Export blocked: generate panels + layout first"
You must complete the pipeline in order: Script → Panels → Layout → Export.
### "DeepSeek key missing"
Set the `DEEPSEEK_API_KEY` environment variable before launching, or use Local script generation.
### Project won't save
Check that the project path ends with `.comic.json`. Use **Fix Proj Ext** to normalize.
### GUI looks cramped
The application adapts to screen size. On heights below 860px, non-essential hints are hidden. Resize the window or use a larger display.
### Memory warnings in tests
Some test warnings about string leaks are known (literal strings vs owned strings). These do not affect runtime behavior.

File diff suppressed because it is too large Load Diff

View File

@ -1,303 +0,0 @@
# Comic-Odin Production Readiness Plan
## Current State Summary
| Metric | Value |
|--------|-------|
| **Source files** | 38 .odin files |
| **Test count** | 156 passing |
| **GUI screens** | 8/8 defined (Community is placeholder) |
| **Workflow steps** | 8/8 implemented |
| **Export formats** | 3/3 (PDF with real page rendering) |
| **CLI/TUI** | Fully functional |
| **Native GUI** | ~1200 lines Raylib, dark theme |
| **P0 items** | 5/5 complete |
| **P1 items** | 5/5 complete |
| **P2 items** | 5/5 complete (all done) |
## Critical Production Gaps (P0 - Must Fix Before Release)
### P0-1: PDF Export — Replace Text Placeholder with Real Page Rendering
**Current**: Writes a minimal text-only PDF with panel count. No images embedded.
**Target**: Canvas-like page composition with panel images positioned per layout cells.
**Implementation plan**:
1. Add `stb_image.h` binding (or use existing Odin image loading) to load panel images from URLs/local paths
2. Create `render_page_to_image` proc that:
- Creates a blank canvas at page dimensions (from `Page_Size`)
- Fills white background
- For each panel: loads image, calculates pixel position from `Layout_Cell` fractions, draws with gutter margins, draws black border
3. Integrate with existing PDF writer or switch to a proper PDF library (e.g., `harfbuzz` + `freetype` for text, or use `wkhtmltopdf`/`weasyprint` subprocess like current CBZ zip approach)
4. Add DPI scaling (default 300 DPI)
5. Test: Export a 4-page comic, verify PDF opens in viewer with correct panel positions
**Files to modify**: `src/adapters/export.odin`
**New files**: `src/adapters/image_loading.odin`
**Estimated effort**: 2-3 days
### P0-2: Character Consistency — IP-Adapter Reference Images
**Current**: `generate_panel_image` ignores character reference images (`_ = characters`).
**Target**: Pass character reference image URLs to fal.ai API for consistent character appearance.
**Implementation plan**:
1. Add `reference_images: []string` and `reference_image_strength: f32` to fal.ai request body
2. Collect `character.reference_image_url` for all characters in `panel.characters_present`
3. Pass `reference_image_strength = 0.65` (the "sweet spot" from TypeScript)
4. Add `reference_images` field to `Character` struct (already has `reference_image_url`)
5. Store character reference images in `Comic_State` as a map: `character_ref_images: map[string]string` (character_id → URL)
6. Test: Generate panels with 2+ characters, verify visual consistency across panels
**Files to modify**: `src/adapters/fal.odin`, `src/core/types.odin`, `src/gui/actions.odin`
**Estimated effort**: 1-2 days
### P0-3: Shot-Type-Based Image Sizing
**Current**: All panels generated at fixed 1024x1024.
**Target**: Map shot types to appropriate aspect ratios for better composition.
**Implementation plan**:
1. Add `get_image_size_for_shot_type` proc mapping:
- `establishing`, `wide`, `aerial``landscape_16_9`
- `medium`, `over-shoulder``landscape_4_3`
- `close-up``portrait_4_3`
- `extreme-close-up``square_hd`
2. Pass `image_size` parameter to fal.ai request
3. Store actual returned dimensions in `Panel_Image.width/height`
4. Test: Generate panels with varied shot types, verify different aspect ratios
**Files to modify**: `src/adapters/fal.odin`, `src/core/layout.odin`
**Estimated effort**: 0.5 days
### P0-4: Art Style Keyword Mapping
**Current**: Raw `art_style` string passed to prompts.
**Target**: 8 defined art styles with detailed keyword expansions.
**Implementation plan**:
1. Add `Art_Style_Key` enum: `Manga, Western_Comic, Pixel_Art, Watercolor, Noir, Chibi, Sketch, Cyberpunk`
2. Add `ART_STYLE_KEYWORDS` constant map with detailed prompt keywords per style
3. Add `QUALITY_MODIFIER` constant: `"high quality, detailed, clean lines, vibrant colors, professional illustration, best quality, masterpiece"`
4. Add `get_style_keywords` proc to expand art style into prompt prefix
5. Update `build_local_panel_images` and `generate_panel_image` to prepend style keywords
6. Test: Generate panels with each art style, verify prompt includes correct keywords
**Files to modify**: `src/core/types.odin`, `src/adapters/fal.odin`, `src/gui/local_helpers.odin`
**Estimated effort**: 1 day
### P0-5: Negative Prompts
**Current**: No negative prompts in image generation.
**Target**: Add negative prompts to improve output quality.
**Implementation plan**:
1. Add `negative_prompt: string` to fal.ai request body
2. Character reference negative: `"blurry, low quality, distorted face, extra limbs, bad anatomy, deformed, watermark, signature"`
3. Panel negative: `"blurry, low quality, distorted face, extra limbs, bad anatomy, deformed, watermark, signature, text, speech bubble"`
4. Test: Generate panels with and without negative prompts, compare quality
**Files to modify**: `src/adapters/fal.odin`
**Estimated effort**: 0.5 days
## High Priority Gaps (P1 - Important for Quality)
### P1-1: Multi-Angle Character Sheet Generation
**Current**: Single reference portrait only.
**Target**: 4-angle character sheet (front, 3/4, profile, back) with IP-Adapter consistency.
**Implementation plan**:
1. Add `generate_character_sheet` proc that iterates 4 poses sequentially
2. First pose generates anchor image; subsequent poses use first image as `reference_images` with `reference_image_strength: 0.65`
3. Poses: front-facing, three-quarter, side profile, back view
4. Store sheet URLs in `Character.character_sheet_urls`
5. Add GUI button "Generate Character Sheet" on Characters screen
6. Test: Generate sheet for a character, verify 4 distinct but consistent images
**Files to modify**: `src/adapters/fal.odin`, `src/gui/actions.odin`, `src/gui/summary_views.odin`
**Estimated effort**: 2 days
### P1-2: Emotion Enum + Structured Dialogue
**Current**: `emotion` is a free-form string in `Dialogue`.
**Target**: Proper `Emotion` enum with 6 values.
**Implementation plan**:
1. Add `Emotion` enum: `Happy, Sad, Angry, Surprised, Neutral, Determined`
2. Update `Dialogue` struct: `emotion: Emotion`
3. Update DeepSeek response parser to map string emotions to enum
4. Update bubble auto-placement to consider emotion (e.g., Shout for Angry, Whisper for Sad)
5. Update JSON serialization/deserialization
6. Test: Generate script, verify emotion enum populated correctly
**Files to modify**: `src/core/types.odin`, `src/adapters/deepseek.odin`, `src/core/bubble.odin`
**Estimated effort**: 0.5 days
### P1-3: Character Description Parser
**Current**: Character descriptions are free-form text.
**Target**: Parse natural language descriptions into structured `Character_Prompt_Template`.
**Implementation plan**:
1. Add 10 regex extraction procs (age, gender, hair color, hair style, eye color, skin tone, body type, outfit, accessories, distinguishing features)
2. Add `parse_description_to_template` proc
3. Add `extract_color_palette` proc
4. Add GUI helper: "Parse Description" button on Characters screen
5. Test: Parse "25-year-old female, black long hair, blue eyes, fair skin, slim build, wearing a red dress, glasses, scar on cheek" → structured template
**Files to modify**: `src/core/character_prompt.odin`, `src/gui/actions.odin`
**New files**: `src/core/character_parser.odin`
**Estimated effort**: 1-2 days
### P1-4: CBZ/PNG Export — Use Real Image Rendering Instead of Raw Copies
**Current**: CBZ/PNG exports copy raw panel images without page composition.
**Target**: Render full pages with panels positioned per layout (same as PDF).
**Implementation plan**:
1. Reuse `render_page_to_image` from P0-1
2. For CBZ: render each page to PNG, add to zip with `page_XXX.jpg` naming
3. For PNG: same but output as individual PNG files in a zip
4. Add `ComicInfo.xml` with proper metadata (Title, Series, Count, etc.)
5. Test: Export CBZ, open in comic reader (e.g., CDisplayEx)
**Files to modify**: `src/adapters/export.odin`
**Estimated effort**: 1 day
### P1-5: Progress Tracking During Generation
**Current**: No progress feedback during long operations.
**Target**: Real-time progress updates in GUI.
**Implementation plan**:
1. Add `progress_callback` parameter to `generate_all_panels_batched`
2. Update job manager to track progress percentage
3. Add progress bar to GUI during generation (overlay or inline)
4. Update status message with "Generating panel 3/12 (25%)"
5. Test: Generate 12 panels, verify progress updates visible
**Files to modify**: `src/ui/jobs.odin`, `src/gui/runtime.odin`, `src/adapters/fal.odin`
**Estimated effort**: 1 day
## Medium Priority Gaps (P2 - Polish)
### P2-1: DeepSeek Streaming Generation
**Current**: Waits for full response before showing results.
**Target**: Streaming partial JSON for real-time preview.
**Implementation plan**:
1. Add `stream_comic_script` proc using curl with streaming response
2. Parse partial JSON chunks, update GUI progressively
3. Add "Generating..." overlay with partial script preview
4. Fallback to non-streaming if streaming fails
**Files to modify**: `src/adapters/deepseek.odin`, `src/gui/runtime.odin`
**Estimated effort**: 2 days
### P2-2: Appearance Count Tracking
**Current**: `Character.appearance_count` field exists but is never populated.
**Target**: Auto-count character appearances across panels.
**Implementation plan**:
1. Add `count_character_appearances` proc that iterates all panels
2. Call after script generation and panel generation
3. Display count in Characters screen summary
**Files to modify**: `src/core/script.odin`, `src/gui/summary_views.odin`
**Estimated effort**: 0.5 days
### P2-3: Genre-Based Layout Pattern Selection
**Current**: Layout pattern selection considers genre but `pattern_matches_genre` is basic.
**Target**: Full genre-to-pattern mapping with tightest-fit algorithm.
**Implementation plan**:
1. Add `get_patterns_by_genre` proc
2. Update `select_best_pattern` to use tightest-fit (smallest maxPanels that fits)
3. Add genre filtering to pattern selection
**Files to modify**: `src/core/layout.odin`
**Estimated effort**: 0.5 days
### P2-4: Bubble Text Editing in GUI
**Current**: Bubble text can only be changed via project file edit.
**Target**: Inline text editing in bubble editor.
**Implementation plan**:
1. Add text input field to bubble detail panel
2. Add "Save" button to commit text changes
3. Update `action_update_bubble` to accept new text
4. Add keyboard shortcut for text editing mode
**Files to modify**: `src/gui/bubbles_views.odin`, `src/gui/runtime.odin`, `src/gui/actions.odin`
**Estimated effort**: 1 day
### P2-5: Manual Bubble Positioning
**Current**: Bubbles are auto-placed only.
**Target**: Drag-to-reposition bubbles in GUI.
**Implementation plan**:
1. Add mouse drag detection on bubble preview
2. Update bubble `position` on drag
3. Add "Reset Position" button to revert to auto-place
4. Save position changes to project
**Files to modify**: `src/gui/bubbles_views.odin`, `src/gui/runtime.odin`
**Estimated effort**: 2 days
## Low Priority Gaps (P3 - Nice to Have)
### P3-1: Community Features
**Current**: Placeholder screen.
**Target**: Publish/share/browse functionality.
**Estimated effort**: 5+ days (defer to future release)
### P3-2: Undo/Redo for Bubble and Layout Edits
**Current**: No undo support.
**Target**: Command history for bubble/layout changes.
**Estimated effort**: 2-3 days
### P3-3: Multi-Monitor Awareness
**Current**: Launches on primary monitor only.
**Target**: Remember last window position, support multi-monitor.
**Estimated effort**: 1 day
### P3-4: Image Asset Caching to Disk
**Current**: Images stored in memory only.
**Target**: Cache generated images to `assets/` directory.
**Estimated effort**: 1-2 days
### P3-5: Custom Layout Pattern Assignment
**Current**: Layout regeneration cycles through patterns.
**Target**: Manual pattern selection dropdown.
**Estimated effort**: 1 day
## Implementation Order
| Phase | Milestones | Duration | Tests Added | Status |
|-------|-----------|----------|-------------|--------|
| **Phase 1** | P0-1 (PDF export), P0-3 (shot sizing), P0-5 (negative prompts) | 3 days | +10 | ✅ |
| **Phase 2** | P0-2 (character consistency), P0-4 (art styles) | 2 days | +8 | ✅ |
| **Phase 3** | P1-4 (CBZ/PNG rendering), P1-5 (progress tracking) | 2 days | +6 | ✅ |
| **Phase 4** | P1-1 (character sheets), P1-2 (emotion enum) | 2.5 days | +8 | ✅ |
| **Phase 5** | P1-3 (description parser), P2-3 (genre layouts) | 2 days | +6 | ✅ |
| **Phase 6** | P2-1 (streaming), P2-2 (appearance count), P2-4 (bubble text), P2-5 (bubble position) | 2.5 days | +14 | ✅ |
| **Phase 7** | P2-5 (bubble positioning GUI drag) | 1 day | +13 | ✅ |
| **Phase 8** | P3 items (deferred) | TBD | TBD | ⏳ |
**Total estimated effort**: ~17 days for P0-P2 (production-ready)
**Expected test count after completion**: ~150+
**Actual test count**: 156 ✅
**Total estimated effort**: ~17 days for P0-P2 (production-ready)
**Expected test count after completion**: ~150+
## Release Criteria
Before v0.3.0 release:
- [x] All P0 items complete and tested
- [x] All P1 items complete and tested
- [x] All P2 items complete and tested
- [x] 150+ tests passing (156/150)
- [ ] No memory leaks in test output
- [x] PDF export produces valid comic with images
- [x] CBZ opens in standard comic readers
- [x] Character consistency verified across 10+ panels
- [ ] GUI smoke test: full pipeline (story → script → panels → layout → bubbles → export) works end-to-end
- [ ] CLI smoke test: `auto-all` command completes without errors
- [ ] Package script produces valid artifact with checksums

Some files were not shown because too many files have changed in this diff Show More