Pular para o conteúdo
KW
Voltar aos projetos

amostra 03/em produção

ChecklistQA

Uma extensão que precisou sair de dentro do Google Meet para funcionar em ambientes corporativos.

01

Contexto

Em uma call de vendas é fácil esquecer um passo do roteiro, e anotar em outro lugar desvia a atenção de quem está falando. O ChecklistQA mantém o roteiro e as anotações ao lado da reunião.

A regra desde o início foi não coletar nada: o conteúdo da reunião nunca é lido, e nada sai do navegador.

02

Ponto de partida

A primeira versão injetava o checklist direto na página do Google Meet.

10

itens no roteiro original

3

etapas da call: início, meio e fim

0

dados coletados ou enviados

03

Linha do tempo

12 commits no histórico do projeto. Estes são os marcos.

  1. 29 de ago. de 2026

    Primeira versão

    Extensão Manifest V3 que exibia o checklist dentro do Meet, isolado em Shadow DOM, com política de privacidade e página da loja.

  2. 31 de ago. de 2026

    Barrada no ambiente corporativo

    Em máquinas com política de segurança corporativa, a CSP estrita e o Trusted Types impediam o painel de ser montado dentro do Meet. A extensão foi rearquitetada para rodar numa página própria, no painel lateral do Chrome.

  3. 07 de set. de 2026

    Tópicos da chamada

    Versão 1.2.0 com um bloco de pauta no topo do checklist, que cresce conforme o texto.

04

Mapa de decisões

Reconstruído a partir do histórico de commits, do README e da documentação do projeto.

01

Zero coleta como requisito

Nada é gravado nem enviado. O estado vive só na memória do painel e some ao recarregar, o que simplifica a privacidade e a aprovação na loja.

02

Sair de dentro do Meet

Injetar no Meet funcionava em casa e falhava em máquinas corporativas. Em vez de contornar a política de segurança, o painel passou a rodar numa página da própria extensão, que tem a sua própria CSP.

03

Detectar não é abrir

O Chrome só abre o painel lateral com um gesto do usuário. Por isso o detector apenas acende um aviso no ícone, e o painel abre com um clique ou com Alt+Shift+C.

04

Detector só de leitura

O script que roda no Meet só olha o endereço da aba. Não toca na página, não lê a reunião e não faz nenhuma requisição.

05

Texto do usuário nunca vira HTML

A lista é montada a partir de itens fixos do próprio código. O que a pessoa digita é tratado só como texto, então uma anotação não tem como executar nada.

06

Um painel que fica aberto

Uma vez aberto, o painel lateral acompanha a troca de abas e de calls. Basta abri-lo uma vez no começo do dia.

05

Arquitetura

As mesmas cinco camadas desta página, aplicadas ao projeto.

  1. 000 m/interface

    • Painel lateral nativo do Chrome
    • Checklist em três etapas e pauta
    • Anotações com cópia
  2. 030 m/rota

    • Clique no ícone abre o painel
    • Atalho Alt+Shift+C
  3. 060 m/serviço

    • Service worker
    • Aviso por aba ao entrar numa call
    • Detector só de leitura no Meet
  4. 120 m/persistência

    • Nenhuma: o estado vive só na memória

06

Código

Trechos reais do repositório público.

Um detector que só lê o endereço

Esse é todo o código que roda dentro do Meet. Ele compara o endereço da aba com o formato de uma sala de call e avisa o service worker. Nenhum elemento da página é lido ou alterado.

meet-detect.jsjs
(function () {
  'use strict';

  // Código de call do Meet: 3-4-3 letras (ex.: abc-defg-hij)
  const CALL_CODE_RE = /^\/[a-z]{3}-[a-z]{4}-[a-z]{3}$/i;

  let lastHref = null;
  let lastInCall = null;

  function report() {
    const inCall = CALL_CODE_RE.test(location.pathname);
    if (inCall === lastInCall) return;
    lastInCall = inCall;
    // sendMessage pode falhar se o service worker estiver dormindo; ignoramos.
    try {
      chrome.runtime.sendMessage({ type: 'meet-call-state', inCall });
    } catch (_) { /* no-op */ }
  }

  report();

  // O Meet troca de URL sem recarregar (SPA); poll leve pega entrada/saída da call.
  setInterval(() => {
    if (location.href !== lastHref) {
      lastHref = location.href;
      report();
    }
  }, 1000);
})();

Abrir com gesto, avisar sem gesto

Uma tecla conta como gesto do usuário, então o atalho pode abrir o painel. A entrada numa call não conta, então ela só acende o aviso no ícone daquela aba.

background.jsjs
// (2) Atalho de teclado abre o painel na janela em foco.
chrome.commands.onCommand.addListener(async (command) => {
  if (command !== 'open-checklist') return;
  try {
    const win = await chrome.windows.getLastFocused();
    await chrome.sidePanel.open({ windowId: win.id });
  } catch (err) {
    console.error('Falha ao abrir painel pelo atalho:', err);
  }
});

// (3) Badge de "entrou na call", por aba.
chrome.runtime.onMessage.addListener((msg, sender) => {
  if (!msg || msg.type !== 'meet-call-state') return;
  const tabId = sender.tab && sender.tab.id;
  if (tabId == null) return;

  if (msg.inCall) {
    chrome.action.setBadgeBackgroundColor({ tabId, color: BADGE_COLOR });
    chrome.action.setBadgeText({ tabId, text: BADGE_TEXT });
  } else {
    chrome.action.setBadgeText({ tabId, text: '' });
  }
});

Permissões mínimas

Trecho do manifesto: uma permissão de API, a do painel lateral, e um script restrito às páginas do Meet. Sem acesso a abas, histórico, armazenamento ou rede.

manifest.jsonjson
{
  "manifest_version": 3,
  "permissions": ["sidePanel"],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["https://meet.google.com/*"],
      "js": ["meet-detect.js"],
      "run_at": "document_idle"
    }
  ],
  "side_panel": {
    "default_path": "panel.html"
  }
}

07

Resultado

1

permissão de API: a do painel lateral

0

dados coletados ou enviados

0

dependências externas

12

itens no roteiro atual

Estado atual

Publicada na Chrome Web Store.