remotepad
v0.1.0
Published
Seu celular como superfície de controle programável para o PC — deck de botões que muda sozinho com o programa em foco e mostra a saída dos comandos
Maintainers
Readme
remotepad
Seu celular como superfície de controle programável para o PC — sem app de loja, sem conta, sem nuvem. Um comando no terminal, um QR code, e a tela do celular vira um deck de botões que muda sozinho conforme o programa que está em foco e mostra a saída dos comandos em tempo real.
Sim, é uma alternativa ao Stream Deck. E é bem mais que isso: o celular não é só entrada, é também tela.
Começando
Precisa de Node 22 ou mais novo. Windows é a plataforma suportada nesta versão — o pacote
declara os: win32, então em macOS ou Linux o npm install recusa na hora, em vez de instalar
algo que só falharia no primeiro toque.
npx remotepad initIsso cria um deck.yaml de exemplo no diretório atual. Depois:
npx remotepadO terminal mostra um QR code. Aponte a câmera do celular, abra o link e confira se o número de dez dígitos que aparece no celular é o mesmo que está no terminal — é essa conferência que garante que você pareou com o seu PC, e não com outra pessoa na rede.
O navegador vai avisar sobre o certificado. Isso é esperado: o remotepad gera um certificado próprio no primeiro boot. A confiança de verdade não vem dele, vem das chaves trocadas no pareamento.
Montando os botões
Há dois caminhos, e os dois escrevem o mesmo arquivo.
Pelo navegador do PC
Com o remotepad rodando, abra:
https://127.0.0.1:3000/configClique num botão para editar, em + botão para criar. Salvar grava o deck.yaml e o celular
muda na hora.
Não é preciso saber atalho nenhum. A ação sai de uma lista de mais de 90 prontas, com busca:
| Categoria | O que tem | |---|---| | Mídia | mudo, volume, play/pause, faixa anterior e próxima | | Edição | copiar, colar, recortar, desfazer, refazer, salvar, localizar, imprimir… | | Tela | print, recorte, print da janela, gravar tela, projetar | | Janelas | alternar janela, áreas de trabalho virtuais, encaixar, maximizar | | Sistema | bloquear, explorador, gerenciador de tarefas, emoji, histórico de cópia | | Navegador | abas, voltar/avançar, zoom, favoritos, downloads, ferramentas do dev | | Código | paleta de comandos, terminal, buscar no projeto, depurar, Git | | Reunião | microfone, câmera, mão levantada e compartilhar tela no Teams |
Quem já sabe o atalho continua podendo digitá-lo direto, junto com comando de shell e requisição HTTP.
Botões que abrem programas
Escolha Abrir um programa em Ação e o editor lista o que está instalado na máquina — lido do Menu Iniciar, com o ícone real de cada programa. Buscar "spot" acha o Spotify; escolher já preenche o nome e o ícone do botão.
Funciona com programas comuns e com aplicativos da Microsoft Store. O botão abre e responde na hora — o remotepad não fica esperando você fechar o programa, e encerrar o remotepad não fecha o que ele abriu.
As ferramentas do próprio Windows (Editor do Registro, Prompt de Comando, consoles administrativos) aparecem numa seção separada, no fim: são programas legítimos, mas são dezenas, e não devem disputar espaço com o que você instalou.
Instalou um programa com o remotepad aberto? O botão ↻ ao lado da busca procura de novo.
No deck.yaml isso vira:
- type: key
label: Spotify
icon: 'app:spotify'
action: { open: 'C:\Users\você\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Spotify.lnk' }O alvo é o atalho, e não o executável, de propósito: o .lnk carrega argumentos e diretório de
trabalho, e resolve caminhos que mudam a cada atualização — o Discord instala em
app-1.0.9253, e esse número muda sozinho.
+ página oferece páginas inteiras montadas — Mídia, Reunião, Edição, Janelas, Captura, Navegador, Editor de código, Sistema — em vez de uma grade vazia. A de Editor de código já vem com a regra de foco: ela assume sozinha quando o VS Code está na frente.
Cada botão tem um ícone, escolhido numa grade de mais de 60. É ele que faz o deck ser lido de relance no celular: numa tela de 6", a forma é reconhecida antes de a palavra ser lida.
O editor só abre no computador que roda o remotepad. Nem da rede, nem do celular pareado, nem de
outra aba do navegador: quem escreve o deck.yaml escolhe que comandos esta máquina executa, e
parear autoriza mandar as ações que o deck declara — não reescrever a lista delas.
Salvar pelo editor reescreve o arquivo a partir da estrutura, então comentários se perdem. Se o seu
deck.yamltem comentários que importam, edite-o à mão.
Pelo editor de texto
O deck.yaml é a fonte da verdade. Editar e salvar recarrega no celular sem reiniciar nada, e o
arquivo continua versionável, revisável em PR e compartilhável — o que nenhum concorrente com
editor gráfico e banco local oferece.
O deck.yaml
version: 1
settings:
port: 3000
theme: dark
haptics: true
pages:
# Página padrão: vale quando nenhuma regra de foco casa.
- id: home
grid: [3, 2]
widgets:
- type: key
label: Bloquear
action: { keys: meta+l }
# Assume sozinha quando o VS Code está em foco.
- id: dev
when: { process: [Code.exe, cursor.exe] }
grid: [3, 2]
widgets:
- type: key
label: Testes
action: { run: npm test }
output: stream # a saída aparece na tela do celular
- type: toggle
label: Mic
action: { keys: ctrl+shift+m }
color: { on: "#22c55e", off: "#ef4444" }
- type: key
label: Salvar tudo
# o host traz a janela para frente antes de mandar a tecla
action: { keys: ctrl+k, target: Code.exe }Editar e salvar o arquivo recarrega no celular na hora — não precisa reiniciar. Se o arquivo tiver erro de sintaxe, o deck que está no ar continua funcionando e o erro aparece no terminal com a linha e o que fazer.
Widgets
| Tipo | O que faz |
|---|---|
| key | Botão. Toca e executa a ação. |
| toggle | Botão com estado ligado/desligado, sincronizado com o host. |
| slider | Valor contínuo de 0 a 1. Sem fonte de valor nesta versão — ver limitações. |
| text | Só saída: mostra um valor que o host empurra. Idem. |
Ações
| Ação | Exemplo |
|---|---|
| keys | action: { keys: ctrl+shift+p } — atalho de teclado |
| run | action: { run: npm test } — comando de shell |
| http | action: { http: { url: "http://localhost:4455/cena", method: POST } } |
| open | action: { open: "C:\...\Spotify.lnk" } — abre um programa instalado |
Cada ação carrega exatamente um mecanismo. Some output: stream a um run para ver a saída
rolando no celular, cwd a um run para escolher o diretório, e target: <processo> a um keys
para o host focar a janela certa antes de enviar a tecla.
Cada modificador só vale junto do mecanismo dele. No lugar errado ele sai, com um aviso no
terminal apontando a linha — um target ao lado de um run prometia uma janela em foco que
nunca aconteceria, e um output: stream numa tecla marcava na tela do celular um painel que
nunca abriria. O resto do botão continua funcionando, e o deck sobe.
Já o que o remotepad não reconhece é erro, e o deck não sobe: atalho que não existe, ou campo
escrito errado como key no lugar de keys. A diferença é que aqui não há correção óbvia —
adivinhar seria pior do que recusar dizendo a linha.
Nos dois casos o objetivo é o mesmo: nada vira um botão que aparece na tela e não faz nada.
Teclas
Além de letras, números, f1–f24 e as teclas nomeadas usuais (enter, escape, tab, setas,
printscreen…), o remotepad entende as teclas de mídia, que quase nenhum teclado tem:
| Escreva | Faz |
|---|---|
| mute | Liga e desliga o som do sistema |
| volup / volumeup | Aumenta o volume |
| voldown / volumedown | Diminui o volume |
| play / playpause | Toca ou pausa |
| next / prev | Próxima faixa / anterior |
| stop | Para a reprodução |
Modificadores: ctrl, alt, shift, meta (também aceita win, cmd, super).
Comandos
| Comando | Para quê |
|---|---|
| remotepad | Sobe o servidor e mostra o QR |
| remotepad init | Cria um deck.yaml de exemplo |
| remotepad qr | Mostra um QR novo, para parear outro aparelho (pede ao remotepad que já estiver no ar) |
| remotepad devices | Lista os aparelhos pareados |
| remotepad revoke <id> | Corta o acesso de um aparelho |
Como funciona a segurança
- O QR carrega a chave pública do host e um segredo de uso único que vale 2 minutos.
- O celular gera o próprio par de chaves e faz um handshake Noise_IK com o host — o mesmo padrão que o WireGuard usa.
- O número de dez dígitos que aparece nas duas telas é derivado das duas chaves. Se ele bate, o handshake foi com quem você acha que foi.
- Depois disso, o tráfego vai cifrado com chaves de sessão próprias, por cima do TLS. Mesmo que alguém consiga fazer você aceitar um certificado que não devia, não consegue ler nem forjar comando.
- Aparelho revogado não volta por handshake. Só parear de novo, de propósito, o traz de volta.
O raciocínio por trás de cada escolha está nos comentários do código, junto do código que ele
explica — src/core/identity/ para o handshake, src/core/pairing/ para o QR e o fingerprint.
Limitações conhecidas
Coisas que não funcionam nesta versão, ditas na cara em vez de descobertas no uso:
- Só Windows. A troca de página por foco usa a API de janelas do Windows. macOS e Linux/X11 precisam de um adapter próprio (a porta já existe, a implementação não).
- Um deck, um computador. Não há multi-host nem sincronização entre aparelhos.
- Sem denylist de anti-cheat. O plano é desabilitar
keysquando um anti-cheat kernel-level está rodando. Isso exige enumerar os processos da máquina, não só o em foco — fazer pela metade daria falsa sensação de proteção. Se você joga com anti-cheat, não use açõeskeyscom o jogo aberto. - Sem
deck.config.ts. Só YAML por enquanto. slideretextnão têm de onde tirar valor. Os dois existem no protocolo e o host sabe empurrar estado, mas nada nesta versão produz esse estado — não há leitura de CPU, de volume nem de mic. Na prática: umtextaparece no celular mostrando um travessão, para sempre. O editor avisa isso na hora de escolher o tipo. Otogglefunciona: ele acende quando a ação dá certo.- Comentários somem ao salvar pelo editor visual. O arquivo é reescrito a partir da estrutura.
Quem depende de comentários deve editar o
deck.yamlà mão. - Sem sensores. Giroscópio, câmera, microfone e NFC estão fora desta versão.
- iOS apaga o pareamento depois de 7 dias sem uso. É o comportamento do Safari com sites não
instalados. Instalar como PWA ajuda; se acontecer,
remotepad qre parear de novo resolve. - AP isolation. Em muitas redes de hotel e algumas de empresa, dispositivos não se enxergam. Nesse caso, use o hotspot do celular: o PC entra na rede do celular e o problema some.
Rodando a partir do repositório
Para trabalhar no código — ou rodar uma versão que ainda não foi publicada — compile e aponte o comando para esta pasta.
npm install
npm run build && npm run build:web
npm linkO npm link deixa remotepad disponível como comando global, apontando para esta pasta. A partir
daí, em qualquer diretório:
remotepad init
remotepadO comando rpad faz exatamente o mesmo, para quem digita muito.
Se preferir não usar npm link, chame o binário direto: node D:/caminho/para/remotepad/dist/cli/index.js.
Depois de mexer no código, recompile (npm run build para o host, npm run build:web para a
PWA) — o npm link continua valendo.
Se o celular não conectar
Na ordem de probabilidade:
- Firewall do Windows. Na primeira vez que o Node abre uma porta, o Windows pergunta se
permite; se você recusou (ou nada apareceu), libere a porta:
Rode num terminal como administrador. Para conferir se o servidor está mesmo escutando em todas as interfaces:netsh advfirewall firewall add rule name="remotepad" dir=in action=allow protocol=TCP localport=3000Get-NetTCPConnection -LocalPort 3000 -State Listen. - Redes diferentes. O celular precisa estar no mesmo Wi-Fi que o PC — não vale um no 5 GHz de convidados e outro no cabo, se as redes forem isoladas.
- AP isolation. Comum em hotel e empresa: os dispositivos não se enxergam. Saída rápida — ligue o hotspot do celular e conecte o PC nele; aí o celular e o PC estão na mesma rede e o isolamento some.
- O endereço do QR. O
remotepadlista os endereços que usou (endereços: ...). Se aparecer algo como172.xde Docker/WSL/Hyper-V em vez do IP da sua rede, abra uma issue — é bug de detecção.
Desenvolvimento
npm install
npm test # suíte do host (Node) + suíte da PWA (jsdom)
npm run typecheck
npm run build # compila o host
npm run build:web # compila a PWA
npm run test:e2e # navegador de verdade, via PlaywrightComo o código está organizado
src/core/ lógica pura, sem I/O — é onde moram as decisões
src/ports/ as interfaces das fronteiras (teclado, shell, janelas, apps…)
src/adapters/ as implementações reais, e os fakes usados nos testes
src/server/ o host: runtime, servidor HTTP/WebSocket, editor
src/cli/ os comandos do terminal
web/src/ a PWA do celular e o editor do PC (Preact)
tests/ suíte do host
e2e/ navegador de verdadePorts & adapters de ponta a ponta: o que precisa de teclado, rede ou relógio de verdade fica atrás de uma porta. É por isso que a suíte roda inteira sem tocar no sistema — e é por isso que os testes de integração sobem o host completo com adapters falsos, em porta 0.
Duas regras que o projeto segue e que explicam boa parte do código:
- Nada de produção sem um teste que falhe antes. Vários defeitos deste repositório foram encontrados assim, inclusive por verificação de mutação — remover a linha e conferir se o teste realmente morde.
- Contrato entre as pontas vira teste. Toda tecla que o núcleo aceita tem tradução no adapter; todo ícone que o catálogo pede existe no conjunto. São falhas silenciosas por natureza — sem teste, quem descobre é o usuário.
O estado atual e o que vem a seguir estão em ROADMAP.md.
