From 4c96e9ba2b6e33d07802657138e564c0d7318166 Mon Sep 17 00:00:00 2001 From: Filip's AI Agent Date: Wed, 15 Jul 2026 22:39:56 +0200 Subject: [PATCH] More accurate warnings. --- README.md | 2 +- src/App.tsx | 6 +++--- src/i18n.ts | 24 ++++++++++++------------ src/lib/audio.ts | 1 + 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 93b85ba..795f237 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Open the localhost URL, enter a Gemini API key, choose a target language, and cl - Chrome or Edge is recommended for tab/system audio sharing - Output-device selection appears only when `HTMLMediaElement.setSinkId()` is supported -Use headphones or a separate output device to prevent translated speech from being captured again. Relay also requests `selfBrowserSurface: "exclude"` where the browser supports it. +Prefer sharing the individual tab that is playing the source audio. Full system-audio capture may also include Relay's translated output in the digital mix. Relay requests `selfBrowserSurface: "exclude"` and `restrictOwnAudio: true` where supported; a separate output device can also help when the operating system isolates its audio from the captured source. ## Privacy and key handling diff --git a/src/App.tsx b/src/App.tsx index 586b697..32627d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,7 +12,6 @@ import { Eye, EyeOff, Gauge, - Headphones, KeyRound, Laptop, Languages as LanguagesIcon, @@ -27,6 +26,7 @@ import { TriangleAlert, Upload, Volume2, + VolumeX, X, } from 'lucide-react' import { APP_LANGUAGES, APP_LANGUAGE_STORAGE, translate, type AppLanguage } from './i18n' @@ -448,7 +448,7 @@ function App() {
- +
{t('guideTipTitle')}

{t('guideTipCopy')}

