diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-18 14:00:13 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-18 14:00:13 +0300 |
| commit | a6e9947b904406ec5b49e88c77689d5c6ef6d04b (patch) | |
| tree | fc89df6a57d6ee3e8ef3487d9d07d01797b4c696 /internal/gui/generator.go | |
| parent | e2f45921c45c9322c2ddb679d0511ba20772dcae (diff) | |
feat: major refactor - APKG export support and subdirectory organization
Major Features:
- Added native .apkg (Anki package) export format with embedded media
- Reorganized file structure to use subdirectories per word
- Enhanced GUI export dialog with format selection (APKG/CSV)
APKG Export Implementation:
- Created apkg_generator.go with full SQLite-based Anki package generation
- Includes custom card templates with professional CSS styling
- Front side: Image + English word
- Back side: Image + Bulgarian word + Audio + Notes
- All media files automatically embedded in package
- Custom deck names supported via --deck-name flag
Directory Structure Changes:
- Each word now gets its own subdirectory (e.g., anki_cards/ябълка/)
- All related files (audio, images, translations, prompts) stored together
- Cleaner organization and easier management
- Prevents file naming conflicts
GUI Updates:
- Export dialog no longer shows file browser, exports directly to anki_cards
- Format selection between APKG (recommended) and CSV (legacy)
- Fixed navigation to properly load image prompts from subdirectories
- Delete function now moves entire word directory to trash
CLI Updates:
- --anki flag now generates APKG by default
- --anki-csv flag for legacy CSV format
- All file generation uses subdirectory structure
Bug Fixes:
- Fixed handling of multi-word entries (e.g., "картоф картофи")
- Fixed GenerateFromDirectory to properly handle words with underscores
- Fixed phonetic files being treated as separate cards
- Fixed image prompt preservation during navigation
Breaking Changes:
- File structure changed from flat to subdirectory-based
- Existing files need to be reorganized into subdirectories
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'internal/gui/generator.go')
| -rw-r--r-- | internal/gui/generator.go | 22 |
1 files changed, 17 insertions, 5 deletions
diff --git a/internal/gui/generator.go b/internal/gui/generator.go index d26bb81..16ea7cf 100644 --- a/internal/gui/generator.go +++ b/internal/gui/generator.go @@ -99,9 +99,15 @@ func (a *Application) generateAudio(word string) (string, error) { return "", err } - // Generate filename + // Create subdirectory for this word filename := sanitizeFilename(word) - outputFile := filepath.Join(a.config.OutputDir, fmt.Sprintf("%s.%s", filename, a.config.AudioFormat)) + wordDir := filepath.Join(a.config.OutputDir, filename) + if err := os.MkdirAll(wordDir, 0755); err != nil { + return "", fmt.Errorf("failed to create word directory: %w", err) + } + + // Generate filename in subdirectory + outputFile := filepath.Join(wordDir, fmt.Sprintf("%s.%s", filename, a.config.AudioFormat)) // Generate audio err = provider.GenerateAudio(a.ctx, word, outputFile) @@ -150,9 +156,16 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string, return "", fmt.Errorf("unknown image provider: %s", a.config.ImageProvider) } + // Create subdirectory for this word + filename := sanitizeFilename(word) + wordDir := filepath.Join(a.config.OutputDir, filename) + if err := os.MkdirAll(wordDir, 0755); err != nil { + return "", fmt.Errorf("failed to create word directory: %w", err) + } + // Create downloader downloadOpts := &image.DownloadOptions{ - OutputDir: a.config.OutputDir, + OutputDir: wordDir, OverwriteExisting: true, CreateDir: true, FileNamePattern: "{word}", @@ -182,8 +195,7 @@ func (a *Application) generateImagesWithPrompt(word string, customPrompt string, usedPrompt := openaiClient.GetLastPrompt() if usedPrompt != "" { // Save the prompt to disk immediately for this word - filename := sanitizeFilename(word) - promptFile := filepath.Join(a.config.OutputDir, fmt.Sprintf("%s_prompt.txt", filename)) + promptFile := filepath.Join(wordDir, fmt.Sprintf("%s_prompt.txt", filename)) os.WriteFile(promptFile, []byte(usedPrompt), 0644) // Only update UI if this word is still the current word |
