summaryrefslogtreecommitdiff
path: root/internal/gui
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-02 07:56:31 +0300
committerPaul Buetow <paul@buetow.org>2026-04-02 07:56:31 +0300
commitb7302580d840fc3d7e3d127df49d79ef12d79d9c (patch)
tree458132230aa1337be76701cb7af6f8c26b3e0d55 /internal/gui
parent0f2b16d1ccf461b706dd426727ed188c4ea3b60a (diff)
Align GUI and CLI provider wiring
Diffstat (limited to 'internal/gui')
-rw-r--r--internal/gui/app.go13
-rw-r--r--internal/gui/app_test.go7
-rw-r--r--internal/gui/generator.go16
-rw-r--r--internal/gui/generator_test.go17
4 files changed, 47 insertions, 6 deletions
diff --git a/internal/gui/app.go b/internal/gui/app.go
index 7c9ecd7..a69b178 100644
--- a/internal/gui/app.go
+++ b/internal/gui/app.go
@@ -24,6 +24,7 @@ import (
"codeberg.org/snonux/totalrecall/internal/anki"
"codeberg.org/snonux/totalrecall/internal/archive"
"codeberg.org/snonux/totalrecall/internal/audio"
+ "codeberg.org/snonux/totalrecall/internal/image"
"codeberg.org/snonux/totalrecall/internal/phonetic"
"codeberg.org/snonux/totalrecall/internal/translation"
)
@@ -114,6 +115,10 @@ type Config struct {
ImageProvider string
OpenAIKey string
GoogleAPIKey string
+ // NanoBananaModel selects the Gemini image model for Nano Banana generation.
+ NanoBananaModel string
+ // NanoBananaTextModel selects the Gemini text model for Nano Banana prompt generation.
+ NanoBananaTextModel string
// GeminiTTSModel selects the Gemini TTS model when Gemini audio is active.
GeminiTTSModel string
// GeminiVoice selects a specific Gemini voice; empty uses the model default.
@@ -139,6 +144,8 @@ func DefaultConfig() *Config {
OutputDir: outputDir,
AudioFormat: audioDefaults.OutputFormat,
AudioProvider: audioDefaults.Provider,
+ NanoBananaModel: image.DefaultNanoBananaModel,
+ NanoBananaTextModel: image.DefaultNanoBananaTextModel,
GeminiTTSModel: audioDefaults.GeminiTTSModel,
ImageProvider: imageProviderNanoBanana,
TranslationProvider: translation.ProviderOpenAI,
@@ -170,6 +177,12 @@ func New(config *Config) *Application {
if config.ImageProvider == "" {
config.ImageProvider = defaults.ImageProvider
}
+ if config.NanoBananaModel == "" {
+ config.NanoBananaModel = defaults.NanoBananaModel
+ }
+ if config.NanoBananaTextModel == "" {
+ config.NanoBananaTextModel = defaults.NanoBananaTextModel
+ }
if config.GeminiTTSModel == "" {
config.GeminiTTSModel = defaults.GeminiTTSModel
}
diff --git a/internal/gui/app_test.go b/internal/gui/app_test.go
index 06525b2..057c5e0 100644
--- a/internal/gui/app_test.go
+++ b/internal/gui/app_test.go
@@ -4,6 +4,7 @@ import (
"testing"
"codeberg.org/snonux/totalrecall/internal/audio"
+ "codeberg.org/snonux/totalrecall/internal/image"
"codeberg.org/snonux/totalrecall/internal/translation"
)
@@ -23,6 +24,12 @@ func TestDefaultConfigUsesOpenAITranslationProvider(t *testing.T) {
if config.AudioFormat != audioDefaults.OutputFormat {
t.Fatalf("DefaultConfig() audio format = %q, want %q", config.AudioFormat, audioDefaults.OutputFormat)
}
+ if config.NanoBananaModel != image.DefaultNanoBananaModel {
+ t.Fatalf("DefaultConfig() NanoBananaModel = %q, want %q", config.NanoBananaModel, image.DefaultNanoBananaModel)
+ }
+ if config.NanoBananaTextModel != image.DefaultNanoBananaTextModel {
+ t.Fatalf("DefaultConfig() NanoBananaTextModel = %q, want %q", config.NanoBananaTextModel, image.DefaultNanoBananaTextModel)
+ }
if config.GeminiTTSModel != audioDefaults.GeminiTTSModel {
t.Fatalf("DefaultConfig() GeminiTTSModel = %q, want %q", config.GeminiTTSModel, audioDefaults.GeminiTTSModel)
}
diff --git a/internal/gui/generator.go b/internal/gui/generator.go
index 704020d..7143cff 100644
--- a/internal/gui/generator.go
+++ b/internal/gui/generator.go
@@ -378,12 +378,18 @@ func (a *Application) newImageSearcher() (promptAwareImageClient, error) {
return newOpenAIImageClient(openaiConfig), nil
case imageProviderNanoBanana:
- if a.config.GoogleAPIKey == "" {
+ config := a.config
+ if config == nil {
+ config = DefaultConfig()
+ }
+ if config.GoogleAPIKey == "" {
return nil, fmt.Errorf("Google API key is required for image generation")
}
nanoBananaConfig := &image.NanoBananaConfig{
- APIKey: a.config.GoogleAPIKey,
+ APIKey: config.GoogleAPIKey,
+ Model: config.NanoBananaModel,
+ TextModel: config.NanoBananaTextModel,
}
return newNanoBananaImageClient(nanoBananaConfig), nil
@@ -487,7 +493,11 @@ func (a *Application) saveAudioMetadata(cardDir string, audioConfig audio.Config
}
fmt.Fprintf(&metadata, "model=%s\n", model)
}
- fmt.Fprintf(&metadata, "voice=%s\n", voice)
+ voiceLine := strings.TrimSpace(voice)
+ if voiceLine == "" && strings.ToLower(strings.TrimSpace(audioConfig.Provider)) == "gemini" {
+ voiceLine = "model-default"
+ }
+ fmt.Fprintf(&metadata, "voice=%s\n", voiceLine)
fmt.Fprintf(&metadata, "speed=%.2f\n", speed)
fmt.Fprintf(&metadata, "format=%s\n", audioConfig.OutputFormat)
fmt.Fprintf(&metadata, "cardtype=%s\n", cardType)
diff --git a/internal/gui/generator_test.go b/internal/gui/generator_test.go
index f9cb2a0..31b4aa2 100644
--- a/internal/gui/generator_test.go
+++ b/internal/gui/generator_test.go
@@ -107,9 +107,11 @@ func TestGenerateImagesWithPromptUsesNanoBananaProvider(t *testing.T) {
tempDir := t.TempDir()
app := &Application{
config: &Config{
- ImageProvider: imageProviderNanoBanana,
- GoogleAPIKey: "google-key",
- OutputDir: tempDir,
+ ImageProvider: imageProviderNanoBanana,
+ GoogleAPIKey: "google-key",
+ NanoBananaModel: "custom-image-model",
+ NanoBananaTextModel: "custom-text-model",
+ OutputDir: tempDir,
},
currentWord: "друго",
}
@@ -125,6 +127,12 @@ func TestGenerateImagesWithPromptUsesNanoBananaProvider(t *testing.T) {
if capturedConfig.APIKey != "google-key" {
t.Fatalf("Nano Banana API key = %q, want %q", capturedConfig.APIKey, "google-key")
}
+ if capturedConfig.Model != "custom-image-model" {
+ t.Fatalf("Nano Banana model = %q, want %q", capturedConfig.Model, "custom-image-model")
+ }
+ if capturedConfig.TextModel != "custom-text-model" {
+ t.Fatalf("Nano Banana text model = %q, want %q", capturedConfig.TextModel, "custom-text-model")
+ }
if fakeClient.searchOpts == nil {
t.Fatal("expected search options to be captured")
}
@@ -319,6 +327,9 @@ func TestGenerateAudioUsesGeminiModelDefaultVoiceAndAttribution(t *testing.T) {
if !strings.Contains(metadata, "audio_file=audio.wav") {
t.Fatalf("gemini metadata missing fresh audio file reference: %q", metadata)
}
+ if !strings.Contains(metadata, "voice=model-default") {
+ t.Fatalf("gemini metadata missing model-default voice marker: %q", metadata)
+ }
if !strings.Contains(metadata, "format=wav") {
t.Fatalf("gemini metadata missing format: %q", metadata)
}