feat: extensão de navegador para gerar CPF/CNPJ no menu de contexto
Adiciona uma extensão MV3 (Firefox e Chromium/Brave) que insere CPF ou CNPJ
válido direto no campo focado, pelo menu de contexto de qualquer input,
textarea ou contenteditable.
Extensões rodam em sandbox sem acesso a processos, então não há como invocar
os binários cpfgen/cnpjgen; fazer isso exigiria um host de native messaging
registrado no SO. Como os algoritmos são curtos e puros, foram portados para
JS em extension/generator.js, a partir de main.go (CPF) e de
cnpj_generator/cnpj/generate.go (CNPJ), trocando math/rand por
crypto.getRandomValues. test_generator.mjs compara as duas implementações
para que não divirjam.
A inserção usa document.execCommand("insertText"), que emite os eventos
nativos e portanto funciona em campos controlados por React/Vue — atribuir
.value diretamente seria descartado por esses frameworks.
Somente desktop: o Firefox para Android não implementa a API menus.
Testes carregam a extensão de verdade nos dois navegadores e verificam que os
8 itens de menu são criados e que a inserção funciona nos três tipos de campo.
This commit is contained in:
parent
688c5a48f3
commit
7632cfde2c
14 changed files with 792 additions and 4 deletions
50
extension/insert.js
Normal file
50
extension/insert.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Injetado na aba via chrome.scripting.executeScript. Roda isolado, sem imports:
|
||||
// recebe o texto já gerado e o escreve no campo que abriu o menu de contexto.
|
||||
function insertIntoFocused(text) {
|
||||
const el = document.activeElement;
|
||||
if (!el) return false;
|
||||
|
||||
const tag = el.tagName;
|
||||
const isInput = tag === "INPUT" || tag === "TEXTAREA";
|
||||
|
||||
if (!isInput && !el.isContentEditable) return false;
|
||||
|
||||
el.focus();
|
||||
|
||||
// Caminho preferido: execCommand emite os eventos de input nativos, então
|
||||
// campos controlados por React/Vue/Angular enxergam a mudança. Uma atribuição
|
||||
// direta em .value seria silenciosamente descartada por esses frameworks.
|
||||
if (document.execCommand("insertText", false, text)) return true;
|
||||
|
||||
// Fallback para quando execCommand é bloqueado ou não suportado.
|
||||
if (isInput) {
|
||||
const start = el.selectionStart ?? el.value.length;
|
||||
const end = el.selectionEnd ?? el.value.length;
|
||||
|
||||
// Usa o setter nativo do prototype: React sobrescreve o setter da instância
|
||||
// para rastrear o estado, e escrever por ele não notificaria o framework.
|
||||
const proto =
|
||||
tag === "TEXTAREA"
|
||||
? HTMLTextAreaElement.prototype
|
||||
: HTMLInputElement.prototype;
|
||||
const setter = Object.getOwnPropertyDescriptor(proto, "value").set;
|
||||
setter.call(el, el.value.slice(0, start) + text + el.value.slice(end));
|
||||
|
||||
const pos = start + text.length;
|
||||
el.setSelectionRange(pos, pos);
|
||||
} else {
|
||||
const sel = window.getSelection();
|
||||
if (sel && sel.rangeCount > 0) {
|
||||
const range = sel.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
range.insertNode(document.createTextNode(text));
|
||||
sel.collapseToEnd();
|
||||
} else {
|
||||
el.textContent += text;
|
||||
}
|
||||
}
|
||||
|
||||
el.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
el.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
return true;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue