feat(desktop): mode de déclenchement « Basculer » en plus du push-to-talk (T-128) #119
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/T-128-desktop-toggle-hotkey"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tâche
T-xxx — (titre dans docs/STATUS.md)
Changements
Contrat modifié ?
packages/contract/supabase/migrations—docs/API.md/docs/DATA.mdmis à jour, labelcontract, approbation humaine requiseTests
scripts/check.shvertImpacts cross-domaine
Fichiers hors périmètre de l'agent (et pourquoi) :
Suivi
docs/STATUS.mdmis à jour (ligne de la tâche)docs/CHANGELOG.md: entrée ajoutée sous Unreleaseddocs/DECISIONS.md/docs/ECARTS.mdmis à jour si décision ou écartReview PR #119 — T-128 / ADR-016 (mode « Basculer »)
Vérifications transverses
apps/desktop/{src-tauri/src/{commands,config,hotkey,lib}.rs, src/lib/tauri.ts, src/settings/pages/General.tsx}+docs/{CLAUDE.md du domaine,DECISIONS,SPEC,STATUS,CHANGELOG}. Aucun fichierpackages/contract/supabase/migrationstouché — confirmé, pas de labelcontractrequis, pas d'approbation humaine bloquante sur ce point.origin/feat/T-128-desktop-toggle-hotkey, checkout via worktree) :cargo test→ 155/155 verts (conforme au chiffre annoncé dansSTATUS.md),cargo clippy -- -D warnings→ clean,pnpm tsc --noEmit→ clean,pnpm lint(eslint) → clean.1. Rétrocompatibilité
AriaConfig— OKconfig.rs:104-109: les 3 champs (hotkey_modevia#[serde(default)]+HotkeyMode: Default,toggle_hotkey/toggle_tap_countvia#[serde(default = "...")]) sont bien couverts. Le testaria_config_missing_toggle_fields_falls_back_field_by_field_not_to_full_default(config.rs:210-225) désérialise une vraie chaîne JSON legacy (r#"{"hotkey":"Alt+Shift+K","microphone":null,"locale":"en-US"}"#), pas unConfigPatchconstruit à la main — exactement la précaution demandée, et il vérifie quehotkey/localeexistants ne sont pas réinitialisés. Rien à redire ici.2. Mutuelle exclusivité push-to-talk / bascule — BLOQUANT sur les OS non-macOS
macOS : logique correcte.
should_swallow_main_keyne renvoie jamaistruepour un combo à modificateur seul (main_key: None), et les deux mécanismes (main watcher /ToggleWatcherState) sont proprement gardés parhotkey_moderelu à chaque événement (hotkey.rsrégion macOS, ~889-928). Aucun risque d'action simultanée, ni d'accaparement du clavier par le simple fait d'avoir le watcher installé.Sur les autres OS, j'ai vérifié le code source réel de
global-hotkey 0.8.0(version pinnée dansCargo.lock:1791-1806, celle quetauri-plugin-global-shortcutembarque) plutôt que de me fier à la seule lecture dekeyboard-types:platform_impl/windows/mod.rs::key_to_vk) : la fonction qui mappe unCodevers unVIRTUAL_KEYavantRegisterHotKeyn'a aucune entrée pourCode::ControlLeft/AltLeft/ShiftLeft/MetaLeft— elle tombe systématiquement sur_ => return None, doncregister()retourneErr(FailedToRegister("Unknown VKCode for ...")).platform_impl/x11/mod.rs::keycode_to_x11_keysym) : même constat, aucune entrée pour ces 4 codes.Donc
other_os_main::modifier_code(hotkey.rs:334-341) etparse_toggle(hotkey.rs:346-353) parsent avec succès un modificateur seul (ex."Control", le défaut —config.rs:23), mais l'appelapp.global_shortcut().on_shortcut(shortcut, ...)dansregister_toggle(hotkey.rs:369-390, l'erreur est jetée aveclet _ =à la ligne 372) échoue systématiquement et silencieusement sur Windows et Linux, sans le moindrelog::warn!(contrairement àparse_toggle_or_default, qui logue bien un échec de parsing, mais ce n'est pas le chemin emprunté ici puisque le parsing réussit). Même chose pourreregister_toggle(hotkey.rs:394-399) si l'utilisateur retape la même touche ou une autre touche seule.Conséquence concrète : le mode Basculer, avec sa configuration par défaut (
toggle_hotkey = "Control"), ou avec n'importe quel modificateur seul choisi via l'UI (bareModifierOf,General.tsx:57, qui encourage explicitement ce choix), ne fonctionnera jamais sur Windows/Linux, sans aucun message d'erreur ni dans les logs ni dans l'UI — un utilisateur croira juste avoir mal tapé sa séquence. C'est différent du risque déjà documenté dansCLAUDE.md/ADR-016 (« rien ne garantit qu'un raccourci global sans touche principale s'enregistre correctement selon la plateforme ») : ce n'est pas une incertitude, c'est un échec garanti par le code de la dépendance pinnée, pour le cas d'usage par défaut de la fonctionnalité.À corriger avant merge, a minima l'un de :
on_shortcut(pas seulement celui deparse_toggle) dansregister_toggle/reregister_toggle, pour que ce ne soit pas totalement invisible ;toggle_hotkeydifférent pour les OS non-macOS (un vrai chord, ex."Alt+K") puisqu'un modificateur seul ne pourra jamais s'enregistrer via ce backend sur Windows/X11 ;apps/desktop/CLAUDE.md/STATUS.md: « confirmé par lecture du code source deglobal-hotkey 0.8.0: un modificateur seul ne peut pas s'enregistrer sur Windows/Linux, le mode Basculer y nécessite un chord complet » — et retirer ou adapter l'option « modificateur seul » de l'UI pour ces OS, ou au moins prévenir l'utilisateur.Le texte actuel de
CLAUDE.md/ADR-016 (« keyboard-types 0.7.0 confirmé par lecture du crate vendu ») est trompeur en l'état : il vérifie que les variantesCode::ControlLeftetc. existent danskeyboard-types, mais pas que le backendglobal-hotkeysait les enregistrer — ce qu'il ne sait pas faire, précisément.3.
ToggleTapTracker/ resynchronisation suris_recording— OKon_tap(hotkey.rs) reçoitis_recordingrecalculé à chaque appel depuisstate.session.state()(macOS et autres OS), jamais un flag interne dupliqué — confirmé par le teststopping_then_a_fresh_double_tap_starts_again. Le compteur repart bien de zéro après unStartRecordingou unStopRecording, donc unEsc/une erreur qui termine l'enregistrement autrement ne laisse aucun état incohérent : le prochain appui recompte depuis 1. Rien à redire.4. RLS/JWT — sans objet, confirmé
Aucun diff hors
apps/desktop/docs.5. UI — cartes mutuellement exclusives — OK
General.tsx:{cfg.hotkey_mode === "push_to_talk" && (...)}/{cfg.hotkey_mode === "toggle" && (...)}— une seule carte visible à la fois, cohérent avec le sélecteur de mode au-dessus (corrigé dans le 3ᵉ commit de la branche,6c2ddf0).Non-bloquant (à ne pas laisser bloquer le merge)
other_os_main::parse_toggle(hotkey.rs:346) n'accepte qu'un seul modificateur nu, alors quemacos_main::parse_toggle_comboaccepte plusieurs modificateurs nus combinés (ex."Control+Shift", testéparse_toggle_combo_accepts_several_bare_modifiers). Incohérence mineure entre OS, actuellement inatteignable depuis l'UI (bareModifierOfn'autorise qu'un seul modificateur tenu à la fois), donc sans impact utilisateur aujourd'hui.register_toggleest appelé sans condition au démarrage (lib.rs) et n'est jamais désenregistré/réenregistré au seul changement dehotkey_mode(seulement au changement de valeur detoggle_hotkey) — actuellement inoffensif partout (macOS : le swallow reste gardé par le mode ; Windows/Linux : la registration d'un modificateur seul échoue de toute façon, cf. point 2) mais latent : si un jour le défaut non-macOS devient un chord qui s'enregistre avec succès, ce chord resterait accaparé au niveau OS même en modePushToTalk. À garder en tête si le point 2 est corrigé en changeant le défaut vers un chord.Verdict : REQUEST_CHANGES
toggle_hotkeypour ces OS, avant merge.Tout le reste (rétrocompatibilité config, resynchronisation
ToggleTapTracker, UI, absence de tout changement de contrat/schéma, suivi docs STATUS/CHANGELOG/DECISIONS/SPEC) est solide et vérifié.Correctif appliqué suite à la review (bug bloquant trouvé) :
config::DEFAULT_TOGGLE_HOTKEYdevient cfg(target_os)-conditionnel ("Control" sur macOS, "Alt+K" sur les autres OS où un modificateur seul ne s'enregistre pas via global-hotkey 0.8.0), etregister_toggle(other_os_main) loggue désormais en warn tout échec deon_shortcutau lieu d'unlet _ =muet. apps/desktop/CLAUDE.md et docs/STATUS.md/CHANGELOG.md mis à jour en conséquence. 155 tests desktop rust verts, clippy -D warnings clean. Redemande de review avant merge.APPROVE — le correctif (
c4c01ae) règle bien le bug bloquant relevé dans la review précédente.Vérifications effectuées (pas seulement relecture du diff) :
Bug corrigé, confirmé par lecture directe du crate
global-hotkey0.8.0 installé localement (~/.cargo/registry/.../global-hotkey-0.8.0/src/platform_impl/{windows,x11}/mod.rs) :key_to_vk(Windows) etkeycode_to_x11_keysym(X11) n'ont effectivement aucune entrée pour unCodede modificateur seul (ControlLeft/AltLeft/ShiftLeft/MetaLeft) — tous deux retombent sur_ => return None/ erreur, jamais de panic. Le chemin d'erreur remonte proprement :key_to_vk→None→Err(FailedToRegister(...))côtéglobal-hotkey::register, propagé viarun_main_thread!(...)?danstauri-plugin-global-shortcut::register_internal, doncon_shortcutretourne bienErr— exactement ce que capte le nouveauif let Err(e) = ... on_shortcut(...) { log::warn!(...) }danshotkey.rs::other_os_main::register_toggle. Le défautDEFAULT_TOGGLE_HOTKEYétant désormais"Alt+K"(chord) sur ces OS via#[cfg(not(target_os = "macos"))], ce chemin d'échec n'est plus jamais emprunté par défaut ; il reste possible seulement si l'utilisateur reconfigure explicitement un modificateur seul, et c'est maintenant loggué enwarnavec un message explicite (au lieu dulet _ =silencieux d'origine). macOS conserve"Control"(fonctionne nativement viaCGEventTap/CGEvent::get_flags(), watcher indépendantToggleWatcherState), donc aucune régression du comportement déjà validé sur cet OS.Pas de régression, vérifié en exécutant moi-même (worktree sur
origin/feat/T-128-desktop-toggle-hotkey, commitc4c01ae) :cargo test(apps/desktop/src-tauri) : 155/155 verts, conforme à ce qu'annonce la PR.cargo clippy --all-targets -- -D warnings: clean, aucun warning.pnpm tsc --noEmitetpnpm lint(apps/desktop) : clean.parse_toggle_default_hotkey_is_a_chord_on_this_oscohérent avec le nouveau défaut par OS ; les tests macOS (ToggleWatcherState/ToggleCombo, défaut"Control"bare) restent inchangés et verts.Les deux points non-bloquants notés à l'origine restent non-bloquants, aucun des deux n'est affecté par ce commit :
parse_toggle(other_os_main) n'accepte toujours qu'un seul modificateur bare (pas de "Control+Shift" bare) alors queparse_toggle_combo(macOS) accepte plusieurs modificateurs bare viatry_fold— asymétrie mineure préexistante, sans impact sur le défaut ("Alt+K"est un chord classique, passe parparsedans les deux cas) ni sur aucun scénario testé.register_toggle/register_mainrestent enregistrés au démarrage indépendamment dehotkey_mode(lib.rs) ; le filtrage se fait à l'exécution du callback (state.config.get().hotkey_mode == ...) plutôt qu'à la registration. Pas de comportement utilisateur incorrect (le mode inactif ne déclenche jamais la session), juste un raccourci global "réservé" inutilement dans l'OS pendant qu'il n'est pas utilisé — cohérent avec ce qui avait déjà été jugé acceptable.Rien d'autre à signaler : docs (
CLAUDE.md,STATUS.md,CHANGELOG.md,DECISIONS.mdADR-016,SPEC.md) cohérents avec le code, pas de fichier hors périmètreapps/desktop, pas de contrat/migration touché.