From 30b57c42952ae90b86fe1673d1027efc22a64115 Mon Sep 17 00:00:00 2001 From: odi <75929767+odedindi@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:36:48 +0200 Subject: [PATCH] fix(issue 522): surface paste errors as UI toast notification (#1198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: surface paste errors as UI toast notification (#522) When pasting the transcription fails (e.g. wtype/xdotool/dotool not available or returns an error on Linux), the error was silently logged to the backend console only. Users had no idea why their text was not pasted. Emit a new 'paste-error' Tauri event from the Rust side and listen for it in the frontend App component, showing a sonner toast with the error detail — mirroring the existing 'recording-error' pattern exactly. Files changed: - src-tauri/src/actions.rs: add PasteErrorEvent struct, emit event on paste failure - src/App.tsx: add useEffect listener that shows toast.error on paste-error - src/lib/types/events.ts: add PasteErrorEvent interface - src/i18n/locales/en/translation.json: add pasteFailedTitle/pasteFailed keys * chore: apply cargo fmt and prettier formatting * add translationss --------- Co-authored-by: CJ Pais --- src-tauri/src/actions.rs | 5 ++++- src/App.tsx | 15 +++++++++++++++ src/i18n/locales/ar/translation.json | 4 +++- src/i18n/locales/bg/translation.json | 4 +++- src/i18n/locales/cs/translation.json | 4 +++- src/i18n/locales/de/translation.json | 4 +++- src/i18n/locales/en/translation.json | 4 +++- src/i18n/locales/es/translation.json | 4 +++- src/i18n/locales/fr/translation.json | 4 +++- src/i18n/locales/he/translation.json | 4 +++- src/i18n/locales/it/translation.json | 4 +++- src/i18n/locales/ja/translation.json | 4 +++- src/i18n/locales/ko/translation.json | 4 +++- src/i18n/locales/pl/translation.json | 4 +++- src/i18n/locales/pt/translation.json | 4 +++- src/i18n/locales/ru/translation.json | 4 +++- src/i18n/locales/sv/translation.json | 4 +++- src/i18n/locales/tr/translation.json | 4 +++- src/i18n/locales/uk/translation.json | 4 +++- src/i18n/locales/vi/translation.json | 4 +++- src/i18n/locales/zh-TW/translation.json | 4 +++- src/i18n/locales/zh/translation.json | 4 +++- 22 files changed, 79 insertions(+), 21 deletions(-) diff --git a/src-tauri/src/actions.rs b/src-tauri/src/actions.rs index bf65667..50792ac 100644 --- a/src-tauri/src/actions.rs +++ b/src-tauri/src/actions.rs @@ -612,7 +612,10 @@ impl ShortcutAction for TranscribeAction { "Text pasted successfully in {:?}", paste_time.elapsed() ), - Err(e) => error!("Failed to paste transcription: {}", e), + Err(e) => { + error!("Failed to paste transcription: {}", e); + let _ = ah_clone.emit("paste-error", ()); + } } utils::hide_recording_overlay(&ah_clone); change_tray_icon(&ah_clone, TrayIconState::Idle); diff --git a/src/App.tsx b/src/App.tsx index ea378e5..453e891 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -122,6 +122,21 @@ function App() { }; }, [t]); + // Listen for paste failures and show a toast. + // The technical error detail is logged to handy.log on the Rust side + // (see actions.rs `error!("Failed to paste transcription: ...")`), + // so we show a localized, user-friendly message here instead of the raw error. + useEffect(() => { + const unlisten = listen("paste-error", () => { + toast.error(t("errors.pasteFailedTitle"), { + description: t("errors.pasteFailed"), + }); + }); + return () => { + unlisten.then((fn) => fn()); + }; + }, [t]); + // Listen for model loading failures and show a toast useEffect(() => { const unlisten = listen("model-state-changed", (event) => { diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index 52c52d1..81f7a14 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "لم يتم اكتشاف أي جهاز إدخال صوتي. يرجى توصيل ميكروفون أو سماعة رأس والمحاولة مرة أخرى.", "recordingFailed": "فشل في بدء التسجيل: {{error}}", "modelLoadFailed": "فشل في تحميل النموذج: {{model}}", - "modelLoadFailedUnknown": "نموذج غير معروف" + "modelLoadFailedUnknown": "نموذج غير معروف", + "pasteFailedTitle": "فشل لصق النص", + "pasteFailed": "تعذر لصق النص في التطبيق النشط." }, "appLanguage": { "title": "لغة التطبيق", diff --git a/src/i18n/locales/bg/translation.json b/src/i18n/locales/bg/translation.json index fada42f..2cb5d79 100644 --- a/src/i18n/locales/bg/translation.json +++ b/src/i18n/locales/bg/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Не е открито аудио входно устройство. Свържете микрофон или слушалки и опитайте отново.", "recordingFailed": "Стартирането на записа не бе успешно: {{error}}", "modelLoadFailed": "Зареждането на модела не бе успешно: {{model}}", - "modelLoadFailedUnknown": "неизвестен модел" + "modelLoadFailedUnknown": "неизвестен модел", + "pasteFailedTitle": "Неуспешно поставяне на текст", + "pasteFailed": "Текстът не можа да бъде поставен в активното приложение." }, "appLanguage": { "title": "Език на приложението", diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index 345bcd4..aa93251 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Nebylo detekováno žádné zvukové vstupní zařízení. Připojte mikrofon nebo sluchátka a zkuste to znovu.", "recordingFailed": "Nepodařilo se spustit nahrávání: {{error}}", "modelLoadFailed": "Nepodařilo se načíst model: {{model}}", - "modelLoadFailedUnknown": "neznámý model" + "modelLoadFailedUnknown": "neznámý model", + "pasteFailedTitle": "Vložení textu se nezdařilo", + "pasteFailed": "Text nebylo možné vložit do aktivní aplikace." }, "appLanguage": { "title": "Jazyk aplikace", diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index 30adc79..821b944 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Es wurde kein Audio-Eingabegerät erkannt. Bitte schließen Sie ein Mikrofon oder Headset an und versuchen Sie es erneut.", "recordingFailed": "Aufnahme konnte nicht gestartet werden: {{error}}", "modelLoadFailed": "Modell konnte nicht geladen werden: {{model}}", - "modelLoadFailedUnknown": "unbekanntes Modell" + "modelLoadFailedUnknown": "unbekanntes Modell", + "pasteFailedTitle": "Text konnte nicht eingefügt werden", + "pasteFailed": "Der Text konnte nicht in die aktive Anwendung eingefügt werden." }, "appLanguage": { "title": "Anwendungssprache", diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index c3fb449..eef96e5 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "No audio input device was detected. Please connect a microphone or headset and try again.", "recordingFailed": "Failed to start recording: {{error}}", "modelLoadFailed": "Failed to load model: {{model}}", - "modelLoadFailedUnknown": "unknown model" + "modelLoadFailedUnknown": "unknown model", + "pasteFailedTitle": "Failed to Paste Text", + "pasteFailed": "Text could not be pasted into the active application." }, "appLanguage": { "title": "Application Language", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index dd4420e..e33d5cb 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "No se detectó ningún dispositivo de entrada de audio. Conecta un micrófono o auriculares e inténtalo de nuevo.", "recordingFailed": "Error al iniciar la grabación: {{error}}", "modelLoadFailed": "Error al cargar el modelo: {{model}}", - "modelLoadFailedUnknown": "modelo desconocido" + "modelLoadFailedUnknown": "modelo desconocido", + "pasteFailedTitle": "Error al pegar el texto", + "pasteFailed": "No se pudo pegar el texto en la aplicación activa." }, "appLanguage": { "title": "Idioma de la aplicación", diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index 9b82798..479d00c 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Aucun périphérique d'entrée audio n'a été détecté. Veuillez connecter un microphone ou un casque et réessayer.", "recordingFailed": "Échec du démarrage de l'enregistrement : {{error}}", "modelLoadFailed": "Échec du chargement du modèle : {{model}}", - "modelLoadFailedUnknown": "modèle inconnu" + "modelLoadFailedUnknown": "modèle inconnu", + "pasteFailedTitle": "Échec du collage du texte", + "pasteFailed": "Le texte n'a pas pu être collé dans l'application active." }, "appLanguage": { "title": "Langue de l'application", diff --git a/src/i18n/locales/he/translation.json b/src/i18n/locales/he/translation.json index 7c3f792..9b0518d 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "לא זוהה התקן קלט שמע. חבר מיקרופון או אוזנייה ונסה שוב.", "recordingFailed": "הפעלת ההקלטה נכשלה: {{error}}", "modelLoadFailed": "טעינת המודל נכשלה: {{model}}", - "modelLoadFailedUnknown": "מודל לא ידוע" + "modelLoadFailedUnknown": "מודל לא ידוע", + "pasteFailedTitle": "ההדבקה נכשלה", + "pasteFailed": "לא ניתן היה להדביק את הטקסט ביישום הפעיל." }, "appLanguage": { "title": "שפת האפליקציה", diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 4670e4b..6f7cd03 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Non è stato rilevato alcun dispositivo di ingresso audio. Collega un microfono o delle cuffie e riprova.", "recordingFailed": "Impossibile avviare la registrazione: {{error}}", "modelLoadFailed": "Impossibile caricare il modello: {{model}}", - "modelLoadFailedUnknown": "modello sconosciuto" + "modelLoadFailedUnknown": "modello sconosciuto", + "pasteFailedTitle": "Impossibile incollare il testo", + "pasteFailed": "Non è stato possibile incollare il testo nell'applicazione attiva." }, "appLanguage": { "title": "Lingua Applicazione", diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index 86313af..a9aad4e 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "オーディオ入力デバイスが検出されませんでした。マイクまたはヘッドセットを接続してから再試行してください。", "recordingFailed": "録音の開始に失敗しました: {{error}}", "modelLoadFailed": "モデルの読み込みに失敗しました: {{model}}", - "modelLoadFailedUnknown": "不明なモデル" + "modelLoadFailedUnknown": "不明なモデル", + "pasteFailedTitle": "テキストの貼り付けに失敗しました", + "pasteFailed": "アクティブなアプリケーションにテキストを貼り付けられませんでした。" }, "appLanguage": { "title": "アプリケーション言語", diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index 954583f..4e168d2 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "오디오 입력 장치가 감지되지 않았습니다. 마이크 또는 헤드셋을 연결한 후 다시 시도해 주세요.", "recordingFailed": "녹음을 시작하지 못했습니다: {{error}}", "modelLoadFailed": "모델을 불러오지 못했습니다: {{model}}", - "modelLoadFailedUnknown": "알 수 없는 모델" + "modelLoadFailedUnknown": "알 수 없는 모델", + "pasteFailedTitle": "텍스트 붙여넣기 실패", + "pasteFailed": "활성 애플리케이션에 텍스트를 붙여넣을 수 없습니다." }, "appLanguage": { "title": "애플리케이션 언어", diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index dff12ca..ed06422 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Nie wykryto żadnego urządzenia wejściowego audio. Podłącz mikrofon lub słuchawki i spróbuj ponownie.", "recordingFailed": "Nie udało się rozpocząć nagrywania: {{error}}", "modelLoadFailed": "Nie udało się załadować modelu: {{model}}", - "modelLoadFailedUnknown": "nieznany model" + "modelLoadFailedUnknown": "nieznany model", + "pasteFailedTitle": "Nie udało się wkleić tekstu", + "pasteFailed": "Nie można było wkleić tekstu do aktywnej aplikacji." }, "appLanguage": { "title": "Język aplikacji", diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index fc31887..a85436f 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Nenhum dispositivo de entrada de áudio foi detectado. Conecte um microfone ou fone de ouvido e tente novamente.", "recordingFailed": "Falha ao iniciar a gravação: {{error}}", "modelLoadFailed": "Falha ao carregar o modelo: {{model}}", - "modelLoadFailedUnknown": "modelo desconhecido" + "modelLoadFailedUnknown": "modelo desconhecido", + "pasteFailedTitle": "Falha ao colar o texto", + "pasteFailed": "Não foi possível colar o texto no aplicativo ativo." }, "appLanguage": { "title": "Idioma da Aplicação", diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index 4c674d7..b196a32 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Аудиоустройство ввода не обнаружено. Подключите микрофон или гарнитуру и попробуйте снова.", "recordingFailed": "Не удалось начать запись: {{error}}", "modelLoadFailed": "Не удалось загрузить модель: {{model}}", - "modelLoadFailedUnknown": "неизвестная модель" + "modelLoadFailedUnknown": "неизвестная модель", + "pasteFailedTitle": "Не удалось вставить текст", + "pasteFailed": "Не удалось вставить текст в активное приложение." }, "appLanguage": { "title": "Язык приложения", diff --git a/src/i18n/locales/sv/translation.json b/src/i18n/locales/sv/translation.json index d809651..de731ef 100644 --- a/src/i18n/locales/sv/translation.json +++ b/src/i18n/locales/sv/translation.json @@ -597,7 +597,9 @@ "modelLoadFailed": "Misslyckades med att ladda modell: {{model}}", "modelLoadFailedUnknown": "okänd modell", "noInputDeviceTitle": "Ingen mikrofon hittades", - "noInputDevice": "Ingen ljudenhet hittades. Anslut en mikrofon eller ett headset och försök igen." + "noInputDevice": "Ingen ljudenhet hittades. Anslut en mikrofon eller ett headset och försök igen.", + "pasteFailedTitle": "Det gick inte att klistra in texten", + "pasteFailed": "Texten kunde inte klistras in i det aktiva programmet." }, "appLanguage": { "title": "Applikationsspråk", diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index e81c7a8..e4cc4e2 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Ses giriş aygıtı algılanamadı. Lütfen bir mikrofon veya kulaklık bağlayın ve tekrar deneyin.", "recordingFailed": "Kayıt başlatılamadı: {{error}}", "modelLoadFailed": "Model yüklenemedi: {{model}}", - "modelLoadFailedUnknown": "bilinmeyen model" + "modelLoadFailedUnknown": "bilinmeyen model", + "pasteFailedTitle": "Metin yapıştırılamadı", + "pasteFailed": "Metin etkin uygulamaya yapıştırılamadı." }, "appLanguage": { "title": "Uygulama Dili", diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index 982a6db..825c1ee 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Не виявлено жодного пристрою введення звуку. Підключіть мікрофон або гарнітуру та спробуйте знову.", "recordingFailed": "Не вдалося розпочати запис: {{error}}", "modelLoadFailed": "Не вдалося завантажити модель: {{model}}", - "modelLoadFailedUnknown": "невідома модель" + "modelLoadFailedUnknown": "невідома модель", + "pasteFailedTitle": "Не вдалося вставити текст", + "pasteFailed": "Не вдалося вставити текст до активної програми." }, "appLanguage": { "title": "Мова інтерфейсу", diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index 9821039..b6bfe7c 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "Không phát hiện thiết bị đầu vào âm thanh nào. Vui lòng kết nối micrô hoặc tai nghe và thử lại.", "recordingFailed": "Không thể bắt đầu ghi âm: {{error}}", "modelLoadFailed": "Không thể tải mô hình: {{model}}", - "modelLoadFailedUnknown": "mô hình không xác định" + "modelLoadFailedUnknown": "mô hình không xác định", + "pasteFailedTitle": "Không thể dán văn bản", + "pasteFailed": "Không thể dán văn bản vào ứng dụng đang hoạt động." }, "appLanguage": { "title": "Ngôn ngữ ứng dụng", diff --git a/src/i18n/locales/zh-TW/translation.json b/src/i18n/locales/zh-TW/translation.json index 3009cf1..6eee963 100644 --- a/src/i18n/locales/zh-TW/translation.json +++ b/src/i18n/locales/zh-TW/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "未偵測到音訊輸入裝置。請連接麥克風或耳機後重試。", "recordingFailed": "錄音啟動失敗: {{error}}", "modelLoadFailed": "載入模型失敗: {{model}}", - "modelLoadFailedUnknown": "未知模型" + "modelLoadFailedUnknown": "未知模型", + "pasteFailedTitle": "貼上文字失敗", + "pasteFailed": "無法將文字貼上至目前作用中的應用程式。" }, "appLanguage": { "title": "應用程式語言", diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index b105580..7dd690c 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -597,7 +597,9 @@ "noInputDevice": "未检测到音频输入设备。请连接麦克风或耳机后重试。", "recordingFailed": "录音启动失败: {{error}}", "modelLoadFailed": "加载模型失败: {{model}}", - "modelLoadFailedUnknown": "未知模型" + "modelLoadFailedUnknown": "未知模型", + "pasteFailedTitle": "粘贴文本失败", + "pasteFailed": "无法将文本粘贴到当前活动的应用程序中。" }, "appLanguage": { "title": "应用语言",