From b9ee95a66b94c8dc7fca946a2d464ed5d3ad86b1 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 3 Apr 2026 23:02:41 +0300 Subject: =?UTF-8?q?feat:=20comic=20pipeline=20v0.10.0=20=E2=80=94=20iterat?= =?UTF-8?q?ive=20chaining,=20title=20slugs,=20comics=20subfolder,=2016:9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 _cover.png, _page_1.png, etc. - All comic output stored under comics// 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 --- cmd/totalrecall/main.go | 1 + comic.pdf | Bin 0 -> 11777107 bytes comic_back.png | Bin 0 -> 2855395 bytes comic_back_attribution.txt | 16 ++ comic_cover.png | Bin 0 -> 3169595 bytes comic_cover_attribution.txt | 16 ++ comic_page_1.png | Bin 0 -> 3061147 bytes comic_page_1_attribution.txt | 16 ++ comic_page_2.png | Bin 0 -> 3070403 bytes comic_page_2_attribution.txt | 16 ++ comic_page_3.png | Bin 0 -> 3064316 bytes comic_page_3_attribution.txt | 16 ++ comic_strip_attribution.txt | 15 ++ .../astra-stellar-shield/astra-stellar-shield.pdf | Bin 0 -> 12901628 bytes .../astra-stellar-shield_back.png | Bin 0 -> 3390411 bytes .../astra-stellar-shield_back_attribution.txt | 16 ++ .../astra-stellar-shield_cover.png | Bin 0 -> 3048070 bytes .../astra-stellar-shield_cover_attribution.txt | 16 ++ .../astra-stellar-shield_narration.mp3 | Bin 0 -> 2360828 bytes .../astra-stellar-shield_page_1.png | Bin 0 -> 3304135 bytes .../astra-stellar-shield_page_1_attribution.txt | 16 ++ .../astra-stellar-shield_page_2.png | Bin 0 -> 3236448 bytes .../astra-stellar-shield_page_2_attribution.txt | 16 ++ .../astra-stellar-shield_page_3.png | Bin 0 -> 3347609 bytes .../astra-stellar-shield_page_3_attribution.txt | 16 ++ .../astra-stellar-shield_story.txt | 25 +++ .../galactic-shield/galactic-shield_narration.mp3 | Bin 0 -> 2051396 bytes comics/galactic-shield/galactic-shield_story.txt | 18 ++ comics/void-cats-apple/void-cats-apple.pdf | Bin 0 -> 10911515 bytes comics/void-cats-apple/void-cats-apple_back.png | Bin 0 -> 3015626 bytes .../void-cats-apple_back_attribution.txt | 16 ++ comics/void-cats-apple/void-cats-apple_cover.png | Bin 0 -> 3007071 bytes .../void-cats-apple_cover_attribution.txt | 16 ++ .../void-cats-apple/void-cats-apple_narration.mp3 | Bin 0 -> 2452892 bytes comics/void-cats-apple/void-cats-apple_page_1.png | Bin 0 -> 2775565 bytes .../void-cats-apple_page_1_attribution.txt | 16 ++ comics/void-cats-apple/void-cats-apple_page_2.png | Bin 0 -> 2611038 bytes .../void-cats-apple_page_2_attribution.txt | 16 ++ comics/void-cats-apple/void-cats-apple_page_3.png | Bin 0 -> 2635224 bytes .../void-cats-apple_page_3_attribution.txt | 16 ++ comics/void-cats-apple/void-cats-apple_story.txt | 27 +++ internal/cli/command.go | 1 + internal/cli/flags.go | 1 + internal/image/nanobanana.go | 66 +++++- internal/image/search.go | 9 +- internal/story/artist.go | 175 ++++++++++----- internal/story/generator.go | 244 +++++++++++++++++++-- internal/story/pdf.go | 5 +- internal/story/runner.go | 76 ++++--- internal/version.go | 2 +- story.txt | 17 ++ story_image_attribution.txt | 16 ++ story_narration.mp3 | Bin 0 -> 2485628 bytes story_tts_todo.txt | 10 + 54 files changed, 849 insertions(+), 99 deletions(-) create mode 100644 comic.pdf create mode 100644 comic_back.png create mode 100644 comic_back_attribution.txt create mode 100644 comic_cover.png create mode 100644 comic_cover_attribution.txt create mode 100644 comic_page_1.png create mode 100644 comic_page_1_attribution.txt create mode 100644 comic_page_2.png create mode 100644 comic_page_2_attribution.txt create mode 100644 comic_page_3.png create mode 100644 comic_page_3_attribution.txt create mode 100644 comic_strip_attribution.txt create mode 100644 comics/astra-stellar-shield/astra-stellar-shield.pdf create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_back.png create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_back_attribution.txt create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_cover.png create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_cover_attribution.txt create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_narration.mp3 create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_page_1.png create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_page_1_attribution.txt create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_page_2.png create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_page_2_attribution.txt create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_page_3.png create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_page_3_attribution.txt create mode 100644 comics/astra-stellar-shield/astra-stellar-shield_story.txt create mode 100644 comics/galactic-shield/galactic-shield_narration.mp3 create mode 100644 comics/galactic-shield/galactic-shield_story.txt create mode 100644 comics/void-cats-apple/void-cats-apple.pdf create mode 100644 comics/void-cats-apple/void-cats-apple_back.png create mode 100644 comics/void-cats-apple/void-cats-apple_back_attribution.txt create mode 100644 comics/void-cats-apple/void-cats-apple_cover.png create mode 100644 comics/void-cats-apple/void-cats-apple_cover_attribution.txt create mode 100644 comics/void-cats-apple/void-cats-apple_narration.mp3 create mode 100644 comics/void-cats-apple/void-cats-apple_page_1.png create mode 100644 comics/void-cats-apple/void-cats-apple_page_1_attribution.txt create mode 100644 comics/void-cats-apple/void-cats-apple_page_2.png create mode 100644 comics/void-cats-apple/void-cats-apple_page_2_attribution.txt create mode 100644 comics/void-cats-apple/void-cats-apple_page_3.png create mode 100644 comics/void-cats-apple/void-cats-apple_page_3_attribution.txt create mode 100644 comics/void-cats-apple/void-cats-apple_story.txt create mode 100644 story.txt create mode 100644 story_image_attribution.txt create mode 100644 story_narration.mp3 create mode 100644 story_tts_todo.txt diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go index 5363190..d6abcf5 100644 --- a/cmd/totalrecall/main.go +++ b/cmd/totalrecall/main.go @@ -68,6 +68,7 @@ func runCommand(cmd *cobra.Command, args []string, flags *cli.Flags) error { ImageTextModel: flags.NanoBananaTextModel, OutputDir: ".", Style: flags.StoryStyle, + Theme: flags.StoryTheme, NarratorVoice: flags.NarratorVoice, }) return runner.Run(flags.StoryFile) diff --git a/comic.pdf b/comic.pdf new file mode 100644 index 0000000..02b4bf4 Binary files /dev/null and b/comic.pdf differ diff --git a/comic_back.png b/comic_back.png new file mode 100644 index 0000000..f97f5f8 Binary files /dev/null and b/comic_back.png differ diff --git a/comic_back_attribution.txt b/comic_back_attribution.txt new file mode 100644 index 0000000..fd16f28 --- /dev/null +++ b/comic_back_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. All visible text (blurb box, labels, banners) MUST be in Bulgarian Cyrillic script. English text anywhere on the back cover is STRICTLY FORBIDDEN. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (back cover — follow exactly, do NOT change clothing): +Captain Elara, a middle-aged woman (early 40s), has dark brown hair tied back in a neat, practical ponytail, with a few loose strands framing her face. Her eyes are sharp, intelligent hazel, and her skin tone is light olive, slightly tanned. She possesses an athletic, lean, and agile build. Elara wears a functional dark grey (almost black) fitted spacesuit with subtle dark blue piping along the seams and collar. The suit features a high stand-up collar, long sleeves, and zips up the front with the zipper visible. It has no visible pockets, patches, or emblems. Her footwear consists of... + +Generated at: 2026-04-03 22:39:29 diff --git a/comic_cover.png b/comic_cover.png new file mode 100644 index 0000000..de6cb20 Binary files /dev/null and b/comic_cover.png differ diff --git a/comic_cover_attribution.txt b/comic_cover_attribution.txt new file mode 100644 index 0000000..c898574 --- /dev/null +++ b/comic_cover_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. All text on the cover (cover lines, banners, labels) MUST be in Bulgarian Cyrillic script. The masthead title must also be rendered in a striking comic-book font. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (cover — follow exactly, do NOT change clothing): +Captain Elara, a middle-aged woman (early 40s), has dark brown hair tied back in a neat, practical ponytail, with a few loose strands framing her face. Her eyes are sharp, intelligent hazel, and her skin tone is light olive, slightly tanned. She possesses an athletic, lean, and agile build. Elara wears a functional dark grey (almost black) fitted spacesuit with subtle dark blue piping along the seams and collar. The suit features a high stand-up collar, long sleeves, and zips up the front with the zipper visible. It has no visible pockets, patches, or emblems. Her footwear ... + +Generated at: 2026-04-03 22:38:20 diff --git a/comic_page_1.png b/comic_page_1.png new file mode 100644 index 0000000..cff7872 Binary files /dev/null and b/comic_page_1.png differ diff --git a/comic_page_1_attribution.txt b/comic_page_1_attribution.txt new file mode 100644 index 0000000..b75b9e0 --- /dev/null +++ b/comic_page_1_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 1 of 3 — follow exactly, do NOT change clothing): +Captain Elara, a middle-aged woman (early 40s), has dark brown hair tied back in a neat, practical ponytail, with a few loose strands framing her face. Her eyes are sharp, intelligent hazel, and her skin tone is light olive, slightly tanned. She possesses an athletic, lean, and agile build. Elara wears a functional dark grey (almost black) fitted spacesuit with subtle dark blue piping along the seams and collar. The suit features... + +Generated at: 2026-04-03 22:38:37 diff --git a/comic_page_2.png b/comic_page_2.png new file mode 100644 index 0000000..ca18c9f Binary files /dev/null and b/comic_page_2.png differ diff --git a/comic_page_2_attribution.txt b/comic_page_2_attribution.txt new file mode 100644 index 0000000..6bb716c --- /dev/null +++ b/comic_page_2_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 2 of 3 — follow exactly, do NOT change clothing): +Captain Elara, a middle-aged woman (early 40s), has dark brown hair tied back in a neat, practical ponytail, with a few loose strands framing her face. Her eyes are sharp, intelligent hazel, and her skin tone is light olive, slightly tanned. She possesses an athletic, lean, and agile build. Elara wears a functional dark grey (almost black) fitted spacesuit with subtle dark blue piping along the seams and collar. The suit features... + +Generated at: 2026-04-03 22:38:50 diff --git a/comic_page_3.png b/comic_page_3.png new file mode 100644 index 0000000..a896766 Binary files /dev/null and b/comic_page_3.png differ diff --git a/comic_page_3_attribution.txt b/comic_page_3_attribution.txt new file mode 100644 index 0000000..b3d7ef0 --- /dev/null +++ b/comic_page_3_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 3 of 3 — follow exactly, do NOT change clothing): +Captain Elara, a middle-aged woman (early 40s), has dark brown hair tied back in a neat, practical ponytail, with a few loose strands framing her face. Her eyes are sharp, intelligent hazel, and her skin tone is light olive, slightly tanned. She possesses an athletic, lean, and agile build. Elara wears a functional dark grey (almost black) fitted spacesuit with subtle dark blue piping along the seams and collar. The suit features... + +Generated at: 2026-04-03 22:39:11 diff --git a/comic_strip_attribution.txt b/comic_strip_attribution.txt new file mode 100644 index 0000000..a5c09a5 --- /dev/null +++ b/comic_strip_attribution.txt @@ -0,0 +1,15 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 768x1376 +Result: Generated educational image for vocabulary story + +Prompt used: +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +A single tall comic strip with 3 vertically stacked panels showing the beginning, middle, and end of the story. Keep all characters visually consistent across panels. Scene based on this Bulgarian vocabulary story: + +Всяка сутрин в къщата на баба Елица се събуждаше с едно приятно гъделичкане. Не беше слънцето, което я будеше, а нейната любима 1. котка, Мурка. Мурка обичаше да се катери по завивките, да мърка силно и да бута с глава ръката на Елица, докато момиченцето не отвори очи. "Добро утро, Мурка," прошепна Елица, протягайки се с усмивка. 1. Котето се изви грациозно, разтегна лапи и скочи от легл... + +Generated at: 2026-04-03 13:28:31 diff --git a/comics/astra-stellar-shield/astra-stellar-shield.pdf b/comics/astra-stellar-shield/astra-stellar-shield.pdf new file mode 100644 index 0000000..9c14dbf Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield.pdf differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_back.png b/comics/astra-stellar-shield/astra-stellar-shield_back.png new file mode 100644 index 0000000..6c273d3 Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield_back.png differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_back_attribution.txt b/comics/astra-stellar-shield/astra-stellar-shield_back_attribution.txt new file mode 100644 index 0000000..769bcb9 --- /dev/null +++ b/comics/astra-stellar-shield/astra-stellar-shield_back_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 1376x768 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. All visible text (blurb box, labels, banners) MUST be in Bulgarian Cyrillic script. English text anywhere on the back cover is STRICTLY FORBIDDEN. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (back cover — follow exactly, do NOT change clothing): +Astra: Middle-aged, long flowing dark brown hair often styled in a high ponytail or half-up, determined emerald green eyes, warm olive skin tone, very athletic and muscular build. She wears a golden armored breastplate with ornate eagle-wing motifs, red form-fitting leggings, golden knee-high boots, and golden wrist gauntlets. A deep blue cape with gold trim flows from her shoulders. Zara: Young adult, short spiky bright purple hair, sharp sky blue eyes, pale skin tone, lean and agile build. Her uniform is a fitted dark grey flight suit with bright cyan accents on the shoulders and cu... + +Generated at: 2026-04-03 23:00:03 diff --git a/comics/astra-stellar-shield/astra-stellar-shield_cover.png b/comics/astra-stellar-shield/astra-stellar-shield_cover.png new file mode 100644 index 0000000..bd7b10a Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield_cover.png differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_cover_attribution.txt b/comics/astra-stellar-shield/astra-stellar-shield_cover_attribution.txt new file mode 100644 index 0000000..01bea79 --- /dev/null +++ b/comics/astra-stellar-shield/astra-stellar-shield_cover_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 1376x768 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. All text on the cover (cover lines, banners, labels) MUST be in Bulgarian Cyrillic script. The masthead title must also be rendered in a striking comic-book font. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (cover — follow exactly, do NOT change clothing): +Astra: Middle-aged, long flowing dark brown hair often styled in a high ponytail or half-up, determined emerald green eyes, warm olive skin tone, very athletic and muscular build. She wears a golden armored breastplate with ornate eagle-wing motifs, red form-fitting leggings, golden knee-high boots, and golden wrist gauntlets. A deep blue cape with gold trim flows from her shoulders. Zara: Young adult, short spiky bright purple hair, sharp sky blue eyes, pale skin tone, lean and agile build. Her uniform is a fitted dark grey flight suit with bright cyan accents on the shoul... + +Generated at: 2026-04-03 22:58:32 diff --git a/comics/astra-stellar-shield/astra-stellar-shield_narration.mp3 b/comics/astra-stellar-shield/astra-stellar-shield_narration.mp3 new file mode 100644 index 0000000..872fcd1 Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield_narration.mp3 differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_page_1.png b/comics/astra-stellar-shield/astra-stellar-shield_page_1.png new file mode 100644 index 0000000..ce7ae13 Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield_page_1.png differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_page_1_attribution.txt b/comics/astra-stellar-shield/astra-stellar-shield_page_1_attribution.txt new file mode 100644 index 0000000..af6ad50 --- /dev/null +++ b/comics/astra-stellar-shield/astra-stellar-shield_page_1_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 1376x768 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 1 of 3 — follow exactly, do NOT change clothing): +Astra: Middle-aged, long flowing dark brown hair often styled in a high ponytail or half-up, determined emerald green eyes, warm olive skin tone, very athletic and muscular build. She wears a golden armored breastplate with ornate eagle-wing motifs, red form-fitting leggings, golden knee-high boots, and golden wrist gauntlets. A deep blue cape with gold trim flows from her shoulders. Zara: Young adult, short spiky bright purple h... + +Generated at: 2026-04-03 22:58:51 diff --git a/comics/astra-stellar-shield/astra-stellar-shield_page_2.png b/comics/astra-stellar-shield/astra-stellar-shield_page_2.png new file mode 100644 index 0000000..ce8d549 Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield_page_2.png differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_page_2_attribution.txt b/comics/astra-stellar-shield/astra-stellar-shield_page_2_attribution.txt new file mode 100644 index 0000000..26c95b2 --- /dev/null +++ b/comics/astra-stellar-shield/astra-stellar-shield_page_2_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 1376x768 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 2 of 3 — follow exactly, do NOT change clothing): +Astra: Middle-aged, long flowing dark brown hair often styled in a high ponytail or half-up, determined emerald green eyes, warm olive skin tone, very athletic and muscular build. She wears a golden armored breastplate with ornate eagle-wing motifs, red form-fitting leggings, golden knee-high boots, and golden wrist gauntlets. A deep blue cape with gold trim flows from her shoulders. Zara: Young adult, short spiky bright purple h... + +Generated at: 2026-04-03 22:59:14 diff --git a/comics/astra-stellar-shield/astra-stellar-shield_page_3.png b/comics/astra-stellar-shield/astra-stellar-shield_page_3.png new file mode 100644 index 0000000..4081140 Binary files /dev/null and b/comics/astra-stellar-shield/astra-stellar-shield_page_3.png differ diff --git a/comics/astra-stellar-shield/astra-stellar-shield_page_3_attribution.txt b/comics/astra-stellar-shield/astra-stellar-shield_page_3_attribution.txt new file mode 100644 index 0000000..3bb6541 --- /dev/null +++ b/comics/astra-stellar-shield/astra-stellar-shield_page_3_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 1376x768 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 3 of 3 — follow exactly, do NOT change clothing): +Astra: Middle-aged, long flowing dark brown hair often styled in a high ponytail or half-up, determined emerald green eyes, warm olive skin tone, very athletic and muscular build. She wears a golden armored breastplate with ornate eagle-wing motifs, red form-fitting leggings, golden knee-high boots, and golden wrist gauntlets. A deep blue cape with gold trim flows from her shoulders. Zara: Young adult, short spiky bright purple h... + +Generated at: 2026-04-03 22:59:42 diff --git a/comics/astra-stellar-shield/astra-stellar-shield_story.txt b/comics/astra-stellar-shield/astra-stellar-shield_story.txt new file mode 100644 index 0000000..baec415 --- /dev/null +++ b/comics/astra-stellar-shield/astra-stellar-shield_story.txt @@ -0,0 +1,25 @@ +Командир Астра стоеше на мостика на звездния си кораб „Зора“, оглеждайки безкрайната космическа шир. Нейната екипажна, състояща се от пилота Зара и техник Лео, работеха в синхрон, подготвяйки се за скок през хиперпространството. Астра, с нейната внушителна осанка и поглед, изпълнен с решителност, беше жива легенда – защитник на галактиката, известна с несравнимата си сила и безстрашие. + +„Скок след трийсет секунди, командир“, докладва Зара, докато пръстите ѝ танцуваха по контролния панел. +„Разбрано, Зара. Лео, всички системи ли са зелени?“ +„Да, командир. Всичко е стабилно“, отвърна Лео, без да откъсва поглед от холографските дисплеи пред него. +Внезапно аларма прониза тишината на мостика. Екраните присветнаха в червено. +„Нещо излиза от хиперпространството точно пред нас!“ извика Лео. „Множество кораби, командир! Неизвестна формация!“ + +Преди Астра да успее да реагира, мощна енергийна вълна разтърси „Зора“. Корабът се затресе. „Ксениди!“ процеди Астра, вече поемайки контрол. Тя знаеше тези същества – агресивни, технологично напреднали извънземни, които се появяваха от нищото, за да унищожават. +Астра моментално се преобрази. Нейният брониран боен костюм, украсен със златни символи и светещ щит, се появи, заменяйки обикновената ѝ униформа. Тя грабна своето енергийно копие. „Зара, защитни щитове на максимум! Лео, открий им слабост!“ + +Докато битката бушуваше, една малка, черна 1. котка се стрелна от вентилационния отвор и се сви до краката на Лео, търсейки утеха. Луна, корабното талисманче, бе свикнала с битките, но все пак предпочиташе спокойствието. Лео галеше успокоително главата ѝ, докато продължаваше да анализира тактиката на врага. + +Ксенидите бяха безмилостни, корабите им бяха бързи и смъртоносни. Един от вражеските кораби се приближи опасно и започна да стреля по мостика. Част от енергийния щит на „Зора“ отслабна. +„Те ще се опитат да ни абордират!“ извика Зара. +„Не и докато аз съм тук!“ прогърмя гласът на Астра. Тя се изправи пред вратата на мостика, копието ѝ излъчваше синя светлина. Когато първите Ксениди пробиха бронята на кораба и се появиха, тя ги посрещна с безмилостна точност и сила. Всяко движение беше прецизно, всеки удар – фатален. + +Докато Астра отблъскваше вълна след вълна от нападатели, Лео най-накрая извика: „Командир! Открих слабост в техните основни кораби! Енергийните им ядра са уязвими, когато преминават в нападателен режим!“ +„Зара, приготви торпедата! Фокусирай огъня върху основния им флагман! Лео, насочи я!“ +Астра се върна на бойния си пост, избърсвайки капка пот от челото си. Зара ѝ подаде голяма, червена 2. ябълка. „За енергия, командир. Нужна ни е на всички.“ +Астра отхапа от ябълката, усещайки освежаващия вкус. „Благодаря, Зара. Всички оставаме съсредоточени! Почти сме готови!“ + +С новата информация и подновена сила, екипажът на „Зора“ започна контраатака. Торпедата, насочени прецизно от Лео и изстреляни от Зара, пробиха щитовете на флагмана на Ксенидите. Експлозия разтърси вражеския кораб. Останалите Ксенидски кораби, обезкуражени от загубата на лидера си, започнаха да се оттеглят обратно в хиперпространството. +„Успех!“ възкликна Зара. +Астра свали щита си, но запази копието в ръка. „Не е свършило, Зара. Те ще се върнат. Но и ние ще сме готови.“ Тя погледна към безкрайното пространство, истински пазител на реда в хаотична галактика. diff --git a/comics/galactic-shield/galactic-shield_narration.mp3 b/comics/galactic-shield/galactic-shield_narration.mp3 new file mode 100644 index 0000000..f93d619 Binary files /dev/null and b/comics/galactic-shield/galactic-shield_narration.mp3 differ diff --git a/comics/galactic-shield/galactic-shield_story.txt b/comics/galactic-shield/galactic-shield_story.txt new file mode 100644 index 0000000..2ab9fba --- /dev/null +++ b/comics/galactic-shield/galactic-shield_story.txt @@ -0,0 +1,18 @@ +Звездолетът „Аврора“ прорязваше безкрайния космос, блестящ като диамант сред чернотата. На мостика, капитан Атала стоеше изправена, със стоманена решимост в погледа си. Мускулестата ѝ фигура, обвита в бойна броня, излъчваше непоклатима сила, а лицето ѝ, белязано от безброй битки, носеше мъдростта на воин. До нея, с елегантна стъпка, се промъкна Луна – нейната вярна черна **1. котка**. Животното се настани до командния панел, очите му с цвят на кехлибар наблюдаваха звездите, сякаш разбираха всяка променяща се опасност в дълбините на пространството. Луна беше повече от домашен любимец; тя беше талисман на „Аврора“, неин тих пазител. + +Внезапно червени аларми закрещяха. „Капитане, неидентифицирани кораби! Множество! Идват бързо!“ – извика пилотът Кай, млад мъж с остър ум и бързи рефлекси. +„Щитове на максимум! Отворете огън по моя команда!“ – заповяда Атала, гласът ѝ ехтеше с авторитет. +На екраните се появиха зловещите силуети на корабите на Зоргарите – безпощадна извънземна раса, която от векове тероризираше галактиката. Те бяха по-многобройни от очакваното. + +Битката започна. Енергийни лъчи разсичаха мрака, експлозии осветяваха бойното поле. Атала се движеше сред екипажа си, давайки указания, вдъхвайки кураж. Един от главните двигатели беше ударен. Корабът се разтресе. Луна изсъска, козината ѝ настръхна, но остана на място, наблюдавайки с тревожност. + +„Трябва да пробием защитата им, Кай! Фокусирай огъня върху флагмана!“ – извика Атала. +В този момент, инженера Зора, жена с лъскава черна плитка и очи, пълни с решителност, подаде на Атала хрупкава **2. ябълка**. „За енергия, капитане. Нуждаем се от теб във върхова форма.“ +Атала отхапа от свежия плод, вкусът му беше ободряващ на фона на хаоса. За момент, тази проста `ябълка` я заземи, напомняйки ѝ за дома, за всичко, което защитава. + +С възвърната сила, Атала грабна своя енергиен ласо и изрича: „Всички да ме следват! Ще ги ударим в сърцето!“ +Тя не беше просто командир; тя беше предводител. С невероятна грация и сила, Атала поведе десантен екип към абордаж на най-големия вражески кораб, пробивайки път през гъмжащи орди от Зоргари с всеки замах. Енергийното ѝ ласо пулсираше със синя светлина, неутрализирайки врагове, които се осмеляваха да застанат на пътя ѝ. Нейната смелост беше заразителна, вдъхновявайки екипа ѝ да се бори с още по-голяма решителност. + +Обратно на „Аврора“, Кай и Зора успяха да пренасочат енергията и да нанесат критичен удар по флотилията на Зоргарите. Вражеските кораби започнаха да се разпадат. На борда на флагмана, Атала и нейният екип унищожиха основния им контролен възел. С писък, Зоргарите започнаха хаотично отстъпление. + +„Отлична работа, екип! Пригответе се за скок!“ – обяви Атала, връщайки се на мостика, където Луна я посрещна с тихо мъркане, сякаш знаеше, че опасността е отминала. Галактиката беше спасена за пореден път, благодарение на непоклатимата воля на Атала и духа на „Аврора“. diff --git a/comics/void-cats-apple/void-cats-apple.pdf b/comics/void-cats-apple/void-cats-apple.pdf new file mode 100644 index 0000000..132c562 Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple.pdf differ diff --git a/comics/void-cats-apple/void-cats-apple_back.png b/comics/void-cats-apple/void-cats-apple_back.png new file mode 100644 index 0000000..7c3a439 Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple_back.png differ diff --git a/comics/void-cats-apple/void-cats-apple_back_attribution.txt b/comics/void-cats-apple/void-cats-apple_back_attribution.txt new file mode 100644 index 0000000..622ddd2 --- /dev/null +++ b/comics/void-cats-apple/void-cats-apple_back_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. All visible text (blurb box, labels, banners) MUST be in Bulgarian Cyrillic script. English text anywhere on the back cover is STRICTLY FORBIDDEN. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (back cover — follow exactly, do NOT change clothing): +Kapitan Alex, middle-aged (early 40s), short dark brown slightly messy hair, deep blue eyes, light olive skin, athletic lean build. Wears a form-fitting dark grey spacesuit with subtle silver trim on the shoulders and collar, a black utility belt with a silver buckle, and sturdy black combat boots. No visible branding on the suit. Kosmos, a Sphynx cat, has hairless light pinkish-grey velvety skin, striking emerald green eyes, and is of medium domestic cat size. Small faint scattered darker grey spots mark her back and legs. She is typically alert, often perched or curled up, but can a... + +Generated at: 2026-04-03 22:51:14 diff --git a/comics/void-cats-apple/void-cats-apple_cover.png b/comics/void-cats-apple/void-cats-apple_cover.png new file mode 100644 index 0000000..0ddd15a Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple_cover.png differ diff --git a/comics/void-cats-apple/void-cats-apple_cover_attribution.txt b/comics/void-cats-apple/void-cats-apple_cover_attribution.txt new file mode 100644 index 0000000..4d35543 --- /dev/null +++ b/comics/void-cats-apple/void-cats-apple_cover_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. All text on the cover (cover lines, banners, labels) MUST be in Bulgarian Cyrillic script. The masthead title must also be rendered in a striking comic-book font. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (cover — follow exactly, do NOT change clothing): +Kapitan Alex, middle-aged (early 40s), short dark brown slightly messy hair, deep blue eyes, light olive skin, athletic lean build. Wears a form-fitting dark grey spacesuit with subtle silver trim on the shoulders and collar, a black utility belt with a silver buckle, and sturdy black combat boots. No visible branding on the suit. Kosmos, a Sphynx cat, has hairless light pinkish-grey velvety skin, striking emerald green eyes, and is of medium domestic cat size. Small faint scattered darker grey spots mark her back and legs. She is typically alert, often perched or curled up... + +Generated at: 2026-04-03 22:50:10 diff --git a/comics/void-cats-apple/void-cats-apple_narration.mp3 b/comics/void-cats-apple/void-cats-apple_narration.mp3 new file mode 100644 index 0000000..b1ccc60 Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple_narration.mp3 differ diff --git a/comics/void-cats-apple/void-cats-apple_page_1.png b/comics/void-cats-apple/void-cats-apple_page_1.png new file mode 100644 index 0000000..421539a Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple_page_1.png differ diff --git a/comics/void-cats-apple/void-cats-apple_page_1_attribution.txt b/comics/void-cats-apple/void-cats-apple_page_1_attribution.txt new file mode 100644 index 0000000..5e86ca3 --- /dev/null +++ b/comics/void-cats-apple/void-cats-apple_page_1_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 1 of 3 — follow exactly, do NOT change clothing): +Kapitan Alex, middle-aged (early 40s), short dark brown slightly messy hair, deep blue eyes, light olive skin, athletic lean build. Wears a form-fitting dark grey spacesuit with subtle silver trim on the shoulders and collar, a black utility belt with a silver buckle, and sturdy black combat boots. No visible branding on the suit. Kosmos, a Sphynx cat, has hairless light pinkish-grey velvety skin, striking emerald green eyes, and... + +Generated at: 2026-04-03 22:50:27 diff --git a/comics/void-cats-apple/void-cats-apple_page_2.png b/comics/void-cats-apple/void-cats-apple_page_2.png new file mode 100644 index 0000000..873035f Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple_page_2.png differ diff --git a/comics/void-cats-apple/void-cats-apple_page_2_attribution.txt b/comics/void-cats-apple/void-cats-apple_page_2_attribution.txt new file mode 100644 index 0000000..6a2dced --- /dev/null +++ b/comics/void-cats-apple/void-cats-apple_page_2_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 2 of 3 — follow exactly, do NOT change clothing): +Kapitan Alex, middle-aged (early 40s), short dark brown slightly messy hair, deep blue eyes, light olive skin, athletic lean build. Wears a form-fitting dark grey spacesuit with subtle silver trim on the shoulders and collar, a black utility belt with a silver buckle, and sturdy black combat boots. No visible branding on the suit. Kosmos, a Sphynx cat, has hairless light pinkish-grey velvety skin, striking emerald green eyes, and... + +Generated at: 2026-04-03 22:50:45 diff --git a/comics/void-cats-apple/void-cats-apple_page_3.png b/comics/void-cats-apple/void-cats-apple_page_3.png new file mode 100644 index 0000000..01587f6 Binary files /dev/null and b/comics/void-cats-apple/void-cats-apple_page_3.png differ diff --git a/comics/void-cats-apple/void-cats-apple_page_3_attribution.txt b/comics/void-cats-apple/void-cats-apple_page_3_attribution.txt new file mode 100644 index 0000000..7646019 --- /dev/null +++ b/comics/void-cats-apple/void-cats-apple_page_3_attribution.txt @@ -0,0 +1,16 @@ +Image generated by Google Gemini Nano Banana + +Model: gemini-3.1-flash-image-preview +Text model: gemini-2.5-flash +Aspect ratio: 4:3 +Size: 896x1200 +Result: Generated educational image for vocabulary story + +Prompt used: +ЗАДЪЛЖИТЕЛНО / MANDATORY LANGUAGE RULE: This is a BULGARIAN comic book. Every word of text inside speech bubbles, thought bubbles, caption boxes, and panel labels MUST be written in Bulgarian Cyrillic script (например: Здравей! Какво правиш? Побързай!). English text anywhere in the panels is STRICTLY FORBIDDEN — use ONLY Bulgarian. + +Art style: ultra realistic comic strip with photographic detail and dramatic lighting. +CHARACTER & SETTING REFERENCE (story page 3 of 3 — follow exactly, do NOT change clothing): +Kapitan Alex, middle-aged (early 40s), short dark brown slightly messy hair, deep blue eyes, light olive skin, athletic lean build. Wears a form-fitting dark grey spacesuit with subtle silver trim on the shoulders and collar, a black utility belt with a silver buckle, and sturdy black combat boots. No visible branding on the suit. Kosmos, a Sphynx cat, has hairless light pinkish-grey velvety skin, striking emerald green eyes, and... + +Generated at: 2026-04-03 22:50:58 diff --git a/comics/void-cats-apple/void-cats-apple_story.txt b/comics/void-cats-apple/void-cats-apple_story.txt new file mode 100644 index 0000000..5861ade --- /dev/null +++ b/comics/void-cats-apple/void-cats-apple_story.txt @@ -0,0 +1,27 @@ +На мостика на космическия кораб „Звезден Скитник“ цареше напрегнато мълчание. Капитан Алекс, с поглед вперен в основния екран, едва чуваше равномерното бръмчене на двигателите. До краката му, на една от празните контролни конзоли, седеше Космос – неговата 1. **котка**. Сфинксът, с кожа като кадифе и големи зелени очи, беше необичаен спътник за капитан на звездолет, но Алекс вярваше в нейните инстинкти. Откакто бяха напуснали последния аванпост, Космос беше неспокойна, мяукаше тихо и въртеше нервно глава. + +„Капитане, наближаваме аномалията“, докладва първият офицер Лира, гласът ѝ прекъсвайки тишината. „Сензорите отчитат огромна структура, неорганична, на около 2000 км от нас. Не е обозначена на нито една карта.“ + +Алекс кимна. „Пригответе се за скачване. Само аз и Космос ще се спуснем.“ + +Лира повдигна вежди. „Космос, сър? Експедицията е рискована.“ + +„Тя винаги ме е пазила“, отвърна Алекс, не откъсвайки поглед от приближаващата се структура – колосален, черен обект, наподобяващ древен, забравен храм, реещ се в бездната. + +След като се скачиха успешно с шлюз, Алекс и Космос преминаха през въздушния шлюз. Вътрешността беше мрачна и потискаща, осветена от редки, пулсиращи зелени светлини. Космос се притисна плътно до крака на Алекс, очите ѝ бяха разширени. Атмосферата беше дишаща, но въздухът беше тежък, със странен метален привкус. + +„Сканирам за живот“, прошепна Алекс в комуникатора си. „Нищо. Пълна тишина.“ + +Внезапно Космос издаде ниско ръмжене – звук, който Алекс рядко беше чувал от нея. Тя се изправи на задни лапи и зашипя към една от огромните, напълно черни стени. Точно тогава, зелените светлини започнаха да пулсират по-бързо, а сензорите на Алекс изпискаха тревожно. Стената пред тях започна да се разтваря, разкривайки коридор, водещ към още по-дълбоки мрежи от тунели. + +От мрака изникнаха сенки – високи, призрачни фигури с множество крайници. Не приличаха на нищо, което Алекс някога беше виждал. Те издаваха пронизителен, свистящ звук, който караше ушите му да бучат. + +„Алекс, какво става там?“, чу се паникьосаният глас на Лира по комуникатора. + +„Изглежда, че не сме сами…“ Алекс бързо извади бластерното си оръжие, но знаеше, че е превъзхождан. Космос скочи от него и се втурна към една от фигурите, издавайки яростно съскане. За изненада на Алекс, съществото отстъпи леко, сякаш изненадано от малкото животно. + +В този миг на отвличане на вниманието, Алекс забеляза малка ниша в стената, която се затваряше. Трябваше му секунда. Той се сети за 2. **ябълка**, която беше оставил в джоба си, предназначен за бърз обяд. Издърпа я, хвърли я с всичка сила в противоположната посока на нишата, към друг тунел. Звукът от удара привлече вниманието на две от съществата. + +„Космос, при мен!“ извика Алекс, грабвайки котката, докато тичаше към нишата. Тя се затвори почти веднага след като се плъзнаха вътре. От другата страна се чуваха удари и пронизителни писъци, но стената издържаше. + +„Това беше… близо“, промълви Алекс, прегръщайки треперещата котка. „Добро момиче. Понякога и една ябълка може да спаси деня.“ Те бяха в капан, но поне живи, за момента. И Космос, както винаги, беше била права. diff --git a/internal/cli/command.go b/internal/cli/command.go index ff96009..6f0b2a2 100644 --- a/internal/cli/command.go +++ b/internal/cli/command.go @@ -67,6 +67,7 @@ func setupFlags(cmd *cobra.Command, flags *Flags) { cmd.Flags().StringVar(&flags.BatchFile, "batch", "", "Process words from file (one per line)") cmd.Flags().StringVar(&flags.StoryFile, "story", "", "Generate a vocabulary story + comic image from a batch-format file (outputs to current directory)") cmd.Flags().StringVar(&flags.StoryStyle, "story-style", "", "Art style for comic pages (default: random). E.g. \"ultra realistic comic strip with photographic detail and dramatic lighting\"") + cmd.Flags().StringVar(&flags.StoryTheme, "story-theme", "", "Genre/theme for the story (default: random). E.g. \"a thrilling space adventure with aliens and spaceships\"") cmd.Flags().StringVar(&flags.NarratorVoice, "narrator-voice", "", "Gemini voice for cinematic story narration (default: random from cinematic pool). "+ "Valid values: Charon, Fenrir, Enceladus, Algieba, Aoede, Schedar") diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 30e3854..b9d28c9 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -22,6 +22,7 @@ type Flags struct { BatchFile string StoryFile string // --story : generate vocabulary story + comic image StoryStyle string // --story-style: override the random art style (empty = random) + StoryTheme string // --story-theme: override the random genre pick (empty = random) NarratorVoice string // --narrator-voice: Gemini voice for cinematic narration (empty = random) SkipAudio bool SkipImages bool diff --git a/internal/image/nanobanana.go b/internal/image/nanobanana.go index 8bcd945..befe74c 100644 --- a/internal/image/nanobanana.go +++ b/internal/image/nanobanana.go @@ -114,7 +114,16 @@ func (c *NanoBananaClient) Search(ctx context.Context, opts *SearchOptions) ([]S fmt.Printf("Nano Banana Image Generation Prompt (%d chars): %s\n", len(prompt), prompt) fmt.Printf("Nano Banana Image Generation: Using model '%s' with aspect ratio '%s'\n", c.modelName(), aspectRatio) - imageBytes, mimeType, err := nanoBananaGenerateImage(ctx, c, prompt, aspectRatio) + var imageBytes []byte + var mimeType string + // Use multimodal chaining when reference images are available — this keeps + // character appearance consistent across pages far more reliably than + // injecting a text-only character bible. + if len(opts.ReferenceImages) > 0 { + imageBytes, mimeType, err = c.generateImageWithRefs(ctx, prompt, aspectRatio, opts.ReferenceImages) + } else { + imageBytes, mimeType, err = nanoBananaGenerateImage(ctx, c, prompt, aspectRatio) + } if err != nil { if searchErr, ok := err.(*SearchError); ok { return nil, searchErr @@ -420,6 +429,61 @@ func (c *NanoBananaClient) generateImage(ctx context.Context, prompt, aspectRati return imageBytes, mimeType, nil } +// generateImageWithRefs sends reference images alongside the text prompt so the +// model can match character appearance from the existing pages. This implements +// the iterative chaining technique: each new page is conditioned on the visual +// look established by previous pages rather than relying on text descriptions alone. +func (c *NanoBananaClient) generateImageWithRefs(ctx context.Context, prompt, aspectRatio string, refs [][]byte) ([]byte, string, error) { + if aspectRatio == "" { + aspectRatio = nanoBananaAspectRatio + } + cfg := &genai.GenerateContentConfig{ + ResponseModalities: []string{string(genai.ModalityImage)}, + ImageConfig: &genai.ImageConfig{AspectRatio: aspectRatio}, + } + + // Build multimodal content: reference image bytes first, then the instruction + // + prompt text. Leading with images means they are processed before the text. + parts := make([]*genai.Part, 0, len(refs)+1) + for _, ref := range refs { + if len(ref) > 0 { + parts = append(parts, &genai.Part{ + InlineData: &genai.Blob{MIMEType: "image/png", Data: ref}, + }) + } + } + refNote := fmt.Sprintf( + "The %d reference image(s) above show the EXACT character appearance that must be preserved. "+ + "Every character — same face, same age, same hair, same clothing, same animal breed and markings — "+ + "must look IDENTICAL in the new image. Now generate:\n\n", + len(refs), + ) + parts = append(parts, &genai.Part{Text: refNote + prompt}) + + resp, err := c.client.Models.GenerateContent(ctx, c.modelName(), + []*genai.Content{{Role: string(genai.RoleUser), Parts: parts}}, + cfg, + ) + if err != nil { + return nil, "", &SearchError{ + Provider: nanoBananaSource, + Code: "API_ERROR", + Message: fmt.Sprintf("failed to generate image with refs: %v", err), + } + } + + imageBytes, mimeType, err := extractGeneratedImage(resp) + if err != nil { + return nil, "", &SearchError{ + Provider: nanoBananaSource, + Code: "NO_RESULTS", + Message: err.Error(), + } + } + + return imageBytes, mimeType, nil +} + func extractGeneratedImage(response *genai.GenerateContentResponse) ([]byte, string, error) { if response == nil { return nil, "", fmt.Errorf("no response from Gemini") diff --git a/internal/image/search.go b/internal/image/search.go index 8de97e0..80d5e33 100644 --- a/internal/image/search.go +++ b/internal/image/search.go @@ -27,8 +27,13 @@ type SearchOptions struct { Page int // Page number (1-based) ImageType string // Type: "photo", "illustration", "vector", "all" Orientation string // Orientation: "horizontal", "vertical", "all" - CustomPrompt string // Custom prompt for AI image generation - AspectRatio string // Override aspect ratio (e.g. "9:16"); empty = provider default + CustomPrompt string // Custom prompt for AI image generation + AspectRatio string // Override aspect ratio (e.g. "9:16"); empty = provider default + // ReferenceImages holds raw PNG bytes of previously generated images. + // When non-empty, the NanoBanana client sends them as multimodal content + // alongside the text prompt so the model can match character appearance + // across pages (iterative chaining technique). + ReferenceImages [][]byte } // DefaultSearchOptions returns sensible defaults for Bulgarian word searches diff --git a/internal/story/artist.go b/internal/story/artist.go index c76638c..e40bb6a 100644 --- a/internal/story/artist.go +++ b/internal/story/artist.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/rand/v2" + "os" "strings" "time" @@ -16,9 +17,10 @@ const ( // storyPageCount is the number of 9-panel story pages (excluding cover/back). storyPageCount = 3 - // comicPageAspectRatio: portrait (3:4) matches a standard comic book page - // and gives the model room for a 3×3 panel grid. - comicPageAspectRatio = "3:4" + // comicPageAspectRatio: 16:9 is the closest supported widescreen ratio for + // the ThinkPad X1 Gen 9 (2560×1600 / 16:10), filling the display with minimal + // letterboxing. The API supports 16:9 but not 16:10. + comicPageAspectRatio = "16:9" // comicPromptMaxChars caps each page's story excerpt in the NanoBanana prompt. comicPromptMaxChars = 900 @@ -63,11 +65,11 @@ var comicStyles = []string{ // gemini-2.5-flash allocates its thinking budget correctly instead of returning empty. const bibleSystemInstruction = `You are a comic-book art director producing a CHARACTER CONSISTENCY GUIDE in English for an illustrator. -For every named HUMAN character provide: name, EXACT age (e.g. "8-year-old girl", "65-year-old woman"), -hair (colour + style), eye colour, skin tone, build, and the EXACT clothing they wear — -specify garment, colour, pattern, and fit. +For every named HUMAN character provide: name, apparent age category (young child, teenager, +young adult, middle-aged, elderly), hair (colour + style), eye colour, skin tone, build, +and the EXACT clothing they wear — specify garment, colour, pattern, and fit. The character's apparent age MUST NOT change across any panel, page, cover, or back cover — -they must always look the same age. Clothing must NOT change between panels unless the story +they must always look the same. Clothing must NOT change between panels unless the story explicitly describes a change; if no change is described, list the same outfit for all appearances. For every named ANIMAL character provide: name, species, exact breed, fur/feather/scale colour @@ -131,50 +133,62 @@ func NewArtist(config *ArtistConfig) *Artist { } // DrawComicPages generates 5 images total: -// - comic_cover.png — full-bleed cover -// - comic_page_1.png … comic_page_3.png — 9-panel (3×3) story pages -// - comic_back.png — back cover +// - _cover.png — full-bleed cover +// - _page_1.png … _3 — 9-panel (3×3) story pages +// - _back.png — back cover // -// A character bible is built first and injected into every prompt so -// characters, clothing, and setting stay consistent across all pages. +// A character bible injected into every prompt keeps characters, clothing, and +// setting consistent across all pages. The bible is produced by GenerateFull in +// the same Gemini call as the story; prebuiltBible is passed in from there. +// titleSlug is used as the file-name prefix; it must already be a safe slug. // Returns the list of saved image paths in order. -func (a *Artist) DrawComicPages(storyText string) ([]string, error) { +func (a *Artist) DrawComicPages(storyText, prebuiltBible, titleSlug string) ([]string, error) { style := a.style if style == "" { style = pickStyle() } fmt.Printf(" Comic style: %s\n", style) - bible, blurb := a.buildHelperTexts(storyText) + bible, blurb := a.resolveHelperTexts(storyText, prebuiltBible) var paths []string + // recentRefs holds image bytes from recently generated pages for iterative + // chaining: each new page receives the cover + the previous page as visual + // reference so the model can match character appeara