summaryrefslogtreecommitdiff
path: root/internal/gui
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-07-16 21:44:28 +0300
committerPaul Buetow <paul@buetow.org>2025-07-16 21:44:28 +0300
commite2e75315e5e7c3eaccdc38881bd2fa669bb9dda5 (patch)
treec751e8c9b4d1efd858310fba54451e791c67737b /internal/gui
parent6b0b4c9ce28b88ab0abbff03c5f367d89ba97c89 (diff)
only one img per card
Diffstat (limited to 'internal/gui')
-rw-r--r--internal/gui/app.go52
-rw-r--r--internal/gui/generator.go32
-rw-r--r--internal/gui/navigation.go20
-rw-r--r--internal/gui/queue.go6
4 files changed, 53 insertions, 57 deletions
diff --git a/internal/gui/app.go b/internal/gui/app.go
index 836ebca..3a8dd33 100644
--- a/internal/gui/app.go
+++ b/internal/gui/app.go
@@ -51,7 +51,7 @@ type Application struct {
// State management
currentWord string
currentAudioFile string
- currentImages []string
+ currentImage string
currentTranslation string
currentJobID int
savedCards []anki.Card
@@ -81,7 +81,6 @@ type Config struct {
OutputDir string
AudioFormat string
ImageProvider string
- ImagesPerWord int
EnableCache bool
OpenAIKey string
PixabayKey string
@@ -94,7 +93,6 @@ func DefaultConfig() *Config {
OutputDir: "./anki_cards",
AudioFormat: "mp3",
ImageProvider: "openai",
- ImagesPerWord: 1,
EnableCache: true,
}
}
@@ -361,7 +359,7 @@ func (a *Application) generateMaterials(word string) {
// Get custom prompt from UI
customPrompt := a.imagePromptEntry.Text
- images, err := a.generateImagesWithPrompt(word, customPrompt)
+ imageFile, err := a.generateImagesWithPrompt(word, customPrompt)
a.decrementProcessing() // Image processing ends
if err != nil {
@@ -371,10 +369,12 @@ func (a *Application) generateMaterials(word string) {
})
return
}
- a.currentImages = images
- fyne.Do(func() {
- a.imageDisplay.SetImages(images)
- })
+ if imageFile != "" {
+ a.currentImage = imageFile
+ fyne.Do(func() {
+ a.imageDisplay.SetImages([]string{imageFile})
+ })
+ }
// Enable action buttons
fyne.Do(func() {
@@ -388,12 +388,12 @@ func (a *Application) generateMaterials(word string) {
// onKeepAndContinue saves the current card and clears for a new word
func (a *Application) onKeepAndContinue() {
// Check if we have a complete word to save
- if a.currentWord != "" && a.currentAudioFile != "" && len(a.currentImages) > 0 {
+ if a.currentWord != "" && a.currentAudioFile != "" && a.currentImage != "" {
// Save current card
card := anki.Card{
Bulgarian: a.currentWord,
AudioFile: a.currentAudioFile,
- ImageFiles: a.currentImages,
+ ImageFile: a.currentImage,
Translation: a.currentTranslation,
}
@@ -457,16 +457,18 @@ func (a *Application) onRegenerateImage() {
defer a.wg.Done()
defer a.decrementProcessing() // Image processing ends
- images, err := a.generateImagesWithPrompt(a.currentWord, customPrompt)
+ imageFile, err := a.generateImagesWithPrompt(a.currentWord, customPrompt)
if err != nil {
fyne.Do(func() {
a.showError(fmt.Errorf("Image regeneration failed: %w", err))
})
} else {
- a.currentImages = images
- fyne.Do(func() {
- a.imageDisplay.SetImages(images)
- })
+ if imageFile != "" {
+ a.currentImage = imageFile
+ fyne.Do(func() {
+ a.imageDisplay.SetImages([]string{imageFile})
+ })
+ }
}
fyne.Do(func() {
@@ -734,7 +736,7 @@ func (a *Application) processWordJob(job *WordJob) {
})
// Use the custom prompt from the job
- imageFiles, err := a.generateImagesWithPrompt(job.Word, job.CustomPrompt)
+ imageFile, err := a.generateImagesWithPrompt(job.Word, job.CustomPrompt)
a.decrementProcessing() // Image processing ends
if err != nil {
@@ -749,18 +751,22 @@ func (a *Application) processWordJob(job *WordJob) {
a.updateStatus(fmt.Sprintf("Finalizing '%s'...", job.Word))
})
- a.queue.CompleteJob(job.ID, translation, audioFile, imageFiles)
+ a.queue.CompleteJob(job.ID, translation, audioFile, imageFile)
// Update UI with results if this is still the current job
a.mu.Lock()
if a.currentJobID == job.ID {
a.currentTranslation = translation
a.currentAudioFile = audioFile
- a.currentImages = imageFiles
+ if imageFile != "" {
+ a.currentImage = imageFile
+ }
fyne.Do(func() {
a.translationText.SetText(fmt.Sprintf("%s = %s", job.Word, translation))
- a.imageDisplay.SetImages(imageFiles)
+ if imageFile != "" {
+ a.imageDisplay.SetImages([]string{imageFile})
+ }
a.audioPlayer.SetAudioFile(audioFile)
a.hideProgress()
a.setActionButtonsEnabled(true)
@@ -817,7 +823,7 @@ func (a *Application) onJobComplete(job *WordJob) {
// Only update UI if the current word is still empty (waiting for this job)
if job.Word == a.currentWord && job.ID != a.currentJobID {
// Check if the UI is still empty/waiting for content
- hasContent := a.currentAudioFile != "" || len(a.currentImages) > 0
+ hasContent := a.currentAudioFile != "" || a.currentImage != ""
if !hasContent {
// Update the UI with the completed results since it's still waiting
@@ -831,9 +837,9 @@ func (a *Application) onJobComplete(job *WordJob) {
a.audioPlayer.SetAudioFile(job.AudioFile)
a.regenerateAudioBtn.Enable()
}
- if len(job.ImageFiles) > 0 && len(a.currentImages) == 0 {
- a.currentImages = job.ImageFiles
- a.imageDisplay.SetImages(job.ImageFiles)
+ if job.ImageFile != "" && a.currentImage == "" {
+ a.currentImage = job.ImageFile
+ a.imageDisplay.SetImages([]string{job.ImageFile})
a.regenerateImageBtn.Enable()
}
diff --git a/internal/gui/generator.go b/internal/gui/generator.go
index 9738d88..7a6d26e 100644
--- a/internal/gui/generator.go
+++ b/internal/gui/generator.go
@@ -86,12 +86,12 @@ func (a *Application) generateAudio(word string) (string, error) {
}
// generateImages downloads images for a word
-func (a *Application) generateImages(word string) ([]string, error) {
+func (a *Application) generateImages(word string) (string, error) {
return a.generateImagesWithPrompt(word, "")
}
-// generateImagesWithPrompt downloads images for a word with optional custom prompt
-func (a *Application) generateImagesWithPrompt(word string, customPrompt string) ([]string, error) {
+// generateImagesWithPrompt downloads a single image for a word with optional custom prompt
+func (a *Application) generateImagesWithPrompt(word string, customPrompt string) (string, error) {
// Create image searcher based on provider
var searcher image.ImageSearcher
var err error
@@ -102,11 +102,11 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string)
case "unsplash":
if a.config.UnsplashKey == "" {
- return nil, fmt.Errorf("Unsplash API key is required")
+ return "", fmt.Errorf("Unsplash API key is required")
}
searcher, err = image.NewUnsplashClient(a.config.UnsplashKey)
if err != nil {
- return nil, err
+ return "", err
}
case "openai":
@@ -127,7 +127,7 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string)
}
default:
- return nil, fmt.Errorf("unknown image provider: %s", a.config.ImageProvider)
+ return "", fmt.Errorf("unknown image provider: %s", a.config.ImageProvider)
}
// Create downloader
@@ -147,20 +147,10 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string)
searchOpts.CustomPrompt = customPrompt
}
- // Download images
- var paths []string
-
- if a.config.ImagesPerWord == 1 {
- _, path, err := downloader.DownloadBestMatchWithOptions(a.ctx, searchOpts)
- if err != nil {
- return nil, err
- }
- paths = []string{path}
- } else {
- paths, err = downloader.DownloadMultipleWithOptions(a.ctx, searchOpts, a.config.ImagesPerWord)
- if err != nil {
- return nil, err
- }
+ // Download single image
+ _, path, err := downloader.DownloadBestMatchWithOptions(a.ctx, searchOpts)
+ if err != nil {
+ return "", err
}
// If using OpenAI, get the last used prompt and update the UI
@@ -175,7 +165,7 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string)
}
}
- return paths, nil
+ return path, nil
}
// saveAudioAttribution saves attribution info for generated audio
diff --git a/internal/gui/navigation.go b/internal/gui/navigation.go
index 05bf988..bc65c8f 100644
--- a/internal/gui/navigation.go
+++ b/internal/gui/navigation.go
@@ -175,7 +175,7 @@ func (a *Application) loadWordByIndex(index int) {
// Load from queue job
a.currentTranslation = job.Translation
a.currentAudioFile = job.AudioFile
- a.currentImages = job.ImageFiles
+ a.currentImage = job.ImageFile
fyne.Do(func() {
if job.Translation != "" {
@@ -184,8 +184,8 @@ func (a *Application) loadWordByIndex(index int) {
if job.AudioFile != "" {
a.audioPlayer.SetAudioFile(job.AudioFile)
}
- if len(job.ImageFiles) > 0 {
- a.imageDisplay.SetImages(job.ImageFiles)
+ if job.ImageFile != "" {
+ a.imageDisplay.SetImages([]string{job.ImageFile})
}
a.updateStatus(fmt.Sprintf("Loaded from queue: %s", word))
})
@@ -234,8 +234,8 @@ func (a *Application) loadExistingFiles(word string) {
})
}
- // Load image files
- a.currentImages = []string{}
+ // Load image file
+ a.currentImage = ""
// Try to find images with different patterns
patterns := []string{
fmt.Sprintf("%s.jpg", sanitized),
@@ -249,20 +249,20 @@ func (a *Application) loadExistingFiles(word string) {
for _, pattern := range patterns {
imagePath := filepath.Join(a.config.OutputDir, pattern)
if _, err := os.Stat(imagePath); err == nil {
- a.currentImages = append(a.currentImages, imagePath)
+ a.currentImage = imagePath
break // Just load the first image found
}
}
- if len(a.currentImages) > 0 {
+ if a.currentImage != "" {
fyne.Do(func() {
- a.imageDisplay.SetImages(a.currentImages)
+ a.imageDisplay.SetImages([]string{a.currentImage})
})
// Try to load the prompt from attribution file if using OpenAI
- if a.config.ImageProvider == "openai" && len(a.currentImages) > 0 {
+ if a.config.ImageProvider == "openai" {
// Look for attribution file
- baseImagePath := a.currentImages[0]
+ baseImagePath := a.currentImage
attrPath := strings.TrimSuffix(baseImagePath, filepath.Ext(baseImagePath)) + "_attribution.txt"
if data, err := os.ReadFile(attrPath); err == nil {
// Parse prompt from attribution file
diff --git a/internal/gui/queue.go b/internal/gui/queue.go
index aaa0c55..df39908 100644
--- a/internal/gui/queue.go
+++ b/internal/gui/queue.go
@@ -13,7 +13,7 @@ type WordJob struct {
Word string
Translation string
AudioFile string
- ImageFiles []string
+ ImageFile string // Changed from ImageFiles []string to single image
Status JobStatus
Error error
StartedAt time.Time
@@ -194,7 +194,7 @@ func (q *WordQueue) Stop() {
}
// CompleteJob marks a job as completed with results
-func (q *WordQueue) CompleteJob(jobID int, translation, audioFile string, imageFiles []string) {
+func (q *WordQueue) CompleteJob(jobID int, translation, audioFile, imageFile string) {
q.mu.Lock()
defer q.mu.Unlock()
@@ -202,7 +202,7 @@ func (q *WordQueue) CompleteJob(jobID int, translation, audioFile string, imageF
job.Status = StatusCompleted
job.Translation = translation
job.AudioFile = audioFile
- job.ImageFiles = imageFiles
+ job.ImageFile = imageFile
job.CompletedAt = time.Now()
delete(q.processing, jobID)