diff options
| author | Paul Buetow <paul@buetow.org> | 2025-07-19 16:37:04 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-07-19 16:37:04 +0300 |
| commit | 77538381c28e4de8fa7d3f3731412c97699bb889 (patch) | |
| tree | 6abc09587e3dcb279f86f9c63c848ebfbee7b926 | |
| parent | 14357aee5c0db8665bea2da817d67f12d0499451 (diff) | |
feat: add app icon and change default output directory
- Add custom app icon with memory/brain theme
- Icon now displays in GNOME app menu, window title bar, and process list
- Change default output directory to ~/.local/state/totalrecall/
- Make output directory configurable via CLI flag, config file, or env var
- Add desktop entry file for GNOME integration
- Add install script for easy icon installation
- Update README with icon display and default directory information
- Bump version to 0.5.1
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | README.md | 52 | ||||
| -rw-r--r-- | assets/icons/totalrecall.svg | 66 | ||||
| -rw-r--r-- | assets/icons/totalrecall_128.png | bin | 0 -> 26276 bytes | |||
| -rw-r--r-- | assets/icons/totalrecall_16.png | bin | 0 -> 1700 bytes | |||
| -rw-r--r-- | assets/icons/totalrecall_256.png | bin | 0 -> 62217 bytes | |||
| -rw-r--r-- | assets/icons/totalrecall_32.png | bin | 0 -> 4560 bytes | |||
| -rw-r--r-- | assets/icons/totalrecall_48.png | bin | 0 -> 7238 bytes | |||
| -rw-r--r-- | assets/icons/totalrecall_512.png | bin | 0 -> 40153 bytes | |||
| -rw-r--r-- | assets/icons/totalrecall_64.png | bin | 0 -> 10653 bytes | |||
| -rw-r--r-- | cmd/totalrecall/main.go | 11 | ||||
| -rwxr-xr-x | install-icon.sh | 66 | ||||
| -rw-r--r-- | internal/gui/app.go | 6 | ||||
| -rw-r--r-- | internal/gui/icon.go | 17 | ||||
| -rw-r--r-- | internal/gui/totalrecall_256.png | bin | 0 -> 62217 bytes | |||
| -rw-r--r-- | internal/version.go | 2 | ||||
| -rw-r--r-- | totalrecall.desktop | 13 |
17 files changed, 232 insertions, 6 deletions
@@ -44,3 +44,8 @@ anki_cards # Configuration with API keys .totalrecall.yaml .image_cache/ + +# Test output directories +test_output/ +test_output2/ +test_phonetic/ @@ -1,5 +1,9 @@ # totalrecall - Bulgarian Anki Flashcard Generator +<p align="center"> + <img src="assets/icons/totalrecall_512.png" alt="TotalRecall Icon" width="256" height="256"> +</p> + `totalrecall` is a versatile tool for generating Anki flashcard materials from Bulgarian words. It offers both a command-line interface (CLI) and a graphical user interface (GUI) for creating audio pronunciation files and AI-generated images. It has mainly been vibe coded using Claude Code CLI. @@ -22,6 +26,7 @@ It has mainly been vibe coded using Claude Code CLI. - Anki-compatible CSV export with translations - Random voice variants and speech speed - Audio caching to save API costs +- **Default output directory**: `~/.local/state/totalrecall/` (configurable) ## Installation @@ -42,12 +47,45 @@ cd totalrecall go build -o totalrecall ./cmd/totalrecall ``` -Or install directly: +### Installing to Go Bin Directory + +Using Task (recommended): +```bash +cd totalrecall +task install +``` + +Or using go install directly: +```bash +cd totalrecall +go install ./cmd/totalrecall +``` +Or install from remote repository: ```bash go install codeberg.org/snonux/totalrecall/cmd/totalrecall@latest ``` +This will install the binary to `~/go/bin/totalrecall`, which should be in your PATH. + +### Desktop Icon Installation (GNOME/Fedora) + +TotalRecall includes a desktop icon for GNOME integration. To install: + +**For current user only:** +```bash +cd totalrecall +./install-icon.sh +``` + +**System-wide installation:** +```bash +cd totalrecall +sudo ./install-icon.sh +``` + +After installation, you may need to log out and log back in for the icon to appear in GNOME's application menu. The icon will show up as "TotalRecall" in the Education category. + ## Quick Start **Note:** By default, totalrecall uses OpenAI for both audio and images. Make sure to set your OpenAI API key: @@ -92,7 +130,13 @@ Launch the interactive graphical interface: totalrecall --gui ``` -Then use keyboard shortcuts or buttons to generate and manage flashcards interactively. +The GUI is best navigated using keyboard shortcuts for efficient workflow. Press **`h`** at any time to display a complete list of all available keyboard shortcuts. + +Key features: +- Fast keyboard-driven interface +- Real-time audio playback +- Batch processing support +- Visual feedback for all operations ## Configuration @@ -123,7 +167,7 @@ image: openai_style: "natural" # Style: natural or vivid (dall-e-3 only) output: - directory: ./anki_cards + directory: ~/.local/state/totalrecall # Default location (can be overridden) naming: "{word}_{type}" ``` @@ -174,6 +218,8 @@ When translations are provided, they are used directly without calling the trans ### Output Files +By default, all files are saved to `~/.local/state/totalrecall/`. You can override this with the `-o` flag or the `output.directory` config option. + For each word, the tool generates: - `word.mp3` - Audio pronunciation (random voice) - `word_translation.txt` - English translation diff --git a/assets/icons/totalrecall.svg b/assets/icons/totalrecall.svg new file mode 100644 index 0000000..145018e --- /dev/null +++ b/assets/icons/totalrecall.svg @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"> + <!-- Background circle with gradient --> + <defs> + <linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%"> + <stop offset="0%" style="stop-color:#1565C0;stop-opacity:1" /> + <stop offset="100%" style="stop-color:#0D47A1;stop-opacity:1" /> + </linearGradient> + <linearGradient id="brainGradient" x1="0%" y1="0%" x2="100%" y2="100%"> + <stop offset="0%" style="stop-color:#E91E63;stop-opacity:1" /> + <stop offset="50%" style="stop-color:#9C27B0;stop-opacity:1" /> + <stop offset="100%" style="stop-color:#673AB7;stop-opacity:1" /> + </linearGradient> + </defs> + + <!-- Background circle --> + <circle cx="256" cy="256" r="240" fill="url(#bgGradient)"/> + + <!-- Brain outline with memory circuits --> + <g transform="translate(256, 256)"> + <!-- Brain shape --> + <path d="M -80,-60 C -80,-100 -40,-120 0,-120 C 40,-120 80,-100 80,-60 C 80,-40 70,-20 60,0 C 70,20 80,40 80,60 C 80,100 40,120 0,120 C -40,120 -80,100 -80,60 C -80,40 -70,20 -60,0 C -70,-20 -80,-40 -80,-60 Z" + fill="url(#brainGradient)" stroke="#FFFFFF" stroke-width="6"/> + + <!-- Central dividing line --> + <path d="M 0,-120 C -20,-80 20,-40 0,0 C -20,40 20,80 0,120" + stroke="#FFFFFF" stroke-width="4" fill="none"/> + + <!-- Memory circuit patterns on left hemisphere --> + <g opacity="0.8"> + <circle cx="-40" cy="-40" r="8" fill="#FFFFFF"/> + <circle cx="-50" cy="0" r="8" fill="#FFFFFF"/> + <circle cx="-40" cy="40" r="8" fill="#FFFFFF"/> + <path d="M -40,-40 L -50,0 L -40,40" stroke="#FFFFFF" stroke-width="3" fill="none"/> + <path d="M -40,-40 L -20,-60" stroke="#FFFFFF" stroke-width="2" fill="none"/> + <path d="M -50,0 L -70,0" stroke="#FFFFFF" stroke-width="2" fill="none"/> + <path d="M -40,40 L -20,60" stroke="#FFFFFF" stroke-width="2" fill="none"/> + </g> + + <!-- Memory circuit patterns on right hemisphere --> + <g opacity="0.8"> + <circle cx="40" cy="-40" r="8" fill="#FFFFFF"/> + <circle cx="50" cy="0" r="8" fill="#FFFFFF"/> + <circle cx="40" cy="40" r="8" fill="#FFFFFF"/> + <path d="M 40,-40 L 50,0 L 40,40" stroke="#FFFFFF" stroke-width="3" fill="none"/> + <path d="M 40,-40 L 20,-60" stroke="#FFFFFF" stroke-width="2" fill="none"/> + <path d="M 50,0 L 70,0" stroke="#FFFFFF" stroke-width="2" fill="none"/> + <path d="M 40,40 L 20,60" stroke="#FFFFFF" stroke-width="2" fill="none"/> + </g> + + <!-- Glowing effect dots representing memories --> + <circle cx="-30" cy="-20" r="4" fill="#FFD700" opacity="0.9"/> + <circle cx="30" cy="-20" r="4" fill="#FFD700" opacity="0.9"/> + <circle cx="0" cy="20" r="4" fill="#FFD700" opacity="0.9"/> + <circle cx="-25" cy="30" r="3" fill="#FFD700" opacity="0.7"/> + <circle cx="25" cy="30" r="3" fill="#FFD700" opacity="0.7"/> + + <!-- Flashcard hint in bottom corner --> + <g transform="translate(100, 100)"> + <rect x="-30" y="-20" width="60" height="40" rx="4" fill="#FFFFFF" stroke="#333333" stroke-width="2" opacity="0.9"/> + <line x1="-20" y1="-10" x2="20" y2="-10" stroke="#333333" stroke-width="1" opacity="0.5"/> + <line x1="-20" y1="0" x2="20" y2="0" stroke="#333333" stroke-width="1" opacity="0.5"/> + <line x1="-20" y1="10" x2="20" y2="10" stroke="#333333" stroke-width="1" opacity="0.5"/> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/assets/icons/totalrecall_128.png b/assets/icons/totalrecall_128.png Binary files differnew file mode 100644 index 0000000..cce572f --- /dev/null +++ b/assets/icons/totalrecall_128.png diff --git a/assets/icons/totalrecall_16.png b/assets/icons/totalrecall_16.png Binary files differnew file mode 100644 index 0000000..381f124 --- /dev/null +++ b/assets/icons/totalrecall_16.png diff --git a/assets/icons/totalrecall_256.png b/assets/icons/totalrecall_256.png Binary files differnew file mode 100644 index 0000000..1d5c297 --- /dev/null +++ b/assets/icons/totalrecall_256.png diff --git a/assets/icons/totalrecall_32.png b/assets/icons/totalrecall_32.png Binary files differnew file mode 100644 index 0000000..1611629 --- /dev/null +++ b/assets/icons/totalrecall_32.png diff --git a/assets/icons/totalrecall_48.png b/assets/icons/totalrecall_48.png Binary files differnew file mode 100644 index 0000000..e33f4ec --- /dev/null +++ b/assets/icons/totalrecall_48.png diff --git a/assets/icons/totalrecall_512.png b/assets/icons/totalrecall_512.png Binary files differnew file mode 100644 index 0000000..724aaf3 --- /dev/null +++ b/assets/icons/totalrecall_512.png diff --git a/assets/icons/totalrecall_64.png b/assets/icons/totalrecall_64.png Binary files differnew file mode 100644 index 0000000..824dc55 --- /dev/null +++ b/assets/icons/totalrecall_64.png diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go index 7fffbaf..f8039a8 100644 --- a/cmd/totalrecall/main.go +++ b/cmd/totalrecall/main.go @@ -73,11 +73,15 @@ func init() { // Initialize random number generator rand.Seed(time.Now().UnixNano()) + // Set default output directory + home, _ := os.UserHomeDir() + defaultOutputDir := filepath.Join(home, ".local", "state", "totalrecall") + // Global flags rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.totalrecall.yaml)") // Local flags - rootCmd.Flags().StringVarP(&outputDir, "output", "o", "./anki_cards", "Output directory") + rootCmd.Flags().StringVarP(&outputDir, "output", "o", defaultOutputDir, "Output directory") rootCmd.Flags().StringVarP(&audioFormat, "format", "f", "mp3", "Audio format (wav or mp3)") rootCmd.Flags().StringVar(&imageAPI, "image-api", "openai", "Image source (only openai supported)") rootCmd.Flags().StringVar(&batchFile, "batch", "", "Process words from file (one per line)") @@ -151,6 +155,11 @@ func initConfig() { } func runCommand(cmd *cobra.Command, args []string) error { + // Check if output directory was set in config file + if !cmd.Flags().Changed("output") && viper.IsSet("output.directory") { + outputDir = viper.GetString("output.directory") + } + // Handle --list-models flag if listModels { return listAvailableModels() diff --git a/install-icon.sh b/install-icon.sh new file mode 100755 index 0000000..dd5c224 --- /dev/null +++ b/install-icon.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Script to install TotalRecall icon for GNOME on Fedora Linux +# Run with sudo for system-wide installation + +set -e + +# Check if running as root for system-wide install +if [[ $EUID -eq 0 ]]; then + echo "Installing TotalRecall icon system-wide..." + ICON_BASE="/usr/share/icons/hicolor" + APP_DIR="/usr/share/applications" + BINARY_PATH="/usr/local/bin/totalrecall" +else + echo "Installing TotalRecall icon for current user..." + ICON_BASE="$HOME/.local/share/icons/hicolor" + APP_DIR="$HOME/.local/share/applications" + BINARY_PATH="$HOME/go/bin/totalrecall" +fi + +# Create directories +mkdir -p "$ICON_BASE"/{16x16,32x32,48x48,64x64,128x128,256x256,512x512}/apps +mkdir -p "$APP_DIR" + +# Copy icons +cp assets/icons/totalrecall_16.png "$ICON_BASE/16x16/apps/totalrecall.png" +cp assets/icons/totalrecall_32.png "$ICON_BASE/32x32/apps/totalrecall.png" +cp assets/icons/totalrecall_48.png "$ICON_BASE/48x48/apps/totalrecall.png" +cp assets/icons/totalrecall_64.png "$ICON_BASE/64x64/apps/totalrecall.png" +cp assets/icons/totalrecall_128.png "$ICON_BASE/128x128/apps/totalrecall.png" +cp assets/icons/totalrecall_256.png "$ICON_BASE/256x256/apps/totalrecall.png" +cp assets/icons/totalrecall_512.png "$ICON_BASE/512x512/apps/totalrecall.png" + +# Copy scalable icon +mkdir -p "$ICON_BASE/scalable/apps" +cp assets/icons/totalrecall.svg "$ICON_BASE/scalable/apps/totalrecall.svg" + +# Copy and update desktop file with correct binary path +# First check if totalrecall is in standard locations +if command -v totalrecall &> /dev/null; then + TOTALRECALL_PATH=$(command -v totalrecall) +elif [[ -x "$HOME/go/bin/totalrecall" ]]; then + TOTALRECALL_PATH="$HOME/go/bin/totalrecall" +elif [[ -x "/usr/local/bin/totalrecall" ]]; then + TOTALRECALL_PATH="/usr/local/bin/totalrecall" +elif [[ -x "/usr/bin/totalrecall" ]]; then + TOTALRECALL_PATH="/usr/bin/totalrecall" +else + echo "Warning: totalrecall binary not found in standard locations" + echo "Please install totalrecall first with 'task install' or 'go install ./cmd/totalrecall'" + TOTALRECALL_PATH="totalrecall" +fi + +# Create desktop file with proper exec path +sed "s|Exec=totalrecall|Exec=$TOTALRECALL_PATH|g" totalrecall.desktop > "$APP_DIR/totalrecall.desktop" + +# Update caches +if command -v gtk-update-icon-cache &> /dev/null; then + gtk-update-icon-cache -f -t "$ICON_BASE" 2>/dev/null || true +fi + +if command -v update-desktop-database &> /dev/null; then + update-desktop-database "$APP_DIR" 2>/dev/null || true +fi + +echo "TotalRecall icon installed successfully!" +echo "You may need to log out and log back in to see the icon in GNOME."
\ No newline at end of file diff --git a/internal/gui/app.go b/internal/gui/app.go index 31d2460..2226eb1 100644 --- a/internal/gui/app.go +++ b/internal/gui/app.go @@ -112,8 +112,11 @@ func New(config *Config) *Application { ctx, cancel := context.WithCancel(context.Background()) + myApp := app.New() + myApp.SetIcon(GetAppIcon()) + app := &Application{ - app: app.New(), + app: myApp, config: config, ctx: ctx, cancel: cancel, @@ -152,6 +155,7 @@ func New(config *Config) *Application { // setupUI creates the main user interface func (a *Application) setupUI() { a.window = a.app.NewWindow(fmt.Sprintf("TotalRecall v%s - Bulgarian Flashcard Generator", internal.Version)) + a.window.SetIcon(GetAppIcon()) a.window.Resize(fyne.NewSize(800, 700)) // Create input section with navigation diff --git a/internal/gui/icon.go b/internal/gui/icon.go new file mode 100644 index 0000000..f778225 --- /dev/null +++ b/internal/gui/icon.go @@ -0,0 +1,17 @@ +package gui + +import ( + _ "embed" + "fyne.io/fyne/v2" +) + +//go:embed totalrecall_256.png +var iconData []byte + +// GetAppIcon returns the application icon as a Fyne resource +func GetAppIcon() fyne.Resource { + return &fyne.StaticResource{ + StaticName: "totalrecall.png", + StaticContent: iconData, + } +}
\ No newline at end of file diff --git a/internal/gui/totalrecall_256.png b/internal/gui/totalrecall_256.png Binary files differnew file mode 100644 index 0000000..1d5c297 --- /dev/null +++ b/internal/gui/totalrecall_256.png diff --git a/internal/version.go b/internal/version.go index c658e16..360eeb8 100644 --- a/internal/version.go +++ b/internal/version.go @@ -1,3 +1,3 @@ package internal -const Version = "0.5.0" +const Version = "0.5.1" diff --git a/totalrecall.desktop b/totalrecall.desktop new file mode 100644 index 0000000..41be16e --- /dev/null +++ b/totalrecall.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=TotalRecall +GenericName=Bulgarian Flashcard Generator +Comment=Generate Anki flashcard materials from Bulgarian words +Exec=totalrecall --gui +Icon=totalrecall +Terminal=false +Categories=Education;Languages; +Keywords=bulgarian;flashcards;anki;language;learning; +StartupNotify=true +StartupWMClass=totalrecall
\ No newline at end of file |
