Documentação Técnica - Parte 1
-
Documentação Técnica — Insoft Readers AppVersão: 1.0.0
Data: Abril de 2026
Plataforma: Windows (x64)
Público: Equipe de Suporte Técnico · Desenvolvedores
1. VISÃO GERAL DO SISTEMAObjetivo do Sistema
O Insoft Readers App é uma aplicação desktop para Windows que gerencia leitores de cartões RFID/Mifare (modelo Digicon DG-710) e expõe uma API local para que outros sistemas possam receber e processar os dados de leitura.
Problema que Resolve
Empresas que utilizam leitores de cartão de proximidade precisam integrar essas leituras a outros softwares (sistemas de ponto, acesso, automação etc.). O Insoft Readers App resolve isso ao:
- Detectar automaticamente o leitor físico conectado ao computador
- Ler os cartões aproximados ao leitor em tempo real
- Enviar o código do cartão lido automaticamente para qualquer campo de texto ativo em qualquer aplicativo (como um teclado virtual)
- Disponibilizar uma API local REST para que outros sistemas consumam os eventos de leitura
Público-alvo
Perfil Uso Usuário final Vê o ícone na bandeja do sistema; o cartão é automaticamente "digitado" na aplicação aberta Analista de Suporte Monitora status dos leitores, diagnostica falhas de conexão e configuração Desenvolvedor Integra o sistema via API HTTP local, configura parâmetros de leitura Principais Funcionalidades
Leitura automática de cartões Mifare/RFID via leitor Digicon DG-710
Auto-paste: cola o código do cartão no campo de texto ativo do Windows
API REST local (porta 3791) para integração com outros sistemas
Stream de eventos em tempo real (SSE) para consumo live dos cartões lidos
Interface gráfica (dashboard) para monitoramento e configuração
Ícone na bandeja do sistema (system tray) com notificações
Configuração de prefixo/sufixo, intervalo de polling e modo contínuo
Suporte a execução como Serviço do Windows (via NSSM)
Modo mock (sem hardware): simula leituras para desenvolvimento/testes
️ 2. ARQUITETURA DO SISTEMAVisão Macro
O sistema é composto por três camadas independentes que se comunicam via HTTP local:
┌─────────────────────────────────────────────────────────────────┐ │ HARDWARE │ │ Leitor Digicon DG-710 (USB/Serial) │ │ DG710Facade.dll (DLL nativa Windows) │ └──────────────────────────┬──────────────────────────────────────┘ │ FFI (koffi) ┌──────────────────────────▼──────────────────────────────────────┐ │ BACKEND API (@insoft/backend) │ │ Express HTTP · porta 3791 · Node.js / TypeScript │ │ Gerencia leitores · processa eventos · persiste configurações │ └────────────┬──────────────────────────────────────┬─────────────┘ │ REST + SSE (HTTP) │ HTTP POST /notify │ │ ┌────────────▼──────────────┐ ┌────────────▼─────────────┐ │ FRONTEND (@insoft/ │ │ ELECTRON (@insoft/ │ │ frontend) │ │ electron) │ │ React · Vite · Zustand │◄─────────│ Processo principal │ │ Interface de usuário │ IPC │ porta control: 3792 │ │ porta dev: 5173 │ │ System Tray · Auto-paste │ └───────────────────────────┘ └──────────────────────────┘Como os Componentes se Comunicam
De Para Protocolo Finalidade Frontend (React) Backend API HTTP REST (axios) Listar leitores, controlar conexão, buscar configurações Frontend (React) Backend API SSE (EventSource) Receber eventos de cartão em tempo real Electron (main) Backend API HTTP REST (fetch) Garantir que a API está rodando, reconectar leitores Electron (main) Backend API SSE (fetch + stream) Escutar eventos de cartão para auto-paste Backend API Electron (main) HTTP POST porta 3792 Enviar notificações para a interface Frontend Electron (main) IPC (contextBridge) Controle de janela, tema, cache de settings Electron (main) Sistema Windows VBScript (wscript.exe) Simular Ctrl+V no aplicativo ativo Tecnologias Utilizadas
Tecnologia Versão Uso Node.js ≥ 20 Runtime do Backend e Electron TypeScript 5.8 Linguagem principal (todos os módulos) Electron 35 Aplicação desktop (janela, tray, IPC) Express 4.21 Servidor HTTP da API REST React 18.3 Interface gráfica do usuário Vite 6.2 Bundler do frontend Zustand 5.0 Gerenciamento de estado do frontend Zod 3.24 Validação de dados (backend e frontend) koffi 2.13 FFI: chama funções da DLL nativa Digicon pino 9.6 Logging estruturado no backend Tailwind CSS 3.4 Estilos do frontend Flowbite React 0.10 Componentes UI axios 1.8 Cliente HTTP do frontend electron-builder 25 Empacotamento do instalador NSSM — Gerenciador de Serviço Windows (externo)
3. ESTRUTURA DE PASTAS E COMPONENTESO projeto é um monorepo npm workspaces com três pacotes independentes:
insoft-readers-app/ ← Raiz do monorepo ├── package.json ← Workspace root (scripts globais) ├── insoft-readers-api/ ← Pacote: @insoft/backend └── insoft-readers-app/ ├── electron/ ← Pacote: @insoft/electron └── frontend/ ← Pacote: @insoft/frontend
insoft-readers-api/— Backend APIDescrição funcional (para suporte):
É o "motor" do sistema. Fica rodando em segundo plano, se comunica com o hardware do leitor e disponibiliza um servidor web local para que a interface e outros sistemas possam receber os dados dos cartões. Roda na porta 3791.
Responsabilidades técnicas:
- Gerenciar o ciclo de vida dos leitores de cartão (inicializar, conectar, desconectar, iniciar/parar polling)
- Processar eventos de leitura aplicando formatação (prefixo/sufixo)
- Manter histórico dos últimos 50 cartões lidos
- Persistir configurações de runtime em arquivo JSON
- Emitir eventos SSE para clientes conectados
- Garantir que somente uma instância está rodando (single-instance check)
src/server.ts— Ponto de entrada do BackendDescrição funcional:
Inicializa tudo ao ser executado: verifica se já existe uma API rodando, registra os leitores, monta o servidor HTTP e começa a escutar na porta 3791.
Descrição técnica:
- Executa
ensureSingleApiInstance(): verifica via/healthse já existe instância; se sim, encerra comprocess.exit(0); se a porta estiver ocupada por outro processo, encerra comprocess.exit(1) - Instancia
ReaderRegistry,DeviceManager,CardService,CardEventBroker,AppProcessManager - Monta rotas Express:
/health,/readers,/cards,/settings - Configura middleware:
cors,express.json(),pinoHttp(logging HTTP) - Tratamento global de erros:
ZodError→ HTTP 400; erros Digicon conhecidos → HTTP 503; outros → HTTP 500 - Inicia timer de manutenção (
maintainReaders) a cada 1.500ms - Registra handlers de
SIGINT/SIGTERMpara shutdown gracioso
src/core/— Núcleo da AplicaçãoReaderRegistry.tsDescrição funcional:
Um catálogo que registra quais tipos de leitores o sistema conhece. Funciona como uma "fábrica" de leitores.
Descrição técnica:
- Padrão de projeto: Registry + Factory
register(key, factory): registra uma função construtora associada a uma chave única (ex:"digicon-dg710")create(key): chama a factory correspondente, retornando uma nova instância deICardReader- Lança
Errorse a chave já existe (prevent duplicates) ou se a chave não foi registrada
DeviceManager.tsDescrição funcional:
O gerente dos leitores físicos. Agrupa todos os leitores e permite operações em batch (inicializar todos, descobrir todos, aplicar configurações em todos).
Descrição técnica:
- Estende
EventEmitterdo Node.js - Mantém um
Map<string, ICardReader>internamente addReader(reader): adiciona o leitor ao mapa e registra o callbackonCardReadpara reemitir o evento"cardRead"no próprioDeviceManagerinitializeAll(): chamareader.initialize()em cada leitor; falhas são logadas mas não interrompem a inicialização dos demaisdiscoverAll(): chamareader.discover()opcionalmente (método opcional na interface)applyReaderSettings(settings): propaga as configurações de runtime para todos os leitores que implementamapplySettingsonCardRead(listener): atalho para escutar o evento"cardRead"do EventEmitter
ReaderSettingsStore.tsDescrição funcional:
Salva e carrega as configurações de como o leitor deve funcionar (ex: intervalo de leitura, prefixo/sufixo do cartão). As configurações ficam gravadas no arquivo
reader-runtime-settings.json.Descrição técnica:
- Lê/escreve o arquivo
reader-runtime-settings.json(caminho configurável viaREADER_SETTINGS_FILE) load(): carrega do disco; se o arquivo não existir ou for inválido, usadefaultReaderRuntimeSettingsget(): retorna as configurações em memóriaupdate(next): mescla com as configurações atuais, valida/normaliza vianormalizeSettings()e persiste em disconormalizeSettings(): garante queintervalMsestá entre 50ms e 10.000ms, prefixo/sufixo têm no máximo 32 chars
CardService.tsDescrição funcional:
O cérebro do sistema. Orquestra tudo: recebe as leituras do hardware, aplica formatação, guarda o histórico, distribui para os ouvintes e controla individualmente cada leitor.
Descrição técnica:
- Injeta
DeviceManager(obrigatório) eReaderSettingsStore(opcional, cria instância padrão) - No construtor: registra listener em
deviceManager.onCardReadpara:- Aplicar prefixo/sufixo ao
cardIdviaapplyRuntimeSettingsToEvent() - Prepend no array
recentReads(máximo 50 itens viasplice) - Notificar todos os listeners registrados em
this.listeners
- Aplicar prefixo/sufixo ao
initializeRuntimeSettings(): carrega settings do disco e aplica nos leitoresmaintainReaders(): para cada leitor, se desconectado tentaconnect(); se conectado mas não escutando tentastartListening()— usado pelo timer de manutenção do servidorapplyRuntimeSettingsToEvent(event): retorna novo objeto comcardId = prefix + cardId + suffix
logger.tsDescrição funcional:
Responsável por gerar os logs do sistema. Em produção, escreve JSON puro; em desenvolvimento, exibe logs coloridos e formatados no terminal.
Descrição técnica:
- Usa biblioteca pino com nível configurável via variável de ambiente
LOG_LEVEL(padrão:"info") - Em
NODE_ENV !== "production": ativapino-prettycom cores e timestamp legível - Exporta instância singleton
loggerusada em todo o backend
AppProcessManager.tsDescrição funcional:
Permite que a API avise a interface gráfica quando um cartão é lido. Se a interface não estiver aberta, ela é iniciada automaticamente só para exibir a notificação.
Descrição técnica:
notifyApiUsage(payload): verifica se o AppControlServer (porta 3792) está vivo via/health; se não, executa o.exeda interface com flag--tray; aguarda 1.200ms; tenta novamente; se disponível, faz POST/notifylaunchAppTray(): usaspawncomdetached: truepara iniciar o processo filho sem bloquear a APIgetExecutablePath(): resolve o caminho do executável da interface nos cenários dev e produção (viaprocess.pkgpara binário compilado compkg)
src/api/routes/— Rotas HTTPGET /healthRetorna
{ "status": "ok" }. Usado por health checks de todas as camadas.GET /readersChama
service.getReaders()→deviceManager.discoverAll()+ retorna lista deReaderInfo[].GET /readers/statusRetorna
ReaderStatus[]com status atual de conexão/escuta de cada leitor.POST /readers/connect·POST /readers/disconnectBody:
{ "key": "digicon-dg710" }— conecta ou desconecta o leitor identificado pela chave.POST /readers/start·POST /readers/stopInicia ou para o polling de leitura no leitor identificado.
GET /cards/recentRetorna os últimos 50 cartões lidos em memória.
GET /cards/eventsAbre uma conexão SSE (Server-Sent Events). O cliente recebe:
- Evento
connectedao conectar - Evento
card-reada cada cartão detectado (payload:CardReadEvent)
GET /settings/readerRetorna as configurações de runtime atuais.
PUT /settings/readerBody:
ReaderRuntimeSettings— valida via Zod, salva no disco e propaga para os leitores.
src/api/sse/CardEventBroker.tsDescrição funcional:
Mantém abertas as conexões dos clientes que querem receber cartões em tempo real e distribui cada evento para todos ao mesmo tempo.
Descrição técnica:
- Mantém
Set<Response>de conexões HTTP ativas attachClient(res): configura headers SSE, envia eventoconnected, registra listenerres.on("close")para remoção automáticabroadcast(event): serializaCardReadEventcomo SSE e escreve em todos os clientes conectados
src/devices/digicon/— Implementação do HardwareDigiconDG710Reader.tsDescrição funcional:
É o "driver" do leitor Digicon DG-710. Faz a comunicação com o hardware e gera um evento toda vez que um cartão é detectado.
Descrição técnica:
- Implementa
ICardReader(interface contratual de todos os leitores) - Chave única:
"digicon-dg710" - Usa
DigiconFacadepara todas as chamadas à DLL nativa - Polling por timeout: usa
setTimeoutrecursivo com intervalo configurável (padrão 1.000ms) - Lógica de detecção:
continuousRead = false(padrão): só dispara evento na transição ausente→presente (wasCardPresent)continuousRead = true: dispara em todo tick enquanto o cartão estiver presente
applySettings(settings): atualizapollingMsecontinuousRead; reinicia o loop se estiver ativo- Estados internos:
connected,listening,handle,serial,lastError - Formato do evento emitido:
{ "readerKey": "digicon-dg710", "cardId": "12345678", "rawHex": "BC614E", "protocol": "mifare", "timestamp": "2026-04-02T10:00:00.000Z" }
DigiconFacade.tsDescrição funcional:
Camada que conversa diretamente com a DLL
DG710Facade.dlldo fabricante Digicon. É a ponte entre o código TypeScript e o hardware físico.Descrição técnica:
- Usa koffi para FFI (Foreign Function Interface) — chama funções C da DLL sem Node addons
- Mock mode: se a DLL não for encontrada ou não carregar, opera em modo simulação:
getChannels()retorna[710]isCardPresent()simula presença de cartão em intervalos aleatórios (a cada ~5 segundos)readMifareCard()retorna cardId aleatório
resolveDllPath(): buscaDG710Facade.dllem ~9 caminhos candidatos; configurável viaDIGICON_DLL_PATHinitializeRegistry(): inicia o registro Digicon (necessário antes de qualquer operação); idempotentereadMifareCard(handle): lê 4 bytes em little-endian → converte para hex big-endian → converte BigInt para decimal (10 dígitos)- Detecta incompatibilidade de arquitetura (x86 vs x64) e loga mensagem específica
src/interfaces/— Contratos de DadosInterface Campos ICardReaderkey,getInfo(),getStatus(),initialize(),connect(),disconnect(),startListening(),stopListening(),onCardRead(),discover?(),applySettings?()CardReadEventreaderKey,cardId,protocol,timestamp,rawHex?ReaderInfokey,name,manufacturer,model,connected,listening,serial?,capabilities[]ReaderStatuskey,connected,listening,lastError?ReaderRuntimeSettingsprefix,suffix,intervalMs,appendNewLine,continuousRead
insoft-readers-app/electron/— Processo Principal ElectronDescrição funcional:
É o "casca" desktop da aplicação. Cria a janela, o ícone na bandeja do sistema, garante que a API está rodando e implementa o auto-paste (cola o cartão no aplicativo ativo).
electron/main/main.ts— Ponto de Entrada do ElectronResponsabilidades:
- Garantir instância única da aplicação (
app.requestSingleInstanceLock) - Chamar
apiProcessManager.ensureApiRunning()antes de criar a janela - Criar e gerenciar
BrowserWindow(frameless, 1200×820) - Iniciar subscrição SSE aos eventos de cartão (
startCardEvents) - Implementar
handleCardRead():- Se a janela estiver em foco: envia evento IPC
card:readpara a UI - Se a janela estiver em background: executa
pasteToExternalFocusedApp()
- Se a janela estiver em foco: envia evento IPC
pasteToExternalFocusedApp(): escreve ocardIdna área de transferência + executa VBScript pré-gerado para simularCtrl+V(com ou sem{ENTER}conformeappendNewLine)- Manter cache local de
runtimeSettingsCache(atualizado a cada 1.200ms) - Registrar handlers IPC:
window:minimize,window:maximize-toggle,window:close,window:get-state,reader-runtime-settings:update-cache - Gerenciar
AppControlServer(porta 3792) ependingNotifications
electron/main/services/ApiProcessManager.tsDescrição funcional:
Garante que a API backend está rodando. Verifica se já está ativa, tenta iniciar como Serviço Windows, e se nada funcionar, inicia como processo filho.
Descrição técnica:
ensureApiRunning()— fluxo de tentativas em ordem:- Verifica
/health→ se OK, retorna - Verifica se serviço Windows está instalado → tenta
nssm start - Aguarda health check (12 tentativas × 800ms = ~9,6s)
- Se ainda falhou: chama
spawnApiProcess() - Aguarda health check final → se falhar, lança
Error
- Verifica
- Em desenvolvimento: executa
npm run dev:once -w @insoft/backend - Em produção: executa
insoft-reader-api.exeda pastaresources/api/
electron/main/services/AppControlServer.tsDescrição funcional:
Um mini servidor HTTP interno (porta 3792) que recebe notificações da API backend e as repassa para a interface gráfica.
Descrição técnica:
- Servidor HTTP puro (
node:http) sem dependências externas - Rotas:
GET /health→{ status: "ok" }|POST /notify→ disparaonNotification(payload) onNotificationé um callback injetado no construtor que enviamainWindow.webContents.send("app:notification", payload)
electron/main/services/HealthCheckService.tsDescrição funcional:
Verifica se a API está "viva" fazendo uma requisição ao endpoint
/health.Descrição técnica:
isHealthy(url): GET com timeout de 1.500ms; verificaresponse.okepayload.status === "ok"waitForHealthy(url, retries, delayMs): loop com tentativas e espera
electron/main/services/WindowsServiceManager.tsDescrição funcional:
Verifica e inicia o serviço Windows da API usando as ferramentas
scenssm.Descrição técnica:
isInstalled(name): executasc query <name>; considera instalado se o comando retornar código 0 ou se a saída não contiver "does not exist"start(name): executanssm start <name>; retornatruese código de saída for 0- Funciona apenas em
win32; retornafalseem outras plataformas
electron/main/tray.tsDescrição funcional:
Cria o ícone na bandeja do sistema (canto inferior direito da tela) com um menu de contexto.
Menu disponível:
- Abrir interface — exibe e foca a janela principal
- Reconectar leitores — chama
performReconnectReaders() - Sair — encerra o aplicativo
electron/main/uiActions.tsDescrição funcional:
Gerencia o tema visual (dark/light) persistindo em arquivo de preferências do usuário.
Descrição técnica:
- Arquivo:
%APPDATA%\Insoft Readers App\preferences.json - Handlers IPC:
theme:getetheme:set - Aplica o tema ao sistema operacional via
nativeTheme.themeSource
electron/ipc/reader.ipc.tsDescrição funcional:
Implementa o comando "Reconectar Leitores" que faz disconnect + connect + start em todos os leitores de uma vez.
Descrição técnica:
performReconnectReaders(): GET/readers→ para cada leitor: POST/readers/disconnect→ POST/readers/connect→ POST/readers/start- Registra handler IPC
reader:reconnectque chama a função acima
electron/preload/preload.tsDescrição funcional:
Ponte segura entre o código da interface (React) e o Electron. Expõe apenas as funções necessárias para a UI, sem dar acesso completo ao Node.js.
APIs expostas via
contextBridge:Namespace Métodos window.readerApireconnectReaders(),syncRuntimeSettings(settings),onCardRead(callback)window.appWindowgetState(),onStateChange(callback)window.appThemeget(),set(theme)window.electronminimizeApp(),maximizeRestoreApp(),closeApp(),getWindowState(),onWindowStateChange(callback)window.appNotificationsonNotify(callback)
insoft-readers-app/frontend/— Interface Gráfica (React)Descrição funcional:
A tela do sistema. Exibe o status dos leitores, os cartões lidos, permite configurar o comportamento do leitor e monitorar eventos em tempo real.
src/store/reader.store.ts— Estado GlobalDescrição técnica:
- Usa Zustand (store reativo sem Context API do React)
- Estado:
readers[],statuses[],recentCards[],activeReaderKey prependCard(event): adiciona na frente e mantém máximo 50 itenssetRecentCards(events): limita a 50 itens
src/hooks/— Hooks ReactuseReaders.ts- Busca dados do backend a cada chamada de
refresh():fetchReaders(),fetchReaderStatus(),fetchRecentCards()em paralelo - Expõe:
loading,error,connect(key?),disconnect(key?),start(key?),stop(key?),refresh() - Usa
activeReaderKeydo store quandokeynão for informado
useCardEvents.ts- Abre conexão
EventSourceparaGET /cards/events - Escuta evento
card-reade chamaprependCard()no store - Fecha a conexão no
useEffectcleanup
src/services/api.ts— Cliente HTTP- Instância axios com
baseURL = VITE_BACKEND_URL ?? "http://127.0.0.1:3791"e timeout de 5s - Funções exportadas:
fetchReaders,fetchReaderStatus,fetchRecentCards,connectReader,disconnectReader,startReader,stopReader,fetchReaderSettings,saveReaderSettings,buildCardEventsUrl
src/pages/— Páginas da InterfacePágina Rota interna Descrição DashboarddashboardVisão geral: 4 cards de estatística, tabela de status, ações rápidas ReadersreadersGerencia leitores: seleção, connect/disconnect/start/stop CardMonitoringmonitoringStream ao vivo de eventos SSE, métricas de cartões únicos DeviceLogslogsPainel de logs baseado nos dados de status e eventos recentes SettingssettingsFormulário de configurações de runtime do leitor
src/components/settings/SettingsForm.tsxDescrição funcional:
Formulário para configurar como o leitor funciona: intervalo de leitura, prefixo/sufixo do código, modo contínuo, nova linha.
Descrição técnica:
- Usa
react-hook-form+zodResolverpara validação - No load: busca
GET /settings/readere popula o formulário comreset(settings) - No submit: chama
PUT /settings/reader+window.readerApi?.syncRuntimeSettings(updated)para sincronizar o cache do processo Electron
4. FLUXOS PRINCIPAIS DO SISTEMA
Fluxo 1: Inicialização do SistemaExplicação simplificada (Suporte):
Quando o usuário abre o Insoft Readers App, o programa verifica se a API está funcionando. Se não estiver, ele a inicia automaticamente. Depois cria a janela e começa a escutar os cartões.
Explicação técnica (Dev):
app.on("ready") │ ├─► apiProcessManager.ensureApiRunning() │ ├─ GET /health → OK? → continua │ ├─ Não OK → verifica serviço Windows (sc query) │ │ ├─ Instalado → nssm start → aguarda health (12×800ms) │ │ └─ Não instalado → spawnApiProcess() → aguarda health │ └─ Falha total → app.quit() │ ├─► createMainWindow(startInTray) │ ├─ BrowserWindow frameless 1200×820 │ ├─ Carrega frontend (URL dev ou arquivo dist) │ ├─ registerReaderIpc(ipcMain) │ └─ createAppTray(...) │ ├─► initializeUIActions() │ └─ Lê preferences.json → aplica tema │ ├─► appControlServer.start() (porta 3792) │ └─► startCardEvents() ├─ refreshRuntimeSettings() (busca /settings/reader) ├─ setInterval(refreshRuntimeSettings, 1200ms) └─ connectCardEvents() → GET /cards/events (SSE stream)
Fluxo 2: Leitura de um Cartão — Ponta a PontaExplicação simplificada (Suporte):
O usuário aproxima o cartão do leitor. O hardware detecta, o sistema processa e automaticamente cola o número do cartão no campo ativo do Windows. Se a janela estiver aberta, aparece uma janela popup com o número.
Explicação técnica (Dev):
[Hardware DG-710] │ Cartão aproximado ▼ DigiconFacade.isCardPresent(handle) → true │ DigiconFacade.readMifareCard(handle) │ rawHex = bytes[3..0] em hex big-endian │ cardId = BigInt(0x{rawHex}).toString(10) │ Exemplo: bytes = [0x4E, 0x61, 0xBC, 0x00] │ rawHex = "00BC614E" │ cardId = "12345678" ▼ DigiconDG710Reader.runPollingTick() │ continuousRead=false: só dispara na transição │ continuousRead=true: dispara a cada tick ▼ DeviceManager.emit("cardRead", rawEvent) ▼ CardService.onCardRead listener │ applyRuntimeSettingsToEvent(rawEvent) │ cardId = prefix + "12345678" + suffix │ recentReads.unshift(processedEvent) ← histórico in-memory (max 50) ▼ CardEventBroker.broadcast(processedEvent) ← notifica clientes SSE │ AppProcessManager.notifyApiUsage(...) ← notifica interface │ ├─► [Clientes SSE do frontend React] │ useCardEvents → prependCard(event) → store atualizado → UI re-renderiza │ └─► [Electron main process — SSE stream] handleCardRead(event) ├─ janela em foco? → ipcMain.send("card:read", event) → CardReadModal abre └─ janela em background? ├─ clipboard.writeText(cardId) └─ spawn("wscript", ["//B", vbsPaste]) → Ctrl+V no app ativo
A documentação continua na parte 2, através da seguinte URL:
Documentação Técnica - Parte 2