Skip to content

feat: detectar instalação do LouvorJA Classo (Delphi) e reutilizar/migrar conteúdo — FASE 1 - #143

Open
rafaumeu wants to merge 41 commits into
stagingfrom
feat/classo-detect
Open

feat: detectar instalação do LouvorJA Classo (Delphi) e reutilizar/migrar conteúdo — FASE 1#143
rafaumeu wants to merge 41 commits into
stagingfrom
feat/classo-detect

Conversation

@rafaumeu

Copy link
Copy Markdown
Collaborator

Closes #142

O que esta PR entrega (FASE 1 — detecção)

  1. electron/classo-detect.mjs — funções puras testáveis:
    • classoCandidatePaths() (paths padrão Windows, win32.join determinístico)
    • looksLikeClassoInstall() (confirma via configPT.ja)
    • scanClassoMedia() (agrupa MP3 por pasta-álbum, máx. 2 níveis)
    • findClassoDataFiles() (liturgia.ja, itensAgendados*.xml, configPT.ja)
    • detectClassoInstallation() (registry → paths padrão)
    • probeClassoRegistry() (reg query HKCU/HKLM, nunca lança)
  2. IPC classo:detect + window.louvorja.classo.detect() no preload
  3. ClassoDetectCard.vue em Settings → Mídia (antes placeholder): botão detectar, mostra root, álbauns ~MB, liturgia/XML encontrados. Locales pt-BR/en/es.
  4. Tipos ClassoApi/ClassoDetectionResult no desktop-bridge (opcional — web não quebra).

O que fica pra FASE 2 (mesma issue)

  • Reutilizar (link da mídia do Classo sem cópia, fallback pra API)
  • Migrar (cópia com casamento fuzzy nome→ID do catálogo; nunca copiar às cegas)
  • Import assistido de liturgia.ja/XML pela wizard (parsers já existem)

Evidências

  • 19 testes novos (14 detector + 5 card); suite 368 testes (1 falha pré-existente: paridade receiver embutido↔webos, CRLF — não relacionada, já documentada)
  • npm run build verde
  • Spec completa: Obsidian 00-Projects/PIANO/Classo-Delphi-Reuso-Migracao.md

…+ hint de atalhos + ESC no operador encerra

Monitor único (Windows principalmente): projeção fullscreen screen-saver
cobria o app do operador permanentemente — só saía fechando o aplicativo.

- Ctrl+Alt+P global enquanto houver projeção viva; toggle hide/show com
  foco; unregister em todos os caminhos de fechamento (incl. will-quit)
- Hint discreto (6s) nas janelas de projeção: ESC encerra · Ctrl+Alt+P alterna
- ESC no operador com projeção ativa confirma encerramento (AppConfirm)
- 9 testes novos (decisão pura do toggle + controller); suite 45/45 verde
… web-projection) e ESC fecha popup

O commit anterior só enganchava o fluxo video/pdf/site (web-projection.mjs).
A projeção de hinos/slides usa window.open → did-create-window no main.mjs e
não passava por lá — hotkey e hint nunca eram registrados no fluxo principal.

- projection-hotkey vira SINGLETON com windowProviders: cada fluxo registra
  suas janelas; registro do globalShortcut é único (2º register falharia)
- main.mjs: init no attach, provider das janelas popup, ensure no window-open
  e ready-to-show, release no closed, hint injetado no popup
- ESC na popup de hinos/slides FECHA a janela (relato Caique: 'sinto falta do
  ESC fechar as telas') — estado do player permanece no operador
- web-projection: provider próprio + hint reutilizado do módulo
- 14 testes (reset de singleton p/ ESM cache entre testes); suite 49/49
…ha direto)

Decisão Rafael 30/08: qualquer projeção antes de ser fechada deve exibir
um confirm só do lado do operador — a decisão de encerrar é sempre dele.

- ESC na janela de projeção → main envia 'projection:close-requested' à
  janela do operador (não fecha mais direto)
- Operador: useOperatorEscapeToCloseProjection centraliza os dois gatilhos
  (ESC local + evento IPC) e exibe AppConfirm 'Encerrar projeção?'
- projection:external-alive agora cobre popups (bíblia/hinos) + web-projection
  — confirm aparece para TODAS as projeções
- instrumentação de debug /tmp/hotkey-debug.log na hotkey (temporária)
…rca em memória na criação

Sintoma real (usuário): 1º Ctrl+Alt+P escondia a projeção, mas ela não
voltava — o Alt+Tab fazia o papel. Causa: o provider identificava a popup
por webContents.getURL().includes('#/popup'), que deixa de valer depois
da navegação interna — collectAliveProjectionWindows() retornava vazio
no caminho de volta (toggle → none com projeção aberta).

- main.mjs: popup marcada em projectionPopups.add(childWindow) no
  did-create-window (fonte de verdade, independe de URL em runtime)
- toggle show-projection: win.moveTop() — alguns WMs restauram sem
  reaplicar o alwaysOnTop e a projeção voltava afundada
- 49/49 testes verdes
… inclusas) e limpa estado da UI

Bug reportado pelo Rafael: ESC → confirm 'Encerrar projeção?' aparecia,
mas ao confirmar a popup de bíblia/hinos CONTINUAVA projetando — o botão
Projetar ficava travado ativo. projection:close-url só fechava o fluxo
web-projection (vídeo/pdf/site), nunca as popups do window.open.

