diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-06 11:06:19 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-06 11:06:19 +0300 |
| commit | 05f54cc0cb8cf3535698ab5027d200842bdb28e3 (patch) | |
| tree | 30b7e778be29db63301119b7cfa8be5a5edc041e /internal/gui | |
| parent | 23160bce9a18a70080a85dda6e9c654499aba7f7 (diff) | |
refactor: consolidate provider factory test seams into shared named types
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>
Diffstat (limited to 'internal/gui')
| -rw-r--r-- | internal/gui/app.go | 20 | ||||
| -rw-r--r-- | internal/gui/generator.go | 8 | ||||
| -rw-r--r-- | internal/gui/generator_test.go | 4 | ||||
| -rw-r--r-- | internal/gui/orchestrator.go | 46 |
4 files changed, 39 insertions, 39 deletions
diff --git a/internal/gui/app.go b/internal/gui/app.go index 126a50b..e1895a4 100644 --- a/internal/gui/app.go +++ b/internal/gui/app.go @@ -109,9 +109,10 @@ type Application struct { // Injectable factory functions — replaced in tests to avoid real API calls. // These are kept on Application so tests can set them before construction // of the orchestrator; New() copies them into the orchestrator. - newOpenAIImageClient func(*image.OpenAIConfig) promptAwareImageClient - newNanoBananaImageClient func(*image.NanoBananaConfig) promptAwareImageClient - newAudioProvider func(*audio.Config) (audio.Provider, error) + // imageFactories uses the shared image.ClientFactories type so the factory + // signatures are defined once in the image package rather than duplicated here. + imageFactories image.ClientFactories + newAudioProvider audio.ProviderFactory // Service layer — decoupled from the UI event-wiring in Application. cardSvc *CardService // file discovery, directory management, persistence @@ -200,9 +201,10 @@ func New(config *Config) *Application { autoPlayEnabled: config.AutoPlay, // Production-default factory functions; replaced in tests. - newOpenAIImageClient: func(c *image.OpenAIConfig) promptAwareImageClient { return image.NewOpenAIClient(c) }, - newNanoBananaImageClient: func(c *image.NanoBananaConfig) promptAwareImageClient { return image.NewNanoBananaClient(c) }, - newAudioProvider: audio.NewProvider, + // image.DefaultClientFactories() is the single source of truth for the + // image factory signatures shared with the processor package. + imageFactories: image.DefaultClientFactories(), + newAudioProvider: audio.NewProvider, } a.initAppServices(config) @@ -285,8 +287,7 @@ func (a *Application) initAppServices(config *Config) { a.audioConfig, a.phoneticFetcher, a.translator, - a.newOpenAIImageClient, - a.newNanoBananaImageClient, + a.imageFactories, a.newAudioProvider, ) } @@ -368,8 +369,7 @@ func (a *Application) getOrchestrator() *GenerationOrchestrator { a.audioConfig, a.phoneticFetcher, a.translator, - a.newOpenAIImageClient, - a.newNanoBananaImageClient, + a.imageFactories, a.newAudioProvider, ) } diff --git a/internal/gui/generator.go b/internal/gui/generator.go index 312baba..153b365 100644 --- a/internal/gui/generator.go +++ b/internal/gui/generator.go @@ -6,16 +6,8 @@ import ( "time" "codeberg.org/snonux/totalrecall/internal/audio" - "codeberg.org/snonux/totalrecall/internal/image" ) -// promptAwareImageClient extends ImageClient with prompt-callback support -// used by the GUI to capture and display the last generated image prompt. -type promptAwareImageClient interface { - image.ImageClient - SetPromptCallback(func(prompt string)) -} - // randomVoice picks a random voice from the provided list. // Used by GenerationOrchestrator for both OpenAI and Gemini voice selection. func randomVoice(voices []string) string { diff --git a/internal/gui/generator_test.go b/internal/gui/generator_test.go index c8bcf42..40c0e2c 100644 --- a/internal/gui/generator_test.go +++ b/internal/gui/generator_test.go @@ -107,7 +107,7 @@ func TestGenerateImagesWithPromptUsesNanoBananaProvider(t *testing.T) { }, currentWord: "друго", } - app.newNanoBananaImageClient = func(config *image.NanoBananaConfig) promptAwareImageClient { + app.imageFactories.NewNanoBananaClient = func(config *image.NanoBananaConfig) image.PromptAwareClient { capturedConfig = &image.NanoBananaConfig{ APIKey: config.APIKey, Model: config.Model, @@ -115,7 +115,7 @@ func TestGenerateImagesWithPromptUsesNanoBananaProvider(t *testing.T) { } return fakeClient } - app.newOpenAIImageClient = func(*image.OpenAIConfig) promptAwareImageClient { + app.imageFactories.NewOpenAIClient = func(*image.OpenAIConfig) image.PromptAwareClient { t.Fatal("unexpected OpenAI image client construction") return nil } diff --git a/internal/gui/orchestrator.go b/internal/gui/orchestrator.go index 5081a98..2861c4f 100644 --- a/internal/gui/orchestrator.go +++ b/internal/gui/orchestrator.go @@ -19,37 +19,42 @@ import ( // GenerationOrchestrator coordinates audio, image, and phonetics generation // for a single card. It holds all injectable factory functions so tests can // substitute fakes without touching the UI layer. +// image.ClientFactories groups the two image-factory functions so the field +// definitions are not duplicated between this type and processor.Processor. type GenerationOrchestrator struct { config *Config audioConfig *audio.Config phonetics *phonetic.Fetcher translator *translation.Translator - // Injectable factory functions — replaced in tests to avoid real API calls. - newOpenAIImageClient func(*image.OpenAIConfig) promptAwareImageClient - newNanoBananaImageClient func(*image.NanoBananaConfig) promptAwareImageClient - newAudioProvider func(*audio.Config) (audio.Provider, error) + // imageFactories groups the two image-provider construction functions. + // Production code uses image.DefaultClientFactories(); tests replace fields. + imageFactories image.ClientFactories + + // newAudioProvider constructs an audio.Provider from a Config. + // Production code uses audio.NewProvider; tests replace it with a fake. + newAudioProvider audio.ProviderFactory } // NewGenerationOrchestrator constructs an orchestrator wired to the given app -// configuration and service dependencies. +// configuration and service dependencies. imageFactories and newAudio are the +// injectable test seams — pass image.DefaultClientFactories() and +// audio.NewProvider for production behaviour. func NewGenerationOrchestrator( config *Config, audioCfg *audio.Config, phonetics *phonetic.Fetcher, translator *translation.Translator, - newOpenAI func(*image.OpenAIConfig) promptAwareImageClient, - newNanoBanana func(*image.NanoBananaConfig) promptAwareImageClient, - newAudio func(*audio.Config) (audio.Provider, error), + imageFactories image.ClientFactories, + newAudio audio.ProviderFactory, ) *GenerationOrchestrator { return &GenerationOrchestrator{ - config: config, - audioConfig: audioCfg, - phonetics: phonetics, - translator: translator, - newOpenAIImageClient: newOpenAI, - newNanoBananaImageClient: newNanoBanana, - newAudioProvider: newAudio, + config: config, + audioConfig: audioCfg, + phonetics: phonetics, + translator: translator, + imageFactories: imageFactories, + newAudioProvider: newAudio, } } @@ -478,8 +483,11 @@ func (o *GenerationOrchestrator) imagePromptCallback(cardDir, word string) func( } // newImageSearcher constructs the appropriate image client based on the -// configured image provider. -func (o *GenerationOrchestrator) newImageSearcher() (promptAwareImageClient, error) { +// configured image provider. Returns image.PromptAwareClient so callers can +// call SetPromptCallback directly without a type-assertion. The factory +// functions are sourced from imageFactories (the shared image.ClientFactories +// value) to avoid duplicating the factory signatures in this package. +func (o *GenerationOrchestrator) newImageSearcher() (image.PromptAwareClient, error) { switch o.config.ImageProvider { case imageProviderOpenAI: if o.config.OpenAIKey == "" { @@ -494,7 +502,7 @@ func (o *GenerationOrchestrator) newImageSearcher() (promptAwareImageClient, err Style: "natural", } - return o.newOpenAIImageClient(openaiConfig), nil + return o.imageFactories.NewOpenAIClient(openaiConfig), nil case imageProviderNanoBanana: cfg := o.config @@ -511,7 +519,7 @@ func (o *GenerationOrchestrator) newImageSearcher() (promptAwareImageClient, err TextModel: cfg.NanoBananaTextModel, } - return o.newNanoBananaImageClient(nanoBananaConfig), nil + return o.imageFactories.NewNanoBananaClient(nanoBananaConfig), nil default: return nil, fmt.Errorf("unknown image provider: %s", o.config.ImageProvider) |
