summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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"