-2

Juntei Obsidian, Notion e TickTick num app só (open source) — e o que aprendi fazendo ele funcionar offline

Durante muito tempo eu usei três apps ao mesmo tempo para me organizar:

  • Obsidian para notas
  • Notion para projetos e tabelas
  • TickTick para tarefas e lembretes

Três assinaturas, três lugares para procurar a mesma informação e nenhum conversava com o outro. Um dia resolvi juntar o que eu mais gostava de cada um num app só, rodando no meu próprio servidor. O nome saiu das iniciais: Obsidian + Notion + TickTick + Y = ONTY.

Hoje uso ele todos os dias e decidi abrir o código.

Tarefas em kanban

O que ele faz

  • Tarefas estilo TickTick: listas com kanban, subtarefas, lembretes, repetição, matriz de Eisenhower, pomodoro e uma captura rápida que entende amanhã 10h ^Trabalho #cliente !alta.
  • Notas estilo Obsidian: arquivos .md comuns (abrem no Obsidian), [[links]], backlinks e grafo.
  • Tabelas estilo Notion: visões de tabela, quadro, calendário, galeria e gráficos, além de uma tabela de finanças com fluxo de caixa.
  • Calendário dia/semana/mês/ano que também mostra o Google Agenda (via link iCal) e publica as tarefas como calendário no iPhone.
  • Inbox: tudo o que eu penso cai ali primeiro — digitado, pelo atalho do computador ou por mensagem e áudio no WhatsApp. Depois decido se vira tarefa ou nota.
  • Modo offline: sem internet continuo usando; as alterações sincronizam quando a conexão volta.

Inbox com mensagens do WhatsApp

A stack (propositalmente simples)

  • Node.js + Express no back-end
  • Vite no front-end, com JavaScript puro e um helper h() para montar o DOM (o editor de blocos usa React/BlockNote)
  • Sem banco de dados: notas são arquivos .md e tarefas ficam em JSON. Backup é copiar uma pasta.
  • Electron para uma janelinha de captura no Windows
  • OpenClaw como agente que recebe as mensagens do WhatsApp e chama a API

4 coisas que aprendi no caminho

1. Modo offline: rodar o código do servidor dentro do navegador

O jeito "normal" de fazer offline seria reescrever no front-end as regras de negócio: o que acontece ao completar uma tarefa repetida, em qual coluna do kanban ela entra quando ganha data, como interpretar amanhã 10h !alta… Isso duplica a lógica e cedo ou tarde as duas versões divergem.

Em vez disso, o navegador roda o mesmo arquivo do servidor (TaskStore). O Vite troca node:fs, node:path e node:crypto por versões mínimas em memória:

// vite.config.js
resolve: { alias: {
  'node:fs': shim('fs'),       // "arquivos" num Map em memória
  'node:path': shim('path'),
  'node:crypto': shim('crypto'), // randomUUID() do navegador
} }

Sem internet, o app carrega a última cópia das tarefas (guardada no IndexedDB), aplica a alteração com o código do servidor e coloca a requisição numa fila. Quando a conexão volta, a fila é enviada em ordem.

Um detalhe que evitou muita dor de cabeça: as tarefas criadas offline já levam o id gerado no dispositivo, e o servidor aceita esse id. Assim não preciso remapear ids depois, e se a mesma requisição chegar duas vezes ela não duplica.

2. null <= 0 é true em JavaScript

Tinha este código no timer de foco:

// null = tarefa sem tempo estimado
if (!st.warned && taskCountdown() <= 0) estimateReached(); // "null <= 0 é false"

O comentário estava errado. Em JS, null <= 0 é true (o null vira 0 na comparação), enquanto null < 0 e null == 0 são false. Resultado: iniciar um pomodoro sem tempo estimado mostrava "Acabou o tempo estimado (0 min)". Só descobri porque estava tirando prints para o README. A correção:

const restante = taskCountdown();
if (!st.warned && restante != null && restante <= 0) estimateReached();

3. Service worker + fallback de SPA = tela preta

O app de captura tem um service worker para abrir rápido. Depois de um deploy, ele abriu com a tela toda preta. A causa:

  1. O HTML em cache apontava para /assets/index-ABC123.js, que não existia mais depois do deploy.
  2. O servidor tinha o fallback de SPA (app.get('*') devolve o index.html)… então o pedido do JS voltava HTML com status 200.
  3. O service worker guardou esse HTML como se fosse o JS.

A correção teve duas partes: /assets/* inexistente agora responde 404 (nunca o fallback), e o service worker só aceita um asset se o content-type bater com a extensão e só troca a cópia da página quando todos os arquivos novos já foram baixados.

4. Sem banco de dados dá (bem) para uso pessoal

Para um usuário, arquivos .md + JSON resolvem: são legíveis, versionáveis, compatíveis com o Obsidian e o backup é trivial. As gravações são atômicas (escreve num .tmp e faz rename) para não corromper nada se o processo cair no meio.

Experimente

É gratuito e open source (AGPL-3.0). Com Docker:

git clone https://github.com/koko00123/onty.git && cd onty
# edite CEREBRO_TOKEN e CEREBRO_PASSWORD no docker-compose.yml
docker compose up -d

Repositório: https://github.com/koko00123/onty

Dois avisos honestos: a interface ainda está em espanhol (tradução para português e inglês está no roadmap, ajuda é bem-vinda) e o app foi feito para uma pessoa por instalação.

Adoraria receber feedback, principalmente sobre a instalação e o modo offline. E se curtir, uma ⭐ no GitHub ajuda muito.


Consejos para publicarlo en TabNews

  • Responde a todos los comentarios el primer día: TabNews premia la conversación y te dará TabCoins (la reputación del sitio).
  • Si alguien pregunta algo técnico, como la cola offline o el service worker, contéstale con detalle. Es la mejor publicidad.
  • No lo publiques a la vez en otros sitios brasileños: espera 1 o 2 días y mira en GitHub Insights → Traffic cuántas visitas llegan desde tabnews.com.br.
Carregando publicação patrocinada...
1

coé cara kk.. esqueceu de remover isso? Consejos para publicarlo en TabNews

no geral o projeto é interessante. ainda mais que abriu o código. eu uso uma dessas ferramentes como ferramenta geral. coloco lá minhas tarefas, metas, etc. qual problema específico a ferramenta resolve? pelo que entendi ela tem um pouquinho de tudo mas não entendi qual o uso específico dela.

— é o hype