Este tutorial cria um protótipo mínimo e útil em Manifest V3: um campo de texto privado nas páginas de detalhes dos Pins, associado ao ID numérico do Pin e salvo em chrome.storage.local. O projeto é intencionalmente um protótipo, não uma réplica das antigas notas do Pinterest pronta para produção.
O DOM do Pinterest pode mudar sem aviso. Isole os seletores, não imite a identidade visual protegida do Pinterest e teste todos os tipos de página e idiomas que você pretende atender.
1. Crie a extensão em Manifest V3
Crie uma pasta com manifest.json e content.js. A referência de manifestos do Chrome é a fonte oficial para consultar as chaves disponíveis.
{
"manifest_version": 3,
"name": "Protótipo de contexto do Pin",
"version": "0.1.0",
"description": "Salva uma nota privada e local para um Pin do Pinterest.",
"permissions": ["storage"],
"content_scripts": [{
"matches": ["https://*.pinterest.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}]
}
O padrão de host é amplo porque o Pinterest usa navegação no cliente. Se o recurso atender menos rotas ou domínios nacionais, restrinja esse padrão e documente o escopo.
2. Extraia uma chave estável da URL do Pin
Não associe a nota à posição de um cartão nem ao título visível. Extraia o ID numérico de um caminho canônico /pin/…/ e retorne null em todas as outras páginas.
function getPinId(url = location.href) {
try {
const { pathname } = new URL(url);
const match = pathname.match(/\/pin\/(\d+)\/?/);
return match ? match[1] : null;
} catch {
return null;
}
}
Teste URLs válidas de detalhes, parâmetros de consulta, páginas que não são Pins, URLs malformadas e todos os padrões regionais que você afirma atender. Nunca associe silenciosamente uma nota quando o ID for incerto.
3. Armazene um registro com versão
Mantenha o esquema explícito para permitir migrações futuras:
async function readNote(pinId) {
const key = 'pin-note:' + pinId;
const result = await chrome.storage.local.get(key);
return result[key] ?? { version: 1, pinId, text: '', updatedAt: null };
}
async function writeNote(pinId, text) {
const key = 'pin-note:' + pinId;
const record = {
version: 1,
pinId,
text,
updatedAt: new Date().toISOString()
};
await chrome.storage.local.set({ [key]: record });
return record;
}
O Chrome documenta um limite padrão de 10 MB para storage.local. A permissão unlimitedStorage remove esse limite. O Chrome também informa que o armazenamento local da extensão é apagado quando ela é removida. Consulte a documentação atual da API Storage e crie uma forma de exportação antes que usuários confiem dados importantes ao produto.
4. Monte um editor isolado e acessível
Um Shadow DOM reduz colisões acidentais de estilos. O exemplo adiciona o editor ao corpo da página para não depender de uma classe frágil do Pinterest:
async function mountEditor(pinId) {
if (document.querySelector('[data-pin-context-root]')) return;
const host = document.createElement('aside');
host.dataset.pinContextRoot = '';
host.setAttribute('aria-label', 'Nota privada do Pin');
document.body.append(host);
const root = host.attachShadow({ mode: 'open' });
const label = document.createElement('label');
label.textContent = 'Nota privada';
const editor = document.createElement('textarea');
editor.id = 'pin-note-editor';
label.htmlFor = editor.id;
editor.setAttribute('aria-label', 'Nota privada para este Pin');
const status = document.createElement('span');
status.setAttribute('role', 'status');
const saved = await readNote(pinId);
editor.value = saved.text;
root.append(label, editor, status);
bindAutosave(editor, status, pinId);
}
Crie textos da interface com textContent, não com conteúdo não confiável em innerHTML. Adicione estilos de foco visíveis, contraste suficiente, um rótulo real, acesso por teclado e mensagens de status que não roubem o foco.
5. Salve sem gravar a cada tecla pressionada
function bindAutosave(editor, status, pinId) {
let saveTimer;
editor.addEventListener('input', () => {
status.textContent = 'Alterações não salvas';
clearTimeout(saveTimer);
saveTimer = setTimeout(async () => {
try {
await writeNote(pinId, editor.value);
status.textContent = 'Salvo localmente';
} catch (error) {
console.error('Falha ao salvar a nota do Pin', error);
status.textContent = 'Não foi possível salvar';
}
}, 400);
});
}
Em produção, grave qualquer entrada pendente antes da mudança de página, impeça que duas abas sobrescrevam uma à outra, defina um tamanho máximo para a nota e mostre os erros de cota ao usuário.
6. Trate a navegação do Pinterest no cliente
Um manipulador executado apenas no carregamento não percebe a navegação entre o feed e a página de detalhes. Use uma função de reconciliação idempotente e observe as mudanças sem remontar o editor a cada mutação:
let scheduled = false;
async function reconcile() {
scheduled = false;
const pinId = getPinId();
const existing = document.querySelector('[data-pin-context-root]');
if (!pinId) {
existing?.remove();
return;
}
if (existing?.dataset.pinId === pinId) return;
existing?.remove();
await mountEditor(pinId);
const mounted = document.querySelector('[data-pin-context-root]');
if (mounted) mounted.dataset.pinId = pinId;
}
new MutationObserver(() => {
if (scheduled) return;
scheduled = true;
requestAnimationFrame(reconcile);
}).observe(document.documentElement, { childList: true, subtree: true });
reconcile();
Uma versão de produção também deve observar diretamente o estado da navegação quando possível e desconectar observadores desnecessários. Meça o código em feeds longos: um recurso correto que prejudica o desempenho da página ainda é uma extensão ruim.
7. Respeite as restrições de segurança do Manifest V3
O Manifest V3 proíbe código executável hospedado remotamente. Empacote todo o JavaScript executável com a extensão, mantenha a política de segurança de conteúdo padrão salvo quando houver uma justificativa, evite execução dinâmica e solicite apenas as permissões necessárias. A visão geral do Manifest V3 do Chrome explica o modelo da plataforma.
8. Acrescente os recursos necessários a um produto real
- exportação e importação em JSON, com validação do esquema e tratamento de conflitos;
- exportação legível por pessoas para fins de recuperação;
- busca e tags opcionais;
- informações sobre uso do armazenamento e cota;
- exclusão de uma nota e de todos os dados;
- política de privacidade coerente com o comportamento observável;
- testes de migração entre versões do esquema;
- avisos claros sobre desinstalação e ausência de sincronização;
- telemetria de erros somente com minimização cuidadosa dos dados e divulgação transparente.
9. Teste os caminhos de falha
- Carregue a extensão descompactada em
chrome://extensions. - Visite um feed, uma pasta, uma página de detalhes de Pin e uma rota que não contenha um Pin.
- Navegue entre dois Pins sem recarregar a página e confirme que as notas permanecem separadas.
- Abra o mesmo Pin em duas abas e teste o tratamento de conflitos.
- Desative o armazenamento ou simule uma falha de cota e confira o aviso.
- Exporte um registro de teste, remova-o e depois restaure-o.
- Teste a navegação por teclado, o zoom, os rótulos para leitores de tela e páginas com temas claro e escuro.
A lição de engenharia
O campo de texto é a parte fácil. Uma extensão confiável para o Pinterest depende de identidade estável, ciclo de vida resiliente, interface acessível, permissões restritas, armazenamento documentado e uma saída testada para os dados. Construa essas qualidades antes de ampliar a lista de recursos.
Continue aprimorando seu sistema no Pinterest
Continue com o guia que corresponde à sua próxima etapa:
Experimente a extensão Notestopin para Chrome
Adicione notas privadas a qualquer Pin, organize-as com etiquetas e encontre depois tudo o que você salvou.
Adicionar ao Chrome