@@ -636,7 +636,7 @@ function App() {
-
+

{t('loopTitle')}

{t('loopCopy')}

diff --git a/src/i18n.ts b/src/i18n.ts index ca30627..cc634ca 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -21,16 +21,16 @@ const english = { guideKeyCopy: 'Paste your own API key into the key field. It is saved only in this browser’s local storage and sent directly to Google when a session starts.', guideLanguageTitle: 'Choose the translation language', guideLanguageCopy: 'Select the language you want to hear. Turn on live transcripts only if you also want original and translated text; transcripts add billable text tokens.', - guideOutputTitle: 'Prepare your audio output', - guideOutputCopy: 'Use headphones whenever possible. In Settings, supported browsers also let you send the translated voice to a separate speaker or headset.', + guideOutputTitle: 'Prefer sharing a single tab', + guideOutputCopy: 'Sharing only the tab playing the source audio normally keeps Relay’s translated output out of the capture. Full system-audio sharing can also capture Relay.', guideShareTitle: 'Start the translation', guideShareCopy: 'Click “Share audio & translate.” Your browser will open its standard screen-sharing picker.', guidePickerTitle: 'Select a source and enable audio', guidePickerCopy: 'Choose the tab, window, or screen that is playing the audio. Explicitly enable “Share audio” before confirming. Relay will reject sources that do not provide an audio track.', guideListenTitle: 'Listen and monitor the session', guideListenCopy: 'The translated voice begins automatically. The live panel shows elapsed time, sent or dropped packets, playback delay, and reconnect attempts. Click “Stop translation” when finished.', - guideTipTitle: 'Prevent an audio loop', - guideTipCopy: 'Do not share Relay’s own audio. Headphones are safest; otherwise share a different tab or choose a separate translated-audio output device.', + guideTipTitle: 'Keep Relay out of the captured mix', + guideTipCopy: 'Relay does not use your microphone. If you share full system audio, its translated output may still be captured digitally. Prefer a single source tab, or use a separate output device where that isolates the audio.', setupSession: 'Set up your session', clientOnly: 'Client-side only', geminiApiKey: 'Gemini API key', @@ -82,8 +82,8 @@ const english = { emptyCopy: 'Set your language, share a source with audio, and Relay will begin speaking the translation.', voiceTranslation: 'Live voice translation', transcripts: 'Original + translated transcripts', - loopTitle: 'Keep the translation from hearing itself', - loopCopy: 'Headphones are the safest option. You can also send translated speech to a separate output device, or share a different tab—Relay asks supported browsers to exclude this tab.', + loopTitle: 'Keep Relay’s output out of the shared audio', + loopCopy: 'Prefer sharing only the tab playing the source audio. Full system-audio sharing may capture Relay’s output digitally; supported browsers are asked to exclude or suppress this app’s own audio.', chooseOutput: 'Choose output', privacyFooter: 'Your key and audio stay between this browser and Google’s Gemini API.', appLanguage: 'App language', @@ -116,16 +116,16 @@ const slovene: Record = { guideKeyCopy: 'Prilepite svoj ključ API v polje za ključ. Shranjen je samo v lokalni shrambi tega brskalnika in se ob začetku seje pošlje neposredno Googlu.', guideLanguageTitle: 'Izberite jezik prevoda', guideLanguageCopy: 'Izberite jezik, v katerem želite poslušati prevod. Prepise v živo vključite le, če želite tudi izvirno in prevedeno besedilo; prepisi ustvarijo dodatne plačljive besedilne žetone.', - guideOutputTitle: 'Pripravite zvočni izhod', - guideOutputCopy: 'Če je mogoče, uporabite slušalke. V nastavitvah lahko v podprtih brskalnikih prevedeni glas pošljete tudi na ločen zvočnik ali slušalke.', + guideOutputTitle: 'Raje delite posamezen zavihek', + guideOutputCopy: 'Če delite samo zavihek, ki predvaja izvorni zvok, prevedeni izhod aplikacije Relay praviloma ni zajet. Skupna raba celotnega sistemskega zvoka lahko zajame tudi Relay.', guideShareTitle: 'Začnite prevajanje', guideShareCopy: 'Kliknite »Deli zvok in prevajaj«. Brskalnik bo odprl standardni izbirnik za skupno rabo zaslona.', guidePickerTitle: 'Izberite vir in vključite zvok', guidePickerCopy: 'Izberite zavihek, okno ali zaslon, ki predvaja zvok. Pred potrditvijo izrecno vključite možnost »Deli zvok«. Relay zavrne vire, ki ne zagotovijo zvočnega posnetka.', guideListenTitle: 'Poslušajte in spremljajte sejo', guideListenCopy: 'Prevedeni glas se začne predvajati samodejno. Plošča v živo prikazuje trajanje, poslane ali izpuščene pakete, zakasnitev predvajanja in poskuse ponovne povezave. Ko končate, kliknite »Ustavi prevajanje«.', - guideTipTitle: 'Preprečite zvočno zanko', - guideTipCopy: 'Ne delite zvoka zavihka Relay. Najvarnejše so slušalke; sicer delite drug zavihek ali izberite ločeno izhodno napravo za prevedeni zvok.', + guideTipTitle: 'Izločite Relay iz zajetega zvoka', + guideTipCopy: 'Relay ne uporablja mikrofona. Če delite celotni sistemski zvok, se lahko prevedeni izhod vseeno zajame digitalno. Raje delite posamezen izvorni zavihek ali uporabite ločeno izhodno napravo, kadar ta loči zvok.', setupSession: 'Nastavite sejo', clientOnly: 'Samo v brskalniku', geminiApiKey: 'Ključ API Gemini', @@ -177,8 +177,8 @@ const slovene: Record = { emptyCopy: 'Izberite jezik in delite vir z zvokom. Relay bo začel predvajati prevod.', voiceTranslation: 'Glasovno prevajanje v živo', transcripts: 'Izvirni in prevedeni prepis', - loopTitle: 'Preprečite, da bi prevod poslušal samega sebe', - loopCopy: 'Najvarnejša izbira so slušalke. Prevedeni govor lahko pošljete tudi na drugo izhodno napravo ali delite drug zavihek—Relay podprte brskalnike zaprosi, naj ta zavihek izključijo.', + loopTitle: 'Izločite izhod Relay iz zvoka v skupni rabi', + loopCopy: 'Raje delite samo zavihek, ki predvaja izvorni zvok. Skupna raba celotnega sistemskega zvoka lahko digitalno zajame izhod Relay; podprte brskalnike zaprosimo, naj zvok te aplikacije izključijo ali zadušijo.', chooseOutput: 'Izberi izhod', privacyFooter: 'Vaš ključ in zvok ostaneta med tem brskalnikom in Googlovim API-jem Gemini.', appLanguage: 'Jezik aplikacije', diff --git a/src/lib/audio.ts b/src/lib/audio.ts index 62327af..9187be3 100644 --- a/src/lib/audio.ts +++ b/src/lib/audio.ts @@ -136,6 +136,7 @@ export class DisplayAudioCapture { noiseSuppression: false, autoGainControl: false, suppressLocalAudioPlayback: false, + restrictOwnAudio: true, }, selfBrowserSurface: 'exclude', surfaceSwitching: 'include',