From 59f615cdb0a0a30b702ab810013351bd97fdc408 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 3 Apr 2026 13:22:19 +0300 Subject: feat: single tall comic strip with consistent characters and more dramatic 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 --- internal/image/nanobanana.go | 21 +++++++++++++++------ internal/image/nanobanana_test.go | 14 +++++++------- internal/image/search.go | 1 + 3 files changed, 23 insertions(+), 13 deletions(-) (limited to 'internal/image') diff --git a/internal/image/nanobanana.go b/internal/image/nanobanana.go index 4e99dfa..8bcd945 100644 --- a/internal/image/nanobanana.go +++ b/internal/image/nanobanana.go @@ -56,8 +56,8 @@ var newNanoBananaClient = genai.NewClient var nanoBananaGenerateText = func(ctx context.Context, c *NanoBananaClient, model, systemPrompt, userPrompt string, temperature float32, maxOutputTokens int32) (string, error) { return c.generateText(ctx, model, systemPrompt, userPrompt, temperature, maxOutputTokens) } -var nanoBananaGenerateImage = func(ctx context.Context, c *NanoBananaClient, prompt string) ([]byte, string, error) { - return c.generateImage(ctx, prompt) +var nanoBananaGenerateImage = func(ctx context.Context, c *NanoBananaClient, prompt, aspectRatio string) ([]byte, string, error) { + return c.generateImage(ctx, prompt, aspectRatio) } // NewNanoBananaClient creates a new Nano Banana client. @@ -105,10 +105,16 @@ func (c *NanoBananaClient) Search(ctx context.Context, opts *SearchOptions) ([]S c.PromptCallback(prompt) } + // Resolve aspect ratio: use caller override if provided, else the default. + aspectRatio := nanoBananaAspectRatio + if opts.AspectRatio != "" { + aspectRatio = opts.AspectRatio + } + 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(), nanoBananaAspectRatio) + fmt.Printf("Nano Banana Image Generation: Using model '%s' with aspect ratio '%s'\n", c.modelName(), aspectRatio) - imageBytes, mimeType, err := nanoBananaGenerateImage(ctx, c, prompt) + imageBytes, mimeType, err := nanoBananaGenerateImage(ctx, c, prompt, aspectRatio) if err != nil { if searchErr, ok := err.(*SearchError); ok { return nil, searchErr @@ -380,11 +386,14 @@ func (c *NanoBananaClient) generateText(ctx context.Context, model, systemPrompt return text, nil } -func (c *NanoBananaClient) generateImage(ctx context.Context, prompt string) ([]byte, string, error) { +func (c *NanoBananaClient) generateImage(ctx context.Context, prompt, aspectRatio string) ([]byte, string, error) { + if aspectRatio == "" { + aspectRatio = nanoBananaAspectRatio + } cfg := &genai.GenerateContentConfig{ ResponseModalities: []string{string(genai.ModalityImage)}, ImageConfig: &genai.ImageConfig{ - AspectRatio: nanoBananaAspectRatio, + AspectRatio: aspectRatio, }, } diff --git a/internal/image/nanobanana_test.go b/internal/image/nanobanana_test.go index 88cc43c..c9cfb61 100644 --- a/internal/image/nanobanana_test.go +++ b/internal/image/nanobanana_test.go @@ -71,7 +71,7 @@ func TestNanoBananaClient_Search_CustomPromptSkipsTextGeneration(t *testing.T) { } var gotPrompt string - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) { gotPrompt = prompt return mustPNGBytes(t), "image/png", nil } @@ -149,7 +149,7 @@ func TestNanoBananaClient_Search_GeneratedPromptFlow(t *testing.T) { return "", nil } - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) { gotPrompt = prompt return mustJPEGBytes(t), "image/jpeg", nil } @@ -249,7 +249,7 @@ func TestNanoBananaClient_Search_TranslationFailureFallsBackToQuery(t *testing.T return "", nil } - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) { return mustPNGBytes(t), "image/png", nil } @@ -297,7 +297,7 @@ func TestNanoBananaClient_Search_TrivialSceneFallsBackToSubjectPrompt(t *testing } var gotPrompt string - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) { gotPrompt = prompt return mustPNGBytes(t), "image/png", nil } @@ -342,7 +342,7 @@ func TestNanoBananaClient_Search_ImageGenerationError(t *testing.T) { return "", nil } - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _ string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _, _ string) ([]byte, string, error) { return nil, "", fmt.Errorf("image generation failed") } @@ -376,7 +376,7 @@ func TestNanoBananaClient_Search_CustomPromptPreservesTranslationMetadata(t *tes t.Fatal("unexpected text generation for custom prompt") return "", nil } - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _ string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _, _ string) ([]byte, string, error) { return mustPNGBytes(t), "image/png", nil } @@ -409,7 +409,7 @@ func TestNanoBananaClient_Search_CustomPromptIgnoresWhitespaceTranslation(t *tes t.Fatal("unexpected text generation for custom prompt") return "", nil } - nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _ string) ([]byte, string, error) { + nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _, _ string) ([]byte, string, error) { return mustPNGBytes(t), "image/png", nil } diff --git a/internal/image/search.go b/internal/image/search.go index be20731..8de97e0 100644 --- a/internal/image/search.go +++ b/internal/image/search.go @@ -28,6 +28,7 @@ type SearchOptions struct { 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 } // DefaultSearchOptions returns sensible defaults for Bulgarian word searches -- cgit v1.2.3