summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-04-13Release v0.29.0: display card counter (Card X / N) in the GUI status barv0.29.0Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10Release 0.28.2v0.28.2Paul Buetow
2026-04-09Release v0.28.1: convert Gemini TTS mono audio to stereov0.28.1Paul Buetow
Duplicate each 16-bit PCM sample into both L/R channels so all audio output uses both speakers, and retroactively converted the 88 existing card MP3s to stereo via ffmpeg. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08Release v0.28.0: story gallery aggregation and stronger ultra-realistic promptsv0.28.0Paul Buetow
- Copy *_gallery_*.png into comics/gallery after each --story run - Expand ultra-realistic rendering instructions in image prompts - Add gallery copy test; document comics/ in gitignore; sample vocab batch asset Made-with: Cursor
2026-04-08audio: propagate Close error from OpenAI TTS output filePaul Buetow
Use named return and defer like anki/generator GenerateCSV so a failed out.Close() is returned when io.Copy already succeeded. Made-with: Cursor
2026-04-08batch: pre-allocate WordEntry slice from line count in ReadBatchFilePaul Buetow
Use make([]WordEntry, 0, len(lines)) after splitting normalized content to avoid repeated growth on append. Return nil when no entries are parsed so empty batch files still yield a nil slice like before. Made-with: Cursor
2026-04-08feat: add gobreaker circuit breakers for OpenAI and Gemini API callsPaul Buetow
Introduce internal/apicircuit with sony/gobreaker: trip after five consecutive failures, 45s open state, 2m count reset, half-open trial cap. context.Canceled is not counted as a failure for breaker stats. Wrap OpenAI TTS, Gemini TTS, OpenAI DALL-E/chat (image path), and Nano Banana Gemini GenerateContent calls. HTTP timeouts remain unchanged in httpctx. Made-with: Cursor
2026-04-08fix(gui): make GetActiveJobs concurrency-safe without channel access under lockPaul Buetow
GetActiveJobs previously drained and refilled q.jobs while holding RLock, racing with AddWordWithPrompt and ProcessNextJob on the same channel. Derive queued jobs from q.results (StatusQueued) and processing jobs from q.processing, matching the authoritative state already updated under the mutex elsewhere. Made-with: Cursor
2026-04-08refactor(cli): split command.go into flags, config, apikeys (SRP)Paul Buetow
Move cobra flag registration, viper binding, and explicit-flag tracking into flags.go alongside Flags and NewFlags. Add config.go for InitConfig and apikeys.go for GetOpenAIKey/GetGoogleAPIKey. command.go now only defines CreateRootCommand. Made-with: Cursor
2026-04-08config: centralize NanoBanana and audio defaults in defaults.goPaul Buetow
Add internal/config/defaults.go with shared model IDs, output format, OpenAI/Gemini audio literals, and speed defaults. Wire audio.DefaultProviderConfig, CLI flags, GUI defaulting paths, and image package re-exports to these constants. Update command_test to use the public config identifiers. Made-with: Cursor
2026-04-08refactor(anki): split APKG generator into SQLiteSchemer, ZipPackager, ↵Paul Buetow
CardTemplate Extract SQLite schema and inserts into SQLiteSchemer, zip creation into ZipPackager, and embedded HTML/CSS templates into CardTemplate with external .html and .css files. APKGGenerator orchestrates copy, media mapping, DB build, and packaging. Made-with: Cursor
2026-04-08refactor(gui): extract NavigationHandler, ExportHandler, QueueManager, ↵Paul Buetow
KeyboardShortcuts Move navigation, export dialog, queue processing, and keyboard wiring out of Application into focused types with app *Application for shared state. Add ensureHandlers() for lazy init so tests that build Application literals still work. Wire queue callbacks to QueueManager; keep thin Application delegates for entry points used across the GUI. Made-with: Cursor
2026-04-08fix(gui): avoid tooltip init race after window closePaul Buetow
Use time.NewTimer with defer Stop in the delayed tooltip goroutine so the timer is not left pending when ctx wins the select. After the delay, check ctx before and inside fyne.Do so SetToolTip never runs after shutdown cancels the application context. Made-with: Cursor
2026-04-08refactor(processor): split BatchProcessor, AnkiExporter, CLIConfigResolverPaul Buetow
Extract CLIConfigResolver for flag/config precedence and GUI wiring; embed it on Processor so audio and image helpers use promoted accessors. Move batch file flow to BatchProcessor and Anki generation to AnkiExporter; Processor delegates while keeping per-word processing and translation cache. Made-with: Cursor
2026-04-08refactor(dip): inject archive, models, story, and gui at composition rootsPaul Buetow
Define Archiver, ModelLister, and StoryRunner interfaces in their packages. gui.New returns App; Application carries an injectable Archiver from Config. cmd/totalrecall wires default implementations via runDeps for tests and DI. Made-with: Cursor
2026-04-08refactor: slim cmd composition root, move story video flow to internal/videoPaul Buetow
- Add newProcessor() and newProcessorConfig in processor_config.go; newStoryRunner and storyUltraRealistic in story.go; main.go focuses on runCommand wiring. - Move gallery prompts, GenerateSelectedVideos, and RunStoryVideos into internal/video; cli.GenerateSelectedVideos delegates for GUI compatibility. - Remove t.Parallel from Veo tests that patch newGenaiClient (race with globals). Made-with: Cursor
2026-04-08refactor: registry pattern for audio and image provider factoriesPaul Buetow
Add internal/registry generic Registry[K,T] for keyed factory registration. Wire audio.NewProvider via registered per-provider constructors; GUI and processor newImageSearcher use registries of *Orchestrator/*Processor methods. Export image.ImageProviderOpenAI and ImageProviderNanoBanana from search.go and use them across gui to avoid duplicate string constants. Made-with: Cursor
2026-04-08feat(httpctx): add timeouts for OpenAI, Gemini, and HTTP downloadsPaul Buetow
Introduce internal/httpctx with non-zero http.Client timeouts for go-openai and google.golang.org/genai, shared image download client, and WithTimeoutUnlessSet for operation-level deadlines when callers use Background. Wire NewOpenAIClient/NewGenAIClient everywhere clients are constructed. Apply Search timeouts for DALL-E and Nano Banana, provider audio timeouts, model-list timeouts, Veo operation timeouts, story page download context, and single-word CLI processing cap. Made-with: Cursor
2026-04-08refactor(gui): extract AudioConfigResolver, VoiceSelector, ParallelRunnerPaul Buetow
Split orchestrator audio resolution, voice selection, and parallel material generation into dedicated types in separate files. GenerationOrchestrator wires AudioConfigResolver and VoiceSelector from NewGenerationOrchestrator and delegates GenerateMaterials to ParallelRunner; public behaviour unchanged. Made-with: Cursor
2026-04-08fix(processor): propagate errors from attribution and sidecar writesPaul Buetow
Return errors from saveAudioAttribution (including audio_metadata.txt), image prompt persistence (callback + saveImagePrompt), and ProcessWordWithTranslationAndType (card type, translation, phonetic) instead of only logging warnings so callers see partial failures. Made-with: Cursor
2026-04-07cleanupPaul Buetow
2026-04-06chore: bump version to 0.27.0v0.27.0Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06feat: mandatory speech bubbles in story panels, v0.26.0v0.26.0Paul Buetow
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>
2026-04-06feat: Gemini panel script drives comic panels in narrative order, v0.25.0v0.25.0Paul Buetow
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>
2026-04-06feat: add --ultra-realistic flag to force photorealistic rendering, v0.24.0v0.24.0Paul Buetow
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>
2026-04-06feat: --narrate opt-in narration, panel variety mandate, v0.23.0v0.23.0Paul Buetow
- 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>
2026-04-06chore: bump version to 0.22.0v0.22.0Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06fix: surface Veo API error field (content policy / geographic restriction) ↵Paul Buetow
with clear message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06chore: bump version to 0.21.0, add maxPollAttempts constant and comments to ↵Paul Buetow
veo.go Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06test: add missing unit tests for Veo video feature (task 016)Paul Buetow
- 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>
2026-04-06docs: document Optional Gallery Videos (Veo) feature in READMEPaul Buetow
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>
2026-04-06feat: add Fyne GUI dialog for gallery video generationPaul Buetow
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>
2026-04-06fix: correct video integration after comic generationPaul Buetow
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>
2026-04-06feat: add --video CLI flag to control Veo video prompt after story generationPaul Buetow
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>
2026-04-06feat: add generateSelectedVideos CLI runner for Veo video generationPaul Buetow
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>
2026-04-06feat: add CLI prompt helper for gallery video generationPaul Buetow
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>
2026-04-06feat: add Veo video generator package for comic gallery animationPaul Buetow
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>
2026-04-06fix: track all GUI goroutines with WaitGroup and ctx.Done() (Go Mistake #62)Paul Buetow
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>
2026-04-06refactor: extract shared CardStore into internal/store to eliminate duplicationPaul Buetow
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>
2026-04-06refactor: consolidate provider factory test seams into shared named typesPaul Buetow
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>
2026-04-06refactor: remove Viper coupling from processor package (DIP)Paul Buetow
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>
2026-04-06refactor: decompose gui.Application god object into focused servicesPaul Buetow
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>
2026-04-06refactor: decompose Processor god object into focused files (SRP)Paul Buetow
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>
2026-04-06chore: bump version to 0.20.0v0.20.0Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04chore: bump version to 0.19.0v0.19.0Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04feat: progressive retry backoff for rate-limited image generationPaul Buetow
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>
2026-04-04feat: skip existing pages on re-run, add --story-slug for targeted repairv0.18.0Paul Buetow
- 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>
2026-04-04fix: remove per-page location lock to allow scene transitions in panelsPaul Buetow
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>
2026-04-04feat: use photography-only style pool for ultra-realistic mode, update READMEv0.17.0Paul Buetow
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>
2026-04-04fix: enforce per-page location from story excerpt, bump to 0.16.0v0.16.0Paul Buetow
- 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>