summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-07-18 23:41:22 +0300
committerPaul Buetow <paul@buetow.org>2025-07-18 23:41:22 +0300
commitd8c65cd0511b558fe62aac7022c267f51d76f3f9 (patch)
tree4c490184ca6a1272f27a216d2e29cd9d666f48b7
parent8779de2c97e445acf82e8422c899fdefa4649bda (diff)
chore: bump version to 0.4.2
- Fix Fyne tooltip layer error by deferring tooltip setup after layer creation - Add phonetic information generation to batch mode processing - Fix GUI not displaying phonetic info for batch-generated cards - Add debug logging for phonetic file loading - Fix checkForMissingFiles to handle placeholder text in phonetic display 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
-rw-r--r--cmd/totalrecall/main.go73
-rw-r--r--internal/gui/app.go33
-rw-r--r--internal/gui/navigation.go5
-rw-r--r--internal/version.go2
4 files changed, 100 insertions, 13 deletions
diff --git a/cmd/totalrecall/main.go b/cmd/totalrecall/main.go
index db1a94e..7fffbaf 100644
--- a/cmd/totalrecall/main.go
+++ b/cmd/totalrecall/main.go
@@ -299,6 +299,13 @@ func processWordWithTranslation(word, providedTranslation string) error {
}
}
+ // Fetch phonetic information
+ fmt.Printf(" Fetching phonetic information...\n")
+ if err := fetchAndSavePhoneticInfo(word); err != nil {
+ // Don't fail the whole process if phonetic info fails
+ fmt.Printf(" Warning: Failed to fetch phonetic info: %v\n", err)
+ }
+
return nil
}
@@ -848,6 +855,72 @@ func saveAudioAttribution(word, audioFile string, config *audio.Config) error {
return nil
}
+func fetchAndSavePhoneticInfo(word string) error {
+ // Check if OpenAI key is available
+ apiKey := getOpenAIKey()
+ if apiKey == "" {
+ return fmt.Errorf("OpenAI API key not configured")
+ }
+
+ client := openai.NewClient(apiKey)
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+
+ req := openai.ChatCompletionRequest{
+ Model: openai.GPT4o,
+ Messages: []openai.ChatCompletionMessage{
+ {
+ Role: openai.ChatMessageRoleSystem,
+ Content: "You are a Bulgarian language expert helping language learners understand pronunciation. Provide detailed phonetic information using the International Phonetic Alphabet (IPA). For each IPA symbol used, give concrete examples of how it sounds using familiar English words or sounds when possible.",
+ },
+ {
+ Role: openai.ChatMessageRoleUser,
+ Content: fmt.Sprintf(`For the Bulgarian word '%s':
+1. Provide the complete IPA transcription
+2. Break down EACH phonetic symbol used in the transcription
+3. For EVERY symbol, explain how it's pronounced with examples:
+ - If similar to an English sound, give English word examples
+ - If not in English, describe tongue/mouth position or compare to similar sounds
+ - Include stress marks and explain which syllable is stressed
+
+Example format:
+Word: [IPA transcription]
+• /p/ - like 'p' in English 'pot'
+• /a/ - like 'a' in 'father'
+• /ˈ/ - stress mark (following syllable is stressed)`, word),
+ },
+ },
+ Temperature: 0.3,
+ MaxTokens: 500,
+ }
+
+ resp, err := client.CreateChatCompletion(ctx, req)
+ if err != nil {
+ return fmt.Errorf("OpenAI API error: %w", err)
+ }
+
+ if len(resp.Choices) == 0 || resp.Choices[0].Message.Content == "" {
+ return fmt.Errorf("no response from OpenAI")
+ }
+
+ phoneticInfo := strings.TrimSpace(resp.Choices[0].Message.Content)
+
+ // Find the card directory for this word
+ wordDir := findCardDirectory(word)
+ if wordDir == "" {
+ return fmt.Errorf("card directory not found for word: %s", word)
+ }
+
+ // Save phonetic info to file
+ phoneticFile := filepath.Join(wordDir, "phonetic.txt")
+ if err := os.WriteFile(phoneticFile, []byte(phoneticInfo), 0644); err != nil {
+ return fmt.Errorf("failed to write phonetic file: %w", err)
+ }
+
+ fmt.Printf(" Saved phonetic information\n")
+ return nil
+}
+
func runGUIMode() error {
// Create GUI configuration from command line flags and viper config
guiConfig := &gui.Config{
diff --git a/internal/gui/app.go b/internal/gui/app.go
index da66287..6949a8f 100644
--- a/internal/gui/app.go
+++ b/internal/gui/app.go
@@ -209,18 +209,15 @@ func (a *Application) setupUI() {
a.window.Canvas().Unfocus()
}
- // Create navigation buttons with tooltips
+ // Create navigation buttons (tooltips will be set after tooltip layer is created)
a.submitButton = ttwidget.NewButton("", a.onSubmit)
a.submitButton.Icon = theme.ConfirmIcon()
- a.submitButton.SetToolTip("Generate word (G)")
a.prevWordBtn = ttwidget.NewButton("", a.onPrevWord)
a.prevWordBtn.Icon = theme.NavigateBackIcon()
- a.prevWordBtn.SetToolTip("Previous word (←)")
a.nextWordBtn = ttwidget.NewButton("", a.onNextWord)
a.nextWordBtn.Icon = theme.NavigateNextIcon()
- a.nextWordBtn.SetToolTip("Next word (→)")
// Create a grid layout for inputs
inputGrid := container.New(layout.NewGridLayout(2),
@@ -295,25 +292,19 @@ func (a *Application) setupUI() {
imageSection,
)
- // Create action buttons with tooltips
+ // Create action buttons (tooltips will be set after tooltip layer is created)
a.keepButton = ttwidget.NewButtonWithIcon("", theme.DocumentCreateIcon(), a.onKeepAndContinue)
- a.keepButton.SetToolTip("Keep card and new word (N)")
a.regenerateImageBtn = ttwidget.NewButtonWithIcon("", theme.ViewRefreshIcon(), a.onRegenerateImage)
- a.regenerateImageBtn.SetToolTip("Regenerate image (I)")
a.regenerateRandomImageBtn = ttwidget.NewButtonWithIcon("", theme.MediaPhotoIcon(), a.onRegenerateRandomImage)
- a.regenerateRandomImageBtn.SetToolTip("Random image (M)")
a.regenerateAudioBtn = ttwidget.NewButtonWithIcon("", theme.MediaRecordIcon(), a.onRegenerateAudio)
- a.regenerateAudioBtn.SetToolTip("Regenerate audio (A)")
a.regenerateAllBtn = ttwidget.NewButtonWithIcon("", theme.ViewFullScreenIcon(), a.onRegenerateAll)
- a.regenerateAllBtn.SetToolTip("Regenerate all (R)")
a.deleteButton = ttwidget.NewButtonWithIcon("", theme.DeleteIcon(), a.onDelete)
a.deleteButton.Importance = widget.DangerImportance
- a.deleteButton.SetToolTip("Delete word (D)")
// Initially disable action buttons
a.setActionButtonsEnabled(false)
@@ -369,6 +360,10 @@ func (a *Application) setupUI() {
// Add the tooltip layer to enable tooltips
a.window.SetContent(fynetooltip.AddWindowToolTipLayer(content, a.window.Canvas()))
+
+ // Now that tooltip layer is created, set all tooltips
+ a.setupTooltips()
+
a.window.SetOnClosed(func() {
// Stop file check ticker
if a.fileCheckTicker != nil {
@@ -1098,6 +1093,22 @@ func (a *Application) clearUI() {
a.setActionButtonsEnabled(false)
}
+// setupTooltips sets up all tooltips after the tooltip layer has been created
+func (a *Application) setupTooltips() {
+ // Navigation button tooltips
+ a.submitButton.SetToolTip("Generate word (G)")
+ a.prevWordBtn.SetToolTip("Previous word (←)")
+ a.nextWordBtn.SetToolTip("Next word (→)")
+
+ // Action button tooltips
+ a.keepButton.SetToolTip("Keep card and new word (N)")
+ a.regenerateImageBtn.SetToolTip("Regenerate image (I)")
+ a.regenerateRandomImageBtn.SetToolTip("Random image (M)")
+ a.regenerateAudioBtn.SetToolTip("Regenerate audio (A)")
+ a.regenerateAllBtn.SetToolTip("Regenerate all (R)")
+ a.deleteButton.SetToolTip("Delete word (D)")
+}
+
// processNextInQueue processes the next word in the queue
func (a *Application) processNextInQueue() {
// Check if we're already processing
diff --git a/internal/gui/navigation.go b/internal/gui/navigation.go
index a73e4d0..e166283 100644
--- a/internal/gui/navigation.go
+++ b/internal/gui/navigation.go
@@ -390,9 +390,12 @@ func (a *Application) loadExistingFiles(word string) {
phoneticFile := filepath.Join(wordDir, "phonetic.txt")
if data, err := os.ReadFile(phoneticFile); err == nil {
phoneticInfo := string(data)
+ fmt.Printf("Loaded phonetic info from file: %s\n", phoneticFile)
fyne.Do(func() {
a.phoneticDisplay.SetText(phoneticInfo)
})
+ } else {
+ fmt.Printf("No phonetic file found at: %s (error: %v)\n", phoneticFile, err)
}
// Load audio file
@@ -539,7 +542,7 @@ func (a *Application) checkForMissingFiles(word string) {
// Check for missing phonetic info
currentPhonetic := a.phoneticDisplay.Text
- if currentPhonetic == "" {
+ if currentPhonetic == "" || currentPhonetic == "Phonetic information will appear here..." {
phoneticFile := filepath.Join(wordDir, "phonetic.txt")
if data, err := os.ReadFile(phoneticFile); err == nil {
phoneticInfo := string(data)
diff --git a/internal/version.go b/internal/version.go
index 60cb1f5..40069bf 100644
--- a/internal/version.go
+++ b/internal/version.go
@@ -1,3 +1,3 @@
package internal
-const Version = "0.4.1"
+const Version = "0.4.2"