Estudo de caso · Código aberto

Parallelepiped

Aplicativo desktop de notas para Windows que trabalha sobre uma pasta comum de arquivos Markdown. As notas se ligam com [[wikilinks]], mostram seus backlinks, têm busca de texto completo e podem ser exploradas num grafo 3D interativo em que cada nota é um neurônio. Escrito em Go e TypeScript com Wails e publicado sob licença MIT.

Papel
Autor único
Stack
Go · Wails · TypeScript · SQLite
Licença
MIT
Código
Público no GitHub
Um cofre de demonstração fictício: seguindo ligações, escrevendo uma ligação com autocompletar, buscando, e girando e aproximando o grafo 3D nos temas escuro e claro.

Visão geral

Conhecimento guardado em notas ligadas só é útil se as ligações puderem ser vistas e seguidas. O Parallelepiped abre qualquer pasta de arquivos Markdown como um cofre e mantém os próprios arquivos como a única fonte da verdade: não há formato proprietário nem etapa de importação. Sobre eles constrói um índice de ligações, tags e texto, usado para os backlinks, a busca e um grafo 3D que pode ser girado, aproximado e percorrido com o mouse para acender as conexões de uma nota. Também lê um repositório de código e gera um mapa ligado dos seus módulos, para que um projeto sem documentação possa ser explorado da mesma forma. É um projeto pessoal original, e não um fork de um aplicativo existente.

Minha contribuição

Sou o autor único. Desenhei as fronteiras entre os módulos e escrevi o backend em Go — cofre, notas, índice, watcher de arquivos e a importação estrutural — com o schema SQLite e as suas migrations; o frontend em TypeScript, com o editor, a coluna de contexto e o grafo 3D, incluindo a geometria dos neurônios e as otimizações de renderização; os temas claro e escuro e o ícone do aplicativo; e o Makefile, os testes e a documentação de arquitetura.

O projeto exercita duas coisas que valorizo: um monólito modular em camadas aplicado fora do mundo HTTP, e gráficos interativos que continuam rápidos sem abrir mão da aparência.

Arquitetura

Um único executável. O frontend roda no WebView2 do sistema e chama o backend em Go pelos bindings do Wails. O backend é um monólito modular: cinco módulos de negócio, cada um dividido em handler, service, model, repository e DTO, com as preocupações transversais numa camada de plataforma separada. Os módulos só conversam por interfaces pequenas definidas por quem as consome.

Como o Parallelepiped é organizado O frontend em TypeScript — editor, coluna de contexto e grafo 3D — chama o backend em Go pelos bindings do Wails. O backend tem cinco módulos: vault, notes, index, watcher e brain. As notas são lidas e gravadas como arquivos Markdown, que são a fonte da verdade. O módulo index mantém ligações, tags e um índice de texto completo em SQLite, que pode ser reconstruído a partir dos arquivos. O watcher observa a pasta, atualiza o índice quando arquivos mudam fora do app e avisa o frontend com um evento. FRONTEND · TS Editor CodeMirror 6, [[ligação]] com autocompletar Coluna de contexto mini-mapa, backlinks, ligações de saída, tags Grafo 3D three.js, layout de forças neurônios e fibras, renderizado sob demanda GO · MONÓLITO MODULAR handler → service → repository, por módulo vault abrir, árvore notes ler, salvar, lixeira index ligações, tags, FTS5 watcher fsnotify, debounce brain código → notas ligadas platform catálogo de erros, validação, banco, log, configuração, ponte com o desktop Arquivos Markdown a pasta do cofre fonte da verdade Índice SQLite ligações, backlinks, tags, FTS5 cache, reconstruído dos arquivos Wails Tracejado: o watcher percebe edições de fora, reindexa e envia vault:changed ao frontend
Desenhado a partir do documento de arquitetura do projeto e dos diretórios internal/modules, internal/platform e frontend/src.

Organização do código

  • main.go é a composition root: monta repositórios, services e handlers e os liga ao Wails.
  • internal/modules reúne vault, notes, index, watcher e brain, cada um com handler, service, model, repository e DTO.
  • internal/platform reúne o catálogo de erros, a validação por campo, o banco, o log e a ponte com o desktop.
  • migrations/ guarda o schema versionado do índice na raiz, embutido no binário.
  • frontend/src separa as views (árvore, abas, editor, contexto, grafo) do cliente da API e dos módulos compartilhados de cores e tema.

Stack

  • Backend: Go com Wails v2, um driver SQLite em Go puro com FTS5 e fsnotify para o watcher da pasta.
  • Frontend: TypeScript e Vite, CodeMirror 6 no editor.
  • Grafo: 3d-force-graph sobre three.js, com geometria própria de neurônio e bloom no tema escuro.
  • Interface: layout original com tema claro e escuro e cores de pasta compartilhadas por todas as views.
  • Entrega: um único executável gerado por Makefile, com instalador NSIS opcional.

Decisões de engenharia

Três decisões que moldaram o projeto, cada uma com a alternativa considerada e o que ela custa.

1. Os arquivos são a fonte da verdade; o banco é cache

