diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-16 15:54:16 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-16 15:54:16 +0300 |
| commit | e49ecfe601c924fa68671477331a860acf8a62f7 (patch) | |
| tree | c57ad6ef1b982f7f0c50cd7b123a4a52c44d6b05 /internal/gui/navigation.go | |
| parent | 7187e7464f16a9d2991ba2da3c672fdb3cf5de72 (diff) | |
feat: add concurrent processing, queue system, and UI improvements
- Implement concurrent word processing with background queue
- Add processing statistics showing active tasks and total cards
- Enable circular navigation through cards
- Fix card deletion to update total count correctly
- Add version number to GUI window title
- Prevent unwanted UI reloads for background-processed cards
- Enable immediate audio playback before image generation completes
- Update 'Keep & Continue' button to 'New Word'
- Improve queue status tracking for all processing operations
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'internal/gui/navigation.go')
| -rw-r--r-- | internal/gui/navigation.go | 133 |
1 files changed, 114 insertions, 19 deletions
diff --git a/internal/gui/navigation.go b/internal/gui/navigation.go index f8931ba..f24dcc1 100644 --- a/internal/gui/navigation.go +++ b/internal/gui/navigation.go @@ -9,6 +9,8 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/dialog" + + "codeberg.org/snonux/totalrecall/internal/anki" ) // scanExistingWords scans the output directory for existing words @@ -62,53 +64,100 @@ func (a *Application) scanExistingWords() { // updateNavigation updates the navigation button states func (a *Application) updateNavigation() { - if len(a.existingWords) > 0 { + // Get all available words (existing + completed from queue) + allWords := a.getAllAvailableWords() + + if len(allWords) > 1 { + // Enable both buttons when there's more than one word (allows circular navigation) a.prevWordBtn.Enable() a.nextWordBtn.Enable() // Find current word index a.currentWordIndex = -1 - for i, word := range a.existingWords { + for i, word := range allWords { if word == a.currentWord { a.currentWordIndex = i break } } - - // Disable at boundaries - if a.currentWordIndex <= 0 { - a.prevWordBtn.Disable() - } - if a.currentWordIndex >= len(a.existingWords)-1 || a.currentWordIndex == -1 { - a.nextWordBtn.Disable() - } + } else if len(allWords) == 1 { + // With only one word, disable navigation + a.prevWordBtn.Disable() + a.nextWordBtn.Disable() } else { + // No words at all a.prevWordBtn.Disable() a.nextWordBtn.Disable() } } +// getAllAvailableWords returns all words (from disk and completed queue jobs) +func (a *Application) getAllAvailableWords() []string { + // Start with existing words from disk + words := make([]string, len(a.existingWords)) + copy(words, a.existingWords) + + // Add completed jobs from queue + completedJobs := a.queue.GetCompletedJobs() + for _, job := range completedJobs { + // Check if this word is already in the list + found := false + for _, w := range words { + if w == job.Word { + found = true + break + } + } + if !found { + words = append(words, job.Word) + } + } + + // Sort the combined list + sort.Strings(words) + return words +} + // onPrevWord loads the previous word func (a *Application) onPrevWord() { - if a.currentWordIndex > 0 { - a.loadWordByIndex(a.currentWordIndex - 1) + allWords := a.getAllAvailableWords() + if len(allWords) == 0 { + return } + + newIndex := a.currentWordIndex - 1 + // Wrap around to the end if at beginning + if newIndex < 0 { + newIndex = len(allWords) - 1 + } + + a.loadWordByIndex(newIndex) } // onNextWord loads the next word func (a *Application) onNextWord() { - if a.currentWordIndex < len(a.existingWords)-1 && a.currentWordIndex >= 0 { - a.loadWordByIndex(a.currentWordIndex + 1) + allWords := a.getAllAvailableWords() + if len(allWords) == 0 { + return } + + newIndex := a.currentWordIndex + 1 + // Wrap around to the beginning if at end + if newIndex >= len(allWords) { + newIndex = 0 + } + + a.loadWordByIndex(newIndex) } -// loadWordByIndex loads a word by its index in existingWords +// loadWordByIndex loads a word by its index in the combined word list func (a *Application) loadWordByIndex(index int) { - if index < 0 || index >= len(a.existingWords) { + allWords := a.getAllAvailableWords() + if index < 0 || index >= len(allWords) { return } - word := a.existingWords[index] + word := allWords[index] a.currentWord = word a.currentWordIndex = index @@ -118,8 +167,38 @@ func (a *Application) loadWordByIndex(index int) { // Clear UI a.clearUI() - // Load existing files - a.loadExistingFiles(word) + // Check if this word is from a completed queue job + var fromQueue bool + completedJobs := a.queue.GetCompletedJobs() + for _, job := range completedJobs { + if job.Word == word && job.Status == StatusCompleted { + // Load from queue job + a.currentTranslation = job.Translation + a.currentAudioFile = job.AudioFile + a.currentImages = job.ImageFiles + + fyne.Do(func() { + if job.Translation != "" { + a.translationText.SetText(fmt.Sprintf("%s = %s", word, job.Translation)) + } + if job.AudioFile != "" { + a.audioPlayer.SetAudioFile(job.AudioFile) + } + if len(job.ImageFiles) > 0 { + a.imageDisplay.SetImages(job.ImageFiles) + } + a.updateStatus(fmt.Sprintf("Loaded from queue: %s", word)) + }) + + fromQueue = true + break + } + } + + // If not from queue, load existing files from disk + if !fromQueue { + a.loadExistingFiles(word) + } // Update navigation a.updateNavigation() @@ -243,12 +322,28 @@ func (a *Application) deleteCurrentWord() { } a.existingWords = newWords + // Also remove from saved cards if present + a.mu.Lock() + newSavedCards := make([]anki.Card, 0, len(a.savedCards)) + for _, card := range a.savedCards { + if card.Bulgarian != a.currentWord { + newSavedCards = append(newSavedCards, card) + } + } + a.savedCards = newSavedCards + a.mu.Unlock() + + // Also remove from completed queue jobs + a.queue.RemoveCompletedJobByWord(a.currentWord) + // Clear UI a.clearUI() // Update status fyne.Do(func() { a.updateStatus(fmt.Sprintf("Deleted %d files for '%s'", deletedCount, a.currentWord)) + // Update queue status to reflect the reduced card count + a.updateQueueStatus() }) // Clear current word |
