daily auto-sync 2026-07-23
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
---
|
||||
Date: 2026-07-23
|
||||
Author: Hermes + Adolfo (interview)
|
||||
Tags: [kids-book, editor, web-app, plan, grace-poppy, pocketbase, interview-decided]
|
||||
Status: planned — ready to scaffold
|
||||
---
|
||||
|
||||
# Kids Book Studio — Final Plan (Interviewed)
|
||||
|
||||
## Decisions Locked
|
||||
|
||||
From interview 2026-07-23:
|
||||
|
||||
- **Users:** You + Alicia reviewing/approving (PB native email/pass auth)
|
||||
- **Review workflow:** Simple — approve / needs-rework + one comment per page (no threads)
|
||||
- **Data backend:** PocketBase on .110 (family BaaS direction). All family custom software on PB.
|
||||
- **App hosting:** Custom Svelte editor as Docker container on .110 next to Instatic sites, Caddy route `books.reynafamily.com` or `studio.reynafamily.com`. NOT inside Instatic CMS.
|
||||
- **Generation worker:** Python service on Mac mini .102 polling PB for `status=queued` jobs — runs Codex imagegen + local TTS, uploads results back to PB files.
|
||||
- **Frontend stack:** SvelteKit + Tailwind, Playfair for titles, Inter body, Mono for prompts. VOUS/Hillsong clean serif headers.
|
||||
- **Seed data:** Auto-import Grace/Poppy existing package (21 pages) as first book
|
||||
- **Image gen:** Codex imagegen (proven) + BYOK configurable model (OpenAI / Gemini / etc via API key in PB settings collection)
|
||||
- **Audio gen:** Moving OFF Voicebox. Use existing reyna-cli TTS abstraction (`espeak-ng` → `say` → `edge-tts` → `REYNA_TTS_COMMAND`) + small local model on .110 Docker (Piper/Kokoro) as future-proof. No Voicebox dependency.
|
||||
- **Audio per page:** One narration clip per page for now, schema designed for multi-clip dialogue later (clips[] array forward-compatible)
|
||||
- **Text:** Support full caption + shorter spoken cut + edit history (caption_history, audio_text_history) for device timing tweaks
|
||||
- **Images:** 320x240 only for now (ESP32 native). Full-res later as extra rendition
|
||||
- **New books:** Start with Grace/Poppy only, then New Book wizard
|
||||
|
||||
## What We're Building
|
||||
|
||||
A web editor to manage kids books — pages with script, generated audio, images, and the prompts used — so you can iterate on specific pages and make new books, with Tactility export.
|
||||
|
||||
## System Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ .110 FamReynaServer │
|
||||
│ ┌──────────────┐ ┌────────────────────────┐ │
|
||||
│ │ PocketBase │◄─┤ Svelte Editor (Docker) │ │
|
||||
│ │ :8095 or new │ │ books.reynafamily.com │ │
|
||||
│ │ - books │ │ - Library grid │ │
|
||||
│ │ - pages │ │ - Book strip + reorder │ │
|
||||
│ │ - assets │ │ - Page 3-col editor │ │
|
||||
│ │ - gen_jobs │ │ - Device 320x240 prev │ │
|
||||
│ │ - users/auth │ │ - Export ZIP/PDF │ │
|
||||
│ │ - settings │ │ │ │
|
||||
│ └──────┬───────┘ └────────────────────────┘ │
|
||||
│ │ ▲ │
|
||||
│ │ │ (PB files + realtime) │
|
||||
└─────────┼─┼──────────────────────────────────────────────┘
|
||||
│ │
|
||||
│ │ polls jobs, uploads results
|
||||
│ │
|
||||
┌─────────┼─┼──────────────────────────────────────────────┐
|
||||
│ .102 Mac mini │
|
||||
│ ┌──────▼──────┐ │
|
||||
│ │ Gen Worker │ - Codex imagegen skill │
|
||||
│ │ Python svc │ - reyna-cli TTS (edge-tts/fallback) │
|
||||
│ │ │ - future: Kokoro local on .110 │
|
||||
│ └─────────────┘ - resize + transcode 320x240 / mp3 │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Network:
|
||||
- PB reachable via Tailscale + Caddy reverse proxy
|
||||
- Worker polls PB via private network (no public exposure needed)
|
||||
- Assets stored as PB files (images, audio) — accessible via PB file URL
|
||||
|
||||
## PocketBase Collections (schema)
|
||||
|
||||
**books**
|
||||
- id (PB auto), slug (text unique), title (text), author (text), created (autoDate)
|
||||
- style_bible: json { art_style, continuity_rules[], color_palette[], reference_images[] file[] }
|
||||
- display: json { width, height } default 320x240
|
||||
- default_voice: json { profile, language } e.g. {profile: "en-US-AriaNeural", language:"en-US"}
|
||||
- cover_page_id -> pages (relation, cover thumbnail)
|
||||
- status_overview: computed via pages (not stored)
|
||||
|
||||
**pages**
|
||||
- id, book (relation -> books), page_number (number), status (select: draft|needs-rework|approved|generating|queued)
|
||||
- caption (text, rich — full text), caption_history (json, array of {text, edited_at, by})
|
||||
- audio_text (text — spoken cut, defaults to caption if empty), audio_text_history (json)
|
||||
- review_comment (text), reviewed_by (relation users), reviewed_at (date)
|
||||
- notes (text — author private notes)
|
||||
- original_page (number optional, for imported books that skipped numbers)
|
||||
- export_include (bool default true)
|
||||
- Future: clips[] json for multi-speaker
|
||||
|
||||
**image_assets** (one active per page, history preserved via versioned files + history JSON)
|
||||
- id, page (relation -> pages), is_active (bool)
|
||||
- file (file, image) — PB stores both original gen + 320x240 variant?
|
||||
- Actually: store original file, gen job creates 320x240 as second file field thumb or via hook
|
||||
- prompt (text), negative_prompt (text), style_bible_applied (bool)
|
||||
- reference_pages (relation pages[]), generation_params (json: {model, size, seed, provider})
|
||||
- prompt_version (number auto-increment per page), notes (text)
|
||||
- generated_at, generated_by (user)
|
||||
- Simple: to keep history, we don't delete old image_assets, just flip is_active. Page shows history slider.
|
||||
|
||||
**audio_assets**
|
||||
- id, page (relation -> pages), is_active (bool)
|
||||
- file (file, audio mp3/wav), duration_sec (number)
|
||||
- text_used (text — snapshot of audio_text at generation time)
|
||||
- voice_profile (text), language (text), engine (text: edge-tts|piper|kokoro|custom)
|
||||
- tts_params (json)
|
||||
- generated_at, generated_by
|
||||
|
||||
**generation_jobs** (queue)
|
||||
- id, page (relation pages), type (select: image|audio), status (queued|running|done|failed)
|
||||
- input (json): {prompt, negative, style_bible, voice, text, etc}
|
||||
- provider (text: codex-imagegen|openai|gemini|edge-tts|piper|kokoro)
|
||||
- result_image / result_audio (relation to image_assets/audio_assets when done)
|
||||
- error (text), attempts (number)
|
||||
- created, started, finished (dates)
|
||||
|
||||
**settings** (singleton-ish, or one per family)
|
||||
- id, image_providers (json: {openai_key_encrypted?, gemini_key? ...}) — or use env, not PB at first
|
||||
- default_image_provider, default_audio_engine
|
||||
- style_presets (json): preset name → {art_style, continuity_rules}
|
||||
|
||||
**users** (PB built-in auth collection: you + Alicia)
|
||||
|
||||
## Job Flow
|
||||
|
||||
1. User edits prompt in Page Editor → clicks Generate Image
|
||||
2. Frontend creates generation_jobs record (status=queued, input={prompt:+style_bible, page, provider})
|
||||
3. Frontend subscribes to job realtime → shows spinner on page
|
||||
4. Worker on .102 polls PB collection generation_jobs filter=status=queued (or realtime subscribe)
|
||||
5. Worker marks running, executes:
|
||||
- For image: calls Codex imagegen OR OpenAI/Gemini depending on provider → saves PNG → resizes to 320x240 thumb → uploads to PB as new image_assets file
|
||||
- For audio: calls reyna-cli tts.py synthesize_wav() → transcodes to mp3 16kHz 24k mono → uploads as audio_assets
|
||||
6. Worker creates image_assets/audio_assets record (is_active=false initially, then activate after user approval, or auto-activate if generation is direct iteration — TBD: auto-activate with history preserved)
|
||||
7. Worker updates job to done with relation to new asset
|
||||
8. Frontend realtime gets event, refreshes page, shows new image with history slider
|
||||
9. Alicia reviews: changes status to approved or needs-rework + comment
|
||||
|
||||
For MVP: steps 5 auto-activates new asset (flips previous is_active=false, new is_active=true), but history still full.
|
||||
|
||||
## Frontend Screens (detailed)
|
||||
|
||||
**Route / (Library)**
|
||||
- Grid of books: cover = first page active image_assets file, else placeholder. Title, author, pages count, % approved bar, last edited.
|
||||
- Button: New Book → modal: title, slug (auto from title), author default Reyna Family, style preset selector (Grace-Poppy watercolor, Minimal, New custom), import tactility zip? toggle.
|
||||
- Each card: Open, Export zip, Play Preview
|
||||
- PB query: books list with expand cover_page_id -> image_assets
|
||||
|
||||
**Route /books/:slug**
|
||||
- Header: title, edit pencil for style bible, % approved, Play Preview (autoplay browser reader 320x240 + audio), Export button.
|
||||
- Page strip: horizontal scrollable, 160x120 thumbs (active image per page), status dot (green approved, yellow needs-rework, gray draft, blue generating). Drag to reorder (updates page_number). Click to open page editor. Plus button to add page.
|
||||
- Style Bible panel (collapsible right drawer): art_style textarea, continuity_rules as editable list (add/remove), color_palette tags, reference image uploader (PB file).
|
||||
- Bulk actions: checkboxes on strip → bulk status change, bulk prompt find/replace, bulk re-generate audio.
|
||||
|
||||
**Route /books/:slug/pages/:pageNumber (Page Editor — core)**
|
||||
- Three columns:
|
||||
- Left (Script): caption textarea with char count + history icon (show caption_history). audio_text textarea (with Fill from caption button + auto-pace helper that calls edge-tts? simple: button to copy caption and add breaks). Play audio button (uses active audio_assets file URL). Voice profile selector (text input or select from settings list of edge-tts voices). Duration display from active audio. Review section at bottom of this column: status select, review_comment.
|
||||
- Center (Image): large preview 320x240 framed + expand to full original. Prompt textarea (Monospace, with toggle: "prepend style bible" on/off — when on, UI shows combined effective prompt). Negative prompt. Reference pages multi-select. Generate button (with provider selector: Codex / OpenAI / Gemini). History carousel below: scrollable thumbs of all image_assets for this page (ordered by created), each shows prompt tooltip, click to make active (swaps). Restore button.
|
||||
- Right (Meta): Page number + up/down reorder, original_page mapping, notes textarea, export_include toggle, created/updated meta, generation job log (last job status/error).
|
||||
|
||||
**Component: Device Preview Modal**
|
||||
- 320x240 black frame mimicking ESP32, image displayed, Play/Prev/Next buttons matching Tactility Book Player. Auto-advance when audio finishes (same as device).
|
||||
|
||||
**Component: Export Modal**
|
||||
- Formats: Tactility v1 ZIP (manifest.json + pageXXX.png + mp3). Options: only approved? include draft? file naming page001 vs page_XXX. Download button.
|
||||
|
||||
## Importer — Grace/Poppy Seed
|
||||
|
||||
Script `scripts/import_grace_poppy.py`:
|
||||
- Reads `~/output/tactility-book-packages/grace-poppy-tactility-book-example/books/grace-poppy/manifest.json`
|
||||
- Creates book record slug=grace-poppy
|
||||
- For each page in manifest, in order:
|
||||
- Create page record with caption from manifest, audio_text = caption, status = approved (since already reviewed), original_page, page_number sequential
|
||||
- Copy image file to PB file upload as image_assets (prompt empty, notes="imported, prompt unknown", is_active true)
|
||||
- Copy audio file to PB file upload as audio_assets (is_active true, voice_profile=Aiden, duration_sec from manifest)
|
||||
- Also sets style_bible with known Grace continuity rules.
|
||||
|
||||
Run once on first deploy.
|
||||
|
||||
## Tech Stack Final
|
||||
|
||||
- Backend: PocketBase (Go binary, Docker on .110, :8095, Caddy /books api)
|
||||
- Frontend: SvelteKit + Tailwind, Vite. Hosted as Docker container on .110 next to Instatic (separate Docker Compose service), Caddy reverse proxy to `books.reynafamily.com` (or `studio.reynafamily.com`). Builds talk directly to PB via JS SDK @pocketbase/js-sdk.
|
||||
- Auth: PB native users collection (email/pass). Two users: you, Alicia. Frontend has login page, guards Library route.
|
||||
- Realtime: PB realtime subscriptions for jobs and pages status.
|
||||
- Worker: Python 3.11, runs on Mac mini .102, deps: pocketbase client, pillow, ffmpeg, edge-tts, codex CLI available. Loop: poll every 5s for queued jobs. Could also move to .110 later with Piper local Docker for audio.
|
||||
- Storage: PB file storage (local filesystem .110). Images and audio files served via PB file URL with auth.
|
||||
|
||||
## Project Layout (to scaffold)
|
||||
|
||||
```
|
||||
kids-book-studio/
|
||||
README.md
|
||||
docker-compose.yml (frontend + optional worker-for-local-dev)
|
||||
pocketbase/
|
||||
Dockerfile / config
|
||||
migrations/ (PB JS migrations for collections)
|
||||
scripts/import_grace_poppy.py
|
||||
frontend/
|
||||
package.json, SvelteKit app
|
||||
src/routes/...
|
||||
src/lib/pocketbase.ts
|
||||
src/lib/components/...
|
||||
static/
|
||||
worker/
|
||||
requirements.txt
|
||||
worker.py (poll loop)
|
||||
tts_engine.py (wraps reyna-cli tts.py + piper/kokoro)
|
||||
image_engine.py (codex + openai + gemini providers)
|
||||
asset_utils.py (resize, transcode)
|
||||
Caddyfile.snippet (for .110 Caddy)
|
||||
docs/
|
||||
plan.md (this file)
|
||||
schema.md
|
||||
```
|
||||
|
||||
## PB Collections JSON Proposal (draft, to convert to migration)
|
||||
|
||||
See code above for fields. Will generate JS migration file for:
|
||||
- books, pages, image_assets, audio_assets, generation_jobs, settings
|
||||
|
||||
## Implementation Phases (revised)
|
||||
|
||||
**Phase 0 — PB + Skeleton**
|
||||
- Create kids-book-studio repo, docker-compose, PB collections migration
|
||||
- Frontend SvelteKit scaffold with PB SDK, login page, Library grid (lists books)
|
||||
- Importer script run, verify Grace/Poppy shows in Library with 21 pages
|
||||
|
||||
**Phase 1 — Read-only Book + Page Editor**
|
||||
- Book Overview strip with thumbs, Style Bible editor drawer
|
||||
- Page Editor 3-col read-only/active asset display, history display
|
||||
- No generation yet, but schema ready.
|
||||
|
||||
**Phase 2 — Generation Worker**
|
||||
- Python worker on .102 that polls PB generation_jobs, executes edge-tts audio + optionally Codex imagegen
|
||||
- Frontend Generate buttons + realtime spinner + history updates
|
||||
- Image history versioning, audio history.
|
||||
|
||||
**Phase 3 — Review + Export**
|
||||
- Alicia review flow: status + comment, PB realtime updates
|
||||
- Export Tactility ZIP with correct manifest.json format (existing format compatible with Book Player app)
|
||||
- Device Preview modal 320x240 autoplay
|
||||
- Drag reorder pages, bulk actions.
|
||||
|
||||
**Phase 4 — Polish + New Book**
|
||||
- New Book wizard, style presets, high-res renditions, reMarkable PDF export
|
||||
- BYOK provider keys UI (settings page), local Piper/Kokoro on .110 Docker
|
||||
- Direct push to ESP32 fleet optional
|
||||
|
||||
## Future (not now)
|
||||
|
||||
- Multi-clip dialogue per page
|
||||
- Character sheet with ref images
|
||||
- Translation tracks (keep image, different audio language)
|
||||
- LLM auto-pace performance text, consistency check
|
||||
- Family review with kid read-only view
|
||||
|
||||
## Open Risks
|
||||
|
||||
- Codex imagegen reliability from worker (has been flaky, needs -o file pattern from brain doc)
|
||||
- PB file size for audio/images — okay, small assets
|
||||
- Caddy route for new subdomain — need to add via api script pattern (see bin/caddy-api)
|
||||
- Worker auth: PB service account token, not user token
|
||||
|
||||
## Next Step (Ready to Build)
|
||||
|
||||
When user says "build it", start Phase 0: scaffold repo, PB docker + migration, frontend login + library + import Grace/Poppy.
|
||||
Reference in New Issue
Block a user