Repository navigation
feat: Rychlé přepínání jazyka v tray a snapshot relace - #37
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MR Overview
Overview
stt_language_favoritesse ukládá jako validované JSON pole vedle dosavadního aktivníhostt_language..envaktivní jazyky zůstávají dostupné; poškozený seznam nesmaže aktivní hodnotu.main.pykopíruje konfiguraci před začátkem nahrávání.Kontext a pozadí
P0 z plánu požaduje rychlé přepínání jazyka bez profilů. Dříve byl aktivní jazyk jen volný text v Settings, seznam oblíbených neexistoval a worker dostával živou konfiguraci až při zastavení nahrávání.
stt.transcribe(audio_wav, config)arewrite(text, config)si ponechávají signatury i výběr providerů. Neznámé starší aktivní kódy zůstávají viditelné i tehdy, když by neprošly novým pravidlem pro oblíbené položky.Stavové hodnoty a důležité proměnné
stt_languageAppConfig""znamená Auto.stt_language_favoritesAppConfig/ INI_session_config_TrayApp_settings_dlg_TrayAppDetail změn
1.
normalize_language_code: kanonický kódsrc/config.py · normalize_language_codeJak to funguje: Řetězec se ořízne, převede na malá písmena a ověří proti omezené syntaxi. Co je změna: Nové favority lze zadat normalizovaně místo původního volného textu bez validace. Proč je to tak: Jedna pravidelná reprezentace předchází duplicitám; neověřuje schopnosti konkrétního STT providera.
2.
normalize_language_favorites: pořadí a limitysrc/config.py · normalize_language_favoritesJak to funguje: Pole má maximálně 32 vstupů a kódy se normalizují a deduplikují při zachování prvního výskytu. Co je změna: Přibyla doménová validace nové uložené kolekce. Proč je to tak: QSettings variantní seznam neurčuje bezpečný přenos ani požadované pořadí a tvar.
3.
language_choices: nabídka i pro staré instalacesrc/config.py · language_choicesJak to funguje: Auto, cs a en jsou vždy první, za nimi unikátní oblíbené a případný dosavadní aktivní kód. Co je změna: Settings a tray mají stejný zdroj nabídky. Proč je to tak: Staré INI nebo
.envjazyky nesmějí po migraci zmizet a neznámý jazyk se zobrazuje svým kódem.4.
load_config: dekódování bez ztráty aktivní volbysrc/config.py · load_configJak to funguje: Chybějící klíč ponechá
[]; nesprávný JSON, typ nebo položka zahodí jen kolekci. Platný aktivní kód se na konci loaderu normalizuje, neplatný starší hint zůstane viditelný beze změny. Co je změna: Scalar-only loader nyní dekóduje jedno JSON pole. Proč je to tak: Poškozené oblíbené položky nesmějí zahodit existující aktivní jazyk ani způsobit tichý výběr jiného.5.
save_config: atomický zápis JSON seznamusrc/config.py · save_configJak to funguje: Seznam se validuje a serializuje v existujícím dočasném INI před
os.replace; aktivní hodnota se zapisuje v témž nahrazení. Co je změna: Nové pole nepoužívá implicitní Qt převod Python listu. Proč je to tak: Formát na disku musí být stabilní a jedna konfigurace musí udržet aktivní volbu a seznam pohromadě.6.
validate_config: kontrola oblíbených položeksrc/config.py · validate_configJak to funguje: Chyba nové kolekce míří na STT tab, ostatní provider kontroly zůstaly. Co je změna: Validace Settings nyní zahrnuje oblíbené kódy, ale neblokuje starý aktivní hint s odlišnou syntaxí. Proč je to tak: Zabrání zapsání neplatného seznamu bez rozbití upgradu uživatelů.
7.
import_from_env: normalizace platného backfillusrc/config.py · import_from_envJak to funguje: Backfill se provede jen pro prázdné pole; platné
STT_LANGUAGEpřejde na kanonický kód, historický neplatný hint zůstane. Co je změna: Jazyk z.envsdílí normalizaci s INI. Proč je to tak: Zobrazená aktivní volba se má shodovat s vestavěnými kódy, aniž se smaže staré nastavení.8.
_TrayApp.__init__: vlastník snapshotusrc/main.py · _TrayApp.__init__Jak to funguje: Qt vlastník drží právě jednu aktuální kopii nebo
None. Co je změna: Předtím byl jen živýself._configpředaný workeru. Proč je to tak: Jedna relace potřebuje neměnné vstupy od startu nahrávání do výsledku.9.
_add_choice_submenu: dialogový zámek volebsrc/main.py · _add_choice_submenuJak to funguje: Nové i po Apply přestavěné choice submenu se deaktivuje při otevřeném Settings. Co je změna: Původní tray nabídky zůstávaly přístupné během nested
exec(). Proč je to tak: Jejich uložené změny by mohl otevřený dialog následně přepsat svým draftem.10.
_rebuild_menu: jazyk a rewrite při otevřeném Settingssrc/main.py · _rebuild_menuJak to funguje: Menu vykreslí radio volbu podle aktivního kódu; AI Rewrite checkbox se navíc deaktivuje při otevřeném Settings, zatímco Enabled/Exit zůstanou. Co je změna: Přibylo Language submenu a uzamčení konfiguračních prvků. Proč je to tak: Rychlá volba nemá vyžadovat Settings ani závodit s jeho Apply.
11.
_start_recording: kopie před audiosessionsrc/main.py · _start_recordingJak to funguje: Kopie vzniká ještě před otevřením zařízení, při start failure se vyčistí. Co je změna: Dříve se konfigurace předala workeru až z živého
self._configpo nahrávání. Proč je to tak: Změna jazyka či rewrite během nahrávání nesmí změnit rozběhnutý diktát.12.
_finalize_recording: jediná kopie pro workerasrc/main.py · _finalize_recordingJak to funguje: Nevyužitý/selhaný capture uvolní kopii; validní WAV jde workeru s konfigurací ze startu. Co je změna: Worker už nepřebírá současný živý stav. Proč je to tak: Fallback a rewrite musí dostat stejný jazyk i při pozdější tray změně.
13.
_cancel_recording: discard kopiesrc/main.py · _cancel_recordingJak to funguje: Disable/discard zastaví časovač a mikrofon a zruší konfigurační kopii bez workera. Co je změna: Nový vlastněný snapshot se při cancel uvolňuje. Proč je to tak: Příští diktát nesmí omylem zdědit konfiguraci zahozeného capture.
14.
_on_worker_succeeded: post-key z aktuální relacesrc/main.py · _on_worker_succeededJak to funguje: Terminální úspěch odebere kopii; stávající cancel/disable/exit/settings guard zůstává a při injekci použije
config.post_type_key. Co je změna: Post-key se dřív četl z živéhoself._config. Proč je to tak: Změna nastavení v tray během HTTP práce se nesmí projevit až při výstupu jiné relace.15.
_on_worker_failed: uvolnění po neúspěchusrc/main.py · _on_worker_failedJak to funguje: Před stávajícím reportováním chyby se odstraní snapshot. Co je změna: Přibyla úklidová větev nové relace. Proč je to tak: Neúspěšný worker nesmí držet konfiguraci pro další nahrávku.
16.
_on_worker_cancelled: uvolnění po zrušenísrc/main.py · _on_worker_cancelledJak to funguje: Terminální cancelled signál vyčistí snapshot a vrátí tray do idle. Co je změna: Dříve neexistoval snapshot k uvolnění. Proč je to tak: Zrušený úkol nesmí ovlivnit následující relaci.
17.
_set_language: save nebo návrat volbysrc/main.py · _set_languageJak to funguje: Tray aktualizuje aktivní kód a hned ukládá; při selhání vrátí live config i radio a ukáže chybu. Po úspěchu menu přestaví jen když to volající chce. Co je změna: Nový okamžitý jazykový setter. Proč je to tak: Uživateli se nesmí ukazovat neuložená volba jako trvalá.
18.
_open_settings: uzamčení před nested loopsrc/main.py · _open_settingsJak to funguje: Po nastavení vlastníka dialogu se menu přestaví do disabled stavu ještě před
exec; dosavadní finally reload po Apply/Cancel obnoví menu. Co je změna: Konfigurační tray akce nejsou během dialogu přístupné. Proč je to tak: Nested Qt loop umožňoval souběh draftu s rychlými tray změnami.19.
_exit: uvolnění snapshotu při shutdownsrc/main.py · _exitJak to funguje: Exit zahodí main-owned kopii a zachová existující čekání na pracovní vlákno. Co je změna: Nová relace má explicitní životnost i při odchodu. Proč je to tak: Snapshot nesmí zůstat vlastněný po konci aplikace.
20.
_build_stt_tab: editor místo volného textusrc/settings_dialog.py · SettingsDialog._build_stt_tabJak to funguje: STT tab nabízí výběr aktivního kódu, seřazený seznam a vstup s Add/Edit/Remove tlačítky; provider/fallback formuláře zůstaly. Co je změna: Volný
QLineEditpro jazyk nahradil spravovaný seznam. Proč je to tak: Oblíbené hodnoty musí jít editovat odděleně od aktivního jazyka.21.
_refresh_language_choices: synchronizace návrhusrc/settings_dialog.py · SettingsDialog._refresh_language_choicesJak to funguje: Po úpravě seznamu znovu postaví výběr a nastaví požadovaný aktivní kód. Co je změna: Stav UI už není jediný textový vstup. Proč je to tak: Auto, vestavěné kódy a starý aktivní jazyk musejí zůstat viditelné i po úpravě favoritu.
22.
_on_favorite_selected: načtení položky k editacisrc/settings_dialog.py · SettingsDialog._on_favorite_selectedJak to funguje: Výběr řádku naplní editační pole přes uložený kód, ne přes zobrazovací label. Co je změna: Nová interakce favoritu. Proč je to tak: Známý jazyk má čitelný název, ale pro editaci musí zůstat přesný provider kód.
23.
_favorite_code: validace uživatelského vstupusrc/settings_dialog.py · SettingsDialog._favorite_codeJak to funguje: Při chybné syntaxi zůstane draft a uživatel uvidí vysvětlení. Co je změna: Add/Edit mají jednotnou hranici validace. Proč je to tak: Neplatný hint nemá vstoupit do perzistentních favoritů.
24.
_on_favorite_add: přidání bez duplicitysrc/settings_dialog.py · SettingsDialog._on_favorite_addJak to funguje: Před přidáním se ověří duplicitní kód i limit, potom se uloží UserRole a obnoví výběr. Co je změna: Nová Add akce zůstává v pracovní kopii Settings do Apply/OK. Proč je to tak: Při chybě nemá dojít k tichému přeskládání nebo uložení neplatného seznamu.
25.
_on_favorite_edit: přejmenování zachovává aktivní kódsrc/settings_dialog.py · SettingsDialog._on_favorite_editJak to funguje: Edit odmítne kolizi s jiným favoritem; pokud byl editovaný kód aktivní, vybere se jeho nová hodnota. Co je změna: Nová edit akce mění draft bez zásahu do živého stavu. Proč je to tak: Přejmenování nesmí nechat neviditelný aktivní kód ani přeskočit na jiný jazyk.
26.
_on_favorite_remove: bezpečný výchozí jazyksrc/settings_dialog.py · SettingsDialog._on_favorite_removeJak to funguje: Smazání aktivního doplňkového jazyka zvolí Auto, odstranění cs/en z favoritů zachová vestavěnou aktivní volbu. Co je změna: Nová Remove akce řízeně řeší ztrátu aktivní položky. Proč je to tak: Zmizelá hodnota nesmí dál figurovat jako volba favoritu.
27.
_populate: načtení jazykového draftusrc/settings_dialog.py · SettingsDialog._populateJak to funguje: Reset/import/otevření vyplní draft z konfigurace; starý neznámý aktivní kód zůstane v combo. Co je změna: Původní jediný
setTextje nahrazen populací obou hodnot. Proč je to tak: UI musí odrážet uložené pořadí a aktivní nastavení bez automatického přidání do favoritů.28.
_collect: společné uložení aktivního kódu a seznamusrc/settings_dialog.py · SettingsDialog._collectJak to funguje: Apply/OK sbírá datové kódy z combo a listu do kopie
AppConfig; existující dialog následně validuje a atomicky ukládá. Co je změna: Namísto jediného volného textu ukládá dvojici hodnot. Proč je to tak: Jedno Apply musí mít konzistentní aktivní výběr i favority.29.
make_tray_app: fixture snapshotutests/test_tray_menu.py · make_tray_appJak to funguje: Ručně vytvořený QObject fixture má stejný počáteční stav relace jako skutečná aplikace. Co je změna: Přibylo pole potřebné pro sloty worker výsledků. Proč je to tak: Regresní testy nepoužívají plný tray constructor.
30.
test_five_minute_limit_processes_recorded_audio_once: worker argumenttests/test_tray_menu.py · TrayMenuTests.test_five_minute_limit_processes_recorded_audio_onceJak to funguje: Stávající limit test stále ověřuje jediný worker, nově s kopií. Co je změna: Očekávání argumentu již nepředpokládá živou konfiguraci. Proč je to tak: Limitní automatický stop musí dodržet stejné snapshot pravidlo jako ruční stop.
31.
test_recording_start_failure_returns_to_idle: bez osiřelé relacetests/test_tray_menu.py · TrayMenuTests.test_recording_start_failure_returns_to_idleJak to funguje: Simulovaný device error neponechá snapshot. Co je změna: Původní idle assertion nyní kontroluje i životnost nové kopie. Proč je to tak: Neúspěšný začátek nesmí přenést stav do příští relace.
32.
test_session_freezes_language_rewrite_and_post_key_at_recording_start: dva momenty mutacetests/test_tray_menu.py · TrayMenuTests.test_session_freezes_language_rewrite_and_post_key_at_recording_startJak to funguje: Test mění live config po startu a znovu před výsledkem; worker dostane cs/aktivní rewrite a
type_textenter. Co je změna: Nový regresní test hranice relace. Proč je to tak: Volba během capture nebo processing má platit až pro příští diktát.33.
test_worker_requests_use_frozen_language_for_fallback_and_rewrite: transporttests/test_tray_menu.py · TrayMenuTests.test_worker_requests_use_frozen_language_for_fallback_and_rewriteJak to funguje: Simulovaný primární HTTP výpadek vynutí fallback a další worker s Auto ověří nepřítomnost language i rewrite hintu. Co je změna: Test skutečné STT/rewrite request skladby proti HTTP boundary. Proč je to tak: Pouhá kontrola
AppConfigby neprokázala konzistenci primary/fallback a LLM.34.
fake_post: deterministický transport v request testutests/test_tray_menu.py · TrayMenuTests.test_worker_requests_use_frozen_language_for_fallback_and_rewrite.fake_postJak to funguje: Jedna vnější HTTP hranice vrací explicitní STT/LLM odpovědi a uloží payloady. Co je změna: Nový lokální test double, ne změna produkčního fallbacku. Proč je to tak: Test má být deterministický bez provider credentials.
35.
test_settings_keeps_tray_config_controls_disabled_through_apply: nested looptests/test_tray_menu.py · TrayMenuTests.test_settings_keeps_tray_config_controls_disabled_through_applyJak to funguje: Měří menu před Apply, po přestavbě během dialogu a po jeho zavření. Co je změna: Nové pokrytí disabled tray akcí. Proč je to tak: Přestavba menu po Apply nesmí znovu povolit jiný konfigurační zápis.
36.
inspect_menu: skutečné stavy QWidgetActiontests/test_tray_menu.py · TrayMenuTests.test_settings_keeps_tray_config_controls_disabled_through_apply.inspect_menuJak to funguje: Pomocný callback přečte názvy QAction i widgetových checkboxů. Co je změna: Nová testovací observace skutečného Qt menu. Proč je to tak: Checkbox
AI Rewritenení obyčejná menu QAction a musí se kontrolovat zvlášť.37.
during_dialog: Apply uvnitř exectests/test_tray_menu.py · TrayMenuTests.test_settings_keeps_tray_config_controls_disabled_through_apply.during_dialogJak to funguje: Mock
exec()vyvolá reload během otevřeného dialogu. Co je změna: Nový kontrolovaný nested-loop scénář. Proč je to tak: Obyčejný test po zavření dialogu by mezistav nezachytil.38.
test_language_submenu_shows_builtins_favorites_and_old_active_code: viditelná volbatests/test_tray_menu.py · TrayMenuTests.test_language_submenu_shows_builtins_favorites_and_old_active_codeJak to funguje: Reálné radio widgets obsahují vestavěné, jeden extra a starý aktivní kód. Co je změna: Nový menu test místo kontroly konfiguračního seznamu samotného. Proč je to tak: Uživateli nesmí zmizet starý aktivní jazyk z rychlé nabídky.
39.
test_language_tray_change_persists_and_next_session_uses_new_value: dvě relacetests/test_tray_menu.py · TrayMenuTests.test_language_tray_change_persists_and_next_session_uses_new_valueJak to funguje: Kliknutí Auto se uloží, následně volba Czech spustí relaci, změna na English ovlivní až druhou. Co je změna: Nový test propojení tray widgetu, save a snapshotu. Proč je to tak: Požadavek není jen persistovat, ale také nezměnit aktuální diktát.
40.
select: Qt radio interakce v testu relacítests/test_tray_menu.py · TrayMenuTests.test_language_tray_change_persists_and_next_session_uses_new_value.selectJak to funguje: Helper používá skutečný submenu widget místo přímého volání setteru. Co je změna: Nová testovací cesta uživatelského radio výběru. Proč je to tak: Právě
toggledcallback musí přenést jazyk do uložené konfigurace.41.
test_language_save_failure_restores_live_and_visible_selection: rollbacktests/test_tray_menu.py · TrayMenuTests.test_language_save_failure_restores_live_and_visible_selectionJak to funguje: Simulovaná chyba
save_configpo kliknutí English ověří původní živý jazyk, radio i notifikaci. Co je změna: Nový chybový regresní test. Proč je to tak: Neuložená volba nesmí zůstat vizuálně označená.42.
test_successful_result_types_on_main_thread: očekávání session post-keytests/test_tray_menu.py · TrayMenuTests.test_successful_result_types_on_main_threadJak to funguje: Stávající test výsledného
type_textnastaví platnou relaci. Co je změna: Příprava testu odpovídá novému zdroji post-key. Proč je to tak: Injekce po dokončení workera už nečte live config.43.
test_normalizes_order_and_rejects_invalid_entries: codec hranicetests/test_config.py · LanguageFavoritesTests.test_normalizes_order_and_rejects_invalid_entriesJak to funguje: Pokrývá stabilní normalizaci i samostatné chyby prázdného/chybného/příliš dlouhého kódu a nadlimitního seznamu. Co je změna: Nová doménová sada. Proč je to tak: Neplatné oblíbené hodnoty nesmějí vstoupit do menu ani INI.
44.
test_old_ini_and_malformed_list_preserve_active_language: migracetests/test_config.py · LanguageFavoritesTests.test_old_ini_and_malformed_list_preserve_active_languageJak to funguje: Starý platný
pt-BRse normalizuje, starší nestandardnízh_CNzůstane a tři poškozené JSON varianty vynulují pouze favorites. Co je změna: Nový migrační test nad skutečným INI. Proč je to tak: Upgrade nesmí vymazat dříve funkční jazyk.45.
test_favorites_and_active_language_round_trip_together: INI roundtriptests/test_config.py · LanguageFavoritesTests.test_favorites_and_active_language_round_trip_togetherJak to funguje: Uloží active+favorites, načte kanonické pořadí a dalším zápisem vybere Auto bez ztráty favoritů. Co je změna: Nový persistence test. Proč je to tak: Obě hodnoty musí přežít restart v témže atomickém INI.
46.
test_choices_include_old_or_env_language_without_adding_favorite: .envtests/test_config.py · LanguageFavoritesTests.test_choices_include_old_or_env_language_without_adding_favoriteJak to funguje: Skutečný
.envbackfill je viditelný vlanguage_choicesa seznam zůstane prázdný. Co je změna: Nový důkaz kompatibility zdroje.env. Proč je to tak: Aktivní kód nesmí vyžadovat automatické přidání do favoritů.47.
test_settings_preserves_legacy_active_code_but_rejects_invalid_favorites: odlišná pravidlatests/test_config.py · LanguageFavoritesTests.test_settings_preserves_legacy_active_code_but_rejects_invalid_favoritesJak to funguje:
zh_CNzůstává jako legacy aktivní hodnota, zatímco--ve favorites blokuje Settings. Co je změna: Nový test migračního pravidla. Proč je to tak: Nová syntaxe nemá retroaktivně rozbít nesouvisející Apply.48.
test_apply_saves_active_and_favorites_and_cancel_discards_later_edits: drafttests/test_settings_dialog.py · LanguageSettingsTests.test_apply_saves_active_and_favorites_and_cancel_discards_later_editsJak to funguje: Po Apply přidá další neuložený favorit a Cancel ověří, že disk i původní konfigurace zůstaly u
de. Co je změna: Nový Qt+disk test. Proč je to tak: Editace Settings nesmí mutovat live stav před Apply/OK.49.
test_ok_saves_active_language_and_favorites: potvrzení dialogutests/test_settings_dialog.py · LanguageSettingsTests.test_ok_saves_active_language_and_favoritesJak to funguje: OK normalizuje nově přidaný
pt-BRa perzistuje jej jako aktivní i oblíbený. Co je změna: Nové ověření druhé save cesty. Proč je to tak: Apply a OK musí mít stejnou persistence záruku.50.
test_remove_active_additional_favorite_selects_auto_but_builtin_stays: odstraněnítests/test_settings_dialog.py · LanguageSettingsTests.test_remove_active_additional_favorite_selects_auto_but_builtin_staysJak to funguje: Odebrání
depřepne Auto, odebrání oblíbenéhocsponechá jeho vestavěnou nabídku a výběr. Co je změna: Nový Qt test obou větví. Proč je to tak: Vestavěné jazyky nejsou závislé na oblíbeném seznamu.51.
test_edit_preserves_selection_and_invalid_or_duplicate_input_is_rejected: úpravatests/test_settings_dialog.py · LanguageSettingsTests.test_edit_preserves_selection_and_invalid_or_duplicate_input_is_rejectedJak to funguje: Edit aktivního favoritu zachová aktivní výběr pod novým kódem; neplatný ani kolizní edit se nepoužije. Co je změna: Nový Qt test hranice editoru. Proč je to tak: Neplatná úprava nesmí poškodit draft ani aktivní volbu.
52.
test_unknown_saved_active_code_is_visible_without_becoming_a_favorite: legacy UItests/test_settings_dialog.py · LanguageSettingsTests.test_unknown_saved_active_code_is_visible_without_becoming_a_favoriteJak to funguje: Dialog vykreslí a zachová starý aktivní hint mimo oblíbený seznam. Co je změna: Nový migrační UI test. Proč je to tak: Přechod z volného pole na combo nesmí starou hodnotu smazat.
53.
LANGUAGE_OPTIONSaAppConfig.stt_language_favorites: datový kontraktsrc/config.py · konstanty·AppConfigJak to funguje: Vestavěné volby zůstávají nezávislé na novém
field(default_factory=list). Co je změna: Přibyla malá mapa štítků, regex a limity 32/32, nikoli provider katalog. Proč je to tak: Auto/cs/en musí být dosažitelné i na starém INI bez seznamu.54. Dokumentace uživatelského toku a otevřeného gate
README.md·docs/IMPLEMENTATION.md·docs/IMPLEMENTATION-PLAN.md·01-language.mdREADME vysvětluje UI; současný API/INI kontrakt je v IMPLEMENTATION.md. Plán označuje kód P0 jako implementovaný a manuální Windows ověření jako otevřené, nepředstírá plnou akceptaci. Tyto dokumentační změny nemají funkční runtime efekt.
Ověření
Provedené ověření
Před commitem
10538c7na Linuxu s izolovaným Python prostředím a lokální knihovnou PortAudio (bez systémového sudo):Výsledek:
Coverage
Ověřeno zejména:
.env, normalizace, disk roundtrip a legacy aktivní hint.Nasazení
Bez migrace schématu:
stt_languagezůstává scalar, novéstt_language_favoritesje volitelné JSON pole ve stejném atomickém INI. Po merge má existující pipeline po úspěšném CI+CodeQL sestavit Windows ZIP a checksum; ruční Windows smoke test je stále otevřený gate podle plánu.Kompatibilita / dependencies
PR
feat/tray-language-switching→main; žádná závislost na dalším PR. Dosavadní signaturytranscribearewritezůstávají; starý neznámý aktivní jazyk lze stále zvolit. Požadovaný approving review a úspěšné CI nelze tímto PR obejít.Data-flow diagram
Jak číst tok: Aktivní kód se uchovává v live configu; snapshot vzniká před nahráváním a worker nedostává pozdější tray změny.
Reviewer focus
Při review má smysl zaměřit se hlavně na:
Out of scope
Tento MR záměrně neřeší: