diff --git a/data/screenshots/timeline.json b/data/screenshots/timeline.json index 4ee73af..9937f32 100644 --- a/data/screenshots/timeline.json +++ b/data/screenshots/timeline.json @@ -1,4 +1,4 @@ { - "updatedAt": "2026-03-29T14:48:56.878Z", + "updatedAt": "2026-04-05T11:43:29.824Z", "entries": [] } \ No newline at end of file diff --git a/docs/tasks/TASK-06-css-modular.md b/docs/tasks/TASK-06-css-modular.md index af72ee3..a5b007b 100644 --- a/docs/tasks/TASK-06-css-modular.md +++ b/docs/tasks/TASK-06-css-modular.md @@ -1,6 +1,6 @@ # TASK-06: CSS Modular + Sistema de Temas -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 2 — Medium Effort **Esforço:** ⭐⭐ Médio (3-4 dias) **Impacto:** 🟠 Médio @@ -16,12 +16,12 @@ Refatorar a estrutura CSS monolítica atual em módulos especializados com desig ## 🎯 Objetivos -- [ ] Refatorar `variables.css` com design tokens completos (cores, espaçamentos, tipografia, sombras) -- [ ] Separar `components.css` (9KB) em 3+ arquivos menores por domínio -- [ ] Criar `chat.css` para estilos específicos de mensagens/snapshot -- [ ] Criar `workspace.css` para file browser, terminal e git panel -- [ ] Criar `assist.css` base para futura TASK-10 -- [ ] Expandir `themes.css` de 2 temas (dark/light) para 5 temas: +- [x] Refatorar `variables.css` com design tokens completos (cores, espaçamentos, tipografia, sombras) +- [x] Separar `components.css` (9KB) em 3+ arquivos menores por domínio +- [x] Criar `chat.css` para estilos específicos de mensagens/snapshot +- [x] Criar `workspace.css` para file browser, terminal e git panel +- [x] Criar `assist.css` base para futura TASK-10 +- [x] Expandir `themes.css` de 2 temas (dark/light) para 5 temas: - `dark` (default) — tema escuro com acentos roxos - `light` — tema claro limpo - `pastel` — tons suaves pastel @@ -127,17 +127,17 @@ DEPOIS: ## 🧪 Testes de Verificação -- [ ] Todos os 5 temas renderizam sem CSS quebrado -- [ ] Troca de tema funciona em tempo real sem flash -- [ ] `meta[name="theme-color"]` atualiza com o tema -- [ ] Nenhum estilo hardcoded (tudo via CSS variables) -- [ ] Mobile responsivo mantido em todos os temas -- [ ] Performance: nenhum layout shift ao trocar tema +- [x] Todos os 5 temas renderizam sem CSS quebrado +- [x] Troca de tema funciona em tempo real sem flash +- [x] `meta[name="theme-color"]` atualiza com o tema +- [x] Nenhum estilo hardcoded (tudo via CSS variables) +- [x] Mobile responsivo mantido em todos os temas +- [x] Performance: nenhum layout shift ao trocar tema ## ✅ Critérios de Aceitação -- [ ] 5 temas funcionais com preview -- [ ] Design tokens centralizados em `variables.css` -- [ ] Zero CSS inline nos arquivos JS (tudo via classes) -- [ ] `themes.css` como single source of truth para cores -- [ ] Backward-compatible: dark/light existentes mantidos idênticos +- [x] 5 temas funcionais com preview +- [x] Design tokens centralizados em `variables.css` +- [x] Zero CSS inline nos arquivos JS (tudo via classes) +- [x] `themes.css` como single source of truth para cores +- [x] Backward-compatible: dark/light existentes mantidos idênticos diff --git a/docs/tasks/TASK-07-supervisor-suggest-mode.md b/docs/tasks/TASK-07-supervisor-suggest-mode.md index 91ee37b..13b9479 100644 --- a/docs/tasks/TASK-07-supervisor-suggest-mode.md +++ b/docs/tasks/TASK-07-supervisor-suggest-mode.md @@ -1,6 +1,6 @@ # TASK-07: Supervisor Suggest Mode -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 2 — Medium Effort **Esforço:** ⭐⭐ Médio (3-5 dias) **Impacto:** 🟠 Médio @@ -17,13 +17,13 @@ Adicionar um modo "Suggest" ao supervisor existente onde, em vez de executar aç ## 🎯 Objetivos -- [ ] Implementar `SuggestQueue` — fila de ações sugeridas pelo supervisor -- [ ] Cada sugestão contém: ação proposta, razão, timestamp, status (pending/approved/rejected) -- [ ] API REST para gerenciar a fila: list, approve, reject, clear -- [ ] WebSocket broadcast quando nova sugestão é adicionada -- [ ] Telegram notification com inline keyboard para aprovação rápida -- [ ] Config toggle: `SUPERVISOR_SUGGEST_MODE=true/false` -- [ ] Máximo de itens na fila configurável via `SUPERVISOR_MAX_QUEUE` +- [x] Implementar `SuggestQueue` — fila de ações sugeridas pelo supervisor +- [x] Cada sugestão contém: ação proposta, razão, timestamp, status (pending/approved/rejected) +- [x] API REST para gerenciar a fila: list, approve, reject, clear +- [x] WebSocket broadcast quando nova sugestão é adicionada +- [x] Telegram notification com inline keyboard para aprovação rápida +- [x] Config toggle: `SUPERVISOR_SUGGEST_MODE=true/false` +- [x] Máximo de itens na fila configurável via `SUPERVISOR_MAX_QUEUE` ## 📁 Arquivos a Modificar/Criar @@ -133,19 +133,19 @@ SUPERVISOR_MAX_QUEUE=10 # Tamanho máximo da fila ## 🧪 Testes de Verificação -- [ ] Com `SUGGEST_MODE=false`: comportamento idêntico ao atual -- [ ] Com `SUGGEST_MODE=true`: ações vão para a fila (não executadas automaticamente) -- [ ] Aprovar uma sugestão executa `completePendingAction()` -- [ ] Rejeitar uma sugestão marca como rejected sem ação -- [ ] Fila respeita `MAX_QUEUE` (remove mais antigo quando cheia) -- [ ] Telegram recebe notificação com botões approve/reject -- [ ] WebSocket broadcast enviado para UI mobile +- [x] Com `SUGGEST_MODE=false`: comportamento idêntico ao atual +- [x] Com `SUGGEST_MODE=true`: ações vão para a fila (não executadas automaticamente) +- [x] Aprovar uma sugestão executa `completePendingAction()` +- [x] Rejeitar uma sugestão marca como rejected sem ação +- [x] Fila respeita `MAX_QUEUE` (remove mais antigo quando cheia) +- [x] Telegram recebe notificação com botões approve/reject +- [x] WebSocket broadcast enviado para UI mobile ## ✅ Critérios de Aceitação -- [ ] Toggle suggest mode via env var sem reiniciar -- [ ] API REST CRUD completa para sugestões -- [ ] Integração com Telegram (inline keyboard) -- [ ] UI mobile mostra contador de sugestões pendentes -- [ ] Zero breaking changes no fluxo auto-approve existente -- [ ] Timeout: sugestões expiram após 30 minutos +- [x] Toggle suggest mode via env var sem reiniciar +- [x] API REST CRUD completa para sugestões +- [x] Integração com Telegram (inline keyboard) +- [x] UI mobile mostra contador de sugestões pendentes +- [x] Zero breaking changes no fluxo auto-approve existente +- [x] Timeout: sugestões expiram após 30 minutos diff --git a/docs/tasks/TASK-08-session-stats.md b/docs/tasks/TASK-08-session-stats.md index a814f23..4a673d6 100644 --- a/docs/tasks/TASK-08-session-stats.md +++ b/docs/tasks/TASK-08-session-stats.md @@ -1,6 +1,6 @@ # TASK-08: Session Stats & Analytics -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 2 — Medium Effort **Esforço:** ⭐⭐ Médio (2-3 dias) **Impacto:** 🟡 Baixo @@ -16,12 +16,12 @@ Criar um módulo de analytics de sessão que rastreie métricas em tempo real: m ## 🎯 Objetivos -- [ ] Criar módulo `src/session-stats.js` com tracking de métricas -- [ ] Integrar no polling loop e nos handlers de ação -- [ ] Widget resumo na UI mobile (stats bar ou modal) -- [ ] Endpoint REST para consulta de stats -- [ ] Reset automático por sessão (baseado em new-chat) -- [ ] Comando `/stats` no Telegram bot +- [x] Criar módulo `src/session-stats.js` com tracking de métricas +- [x] Integrar no polling loop e nos handlers de ação +- [x] Widget resumo na UI mobile (stats bar ou modal) +- [x] Endpoint REST para consulta de stats +- [x] Reset automático por sessão (baseado em new-chat) +- [x] Comando `/stats` no Telegram bot ## 📁 Arquivos a Modificar/Criar @@ -149,18 +149,18 @@ GET /api/stats → SessionStats.getSummary() ## 🧪 Testes de Verificação -- [ ] Contador de mensagens incrementa corretamente -- [ ] Reset funciona ao criar novo chat -- [ ] Stats persistem durante reconexão CDP -- [ ] API REST retorna JSON válido -- [ ] Telegram `/stats` formata corretamente -- [ ] UI widget atualiza em tempo real via WebSocket +- [x] Contador de mensagens incrementa corretamente +- [x] Reset funciona ao criar novo chat +- [x] Stats persistem durante reconexão CDP +- [x] API REST retorna JSON válido +- [x] Telegram `/stats` formata corretamente +- [x] UI widget atualiza em tempo real via WebSocket ## ✅ Critérios de Aceitação -- [ ] 15+ métricas rastreadas de forma silenciosa (sem overhead) -- [ ] Endpoint REST com resumo completo -- [ ] Widget compacto na UI mobile -- [ ] Comando `/stats` no Telegram -- [ ] Reset automático por new-chat -- [ ] Log circular de erros e ações (memória limitada) +- [x] 15+ métricas rastreadas de forma silenciosa (sem overhead) +- [x] Endpoint REST com resumo completo +- [x] Widget compacto na UI mobile +- [x] Comando `/stats` no Telegram +- [x] Reset automático por new-chat +- [x] Log circular de erros e ações (memória limitada) diff --git a/docs/tasks/TASK-09-model-quota-service.md b/docs/tasks/TASK-09-model-quota-service.md index 90c4cc1..057509c 100644 --- a/docs/tasks/TASK-09-model-quota-service.md +++ b/docs/tasks/TASK-09-model-quota-service.md @@ -1,6 +1,6 @@ # TASK-09: Model Quota Service -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 3 — Strategic **Esforço:** ⭐⭐⭐⭐ Muito Alto (5-7 dias) **Impacto:** 🔴 Alto @@ -16,16 +16,16 @@ Implementar serviço de monitoramento de quota de modelos do Antigravity. O serv ## 🎯 Objetivos -- [ ] Criar módulo `src/quota-service.js` com descoberta automática do Language Server -- [ ] Implementar extração de CSRF token do diretório de dados local -- [ ] Query à API `GetUserStatus` do Language Server -- [ ] Parsing do response protobuf/JSON para extrair quotas por modelo -- [ ] Mapeamento de nomes internos → nomes amigáveis de modelos -- [ ] Endpoint REST: `GET /api/quota` -- [ ] Polling periódico (configurável, default: 5min) -- [ ] Integração com Telegram: comando `/quota` -- [ ] Widget visual no mobile com barras de progresso -- [ ] Alertas automáticos quando quota > 80% +- [x] Criar módulo `src/quota-service.js` com descoberta automática do Language Server +- [x] Implementar extração de CSRF token do diretório de dados local +- [x] Query à API `GetUserStatus` do Language Server +- [x] Parsing do response protobuf/JSON para extrair quotas por modelo +- [x] Mapeamento de nomes internos → nomes amigáveis de modelos +- [x] Endpoint REST: `GET /api/quota` +- [x] Polling periódico (configurável, default: 5min) +- [x] Integração com Telegram: comando `/quota` +- [x] Widget visual no mobile com barras de progresso +- [x] Alertas automáticos quando quota > 80% ## 📁 Arquivos a Modificar/Criar @@ -198,20 +198,20 @@ Gemini 3.1 Pro High ## 🧪 Testes de Verificação -- [ ] Descoberta do Language Server funciona em Linux -- [ ] CSRF token extraído corretamente -- [ ] API query retorna dados de quota -- [ ] Model names mapeados corretamente -- [ ] Alerta disparado quando quota > 80% -- [ ] Telegram `/quota` formata barras de progresso -- [ ] Widget mobile atualiza via WebSocket -- [ ] Serviço não causa crash quando Language Server indisponível +- [x] Descoberta do Language Server funciona em Linux +- [x] CSRF token extraído corretamente +- [x] API query retorna dados de quota +- [x] Model names mapeados corretamente +- [x] Alerta disparado quando quota > 80% +- [x] Telegram `/quota` formata barras de progresso +- [x] Widget mobile atualiza via WebSocket +- [x] Serviço não causa crash quando Language Server indisponível ## ✅ Critérios de Aceitação -- [ ] Quota de pelo menos 3 modelos exibida corretamente -- [ ] Alertas automáticos quando > 80% -- [ ] Comando Telegram `/quota` funcional -- [ ] Widget compacto na UI mobile -- [ ] Zero impacto no polling loop (executa em background) -- [ ] Desativado por padrão (`QUOTA_ENABLED=false`) +- [x] Quota de pelo menos 3 modelos exibida corretamente +- [x] Alertas automáticos quando > 80% +- [x] Comando Telegram `/quota` funcional +- [x] Widget compacto na UI mobile +- [x] Zero impacto no polling loop (executa em background) +- [x] Desativado por padrão (`QUOTA_ENABLED=false`) diff --git a/docs/tasks/TASK-10-assist-chat-tab.md b/docs/tasks/TASK-10-assist-chat-tab.md index f0854bb..dab8dda 100644 --- a/docs/tasks/TASK-10-assist-chat-tab.md +++ b/docs/tasks/TASK-10-assist-chat-tab.md @@ -1,6 +1,6 @@ # TASK-10: Assist Chat Tab -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 3 — Strategic **Esforço:** ⭐⭐⭐ Alto (5-7 dias) **Impacto:** 🟠 Médio @@ -16,14 +16,14 @@ Criar uma nova aba "Assist" na interface mobile que permite ao usuário conversa ## 🎯 Objetivos -- [ ] Nova aba "Assist" no workspace panel (ao lado de Files, Terminal, Git) -- [ ] Interface de chat bidirecional: usuário ↔ supervisor AI -- [ ] Respostas do supervisor podem incluir ações clicáveis (approve/reject) -- [ ] Streaming de respostas do supervisor (via OmniRoute/Ollama) -- [ ] Histórico de conversas da sessão (in-memory) -- [ ] Integração com Session Stats (mostra resumo quando perguntado) -- [ ] Integração com Suggest Queue (mostra sugestões pendentes) -- [ ] Markdown rendering nas respostas do supervisor +- [x] Nova aba "Assist" no workspace panel (ao lado de Files, Terminal, Git) +- [x] Interface de chat bidirecional: usuário ↔ supervisor AI +- [x] Respostas do supervisor podem incluir ações clicáveis (approve/reject) +- [x] Streaming de respostas do supervisor (via OmniRoute/Ollama) +- [x] Histórico de conversas da sessão (in-memory) +- [x] Integração com Session Stats (mostra resumo quando perguntado) +- [x] Integração com Suggest Queue (mostra sugestões pendentes) +- [x] Markdown rendering nas respostas do supervisor ## 📁 Arquivos a Modificar/Criar @@ -175,19 +175,19 @@ DELETE /api/assist/history → Limpar histórico ## 🧪 Testes de Verificação -- [ ] Enviar mensagem e receber resposta do supervisor -- [ ] Histórico mantido durante a sessão -- [ ] Ações sugeridas renderizam como botões clicáveis -- [ ] Markdown renderizado corretamente (code blocks, bold, etc.) -- [ ] Mobile responsivo -- [ ] Limpar histórico funciona -- [ ] Tab "Assist" aparece e funciona no workspace panel +- [x] Enviar mensagem e receber resposta do supervisor +- [x] Histórico mantido durante a sessão +- [x] Ações sugeridas renderizam como botões clicáveis +- [x] Markdown renderizado corretamente (code blocks, bold, etc.) +- [x] Mobile responsivo +- [x] Limpar histórico funciona +- [x] Tab "Assist" aparece e funciona no workspace panel ## ✅ Critérios de Aceitação -- [ ] Chat funcional entre usuário e supervisor -- [ ] Respostas contextuais (supervisor sabe estado da sessão) -- [ ] Ações clicáveis inline nas respostas -- [ ] Markdown rendering (pre, code, bold, italic) -- [ ] Mobile-first responsive design -- [ ] Tab integrada no workspace panel existente +- [x] Chat funcional entre usuário e supervisor +- [x] Respostas contextuais (supervisor sabe estado da sessão) +- [x] Ações clicáveis inline nas respostas +- [x] Markdown rendering (pre, code, bold, italic) +- [x] Mobile-first responsive design +- [x] Tab integrada no workspace panel existente diff --git a/docs/tasks/TASK-11-unit-tests-vitest.md b/docs/tasks/TASK-11-unit-tests-vitest.md index 5f7f8de..434812b 100644 --- a/docs/tasks/TASK-11-unit-tests-vitest.md +++ b/docs/tasks/TASK-11-unit-tests-vitest.md @@ -1,6 +1,6 @@ # TASK-11: Unit Tests com Vitest -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 3 — Strategic **Esforço:** ⭐⭐ Médio (3-5 dias) **Impacto:** 🟠 Médio @@ -16,9 +16,9 @@ Configurar Vitest como framework de testes unitários e criar suite de testes ab ## 🎯 Objetivos -- [ ] Instalar e configurar Vitest com ESM support -- [ ] Criar `vitest.config.js` com coverage e watch mode -- [ ] ≥ 20 testes unitários cobrindo: +- [x] Instalar e configurar Vitest com ESM support +- [x] Criar `vitest.config.js` com coverage e watch mode +- [x] ≥ 20 testes unitários cobrindo: - `config.js` — valores padrão, env vars - `supervisor.js` — heurísticas (risky/safe patterns) - `utils/hash.js` — hash consistency @@ -26,8 +26,8 @@ Configurar Vitest como framework de testes unitários e criar suite de testes ab - `utils/telegram.js` — rate limiting, threading, toggles - `session-stats.js` — métricas (se TASK-08 concluída) - `quota-service.js` — model name mapping (se TASK-09 concluída) -- [ ] Script npm: `npm run test:unit` -- [ ] CI/CD integration via GitHub Actions (opcional) +- [x] Script npm: `npm run test:unit` +- [x] CI/CD integration via GitHub Actions (opcional) ## 📁 Arquivos a Modificar/Criar @@ -225,18 +225,18 @@ describe('Telegram Module', () => { ## 🧪 Testes de Verificação -- [ ] `npm run test:unit` executa sem erros -- [ ] ≥ 20 testes passando -- [ ] Coverage report gerado em `coverage/` -- [ ] Watch mode funciona (`npm run test:unit:watch`) -- [ ] Testes existentes (`npm test`) continuam funcionando -- [ ] ESM imports resolvidos pelo Vitest +- [x] `npm run test:unit` executa sem erros +- [x] ≥ 20 testes passando +- [x] Coverage report gerado em `coverage/` +- [x] Watch mode funciona (`npm run test:unit:watch`) +- [x] Testes existentes (`npm test`) continuam funcionando +- [x] ESM imports resolvidos pelo Vitest ## ✅ Critérios de Aceitação -- [ ] Vitest configurado com ESM support -- [ ] ≥ 20 testes unitários passando -- [ ] Coverage ≥ 60% nos módulos core (excluindo server.js) -- [ ] Script `npm run test:all` executa ambos suites -- [ ] Zero mudanças breaking nos testes existentes -- [ ] CI-ready (pode ser adicionado a GitHub Actions) +- [x] Vitest configurado com ESM support +- [x] ≥ 20 testes unitários passando +- [x] Coverage ≥ 60% nos módulos core (excluindo server.js) +- [x] Script `npm run test:all` executa ambos suites +- [x] Zero mudanças breaking nos testes existentes +- [x] CI-ready (pode ser adicionado a GitHub Actions) diff --git a/docs/tasks/TASK-12-screenshot-timeline.md b/docs/tasks/TASK-12-screenshot-timeline.md index 2f4d72e..639ccdb 100644 --- a/docs/tasks/TASK-12-screenshot-timeline.md +++ b/docs/tasks/TASK-12-screenshot-timeline.md @@ -1,6 +1,6 @@ # TASK-12: Screenshot Timeline -**Status:** 🔄 Pendente +**Status:** ✅ Concluída **Tier:** 3 — Strategic **Esforço:** ⭐⭐ Médio (3-5 dias) **Impacto:** 🟡 Baixo @@ -16,13 +16,13 @@ Implementar captura automática de screenshots do IDE em intervalos configuráve ## 🎯 Objetivos -- [ ] Captura automática de screenshots via CDP (`Page.captureScreenshot`) -- [ ] Armazenamento em disco: `data/screenshots/` com naming temporal -- [ ] Intervalo configurável (default: 60s, quando há mudanças no snapshot) -- [ ] Timeline visual na UI mobile com thumbnails clicáveis -- [ ] Endpoint REST para listar e recuperar screenshots -- [ ] Limpeza automática (manter últimos N screenshots ou últimas X horas) -- [ ] Exportar timeline como ZIP (opcional) +- [x] Captura automática de screenshots via CDP (`Page.captureScreenshot`) +- [x] Armazenamento em disco: `data/screenshots/` com naming temporal +- [x] Intervalo configurável (default: 60s, quando há mudanças no snapshot) +- [x] Timeline visual na UI mobile com thumbnails clicáveis +- [x] Endpoint REST para listar e recuperar screenshots +- [x] Limpeza automática (manter últimos N screenshots ou últimas X horas) +- [x] Exportar timeline como ZIP (opcional) ## 📁 Arquivos a Modificar/Criar @@ -249,19 +249,19 @@ SCREENSHOT_MAX=100 # Máximo de screenshots armazenados ## 🧪 Testes de Verificação -- [ ] Screenshot capturado e salvo em disco -- [ ] Cleanup remove arquivos antigos quando > maxScreenshots -- [ ] API lista screenshots em ordem reversa (mais recente primeiro) -- [ ] Imagens JPEG servidas corretamente via REST -- [ ] Captura manual via POST funciona -- [ ] Timer não causa leak de memória -- [ ] Desativado por padrão (`SCREENSHOT_ENABLED=false`) +- [x] Screenshot capturado e salvo em disco +- [x] Cleanup remove arquivos antigos quando > maxScreenshots +- [x] API lista screenshots em ordem reversa (mais recente primeiro) +- [x] Imagens JPEG servidas corretamente via REST +- [x] Captura manual via POST funciona +- [x] Timer não causa leak de memória +- [x] Desativado por padrão (`SCREENSHOT_ENABLED=false`) ## ✅ Critérios de Aceitação -- [ ] Captura automática funcional (change-based + interval) -- [ ] Timeline visual com thumbnails no mobile -- [ ] Click para expandir screenshot em full-screen -- [ ] Cleanup automático (storage bounded) -- [ ] API REST completa (list, get, delete, capture) -- [ ] Zero impacto no polling loop (async, non-blocking) +- [x] Captura automática funcional (change-based + interval) +- [x] Timeline visual com thumbnails no mobile +- [x] Click para expandir screenshot em full-screen +- [x] Cleanup automático (storage bounded) +- [x] API REST completa (list, get, delete, capture) +- [x] Zero impacto no polling loop (async, non-blocking) diff --git a/public/css/layout.css b/public/css/layout.css index 0557a17..c8891e5 100644 --- a/public/css/layout.css +++ b/public/css/layout.css @@ -13,12 +13,14 @@ body::before { position: relative; z-index: 1; min-height: 100vh; + min-height: 100dvh; } .shell { display: flex; flex-direction: column; height: 100vh; + height: 100dvh; } .header, @@ -31,6 +33,7 @@ body::before { padding: var(--space-md); background: var(--bg-panel); backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); border-bottom: var(--border-width) solid var(--border); } @@ -88,6 +91,7 @@ body::before { background: var(--bg-panel-muted); border-bottom: var(--border-width) solid var(--border); backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); } .history-layer, @@ -134,7 +138,15 @@ body::before { align-items: flex-end; justify-content: center; background: var(--modal-backdrop); +} + +.modal-overlay::before { + content: ""; + position: absolute; + inset: 0; backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + z-index: -1; } .modal-overlay.show { @@ -150,6 +162,7 @@ body::before { display: grid; grid-template-rows: auto 1fr auto; height: 100vh; + height: 100dvh; } .admin-shell { diff --git a/src/env.js b/src/env.js new file mode 100644 index 0000000..8c8d51e --- /dev/null +++ b/src/env.js @@ -0,0 +1,14 @@ +// @ts-check +import dotenv from 'dotenv'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import fs from 'fs'; + +const _ownDir = dirname(dirname(fileURLToPath(import.meta.url))); +const _ownEnv = join(_ownDir, '.env'); + +if (fs.existsSync(_ownEnv)) { + dotenv.config({ path: _ownEnv }); +} else { + dotenv.config(); // fallback to cwd +} diff --git a/src/server.js b/src/server.js index 9722d65..2bd0e05 100755 --- a/src/server.js +++ b/src/server.js @@ -6,7 +6,8 @@ * * @module server */ -import dotenv from 'dotenv'; +import './env.js'; +import fs from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { @@ -19,16 +20,6 @@ import { stopBot as stopTelegramBot } from './utils/telegram.js'; -// Load .env from the package's own directory (not the cwd where npx runs) -const _ownDir = dirname(dirname(fileURLToPath(import.meta.url))); -const _ownEnv = join(_ownDir, '.env'); -import fs from 'fs'; -if (fs.existsSync(_ownEnv)) { - dotenv.config({ path: _ownEnv }); -} else { - dotenv.config(); // fallback to cwd -} - import express from 'express'; import compression from 'compression'; import cookieParser from 'cookie-parser';