| Age | Commit message (Collapse) | Author |
|
Two-part fix for missing dialogue bubbles:
1. Panel script prompt: each panel description must include quoted Bulgarian
dialogue for the character to speak/think. At least 3 of 4 panels per page
must have speech or thought text in the description.
2. Image prompt: added MANDATORY SPEECH BUBBLES block requiring at least 3 of 4
panels to contain a bubble, describing exact white-background/black-outline
style for speech and cloud-shape for thought bubbles, all text Bulgarian Cyrillic.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Previously story text was split mechanically into 5 sections and each panel
was guessed from raw prose — causing repeated compositions and incoherent plots.
Now GenerateFull requests a 20-entry panel visual script (P1-A…P5-D) from Gemini
in the same API call as the story and bible. Each panel gets an explicit 1-2 sentence
description of WHO/WHAT/WHERE/expression, written in chronological story order.
buildStoryPagePrompt now uses these descriptions to drive the image model directly
("Draw each panel EXACTLY as described") instead of "scene N from the excerpt".
Falls back to excerpt-driven prompts when the script is absent or incomplete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Previously only --no-ultra-realistic existed (to force standard comic style).
--ultra-realistic now forces photorealistic panels instead of the random 50/50 pick.
storyUltraRealistic() updated to handle three states: forced-on, forced-off, random.
README updated with --ultra-realistic example.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add --narrate flag (default false); narration is now opt-in to save TTS quota
- Add VARIETY MANDATE to story page prompts: each panel must differ in camera
angle, pose, location, lighting, and foreground — prevents repeated compositions
- Wire NarrateEnabled through RunnerConfig and main.go
- Update README: --narrate flag, updated output file table, narrator voice examples
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
with clear message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
veo.go
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- internal/video/veo_test.go: add pageNumFromPath table test, loadGalleryImage
multiple-matches edge case, saveMP4 fallback name, and NewVeoGenerator success
path with mock client injection
- internal/cli/prompts_test.go: add parseSelection edge cases — zero max with
"all", page number exceeding max, and whitespace-only input
- internal/cli/video_runner_test.go: new file covering GenerateSelectedVideos
for empty/nil paths (early return) and empty/whitespace API key (error path)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add a new subsection under the --story CLI section explaining the
interactive Veo video-generation prompt that runs after comic creation,
including the model name (veo-2.0-generate-001), MP4 output paths, the
billing/cost note, and the --video=false skip flag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Adds showGalleryVideoDialog in internal/gui/dialogs.go: a multi-checkbox
list (all pre-selected) lets the user choose which gallery PNGs to animate,
then calls cli.GenerateSelectedVideos in a goroutine while a progress dialog
indicates background work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
PromptForGalleryVideos now returns []string paths (from recursive walk)
instead of []int page numbers, so GenerateSelectedVideos always has the
exact path to each gallery PNG regardless of which comics/<slug>/
subdirectory it lives in.
Previously, GenerateSelectedVideos passed "." as galleryPath and called
loadGalleryImage with a non-recursive filepath.Glob that could not find
PNGs in subdirectories, causing video generation to always fail with
"no gallery image found".
Additional changes:
- Add VeoGenerator.GenerateVideoFromPath that accepts a full image path
and writes the MP4 next to the source PNG
- Make runStoryVideos non-fatal: video errors print a warning and return
nil so comic/PDF/narration outputs are never invalidated by Veo errors
- Add filterPathsByPages helper and tests for the new behaviour
- Add TestFindGalleryPages_Recursive to cover the comics/<slug>/ layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Add VideoEnabled bool (default true) to Flags, register --video bool flag in
command.go, export PromptForGalleryVideos/GenerateSelectedVideos, and wire them
into main.go via runStoryVideos — skipped when --video=false. Also update
findGalleryPages to walk subdirectories recursively so gallery PNGs inside
comics/<slug>/ are discovered from the CWD search root.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Implements the shared CLI runner that calls video.VeoGenerator for each
selected gallery page number sequentially, printing progress and the saved
MP4 path per page. Lives in internal/cli/video_runner.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Implements promptForGalleryVideos(outputDir) which lists *_gallery_*.png
files, asks the user y/n, then prompts for page selection (1,3,5 or all),
returning a sorted slice of chosen page numbers. Also adds the pure helper
parseSelection(input, max) with full unit tests covering all branches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Introduces internal/video/veo.go with NewVeoGenerator and
GenerateVideoFromGallery. Reads gallery PNG pages, submits them to
the Veo API (veo-2.0-generate-001) as image-to-video with an 8-second
16:9 clip request, polls the long-running operation every 15 s, then
saves the resulting MP4 to the output directory.
Unit tests in veo_test.go cover key error paths and helper functions
using mocks — no real API calls required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Fire-and-forget goroutines in the tooltip setup, word-change handler, and
audio playback could write to freed Fyne widgets after the window was closed.
All four patterns are now fixed:
- setupTooltips() and the secondary-toolbar tooltip block: replaced
time.AfterFunc(500ms) with wg-tracked goroutines using select/ctx.Done().
- handleWordChange(): replaced time.AfterFunc(100ms) with the same pattern.
- onWindowClosed(): added wordChangeTimer.Stop() to prevent its AfterFunc
callback from firing after context cancellation.
- AudioPlayer: added ctx context.Context field + SetContext(); the
post-playback goroutine now guards fyne.Do with ctx.Err() == nil, and the
auto-play AfterFunc is replaced with a ctx-aware goroutine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
FindCardDirectory, FindOrCreateCardDirectory, GenerateCardID and the
ScanWords helper previously existed in both internal/utils.go (as
standalone functions) and were partially duplicated in
internal/gui/card_service.go (readWordFromDir, ScanExistingWords).
Introduce internal/store.CardStore as the single source of truth for
all on-disk card-directory operations. Both internal/processor and
internal/gui now hold a *store.CardStore field and delegate to it,
removing the last copy of the directory-scanning loop from
card_service.go. internal/utils.go keeps thin forwarding wrappers for
callers that import the root internal package.
Also adds table-driven unit tests for the new package covering
FindCardDirectory (including legacy _word.txt fallback),
FindOrCreateCardDirectory, and CardStore.ScanWords.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Define audio.ProviderFactory, image.PromptAwareClient,
image.OpenAIClientFactory, image.NanoBananaClientFactory, and
image.ClientFactories as the single source of truth for the three injectable
factory signatures that were previously duplicated across processor.Processor,
gui.Application, and gui.GenerationOrchestrator. Replace all three separate
function-type fields with imageFactories image.ClientFactories +
newAudioProvider audio.ProviderFactory, eliminating the parallel field
declarations and the local promptAwareImageClient interface in gui/generator.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Resolve all Viper config values once at startup in cmd/totalrecall/main.go
via the new newProcessorConfig() helper. The exported processor.Config struct
replaces the internal viperConfig type, and NewProcessor now accepts *Config
instead of querying Viper internally. The processor package no longer imports
Viper at all. Tests are updated to pass Config values directly, eliminating
the viper.Set/Reset boilerplate that coupled tests to the global singleton.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Extract CardService (file I/O, persistence, card directory management) and
GenerationOrchestrator (audio/image/phonetics generation) from the 2852-line
Application struct. Application is now thin UI event-wiring. Also split all
functions over 50 lines into focused helpers throughout app.go.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Extract audio coordination (voice selection, config assembly, attribution
writing) into audio_coordinator.go, card directory management into
card_store.go, and image downloading/searcher construction into
image_downloader.go. processor.go shrinks from ~1119 to ~575 lines,
each file now has a single clear responsibility. Also apply go fmt to
all touched files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Replace fixed 10s retry pause with pageRetryBase × attempt (15s→30s→45s→60s)
and increase max retries from 3 to 5. Extracted shared retryPage() helper used
by both generateStoryPage and generatePageWithRetry to keep retry logic DRY.
Gallery pages that fail due to Gemini rate limits now recover automatically
instead of being silently skipped after just 30 seconds of retries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- loadOrGenerate() in artist.go skips API calls for pages already on disk,
loading bytes for the reference chain. Re-runs now only generate missing
pages instead of regenerating the whole comic.
- --story-slug flag forces the output directory slug so a repair run writes
to the same directory as the original (e.g. --story-slug ai-jungle-quest).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
The strict LOCATION rule (all 4 panels must show the exact same location)
prevented natural scene progression within a page's story excerpt. Removed
so characters can move through the environment across panels on a single page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Ultra-realistic mode now picks from a dedicated realisticStyles pool
(DSLR photography, cinematic stills, hyper-realistic) instead of the
comicStyles pool. This prevents "comic strip" language from dominating
the model's output when photorealistic rendering is requested.
README updated: 12-page comic count (5 gallery pages), rendering mode
description clarified to show both pools and the 50/50 random default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add explicit LOCATION rule to story page prompt: all panels must show only
the location described in that page's excerpt, not any other story location
- Strengthen excerpt label so model illustrates only the current excerpt
- Bump version to 0.16.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Replace all "heroine/her" references in gallery poses with gender-neutral "main character/them"
- Explicitly instruct model to preserve character gender, not invent new characters
- Support multiple main characters in gallery page instruction
- Fix cover and back cover orientation: remove "portrait orientation" (canvas is 16:9 landscape)
- Bump version to 0.15.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Increase galleryPageCount from 3 to 5 (12 total pages per comic)
- Add 2 new gallery poses: profile close-up and power stance full-body
- Fix gallery prompt: explicitly state single 16:9 landscape image, no grid/panels
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
README
- Random 50/50 rendering mode per run (ultra-realistic or standard comic style)
- --no-ultra-realistic flag forces standard comic style when set
- renderingRequirement const centralised and applied to all image prompts via DRY renderReq() method
- Log rendering mode chosen at generation time
- Update README: correct page count (10), output filenames, all --story flags documented
- Bump version to 0.13.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
vocabulary/theme files
- Add renderingRequirement const applied to all prompts (cover, story pages, back cover, gallery)
- Write <slug>_comic_vocabulary.txt with vocabulary words and story text for learning
- Write <slug>_theme.txt recording the --story-theme used for easy reproduction
- Enforce adult (18+) characters in generator to avoid content policy blocks on revealing clothing
- Add cover/back cover retry logic (same pageMaxRetries as story pages)
- Bump version to 0.12.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
TTS chunks
- Increase story pages from 3 to 5 (10 total with cover/gallery/back)
- Add 3 text-free gallery pages with close-up heroine poses before back cover
- Add retry logic to cover and back cover generation (same as story pages)
- Enforce adult (18+) characters in generator prompt to avoid content policy blocks
- Reduce TTS chunk size from 200 to 100 words to stay under 1-minute quality threshold
- Increase TTS timeout from 2 to 3 minutes to avoid spurious deadline exceeded errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
|
|
subfolder, 16:9
- Iterative image chaining: each page receives cover + prev page as pixel-level
reference (multimodal) so the model copies appearance directly, not from text alone
- --story-theme flag overrides random genre pick per run
- Gemini now generates a short comic title (---COMIC TITLE--- separator) used as
the output slug; all files named <slug>_cover.png, <slug>_page_1.png, etc.
- All comic output stored under comics/<slug>/ subdirectory
- Aspect ratio changed to 16:9 (ThinkPad X1 Gen 9 screen fit; 16:10 not supported)
- Bulgarian language rule moved to top of every prompt for stronger compliance
- Dynamic story-specific cover masthead + publisher logo badge matching story genre
- --story-theme "a thrilling space..." forces genre without touching random pool
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- buildConclusion() calls gemini-2.5-flash (same SystemInstruction pattern)
to write a 3-4 sentence Bulgarian cinematic epilogue (~40-60 words, ~15-30s)
that reflects the story's emotion rather than recapping the plot
- narrateConclusion() narrates it as a separate TTS chunk and appends it to
the chunk list before final concatenation; failure is non-fatal
- Narrator struct now stores apiKey for the text-generation call
- Single-chunk path simplified: Rename instead of unnecessary concatenation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Gemini TTS voice quality degrades noticeably over long single-call texts.
Split the story into ~200-word paragraph-aligned chunks, narrate each
separately, then concatenate with ffmpeg's concat demuxer (copy codec,
no re-encoding). Short stories (single chunk) still use one call.
Temp files are written to a OS temp dir and cleaned up automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Bible prompt now requires EXACT age for each character (e.g. "8-year-old girl")
and explicitly states age must not change across any page, cover, or back cover
- Story page consistency rules now call out AGE APPEARANCE alongside face and clothing
- Cover prompt: "Do NOT invent new characters or use generic stand-ins" — only
story characters with matching age/face/clothing may appear
- Back cover prompt: same restriction, plus "same age, same face, same clothing,
same animals as in the interior pages"
- Both covers updated with: "a child must never look older or younger than defined"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Cover/back improvements:
- Front cover: MANDATORY TITLE instruction makes 'BULGARIAN VOCABULARY ADVENTURE'
dominate the top 20% of the cover in bold masthead lettering
- Back cover: Gemini generates a 2-3 sentence English marketing blurb that is
embedded verbatim in the blurb-box instruction (italic display type)
- Both covers now specify Silver-Age / Bronze-Age comic production layout
(price box, barcode strip, cover lines)
Animal consistency:
- Character bible prompt now treats animals as named characters — specifies exact
breed, fur colour/pattern, markings, and eye colour; instructs model never to
substitute a generic animal or change markings between pages
- Story page prompts add explicit animal consistency rule alongside human rules
Bible/blurb reliability:
- Switch helper calls to same SystemInstruction + user-content pattern that the
story generator uses successfully with gemini-2.5-flash
- Increase MaxOutputTokens to 8192 and timeout to 90s to match story generator
- Retry pause extended to 15s for free-tier RPM recovery between rapid calls
- Removed deprecated gemini-2.0-flash model references
PDF generation (new internal/story/pdf.go):
- AssembleComicPDF() combines all 5 images into comic.pdf via ImageMagick convert
- Pages are in reading order: cover → story pages → back cover
- Non-fatal: warns if ImageMagick is not installed, never blocks the pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Add comic_cover.png and comic_back.png flanking the 3 story pages
- Switch story pages to 3:4 portrait aspect ratio for standard comic proportions
- Request 9-panel (3×3) grid layout in every story page prompt
- Generate a Gemini character bible before image generation to lock characters,
clothing, and setting across all 5 pages — clothing only changes if the story
explicitly directs it
- Fix nil-writer panic in cover warning path
- Fix undefined comicPageCount reference in runner.go
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Generate a Gemini character bible before panel image generation
- Bible describes each character (name, age, hair, eyes, clothing) and
setting; it is prepended to every panel prompt to lock visual consistency
- Revert to 3 separate comic_page_N.png files (4:3 aspect ratio)
- bibleMaxTokens=2048 to avoid Gemini 2.5 Flash thinking-token starvation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
narration
- Replace 3 separate comic pages with one 9:16 tall comic_strip.png
- Prompt explicitly requests 3 vertically stacked panels with consistent characters
- Add AspectRatio field to SearchOptions; NanoBanana uses it when set
- Make cinematic narration instruction more dramatic (movie trailer narrator style)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- Generates a ~500-word Bulgarian vocabulary story from a batch file
- Produces 3 comic pages via NanoBanana (90% ultra-realistic style)
- Art style chosen randomly per run; override with --story-style
- Cinematic Gemini TTS narration saved as story_narration.mp3
- Random voice from curated pool (Charon, Fenrir, Enceladus, Algieba, Aoede, Schedar)
- Override narrator voice with --narrator-voice
- Falls back to story_tts_todo.txt if narration fails
- No new API key required — reuses existing GOOGLE_API_KEY
- Bump version to 0.9.4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Gemini 2.5 Flash runs internal reasoning by default. Those thinking tokens
count against MaxOutputTokens, leaving almost no budget for the actual IPA
output — hence the truncated [ˈɡlɛdɐm with no closing bracket.
Set ThinkingBudget=0 in ThinkingConfig so all 200 output tokens are reserved
for the IPA transcription itself. A simple dictionary-style lookup needs no
chain-of-thought reasoning.
Also included: audio_player layout fix (NewBorder center + TextWrapWord) so
long IPA strings are not clipped in the GUI bar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Two issues prevented the IPA from being fully displayed:
1. audio_player.go: switched the bottom bar from NewHBox to NewBorder so the
phonetic label fills the centre column instead of being squeezed to its
minimum width between the stop button and the status label. Also enabled
TextWrapWord so very long IPA strings wrap rather than being clipped.
2. phonetic/fetcher.go: raised MaxOutputTokens from 50 to 200 so Gemini 2.5
Flash has enough budget to emit the full IPA bracket pair without truncation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Gemini 2.5 Flash emits internal reasoning tokens before the IPA bracket pair.
With the 50-token cap the output was silently truncated to just [ˈ, losing the
rest of the transcription. 200 tokens is ample headroom for any Bulgarian word
or short phrase while keeping the response focused.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- audio/fallbacks.go: lowercase error string per Go convention
- gui/app.go: remove empty else branch in keyboard shortcut handler
- audio/provider_test.go: remove unused mockProvider type
- update test assertions in voices_test.go and processor_test.go to match
the corrected lowercase error string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
- startFileCheckTicker: add wg.Add(1)/wg.Done() so the app shutdown handler
waits for the ticker goroutine to exit (it already uses ctx.Done() to stop).
- post-delete cleanup goroutine: add wg.Add(1)/wg.Done() and a ctx.Done()
case so a shutdown during the 5-second polling window terminates cleanly
instead of leaking the goroutine.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Introduce AttributionProvider interface with GetAttribution() separate from
ImageSearcher so callers only needing search/download don't carry attribution.
ImageClient composes both. Downloader.searcher and NewDownloader now take
ImageClient since attribution is needed when saving downloaded files.
Compile-time assertions updated to ImageClient.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|