Como criar uma extensão do Chrome para notas em Pins

Salo Por Salo 5 min de leitura
Como criar uma extensão do Chrome para notas em Pins

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

9. Teste os caminhos de falha

  1. Carregue a extensão descompactada em chrome://extensions.
  2. Visite um feed, uma pasta, uma página de detalhes de Pin e uma rota que não contenha um Pin.
  3. Navegue entre dois Pins sem recarregar a página e confirme que as notas permanecem separadas.
  4. Abra o mesmo Pin em duas abas e teste o tratamento de conflitos.
  5. Desative o armazenamento ou simule uma falha de cota e confira o aviso.
  6. Exporte um registro de teste, remova-o e depois restaure-o.
  7. 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:

Salo

Sobre o autor

Salo é designer de produto e usuário avançado. Escreve sobre organização digital, fluxos de trabalho no Pinterest e ferramentas para pensar melhor.

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