- closeAllProjectionWindows() em web-projection.mjs: fecha os DOIS fluxos
- registerCloseProjectionPopups: inversão de dependência (main registra o
  fechamento das popups; ipc chama sem import circular)
- Renderer: closeLocalProjectionState() chama closeProjectionModule()
  após o confirm — isProjecting/botão/watch voltam ao estado desligado
- 49/49 testes, build e type-check ok
…ADDRINUSE)

Bug real do José (Windows, print 30/08): 'listen EACCES 0.0.0.0:7071' —
o Windows (Hyper-V/WSL) reserva faixas de porta e o new WebSocketServer
lançava exceção NÃO TRATADA no main → 'A JavaScript error occurred in the
main process' e o app nem abria. Mesmo sintoma com EADDRINUSE quando há
duas instâncias (print local).

- attachRemoteServer: tenta PORT..PORT+4; EACCES/EADDRINUSE = próxima
  porta com warn; nenhuma livre = controle remoto desligado, app SEGUE
- uncaughtException handler no main: exceção não tratada loga + diálogo
  amigável, nunca mais crash silencioso sem explicação
…ona de verdade

O fix 400475e tentava capturar EACCES/EADDRINUSE com try/catch em volta
do construtor, mas o listen() do 'ws' e assincrono: o construtor retorna
antes do bind e o erro chega como evento 'error' re-throwado como
uncaughtException. Resultado: app continuava caindo com a porta 7071
ocupada/reservada (bug do Jose, reproduzido localmente 30/08 a noite).

- tryListen(port) retorna Promise: resolve em 'listening', rejeita em
  'error' — o catch do loop agora pega o erro real
- log de 'servidor WS na porta' e QR/pairing-info passam a anunciar a
  porta REAL em uso (wss.address().port), nao a constante 7071

Evidencia: com 7071 ocupada, app sobe e usa 7072 sem uncaughtException.
…1 da issue #142

- electron/classo-detect.mjs: funções puras testáveis (classoCandidatePaths,
  looksLikeClassoInstall, scanClassoMedia, findClassoDataFiles,
  detectClassoInstallation) + probeClassoRegistry (reg query Windows,
  injetável/mockável nos testes)
- IPC classo:detect + preload window.api.classo.detect()
- 14 testes novos (63 no total na suite electron)
- Nunca escaneia disco inteiro: registry + paths padrão, confirma via
  configPT.ja; scan de mídia limitado a 2 níveis de profundidade
…142

- ClassoDetectCard.vue: botão detectar, exibe root/álbuns/MB/liturgia/XML
- ClassoApi no desktop-bridge (opcional — web não quebra)
- locales pt-BR/en/es
- MediaView agora renderiza o card (antes era placeholder)
- 5 testes novos (368 total)
@github-actions github-actions Bot added modulo:biblia Recursos relacionados à Bíblia: pesquisa, leitura, exibição de versículos e referências. modulo:sorteio Recursos de sorteio, seleção aleatória e distribuição de participantes ou itens. modulo:configuracoes Configurações gerais da aplicação, preferências do usuário e parâmetros do sistema. modulo:shared Componentes, utilitários, serviços, tipos e funcionalidades compartilhadas entre os módulos. modulo:electron Código específico do Electron (Main Process, Preload, IPC e integração com o sistema operacional). modulo:ui Componentes visuais reutilizáveis, layout, temas, estilos e biblioteca de interface. modulo:pinia Stores, gerenciamento de estado global e lógica compartilhada. modulo:i18n Internacionalização, traduções e arquivos de idioma. labels Aug 30, 2026
No Linux, setFullScreen(true) é EWMH: o window manager decide o monitor e
vários WMs (Cinnamon/GNOME/XFCE) jogam o fullscreen no primário, ignorando
o monitor alvo passado via window.open (monitor=<id>). Usuário relatou que
a projeção nunca abria no monitor 2 em nenhuma versão Linux (1.8.9, 1.22.1,
1.28) — só arrastando a janela.

- Linux agora usa a estratégia do win32: borderless + setBounds(bounds do
  monitor alvo) + alwaysOnTop (fullscreen fake, imune ao WM)
- Modo janela com monitor explícito passa a posicionar 800x600 centrado no
  workArea do monitor alvo (antes ignorava o monitor completamente)
- Lógica de display extraída p/ electron/projection-display.mjs (pura,
  testável): resolveProjectionDisplay + buildProjectionWindowBounds
- 8 testes novos (43/43 verdes); npm run build ok
@github-actions github-actions Bot added the modulo:liturgia Funcionalidades da liturgia, ordem de culto e gerenciamento da programação. label Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

modulo:biblia Recursos relacionados à Bíblia: pesquisa, leitura, exibição de versículos e referências. modulo:configuracoes Configurações gerais da aplicação, preferências do usuário e parâmetros do sistema. modulo:electron Código específico do Electron (Main Process, Preload, IPC e integração com o sistema operacional). modulo:i18n Internacionalização, traduções e arquivos de idioma. modulo:liturgia Funcionalidades da liturgia, ordem de culto e gerenciamento da programação. modulo:pinia Stores, gerenciamento de estado global e lógica compartilhada. modulo:shared Componentes, utilitários, serviços, tipos e funcionalidades compartilhadas entre os módulos. modulo:sorteio Recursos de sorteio, seleção aleatória e distribuição de participantes ou itens. modulo:ui Componentes visuais reutilizáveis, layout, temas, estilos e biblioteca de interface.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant