From bc1c6e76d5a6ef2623d26d277473c459dd699f81 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 21 Jul 2025 23:22:38 +0300 Subject: feat: Enhanced bulk import, archive functionality, and export improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Bulk Import Enhancements - Added support for three flexible batch file formats: - `BULGARIAN = ENGLISH` - Both provided, no translation needed - `= ENGLISH` - Only English provided, auto-translated to Bulgarian - `BULGARIAN` - Only Bulgarian provided, auto-translated to English - Implemented smart file checking to skip already processed words - Check all required files (word.txt, translation.txt, phonetic.txt, audio/image files and their attribution/metadata) - Added batch processing summary with statistics ## Archive Functionality - Renamed --clear flag to --archive for clarity - Archive cards directory to ~/.local/state/totalrecall/archive/cards-TIMESTAMP - Added archive button to GUI toolbar with folder icon - Archive confirmation dialog supports keyboard shortcuts (y/n/c/ESC) ## Export Improvements - Anki exports now show full file path in output - Changed default export location to home directory (~) for both CLI and GUI - Auto-adjust image size to 1024x1024 when DALL-E 3 is selected ## Other Improvements - Added TranslateEnglishToBulgarian method for reverse translation - Enhanced batch processing with better error handling and progress reporting - Improved file integrity checking for complete word processing 🤖 Generated with [opencode](https://opencode.ai) Co-Authored-By: opencode --- internal/gui/app.go | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 3 deletions(-) (limited to 'internal/gui') diff --git a/internal/gui/app.go b/internal/gui/app.go index ef0ee43..cd4b711 100644 --- a/internal/gui/app.go +++ b/internal/gui/app.go @@ -23,6 +23,7 @@ import ( "codeberg.org/snonux/totalrecall/internal" "codeberg.org/snonux/totalrecall/internal/anki" + "codeberg.org/snonux/totalrecall/internal/archive" "codeberg.org/snonux/totalrecall/internal/audio" ) @@ -345,8 +346,9 @@ func (a *Application) setupUI() { // But keep delete button enabled for cancelling operations a.deleteButton.Enable() - // Create export and help buttons for toolbar + // Create export, archive and help buttons for toolbar exportButton := ttwidget.NewButtonWithIcon("", theme.UploadIcon(), a.onExportToAnki) + archiveButton := ttwidget.NewButtonWithIcon("", theme.FolderOpenIcon(), a.onArchive) helpButton := ttwidget.NewButtonWithIcon("", theme.HelpIcon(), a.onShowHotkeys) // Create toolbar with navigation buttons first, then action buttons @@ -363,6 +365,7 @@ func (a *Application) setupUI() { a.regenerateAllBtn, widget.NewSeparator(), exportButton, + archiveButton, helpButton, ) @@ -400,13 +403,16 @@ func (a *Application) setupUI() { // Now that tooltip layer is created, set all tooltips a.setupTooltips() - // Set tooltips for export and help buttons with a delay + // Set tooltips for export, archive and help buttons with a delay go func() { time.Sleep(500 * time.Millisecond) fyne.Do(func() { if exportButton != nil { exportButton.SetToolTip("Export to Anki (x)") } + if archiveButton != nil { + archiveButton.SetToolTip("Archive all cards") + } if helpButton != nil { helpButton.SetToolTip("Show hotkeys (?)") } @@ -1034,7 +1040,7 @@ func (a *Application) onExportToAnki() { // Export directory selection homeDir, _ := os.UserHomeDir() - defaultExportDir := filepath.Join(homeDir, "Downloads") + defaultExportDir := homeDir // Changed from Downloads to home directory selectedDir := defaultExportDir dirLabel := widget.NewLabel(selectedDir) @@ -1199,6 +1205,110 @@ func (a *Application) onExportToAnki() { customDialog.Show() } +// onArchive archives the current cards directory +func (a *Application) onArchive() { + // Function to perform the archive + performArchive := func() { + // Get the cards directory path + home, _ := os.UserHomeDir() + cardsDir := filepath.Join(home, ".local", "state", "totalrecall", "cards") + + // Archive the cards + if err := archive.ArchiveCards(cardsDir); err != nil { + dialog.ShowError(err, a.window) + return + } + + // Clear the saved cards list + a.mu.Lock() + a.savedCards = []anki.Card{} + a.existingWords = []string{} + a.mu.Unlock() + + // Update status + a.updateStatus("Cards archived successfully") + + // Refresh the current word display + a.scanExistingWords() + if a.currentWord != "" { + a.loadExistingFiles(a.currentWord) + } + } + + // Create confirmation dialog + confirmDialog := dialog.NewConfirm("Archive Cards", + "Are you sure you want to archive all existing cards?\n\nThis will move the cards directory to:\n~/.local/state/totalrecall/archive/cards-TIMESTAMP", + func(confirmed bool) { + if confirmed { + performArchive() + } + }, + a.window, + ) + + // Track if we're in archive confirmation mode + archiveConfirming := true + + // Save original key handlers + oldKeyHandler := a.window.Canvas().OnTypedKey() + oldRuneHandler := a.window.Canvas().OnTypedRune() + + // Handle both Latin and Cyrillic keys + a.window.Canvas().SetOnTypedRune(func(r rune) { + if archiveConfirming { + switch r { + case 'y', 'Y', 'ъ', 'Ъ': + confirmDialog.Hide() + archiveConfirming = false + performArchive() + // Restore original handlers + a.window.Canvas().SetOnTypedKey(oldKeyHandler) + a.window.Canvas().SetOnTypedRune(oldRuneHandler) + case 'n', 'N', 'н', 'Н', 'c', 'C', 'ц', 'Ц': + confirmDialog.Hide() + archiveConfirming = false + // Restore original handlers + a.window.Canvas().SetOnTypedKey(oldKeyHandler) + a.window.Canvas().SetOnTypedRune(oldRuneHandler) + } + } else if oldRuneHandler != nil { + oldRuneHandler(r) + } + }) + + // Handle special keys + a.window.Canvas().SetOnTypedKey(func(ev *fyne.KeyEvent) { + if archiveConfirming { + switch ev.Name { + case fyne.KeyY: + confirmDialog.Hide() + archiveConfirming = false + performArchive() + // Restore original handlers + a.window.Canvas().SetOnTypedKey(oldKeyHandler) + a.window.Canvas().SetOnTypedRune(oldRuneHandler) + case fyne.KeyN, fyne.KeyC, fyne.KeyEscape: + confirmDialog.Hide() + archiveConfirming = false + // Restore original handlers + a.window.Canvas().SetOnTypedKey(oldKeyHandler) + a.window.Canvas().SetOnTypedRune(oldRuneHandler) + } + } else if oldKeyHandler != nil { + oldKeyHandler(ev) + } + }) + + // Set up dialog close handler to restore key handlers + confirmDialog.SetOnClosed(func() { + archiveConfirming = false + a.window.Canvas().SetOnTypedKey(oldKeyHandler) + a.window.Canvas().SetOnTypedRune(oldRuneHandler) + }) + + confirmDialog.Show() +} + // onShowHotkeys displays a dialog with all available keyboard shortcuts func (a *Application) onShowHotkeys() { hotkeys := `[Project Page: https://codeberg.org/snonux/totalrecall](https://codeberg.org/snonux/totalrecall) @@ -1237,6 +1347,12 @@ func (a *Application) onShowHotkeys() { **c/ц** Close dialog **q/ч** Quit application +## Dialogs +**y/ъ** Confirm action +**n/н** Cancel action +**c/ц** Cancel action +**Esc** Cancel action + --- *All hotkeys work with both Latin and Cyrillic keyboards* -- cgit v1.2.3