From fcd16f921e7e1eedc1179a38378171f052a54fa1 Mon Sep 17 00:00:00 2001 From: Phelipe Lorran <112786693+httpsphl@users.noreply.github.com> Date: Sun, 4 Oct 2026 20:24:25 -0300 Subject: [PATCH] fix(desktop): stop every bot with Ctrl+Alt+Esc, which every keyboard has Compact keyboards (60%, 70%) have no End key, so Ctrl+Alt+End could not be pressed there. Esc is on every keyboard, says stop, and Windows does not use Ctrl+Alt+Esc (its own are Ctrl+Shift+Esc, Alt+Esc and Ctrl+Esc). A bot cannot press it: Alt+Esc is among the system's keys, now with a test that names it. --- app/src/i18n/en/desktop.ts | 2 +- app/src/i18n/es/desktop.ts | 2 +- app/src/i18n/pt-BR/desktop.ts | 2 +- crates/botloftd/src/main.rs | 2 +- crates/botloftd/src/platform/desktop/keys.rs | 2 ++ crates/botloftd/src/platform/desktop/mod.rs | 2 +- crates/botloftd/src/platform/desktop/windows/stop_key.rs | 6 +++--- docs/spec.md | 4 ++-- 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/src/i18n/en/desktop.ts b/app/src/i18n/en/desktop.ts index dfaf1ec5..0465da3a 100644 --- a/app/src/i18n/en/desktop.ts +++ b/app/src/i18n/en/desktop.ts @@ -31,7 +31,7 @@ export const desktop = { stoppedBody: (bot: string) => `${bot} cannot read or use your apps until you let it go on.`, emptyTitle: (bot: string) => `${bot} has not used your desktop yet`, emptyBody: "When it reads or uses a window of an app you allowed, the window shows here, live.", - shortcut: "Ctrl+Alt+End stops every bot on your desktop, even with Botloft closed.", + shortcut: "Ctrl+Alt+Esc stops every bot on your desktop, even with Botloft closed.", waiting: "Waiting for the picture…", read: "Read the window", click: (target: string) => `Clicked "${target}"`, diff --git a/app/src/i18n/es/desktop.ts b/app/src/i18n/es/desktop.ts index e434d568..e1ba5022 100644 --- a/app/src/i18n/es/desktop.ts +++ b/app/src/i18n/es/desktop.ts @@ -32,7 +32,7 @@ export const desktop: Messages["desktop"] = { emptyBody: "Cuando lea o use una ventana de una app que permitiste, la ventana aparece aquí, en vivo.", shortcut: - "Ctrl+Alt+Fin detiene a todos los bots en tu escritorio, incluso con Botloft cerrado.", + "Ctrl+Alt+Esc detiene a todos los bots en tu escritorio, incluso con Botloft cerrado.", waiting: "Esperando la imagen…", read: "Leyó la ventana", click: (target) => `Hizo clic en "${target}"`, diff --git a/app/src/i18n/pt-BR/desktop.ts b/app/src/i18n/pt-BR/desktop.ts index 21e048b4..e8708e70 100644 --- a/app/src/i18n/pt-BR/desktop.ts +++ b/app/src/i18n/pt-BR/desktop.ts @@ -31,7 +31,7 @@ export const desktop: Messages["desktop"] = { emptyTitle: (bot) => `${bot} ainda não usou seu desktop`, emptyBody: "Quando ele ler ou usar uma janela de um app que você liberou, a janela aparece aqui, ao vivo.", - shortcut: "Ctrl+Alt+End para todos os bots no seu desktop, mesmo com o Botloft fechado.", + shortcut: "Ctrl+Alt+Esc para todos os bots no seu desktop, mesmo com o Botloft fechado.", waiting: "Esperando a imagem…", read: "Leu a janela", click: (target) => `Clicou em "${target}"`, diff --git a/crates/botloftd/src/main.rs b/crates/botloftd/src/main.rs index c14c0660..db82f339 100644 --- a/crates/botloftd/src/main.rs +++ b/crates/botloftd/src/main.rs @@ -137,7 +137,7 @@ fn run(paths: Paths, config: Config, config_path: PathBuf, scheduled: bool) -> a }); // No bot process survived the last run, so nobody waits for these. approvals::expire_all(&daemon); - // Ctrl+Alt+End stops every bot on the desktop, app open or not (spec 24.9). + // Ctrl+Alt+Esc stops every bot on the desktop, app open or not (spec 24.9). let stopping = Arc::clone(&daemon); match platform::desktop::on_stop_key(move || botloftd::service::desktop::stop_all(&stopping)) { Ok(()) | Err(platform::desktop::DesktopError::Unavailable) => {} diff --git a/crates/botloftd/src/platform/desktop/keys.rs b/crates/botloftd/src/platform/desktop/keys.rs index 7fcf3c29..b50a862a 100644 --- a/crates/botloftd/src/platform/desktop/keys.rs +++ b/crates/botloftd/src/platform/desktop/keys.rs @@ -139,6 +139,8 @@ mod tests { "Alt+Tab", "Alt+Shift+Tab", "Alt+Esc", + // The owner's own key to stop every bot (spec 24.9). + "Ctrl+Alt+Esc", "Ctrl+Esc", "Ctrl+Alt+Delete", "Ctrl+Alt+Del", diff --git a/crates/botloftd/src/platform/desktop/mod.rs b/crates/botloftd/src/platform/desktop/mod.rs index 8b10e1d4..b5f5d04e 100644 --- a/crates/botloftd/src/platform/desktop/mod.rs +++ b/crates/botloftd/src/platform/desktop/mod.rs @@ -150,7 +150,7 @@ pub fn windows() -> Result, DesktopError> { } } -/// Calls `stop` each time the owner presses Ctrl+Alt+End, from now on +/// Calls `stop` each time the owner presses Ctrl+Alt+Esc, from now on /// (spec 24.9). Refused when another program holds the shortcut. pub fn on_stop_key(stop: impl Fn() + Send + 'static) -> Result<(), DesktopError> { #[cfg(windows)] diff --git a/crates/botloftd/src/platform/desktop/windows/stop_key.rs b/crates/botloftd/src/platform/desktop/windows/stop_key.rs index b70cde46..f5322078 100644 --- a/crates/botloftd/src/platform/desktop/windows/stop_key.rs +++ b/crates/botloftd/src/platform/desktop/windows/stop_key.rs @@ -1,11 +1,11 @@ -//! The shortcut that stops every bot on the desktop, Ctrl+Alt+End (spec +//! The shortcut that stops every bot on the desktop, Ctrl+Alt+Esc (spec //! 24.9): registered for the whole session on a thread of its own, so it //! works with the app closed. use std::sync::mpsc; use windows::Win32::UI::Input::KeyboardAndMouse::{ - MOD_ALT, MOD_CONTROL, MOD_NOREPEAT, RegisterHotKey, VK_END, + MOD_ALT, MOD_CONTROL, MOD_NOREPEAT, RegisterHotKey, VK_ESCAPE, }; use windows::Win32::UI::WindowsAndMessaging::{GetMessageW, MSG, WM_HOTKEY}; @@ -27,7 +27,7 @@ pub fn on_stop_key(stop: impl Fn() + Send + 'static) -> Result<(), DesktopError> None, STOP, MOD_CONTROL | MOD_ALT | MOD_NOREPEAT, - u32::from(VK_END.0), + u32::from(VK_ESCAPE.0), ) }; let ok = held.is_ok(); diff --git a/docs/spec.md b/docs/spec.md index 67f19173..fc9c29cd 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -1760,11 +1760,11 @@ No servidor `botloft` (11), como as do navegador. ### 24.9 App -- **Painel "Desktop"** ao lado do chat, o quarto lugar do dock do computador do bot (navegador, terminal, arquivos e desktop, 15.1), com a mesma largura: a foto ao vivo da janela que o bot leu ou em que agiu por último, sobre a mesa na cor do bot, o nome do app e o título da janela, e a linha do que ele acabou de fazer, no idioma do dono ("Clicou em "Save"", "Escreveu em "Nome"", "Escolheu "Large"", "Leu a janela"). No cabeçalho, "Ao vivo" com a foto chegando e **Parar**; parado, o selo "Parado" e um aviso com **Deixar continuar**. Sem janela ainda, o painel explica que ela aparece ali quando o bot ler ou usar um app liberado. Embaixo, a dica do atalho Ctrl+Alt+End. As linhas das tools `desktop_*` no chat têm o botão de tela que abre o painel, como as do navegador abrem o dele. +- **Painel "Desktop"** ao lado do chat, o quarto lugar do dock do computador do bot (navegador, terminal, arquivos e desktop, 15.1), com a mesma largura: a foto ao vivo da janela que o bot leu ou em que agiu por último, sobre a mesa na cor do bot, o nome do app e o título da janela, e a linha do que ele acabou de fazer, no idioma do dono ("Clicou em "Save"", "Escreveu em "Nome"", "Escolheu "Large"", "Leu a janela"). No cabeçalho, "Ao vivo" com a foto chegando e **Parar**; parado, o selo "Parado" e um aviso com **Deixar continuar**. Sem janela ainda, o painel explica que ela aparece ali quando o bot ler ou usar um app liberado. Embaixo, a dica do atalho Ctrl+Alt+Esc. As linhas das tools `desktop_*` no chat têm o botão de tela que abre o painel, como as do navegador abrem o dele. - **O que o daemon guarda** (`DesktopState`, em memória, por bot): a janela (`id`, título, app), a última ação (`DesktopAction`: clicar, escrever, escolher ou rolar, o nome do controle e a opção escolhida; nada depois de só ler), que o app diz no idioma do dono, quando foi, e se o dono o parou. Cada leitura, foto ou ação o atualiza e sai a notificação `desktop.changed` para todos os apps. - **Foto ao vivo:** `desktop.watch {botId}` devolve o estado e a foto mais nova (`DesktopView`) e, enquanto alguma conexão assiste aquele bot, o daemon fotografa a janela dele quatro vezes por segundo (`PrintWindow`, como `desktop_screenshot`) e manda `desktop.frame` só quando a foto mudou, só para quem assiste, como os quadros do navegador (21.7): um app lento recebe a mais nova. Quando a última conexão para (`desktop.unwatch`, outro `desktop.watch` ou a conexão caindo), as fotos param. - **Parar:** `desktop.stop {botId}` marca o bot como parado e `desktop.resume {botId}` o deixa continuar; os dois devolvem o `DesktopState`. Parado, toda tool `desktop_*` dele recusa, de novo depois de esperar o dono ou a vez no desktop, com o aviso de que o dono o parou e que ele deve contar no chat o que fazia. Uma ação já em andamento termina (ela dura no máximo 5 s, 24.5). Parado fica até o dono deixar continuar. Isso vive na memória: um daemon que reinicia começa com ninguém parado. -- **Parar tudo:** o atalho global `Ctrl+Alt+End`, que o daemon registra ao subir (`RegisterHotKey` num thread com fila própria, `MOD_NOREPEAT`), para todos os bots no desktop, mesmo com o app fechado. Se outro programa já tem o atalho (outro daemon do Botloft, de dev, por exemplo), o daemon avisa no log e segue sem ele. +- **Parar tudo:** o atalho global `Ctrl+Alt+Esc`, que o daemon registra ao subir (`RegisterHotKey` num thread com fila própria, `MOD_NOREPEAT`), para todos os bots no desktop, mesmo com o app fechado. Esc está em todo teclado, até nos compactos (60% e 70% não têm End), e o Windows não usa Ctrl+Alt+Esc (os dele são Ctrl+Shift+Esc, Alt+Esc e Ctrl+Esc); um bot não o aperta, porque Alt+Esc está entre as teclas do sistema (24.7). Se outro programa já tem o atalho (outro daemon do Botloft, de dev, por exemplo), o daemon avisa no log e segue sem ele. - O cartão de permissão no chat (24.2) diz " quer ver " com o ícone de tela, o porquê do bot (" diz: ..."), o caminho do executável e o que ver deixa fazer (ler as janelas do app e fotografá-las com o dono no computador, nunca senhas, e onde tirar). Respondido, vira uma linha: " pode ver ", " não pode ver " com a nota, ou sem resposta. As tools `desktop_*` aparecem no chat com o ícone de tela e o porquê como resumo. ### 24.10 Permissões no app