comic/AGENTS.md
2026-05-28 15:20:02 +02:00

89 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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=...
```