CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
OpenSSF Scorecard / Scorecard analysis (push) Canceled after 0s
The owner asked for profile customisation that is "completo e FODA", pointing
at Discord's profile editor. This is the whole thing, end to end, with a live
preview.
What a user can now set, and where it shows:
- Display name style: 10 fonts × 9 effects (solid, gradient, neon, toon,
pop, gummy, prism, glitch, fire) × two colours. Popout and full profile.
- Avatar decoration: 10 (ring, neon, flames, stars, halo, vines, pixel,
hearts, frost, crown). Inline SVG — no image assets, scales to any size.
- Profile effect over the banner: 9 (sparkles, rain, snow, matrix,
confetti, aurora, embers, bubbles, hearts). CSS particles with seeded
positions so re-renders do not change the weather.
- Nameplate behind the member-list row: 10 presets + custom two-colour.
- Theme: two colours washed behind the card, and the banner fallback.
- Pronouns, and a clan-style tag (emoji + 2–4 letters) beside the name,
also on the member row.
- Links, up to 5, with the brand icon picked from the host.
- Board: favourite game, up to 5 in rotation, up to 10 wanted, a quote.
Games come from Steam search through a small server proxy (cached,
10 min) or are typed by hand; covers only load from Steam's CDN.
- Badges, derived rather than stored: instance admin, server owner,
founder (joined in the space's first month), veteran (a year), OG (six
months), stylist. Nothing to grant, revoke or migrate.
Storage is one JSON column, users.profile_style (migration 0019), and one
normaliser in shared/profile.ts that both the server (on write and on read)
and the client (on render) run. It never throws: unknown ids fall back,
strings are cut to their limits, colours must be #rrggbb, link URLs must be
https, cover URLs must be a Steam CDN host. Kept out of the federation
durable-field set on purpose — peers on older builds do not know the field,
and it is cosmetic.
The editor is a new Settings > Profile tab. Its preview is the same
ProfileCard component the popout renders, with the draft style swapped in —
what you see while editing is, by construction, what others see.
Fonts are OFL builds self-hosted from public/fonts alongside DM Sans, latin
subsets only, ~120 KB in total.
Verified: web build, 517 web tests (11 new: normaliser bounds and fallbacks,
link icon detection, badge derivation), server typecheck, 804 server tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
210 lines
14 KiB
Markdown
210 lines
14 KiB
Markdown
# Roadmap — fork Resenha
|
||
|
||
Plano de features próprias desta instância. Arquivo exclusivo do fork
|
||
(nome com sufixo para não colidir com arquivos do upstream em merges).
|
||
|
||
Escrito em português por ser documento de planejamento do dono do fork; o
|
||
código e os commits seguem em inglês, como o resto do repositório.
|
||
|
||
## Entregue
|
||
|
||
| Feature | Commit | Nota |
|
||
|---|---|---|
|
||
| Ir para a call clicando no nome do canal | `c70b0095` | Exigiu o `voiceStore` passar a guardar o espaço da call — antes ele não sabia onde a call estava assim que o usuário navegava para outro servidor |
|
||
| Botão de GIF redesenhado | `20526e1b` | Contorno vazado com letras cheias, no lugar do bloco sólido |
|
||
| Explorador de GIF no banner | `20526e1b` | Sem upload: banner já aceita URL absoluta no cliente e no servidor |
|
||
| Teste de microfone com retorno | `bfe62d70` | `AudioManager.startMicTest/stopMicTest`; devolve o microfone ao parar, com duas travas independentes |
|
||
| Bloco de atividade no perfil | `d7da0ff2` | `ProfileActivity`; inclui correção de validação de assets no servidor |
|
||
| Favoritar GIFs + categorias (#6) | `fb662bfe` | Favoritos no servidor, por conta; guarda o resultado inteiro porque o provedor não busca por id |
|
||
| Registro de auditoria (#9) | `bbb190cb` | Tabela append-only genérica; 8 ganchos; sair ≠ ser expulso |
|
||
| Estatísticas do grupo | `18300517` | Tabela própria de sessões de voz; ganchos no ponto único de entrada/saída |
|
||
| Spotify por OAuth (atividade com capa e progresso) | `75316b08` | Token só no servidor; `state` assinado; atividades por fonte para não brigar com o detector do Electron |
|
||
| Menu do próprio nome | `f5451e1b` | Perfil, status e copiar ID. Sem Clips/trocar conta — não existem aqui |
|
||
| Cronômetro da call | `f5451e1b` | `startedAt` do servidor; zera sozinho porque sala vazia é destruída |
|
||
| Correções do Spotify (sincronia, sumiço, barra) | `c899253e` | Pausa virou estado; horário do servidor no `ready` corrige relógio |
|
||
| Soundboard no app desktop | `c899253e` | `window.prompt` não existe no Electron; campo inline no lugar |
|
||
| Áudio do sistema sem eco | `d525bbb8` `5b848432` | Módulo nativo com isolamento por processo + ganho e preset de música |
|
||
| Instaladores publicados pelo Gitea | `ce6bba05` | GitHub compila, Gitea distribui; atualização automática ligada |
|
||
| Fixar mensagens no canal | `4ceb5cd6` | Estado na própria mensagem; painel no cabeçalho; limite de 50 |
|
||
| Busca: filtros embutidos e tradução | `50a8f12c` | Os filtros já existiam; faltava `de:fulano` e descoberta |
|
||
| Emojis e figurinhas do servidor | `7d003021` | `:nome:` no markdown sem plugin; figurinha como mensagem inteira |
|
||
| Fundação de i18n (en + pt-BR) | `688a1335` | `src/i18n/`; pt-BR é parcial e cai para inglês. Aba Idioma nas configurações |
|
||
| Rebrand: **Resenhacord** + logo nova | (ver git log) | `productName` mudou, `appId` **não** — assim o NSIS troca a instalação antiga no lugar. `userData` migra `Backspace` → `Resenhacord` no primeiro boot (senão todo mundo cairia da sessão). Logo: duas bolhas de conversa, roxo → rosa; fonte em `assets/brand/*.svg`, PNGs e ícones gerados por `pnpm gen-icons` |
|
||
| Aviso "X fixou uma mensagem" | (ver git log) | Tabela `messages` ganhou `type` (migração 0018); a rota de pin posta uma mensagem de sistema. Dois links: pular pra mensagem e abrir o painel de fixadas |
|
||
| Menção `@everyone` | (ver git log) | Reusa o token `<@id>`; permissão `MENTION_EVERYONE` nova, concedida ao `@everyone` por padrão e retroativa via backfill no boot. Servidor neutraliza o token de quem não tem a permissão, no envio **e** na edição |
|
||
| Renomear canal (nome e tópico) | (ver git log) | Rota `PATCH /api/channels/:id` já existia; faltava interface. Normalização em `shared/constants` para servidor e web usarem a mesma regra |
|
||
| Volume da tela em 50% por padrão | (ver git log) | `DEFAULT_STREAM_VOLUME`; o publicador reforça o áudio do sistema, então o padrão anterior chegava alto demais |
|
||
| Sons 50% mais baixos | (ver git log) | Só `SFX_BASE_VOLUME` (0.8 → 0.4); os `.ogg` não foram regerados |
|
||
| Atualização do desktop verificada com mais frequência | (ver git log) | Era só na abertura e de 4 em 4 horas; agora a cada 30 min e ao focar a janela, com trava de 5 min |
|
||
| Preview de perfil nos participantes da call | (ver git log) | O popout já existia e era aberto de 11 lugares; **nenhum era de voz**. Ligado nas linhas da lista de voz e no nome dos tiles da grade |
|
||
|
||
## Já existia no código (verificado, não construir de novo)
|
||
|
||
- **Busca com filtros** — servidor (`q`, `from`, `has`, `before`, `after`),
|
||
client de API e painel no `SearchPopover` já existiam completos. Só faltava
|
||
tradução, sintaxe embutida (`de:fulano`) e descoberta.
|
||
|
||
- **Animação de digitação** — `TypingIndicator.tsx`, três pontos `animate-bounce`
|
||
escalonados em 0/150/300ms.
|
||
- **Sons de call/stream** — `SoundController.tsx`, montado no `AppLayout`:
|
||
stream started/ended, alguém entra/sai da tela, entra/sai da call, câmera,
|
||
mute, ringing. Os `.ogg` estão em `web/public/sounds/`.
|
||
- **Preview de perfil ancorado** — `uiStore.openUserProfile(user, anchor, placement)`
|
||
guarda `userProfilePopout`; popout posicionado no desktop, tela cheia no
|
||
mobile. Já era aberto por mensagens, menções, avatares, lista de membros,
|
||
DMs e painel de atividade. O que faltava era só a voz — agora ligado.
|
||
Continua faltando o bloco de atividade do print do Discord, que é a #8.
|
||
|
||
Se qualquer um dos dois não se manifestar em uso, o trabalho é **depuração**,
|
||
não implementação.
|
||
|
||
## Pendente — pedido original
|
||
|
||
Ordem sugerida: as pequenas primeiro, as grandes uma de cada vez.
|
||
|
||
| # | Feature | Tamanho | Observação técnica |
|
||
|---|---|---|---|
|
||
|
||
## Identidade do perfil (entregue 2026-09-02, pedido: "completo e FODA")
|
||
|
||
Uma coluna JSON (`users.profile_style`, migração 0019) normalizada no servidor
|
||
por `shared/profile.ts`. Aba **Perfil** nas configurações com prévia ao vivo
|
||
(`ProfilePanel` renderiza o mesmo `ProfileCard` que o popout).
|
||
|
||
| Peça | Onde aparece | Detalhe |
|
||
|---|---|---|
|
||
| Estilo do nome — 10 fontes × 9 efeitos × 2 cores | popout, perfil completo | Fontes OFL em `public/fonts`; efeitos são CSS (`.name-fx-*`) |
|
||
| Decoração do avatar — 10 | popout, perfil, editor | SVG inline em `DecoratedAvatar`, sem imagens |
|
||
| Efeito do perfil — 9 | banner do popout e do perfil | Partículas CSS com posições semeadas (`ProfileEffectLayer`) |
|
||
| Nameplate — 10 + personalizado | lista de membros | `.nameplate-*` |
|
||
| Tema (2 cores) | fundo do cartão e banner sem imagem | |
|
||
| Pronomes, tag (emoji + 2–4 letras) | ao lado do nome; tag também na lista de membros | |
|
||
| Links (até 5) | popout e aba Sobre | Ícone detectado pelo host; só https |
|
||
| Quadro: jogo favorito, rotação (5), quero jogar (10), citação | aba Quadro do perfil; tira "Coleção" no popout | Busca na Steam via `/api/games/search` (proxy, cache 10 min); capas só de CDN da Steam |
|
||
| Emblemas derivados | popout e perfil | admin, dono, fundador (1º mês), veterano (1 ano), OG (6 meses), estiloso — nada é armazenado |
|
||
|
||
Fora do escopo desta entrega, candidatos a seguir: perfil por servidor
|
||
(apelido/avatar por servidor), emblemas concedidos pelo admin, molduras
|
||
animadas por upload, status com expiração.
|
||
|
||
## Pendente — ideias aprovadas
|
||
|
||
| Feature | Tamanho | Observação técnica |
|
||
|---|---|---|
|
||
| Watch party | Grande | O screen share do LiveKit já existe; falta sincronizar posição de reprodução entre participantes |
|
||
|
||
## Regra de idioma (a partir de 2026-08-31)
|
||
|
||
Funcionalidade nova sai com interface em **pt-BR**, e a cada update um sistema
|
||
existente é traduzido. Os dois idiomas **coexistem**. Código, comentários e
|
||
commits seguem em inglês.
|
||
|
||
A fundação está pronta (`src/i18n/`). Para traduzir um sistema: adicione as
|
||
chaves em `locales/en.ts`, traduza em `locales/pt-BR.ts` e troque as strings
|
||
fixas por `t('chave')`. O que faltar cai para o inglês sozinho.
|
||
|
||
### Sistemas já traduzidos
|
||
|
||
- Configurações → navegação e aba Idioma
|
||
- Configurações → Voz (dispositivo de entrada, volume, teste de microfone)
|
||
- Cartão de perfil (sobre mim, membro desde, enviar mensagem, atividade)
|
||
- Configurações → Privacidade
|
||
- Configurações → Conexões (nasceu bilíngue)
|
||
- Seletor de GIF
|
||
- Chat: composer e mensagens (placeholder, anexos, responder, reações)
|
||
- Barra lateral: servidores, canais e menus de contexto
|
||
- Lista de membros (grupos por cargo, carregamento)
|
||
- Busca (rótulos, filtros e sintaxe embutida)
|
||
- Registro de auditoria e Estatísticas (nasceram bilíngues)
|
||
- Configurações do canal (visão geral, privacidade, exclusão e rótulos de permissão)
|
||
- Amigos: abas, listas, pedidos, aba de adicionar e cartões de descoberta
|
||
- Modal de convite (incluindo os motivos de falha por convite)
|
||
- Lista de membros da DM em grupo (remover e transferir propriedade)
|
||
- Configurações → Área de trabalho (início automático, atualizações, instância)
|
||
- Configurações → Voz: dispositivo de saída (completa a aba, junto com a entrada)
|
||
- Configurações → Atalhos (incluindo os nomes das ações e o aviso de conflito)
|
||
- Configurações → Conta (perfil, senha, conta desligada da origem, zona de perigo)
|
||
- Explorar servidores (cartões, entrar, pedir para entrar)
|
||
- Configurações → Voz: vídeo (prévia, permissões, escolha de câmera) — fecha a aba
|
||
- Criar canal, categoria e servidor
|
||
- DMs em grupo (nova DM, adicionar amigos, configurações do grupo, sair)
|
||
- Entrar num servidor (descoberta, código de convite, conectar e login federado)
|
||
- Excluir conta, transferir propriedade e a tela de erro geral
|
||
- Configurações do servidor → Visão geral (identidade, ícone, banner, zona de perigo)
|
||
- Configurações do servidor → Membros e banimentos
|
||
- Configurações do servidor → Cargos (incluindo a tabela de permissões)
|
||
- Configurações da categoria (renomear, privacidade, exceções, excluir)
|
||
- Configurações da instância → Geral (nome, descoberta, busca de GIFs)
|
||
- Mensagens de sistema do chat e a prévia delas na barra lateral
|
||
- Login, registro e a página de convite
|
||
- Voz: controles, informações de conexão, folha de entrada no celular, aba Voz
|
||
- Telas do celular (`Mobile*.tsx`)
|
||
- Navegação das configurações (servidor, usuário e instância) + Descoberta e
|
||
pedidos de entrada
|
||
|
||
Os rótulos de permissão viraram um conjunto único (`perm.*`), usado pelo painel
|
||
de cargos e pelos editores de exceção de canal e de categoria — antes havia
|
||
duas cópias da mesma lista.
|
||
|
||
### Fila sugerida de tradução
|
||
|
||
Configurações → Instância: registro, federação, transmissão, armazenamento e
|
||
usuários (os cinco painéis grandes, só o dono vê) · `ConnectedInstances` ·
|
||
`UserProfileModal` · `MainContent` e `ChannelSidebar` · mensagens de erro
|
||
vindas de stores e hooks (`instanceStore`, `useLiveKit`, `transferStore`)
|
||
|
||
## Correções pendentes
|
||
|
||
| Problema | Causa provável | Correção |
|
||
|---|---|---|
|
||
|
||
|
||
## App desktop — já existia (verificado 2026-08-31)
|
||
|
||
As três pedidas já estão implementadas e ligadas de ponta a ponta. **Não
|
||
construir de novo.**
|
||
|
||
- **Bandeja** — `createTray()` em `desktop/src/main.ts`, chamada na inicialização;
|
||
fechar a janela esconde em vez de sair (`mainWindow.on('close')`).
|
||
- **Notificações nativas** — `showNotification()` no processo principal, canal
|
||
IPC `show-notification`, e o web já chama por `platform/notifications.ts`.
|
||
- **Áudio do sistema no compartilhamento** — `setDisplayMediaRequestHandler`
|
||
devolve `audio: 'loopback'`; existe caixa de seleção no `ScreenSharePicker`
|
||
ligada a `screenShareConfig.shareAudio`, que atravessa o IPC.
|
||
|
||
Se algum não se manifestar em uso, o trabalho é **depuração**, não construção.
|
||
|
||
|
||
## Aprovadas, a fazer depois (2026-08-31)
|
||
|
||
Escolhidas pelo dono, inspiradas no Discord. **Nenhuma iniciada.**
|
||
|
||
| Feature | Tamanho | Observação técnica |
|
||
|---|---|---|
|
||
| **Resposta rápida de voz (soundboard por atalho)** | Pequena | O soundboard já está pronto; falta só marcar favoritos e ligar a atalhos de teclado, sem abrir painel. Cuidado: o limite de repetição do servidor continua valendo e é o que impede virar metralhadora |
|
||
| **Cargos com ícone e exibição separada** | Média | Cargos e cores já existem; falta ícone, o campo "exibir separadamente" e o agrupamento na lista de membros |
|
||
|
||
|
||
## Ideias novas — a avaliar
|
||
|
||
Ordenadas por relação valor/custo para um servidor de grupo fechado.
|
||
|
||
| Sistema | Tamanho | Por que faz sentido aqui |
|
||
|---|---|---|
|
||
| **Backup fora da VPS** | Pequena | Hoje app, banco, uploads, backups e as três cópias do repositório morrem no mesmo evento. Um envio periódico para fora resolve |
|
||
| **Aniversários e lembretes** | Pequena | Alto retorno afetivo, custo baixo: campo de data + verificação diária + mensagem no canal |
|
||
| **Perfis por servidor** | Média | Apelido e avatar diferentes por espaço, como no Discord. O modelo já tem membro por espaço |
|
||
| **Eventos agendados com presença** | Média | "Sexta 21h" com confirmação. Encaixa nas notificações e no PWA já instalado |
|
||
| **Notificações push de verdade** | Média | O `vite-plugin-pwa` e o service worker já estão lá; falta Web Push (VAPID) e o registro no servidor |
|
||
| **Níveis e conquistas** | Média | Gamificação por tempo em call e mensagens. **Mesmo registro de eventos da auditoria e das estatísticas** — três features, um mecanismo |
|
||
| **Clipes de call** | Grande | "Salvar os últimos 30 segundos" depois de alguém falar besteira. O LiveKit tem egress; exige buffer contínuo e armazenamento |
|
||
| **Fila de música compartilhada** | Grande | Um participante-robô publicando faixa de áudio na sala LiveKit. É o que mais muda o uso de um servidor de amigos, e o mais caro |
|
||
| **Autenticação em duas etapas** | Média | Só faz sentido depois de decidir o modelo de cadastro |
|
||
|
||
## Dependência que vale respeitar
|
||
|
||
**Auditoria (#9) e Estatísticas compartilham o mesmo mecanismo**: uma tabela de
|
||
eventos append-only no servidor. Construir a auditoria primeiro e as
|
||
estatísticas como leitura agregada dessa mesma tabela evita escrever dois
|
||
sistemas de registro paralelos que divergem com o tempo.
|