Monitor de área de transferência (clipboard) multiplataforma em Java que registra automaticamente tudo o que é copiado.
Escreve em texto (prepend no .txt, estilo diário) e em JSON Lines (.jsonl, append — ideal para integrações).
Conta com configuração externa (via argumentos de linha de comando e/ou system properties), log estruturado com rotação e normalização opcional do conteúdo.
✅ Sem dependências externas. ✅ Empacotado em um único JAR executável (via maven-shade-plugin). ✅ Padrões de projeto aplicados (Strategy + Chain of Responsibility) para extração de conteúdo.
-
🧠 Captura inteligente do conteúdo do clipboard:
- Texto simples (
stringFlavor). - HTML como string/reader (
text/html). - Lista de arquivos (exibe paths).
- Imagens (gera placeholder com dimensões quando possível).
- Fallback para qualquer
text/*via Reader.
- Texto simples (
-
🧾 Registro em dois formatos:
- TXT: estilo diário, com prepend (a entrada mais recente fica no topo).
- JSONL: append (uma linha JSON por entrada), ideal para ETL, indexação e consultas.
-
⚙️ Configuração externa:
- Args (
--history=...,--jsonl=...,--poll=..., etc.) - System properties (
-Dclipboard.history.path=..., etc.)
- Args (
-
🧹 Normalização opcional do conteúdo: quebra de linha, trim trailing por linha e colapso de linhas em branco.
-
📜 Log estruturado com rotação por tamanho e contagem de arquivos (via
java.util.logging). -
🧩 Padrões de design: Strategy + Chain of Responsibility para extratores.
-
🧵 Agendamento com
ScheduledExecutorService(semwhile(true)), shutdown hook para desligamento limpo.
-
Classe principal:
ClipboardHistory(definida comoMain-Classno shade). -
Agendador:
ScheduledExecutorServiceexecuta varreduras a cadaintervaloMs(default: 250ms). -
Assinatura: hash (hex) de tipos (flavors) + conteúdo, evitando duplicações.
-
Escrita:
- TXT: prepend via arquivo temporário +
Files.move(...)(tentaATOMIC_MOVEe faz fallback). - JSONL: cada evento é uma linha JSON (
append).
- TXT: prepend via arquivo temporário +
-
Normalização (opcional, habilitada por padrão):
- Converte quebras para
\n. - Remove espaços à direita por linha.
- Colapsa blocos de linhas em branco (
\n\n\n→\n\n).
- Converte quebras para
-
Logs: console + arquivo com rotação (tamanho e count configuráveis).
-
JDK 25 (conforme
maven.compiler.source/target: 25). -
Maven 3.8+ (recomendado 3.9+).
-
Ambiente com clipboard do AWT disponível:
- Windows / macOS: normalmente presente.
- Linux: requer ambiente gráfico (X11/Wayland). Em servidores, considere Xvfb.
- Evite modo headless (
java.awt.headless=true) — o AWT Clipboard não funciona em headless.
O projeto já inclui o maven-shade-plugin para gerar um JAR executável único.
# Clonar o repositório e entrar na pasta (exemplo)
git clone <URL_DO_SEU_REPO>.git
cd clipboard_history
# Build
mvn -q -DskipTests packageArtefato gerado em target/.
O nome geralmente será clipboard_history-1.0.jar (ou clipboard_history-1.0-shaded.jar, conforme a versão do plugin/empacotamento).
Confira com:
ls -lh target/| Chave | Descrição | Exemplo |
|---|---|---|
--history |
Caminho do TXT (prepend) | --history=/caminho/clipboard-history.txt |
--jsonl |
Caminho do JSONL (append) | --jsonl=/caminho/clipboard-history.jsonl |
--poll |
Intervalo de varredura em ms (default: 250) |
--poll=500 |
--tz |
Timezone (ex.: America/Fortaleza) |
--tz=America/Fortaleza |
--log |
Caminho do arquivo de log | --log=/caminho/clipboard-history.log |
--log-level |
Nível de log (FINE, INFO, WARNING, SEVERE) |
--log-level=FINE |
--log-rotate-size |
Tamanho máx. (bytes) antes de rotacionar | --log-rotate-size=2097152 |
--log-rotate-count |
Quantidade de arquivos de rotação | --log-rotate-count=3 |
--normalize |
Ativa/desativa normalização (true/false) |
--normalize=false |
--normalize-trim-trailing |
Remove espaços à direita por linha | --normalize-trim-trailing=false |
--normalize-collapse-blank |
Colapsa linhas em branco | --normalize-collapse-blank=false |
Equivalentes às flags acima (precedência menor que args):
| Propriedade | Exemplo |
|---|---|
-Dclipboard.history.path |
-Dclipboard.history.path=/caminho/clipboard-history.txt |
-Dclipboard.jsonl.path |
-Dclipboard.jsonl.path=/caminho/clipboard-history.jsonl |
-Dclipboard.poll.ms |
-Dclipboard.poll.ms=250 |
-Dclipboard.tz |
-Dclipboard.tz=America/Fortaleza |
-Dclipboard.log.path |
-Dclipboard.log.path=/caminho/clipboard-history.log |
-Dclipboard.log.level |
-Dclipboard.log.level=INFO |
-Dclipboard.log.rotate.size |
-Dclipboard.log.rotate.size=2097152 |
-Dclipboard.log.rotate.count |
-Dclipboard.log.rotate.count=3 |
-Dclipboard.normalize |
-Dclipboard.normalize=true |
-Dclipboard.normalize.trim_trailing |
-Dclipboard.normalize.trim_trailing=true |
-Dclipboard.normalize.collapse_blank |
-Dclipboard.normalize.collapse_blank=true |
- Args (
--chave=valor) - System properties (
-Dchave=valor) - Defaults (os mesmos do código original)
Linux/macOS:
java -jar target/clipboard_history-1.0.jar \
--history=$HOME/desenvolvimento/temporarios/clipboard-history.txt \
--jsonl=$HOME/desenvolvimento/temporarios/clipboard-history.jsonl \
--poll=250 \
--tz=America/Fortaleza \
--log=$HOME/desenvolvimento/temporarios/clipboard-history.log \
--log-level=INFO \
--log-rotate-size=2097152 \
--log-rotate-count=3 \
--normalize=true \
--normalize-trim-trailing=true \
--normalize-collapse-blank=trueWindows (PowerShell):
java -jar target\clipboard_history-1.0.jar `
--history="C:\Users\Daniel\desenvolvimento\temporarios\clipboard-history.txt" `
--jsonl="C:\Users\Daniel\desenvolvimento\temporarios\clipboard-history.jsonl" `
--poll=250 `
--tz=America/Fortaleza `
--log="C:\Users\Daniel\desenvolvimento\temporarios\clipboard-history.log" `
--log-level=INFO `
--log-rotate-size=2097152 `
--log-rotate-count=3 `
--normalize=true `
--normalize-trim-trailing=true `
--normalize-collapse-blank=trueVia System Properties (-D):
java \
-Dclipboard.history.path=$HOME/desenvolvimento/temporarios/clipboard-history.txt \
-Dclipboard.jsonl.path=$HOME/desenvolvimento/temporarios/clipboard-history.jsonl \
-Dclipboard.poll.ms=250 \
-Dclipboard.tz=America/Fortaleza \
-Dclipboard.log.path=$HOME/desenvolvimento/temporarios/clipboard-history.log \
-Dclipboard.log.level=INFO \
-Dclipboard.log.rotate.size=2097152 \
-Dclipboard.log.rotate.count=3 \
-Dclipboard.normalize=true \
-Dclipboard.normalize.trim_trailing=true \
-Dclipboard.normalize.collapse_blank=true \
-jar target/clipboard_history-1.0.jarFormato humano e fácil de consultar:
[21-10-2025 11:22:33]
--------------------------------------------------------------------------------
Texto copiado (normalizado)...
pode ter múltiplas linhas
--------------------------------------------------------------------------------
- Sempre escreve no topo (entrada mais recente primeiro).
timestampusa o timezone configurado (--tz/-Dclipboard.tz).
Cada linha é um JSON autocontido:
{"ts_iso":"2025-10-21T11:22:33-03:00","timestamp_formatado":"21-10-2025 11:22:33","assinatura":"a4f9b12","flavors":["text/plain; charset=unicode","text/html; class=java.lang.String"],"conteudo":"Texto copiado (normalizado)...\nlinha 2"}Campos:
ts_iso: timestamp ISO-8601 com offset.timestamp_formatado: no padrão configurado (defaultdd-MM-yyyy HH:mm:ss).assinatura: hash (hex) de tipos + conteúdo (usado para evitar duplicação).flavors: lista de MIME types disponíveis no snapshot.conteudo: texto/placeholder (após normalização, se habilitada).
Dica: consulte com
jq, envie para Logstash/Fluent Bit, grep, etc.
- Saída: console + arquivo (configurável).
- Formato: uma linha por evento —
timestamp ISO | nível | logger | mensagem. - Rotação: por tamanho (
--log-rotate-size) e contagem (--log-rotate-count).
Exemplo de mensagem:
2025-10-21T11:22:33-03:00 [INFO] ClipboardHistory - Entrada textual registrada em: /home/.../clipboard-history.txt
Ativada por padrão. Pode ser controlada por:
--normalize=true|false--normalize-trim-trailing=true|false--normalize-collapse-blank=true|false
Aplicações:
\r\ne\r→\n- Remoção de espaços à direita por linha
- Colapso de blocos de linhas em branco (≥3) para 2 linhas em branco
Exemplo:
Entrada original:
"foo \r\n\r\n\r\nbar \r\n"
Após normalização:
"foo\n\nbar\n"
Crie /etc/systemd/system/clipboard-history.service:
[Unit]
Description=Clipboard History (Java)
After=network.target
[Service]
User=SEU_USUARIO
WorkingDirectory=/caminho/do/projeto
ExecStart=/usr/bin/java -jar /caminho/do/projeto/target/clipboard_history-1.0.jar --tz=America/Fortaleza --poll=250
Restart=on-failure
Environment="JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8"
[Install]
WantedBy=multi-user.targetDepois:
sudo systemctl daemon-reload
sudo systemctl enable clipboard-history
sudo systemctl start clipboard-history
sudo systemctl status clipboard-historyNota: o serviço precisa ter acesso a um servidor gráfico (clipboard do AWT). Em ambientes servidores, avalie Xvfb/XWayland. Em headless puro, o AWT Clipboard não funcionará.
-
java.awt.HeadlessExceptionO AWT precisa de ambiente gráfico. Execute com um display válido (X11/Wayland) ou utilize Xvfb. -
IllegalStateException/ “clipboard ocupado” Outro processo está usando o clipboard. O app ignora e tenta novamente automaticamente (próximo tick). -
Nada é gravado
- Verifique permissões de escrita nos caminhos de TXT/JSONL/LOG.
- Confirme se o timezone está correto.
- Aumente a verbosidade:
--log-level=FINE.
-
Wayland Em alguns ambientes Wayland, a implementação do clipboard pode variar. Usar XWayland pode melhorar a compatibilidade.
A extração de conteúdo usa Strategy + Chain of Responsibility. Para suportar novos formatos (ex.: RTF, custom MIME):
-
Crie uma classe que implemente a interface
EstrategiaExtracaocom:boolean suporta(Transferable t)Optional<String> extrair(Transferable t)
-
Adicione a nova estratégia à lista (
extratores) na inicialização, respeitando a ordem de prioridade.
-
Dados sensíveis: o clipboard pode conter senhas, tokens, dados pessoais.
- Armazene os arquivos (TXT/JSONL/LOG) em local seguro.
- Avalie criptografia em disco (LUKS/BitLocker/FileVault) ou partição protegida.
- Se necessário, implemente exclusões/mascaramento por regex antes de gravar.
-
Tamanho do histórico: JSONL cresce continuamente; considere logrotate do sistema para TXT/JSONL também, se desejado.
-
Intervalo de poll:
--poll. Valores menores → mais responsivo; maiores → menor uso de CPU.
Defina a licença conforme sua necessidade (por exemplo, MIT).
Se optar por MIT, adicione um arquivo LICENSE com o texto correspondente.
O pom.xml já define o shade plugin com:
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>ClipboardHistory</mainClass>
</transformer>Ou seja, o JAR final é executável com java -jar e usa ClipboardHistory como entrypoint.