feat: presença por sessão, eventos SSE em tempo real e autosave

- presence.go: hub SSE + registro de presença (TTL 30s por heartbeat)
- GET /api/events (EventSource, auth cookie/Bearer) transmite saved/presence;
  POST /api/presence publica {session, notes[]}; create/update/restore/delete
  publicam savedEvent com a session de origem (eco ignorado pelo autor)
- PUT/POST/restore aceitam campo opcional session
- UI: id de sessão em sessionStorage; selo 👥 na aba, na lista de notas e no
  topo do editor quando outra sessão tem a mesma nota aberta; aba LIMPA
  recarrega sozinha ao receber saved; aba SUJA abre o diálogo de conflito
  automaticamente (conflictPending evita diálogos empilhados)
- Autosave: debounce de 1,5s em qualquer alteração (rascunho vira nota),
  mensagem de revisão 'autosave', status 'salvando…', toggle nas Preferências
- Testes: SSE hello/presence/saved + presença 400/401 via httptest
This commit is contained in:
lgcosta committed 2026-11-04 13:35:33 -03:00
1 parent 3b91c1a509
commit a55591d208
9 files changed
+543 -15

No files matched your search

+14
View File
@@ -919,3 +919,17 @@ html[data-theme="solar-escuro"] #revisions-list li:hover { background: #0c4450;
}
#wysiwyg:empty::before { content: 'Comece a escrever…'; color: var(--muted); }
#wysiwyg [contenteditable="true"]:focus { outline: none; }
/* ------------------------------------------------- presença e autosave */
.presence-badge {
align-self: center;
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--warn);
white-space: nowrap;
flex: none;
}
.tab .tab-pres { color: var(--warn); font-size: 10px; flex: none; }
#st-saved.pending { color: var(--accent); font-weight: 600; }