Decisão:
as notas são arquivos .md comuns, gravados de forma atômica (arquivo temporário e depois renomeação). O índice SQLite de ligações, tags e texto fica fora do cofre e pode ser apagado e reconstruído dos arquivos a qualquer momento; se a atualização do índice falhar depois de um salvamento, o salvamento vale e a falha vai para o log.
Alternativa:
guardar as notas no banco e exportar Markdown quando pedido.
Motivo:
as notas do usuário continuam legíveis e editáveis com qualquer ferramenta e sob qualquer controle de versão, sem o app. Como o índice é derivado e de um único usuário, as suas migrations podem rodar com segurança na abertura — uma exceção deliberada à regra habitual, documentada nas notas de arquitetura.
Limitação:
o índice pode discordar do disco por um instante, e o que não está nos arquivos — como a disposição do grafo — não é guardado junto das notas.

2. Reconhecer as escritas do próprio app pelo hash do conteúdo, não por janela de tempo

Decisão:
o watcher espera cada caminho ficar 300 ms quieto e então o lê do disco. Ele guarda o hash do último conteúdo indexado; se o disco tem exatamente esse conteúdo, o evento veio do próprio app e é descartado.
Alternativa:
ignorar todos os eventos por um curto período depois de o app salvar uma nota.
Motivo:
uma janela de tempo ou engole uma edição externa real feita logo depois de um salvamento, ou deixa o salvamento do próprio app disparar uma reindexação. Comparar conteúdo não tem janela: a edição de fora é sempre percebida e os salvamentos do app nunca geram trabalho extra.
Limitação:
pastas removidas ou renomeadas, e eventos perdidos, ainda caem na reconstrução do índice do cofre inteiro.

3. Deixar o grafo mais rápido sem mudar um pixel

Decisão:
o grafo renderiza sob demanda — enquanto o layout se acomoda, a câmera se move ou o ponteiro está ativo — e pausa após 1,5 segundo de inatividade. Passar o mouse sobre um neurônio restiliza só as fibras ligadas a ele e ao neurônio anterior, atualizando as malhas no lugar.
Alternativa:
simplificar a cena — juntar as fibras em linhas planas ou abandonar a geometria tubular — para cortar o custo.
Motivo:
o estilo visual é a razão de ser do recurso, e uma versão simplificada foi rejeitada assim que foi vista. A biblioteca recriava todas as ligações a cada hover; restilizar só as afetadas levou o hover num cofre de cerca de 860 ligações de aproximadamente 150 ms para 7 ms, e o resultado foi conferido como idêntico ao da própria biblioteca.
Limitação:
a otimização depende de detalhes internos da biblioteca do grafo, então atualizá-la exige repetir essa verificação de equivalência.

Qualidade, segurança e custo

Tudo abaixo está no repositório público e pode ser conferido no código-fonte. Os números medidos vêm de uma única máquina de desenvolvimento e não são garantia para outro hardware.

Testes

  • 13 arquivos de teste em Go com 49 funções de teste nos models, services e repositories de todos os módulos.
  • Cobrem o parse das ligações, as consultas do índice, o debounce e o registro de escritas do watcher, e a análise e as regras de reimportação da importação estrutural.
  • make check roda o go vet, os testes em Go e a checagem de tipos do TypeScript.

Segurança das notas

  • Apagar uma nota a move para a pasta .trash dentro do cofre, com a data no nome; o app nunca exclui um arquivo permanentemente.
  • A importação estrutural só sobrescreve notas que ela mesma gerou, marcadas no front matter; a seção “Minhas anotações” de cada nota gerada sobrevive a toda reimportação.
  • Os erros chegam ao frontend num único envelope, com código e mensagens por campo; os detalhes internos ficam no log.

Consumo

  • Um único executável de cerca de 18 MB, sem runtime para instalar no Windows 11, que já traz o WebView2.
  • O grafo para de desenhar quando está parado ou escondido, e desenha na resolução nativa da tela, sem supersampling.
  • O hover sobre um neurônio custa cerca de 7 ms num cofre de cerca de 860 ligações.

Resultados e limitações

O que existe hoje é um aplicativo funcionando: abas e um editor com autocompletar de ligações; uma coluna de contexto com mini-mapa 2D da vizinhança da nota, backlinks, ligações de saída e tags; busca de texto completo; cores de pasta compartilhadas por todas as views; um grafo 3D com modo só-grafo; temas claro e escuro; um índice que acompanha edições feitas fora do app; e uma importação estrutural que transforma um repositório de código em notas ligadas dentro da sua própria pasta docs/.

Limitações declaradas

  • Só o Windows 11 foi verificado. O Wails atende outros sistemas, mas nenhuma outra plataforma foi compilada ou testada.
  • Renomear uma nota não reescreve as ligações que apontam para ela, e ainda não há pré-visualização renderizada; os dois são os próximos passos, seguidos de plugins isolados em WebAssembly.
  • A importação estrutural é estática. Ela lê pacotes, símbolos exportados e imports em Go e imports relativos em TypeScript e JavaScript; não entende o que o código faz.
  • O executável não é assinado, então o SmartScreen pode avisar na primeira execução.
  • Não há dados de uso real. É um projeto pessoal; não declaro usuários, downloads nem adoção.

Explorar

O repositório é público e traz o README com as instruções de compilação, o documento de arquitetura, o guia de contribuição, o código de conduta e a política de segurança.

O vídeo e as imagens usam um cofre de demonstração fictício.