feat: port desktop/Electron + refactors (Pinia, TS, Vuetify 4)#49
Open
juanaleixo wants to merge 186 commits into
Open
feat: port desktop/Electron + refactors (Pinia, TS, Vuetify 4)#49juanaleixo wants to merge 186 commits into
juanaleixo wants to merge 186 commits into
Conversation
…hadow - Renames src/helpers/String.js -> src/helpers/Strings.js - Updates import identifier in src/main.js to avoid shadowing native String class - Public API ($string global) unchanged Closes plans/refactor-2026/tasks/007-rename-string-helper.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AppData.js: add toggle() as canonical method; toogle() becomes deprecated alias with one-time console.warn in DEV mode - Dev.js: same fix — toggle() added, toogle() marked deprecated - App.vue: migrate the only caller ($dev.toogle → $dev.toggle) Zero active callers of toogle() remain. Alias preserved for backwards-compat until a cleanup task removes it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Theme.js: dead code — $theme registered in globalProperties but never called anywhere. Import and registration removed from main.js. - Window.js: single-call wrapper over window.open(). Logic inlined directly into Popup.js (its only consumer); file deleted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- App.vue: remove phantom v-btn (v-show=false), handleKeydown method,
and console.log("click ") — file reduced to minimal shell
- main.js: register Ctrl+Alt+D in the Hotkeys system (group: system),
calling Dev.toggle() directly
- pt.json / es.json: add hotkeys.ctrl_alt_d translation key
The shortcut continues working; now appears in the F1 cheatsheet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ModuleManager.js:
- Remove commented-out category-creation block (/* if (!moduleGroups...) */)
- Remove installRemoteModule() placeholder method — calls fetchModuleManifest()
and downloadModuleModule() which do not exist anywhere in the codebase
- Remove empty try/catch with commented-out installRemoteModule call
main.js:
- Remove 3-line stale rationale comment above globalProperties assignment
("Equivalente ao mixin.beforeCreate...") — historical noise, not load-bearing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A RFC previa regressão para v3, mas ao executar constatou-se que Vuetify 4.0.6 é a versão `latest` no npm (non-prerelease). O critério de reconsideração foi atingido — downgrade passou a ser custo sem benefício. Decisão: manter v4, travar com tilde (~4.0.6). - package.json: ^4.0.0 → ~4.0.6 - v-number-input mantido (disponível em v4, sem substituição necessária) - ADR criado em docs/adr/0001-vuetify-versao-estavel.md - Build e lint passam sem erros Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Audited 10 suspicious deps; removed 4 with zero usage: - core-js: no imports; Vite targets modern browsers natively - roboto-fontface: Roboto loaded via Google Fonts CDN (webfontloader), npm package never imported - dotenv: Vite loads .env automatically; no require/import found - archiver: zero references in src/ or electron/ Kept: webfontloader (src/plugins), vue-country-flag-next (LanguageSelector), vue-json-pretty (dev module), express/basic-ftp/electron-updater (Electron main process — must stay in dependencies for electron-builder bundling). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Separates framework vendors into dedicated chunks for better browser
caching. Main index.js drops from 380 kB → 162 kB (gzip: 135 → 55 kB).
- vendor-vue: vue + vue-router + vuex (164 kB)
- vendor-i18n: vue-i18n (54 kB)
- vendor-fuse: fuse.js (24 kB)
- Vuetify kept out of manualChunks — vite-plugin-vuetify autoImport
does tree-shaking automatically; forcing a single chunk would break it.
Also adds rollup-plugin-visualizer (devDep) generating dist/stats.html
on every build. Fixed vite.config.js to use async + dynamic import() for
the ESM-only visualizer package, and migrated require("path") to ESM import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds dedicated aliases to vite.config.js and jsconfig.json for the main src/ subdirectories. Existing @/* alias is preserved for backwards compatibility. Imports are not migrated here — that is covered by task #097. New aliases: @helpers, @components, @modules, @layout, @views, @store, @lang Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port 5002 is deliberate: electron/main.cjs hardcodes DEV_URL to http://localhost:5002 and the electron:dev script uses wait-on with that same URL. Changing it requires updating three files in sync. Added port to npm run dev/host descriptions and a note explaining why 5002 is used instead of Vite's default 5173. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Creates LiturgyTimer.vue: renders the timer display (running state, time, prev/next navigation) and start/stop button via props + emits. - Index.vue replaces ~25 lines of inline timer template with <LiturgyTimer> + event bindings; removes 40 lines of timer CSS. - useLiturgyTimer remains in Index.vue setup() to keep timerCurrentIndex and timerItemProgress available for item list highlighting (017c). - Also includes 017a composables (useLiturgyItems, useLiturgyPersistence, useLiturgyTimer) which were created but not yet committed. - Build: clean. Lint: no new errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Creates LiturgyItem.vue (418 lines): renders a single liturgy row — either a categoria header or a normal item card, controlled by props. - Props: element, index, locked, timerActive, timerProgress, defaultColor, isChecked (fn), iconFor (fn), subtitleFor (fn). - Emits: edit, remove, execute, play-music, change-color, toggle-checked. - All lit-card* and lit-category* CSS moved to LiturgyItem.vue (scoped). - .liturgy-list-area--locked .lit-card replaced by .lit-card--locked modifier handled inside the component, removing the parent-selector. - Removes unused manifest import from Index.vue. - Index.vue: 1661 → 1302 lines (-359 lines). Build and lint: clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Creates LiturgyItemForm.vue (254 lines of template + CSS): renders the add/edit item dialog with all 6 item types (anotacao, site, arquivo, musica, itensagendados, categoria). Uses :value + @input/@change event handlers instead of v-model to avoid mutating props; mutations go through setFormField(field, value) function prop — passes vue/no-mutating-props. - Creates LiturgyImportExport.vue (82 lines): wraps save + load buttons and the hidden <input type="file">; emits @save and @file-load(event); removes loadFile() method and $refs.fileInput from Index.vue. - useLiturgyItems: adds setFormField(field, value) closure function, exposed and passed as a prop to LiturgyItemForm. - Index.vue: dialog CSS block (~150 lines) moved to LiturgyItemForm.vue; retains only .lit-input and .lit-hint (used in schedules dialog). - Index.vue: 1302 → 960 lines (-342 lines). Build and lint: clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…up Index.vue Index.vue: 958 → 195 lines (< 200 target). Composables receberam helpers setActiveCatId, setNoteDayIndex, toggleNotes, toggleMenuOpen, closeMenu e saveCategoryName agora aceita nome como parâmetro para evitar acoplamento de ref. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
helpers/Media.js removido; conteúdo movido para src/composables/useMedia.js. Todos os consumidores (CommandRegistry, Shortcuts, main.js, useLiturgyItems) atualizam import. $media removido de globalProperties — zero usos de this.$media. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move all reactive state (media, slides, buttons, menu_modes, etc.) and actions (play/pause/prev/next/seek/etc.) from Player.vue into src/composables/usePlayerState.js. Player.vue keeps the same template and now uses setup() to spread the composable. Methods that previously called this.\$media now call useMedia (imported as Media) directly — fixes runtime breakage introduced by 016e (which removed \$media from globalProperties). Player.vue: 457 → 247 lines (script: 226 → 14). usePlayerState.js: 247. No behavioral change. Build + lint pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracted from Player.vue: - PlayerControls.vue (29 lines): button row driven by `buttons` prop. - PlayerProgress.vue (54 lines): progress bar + timestamps + slide counter. Receives scalar props (progress, currentTime, duration, buffered, etc.) and emits `seek` on click. v-model deliberately replaced by :model-value to avoid prop mutation; seek is the only interactive path. Player.vue script switched to a setup() that exposes a `compactButtons` computed so the compact-menu list can iterate without inline filter syntax. Player.vue: 247 → 231 lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PlayerGauge.vue (35 lines): mute toggle button + volume slider. Forwards `update:model-value` from v-progress-linear as `seek` event so the parent calls setVolume with the new value. - PlayerActions.vue (142 lines): right-column toolbar (mode menu, slide jump menu, maximize/fullscreen/screen/close buttons, compact menu). Imports LScreenBtn locally so Player.vue does not need to. - Player.vue: removed dead `location == 'footer'` blocks (Footer.vue has its own Delphi-style player UI now; only `window` and `fullscreen` callers remain). Result: 231 → 74 lines (< 100 ✅). usePlayerState.setVolume now accepts an optional value so the gauge can pass the click-position volume directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BACKLOG.md: 022 status todo → done. - 022-refatorar-player.md: status frontmatter + detailed sub-PR notes documenting what was extracted, surprises encountered (016e \$media regression, no VU meter to extract, needed PlayerActions.vue beyond spec to hit < 100 lines), and runtime smoke caveat. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mutations.js:
- _walkSet() shared utility (DRY — reused by SET_MODULE_PATH,
SET_USER_DATA_PATH and the deprecated fallbacks).
- Named scalar mutations: SET_LOADING, SET_IS_DARK, SET_IS_DEV,
SET_IS_POPUP, SET_IS_MOBILE, SET_IS_DESKTOP, SET_IS_ONLINE,
SET_IMPORT_MODULES, SET_POPUP, SET_POPUP_MODULE, SET_MODULE_GROUP.
- PATCH_ALERT({ key, value }) — single-field update on state.alert.
- SET_MODULE_PATH({ id, path, value }) — generic typed mutation for
any state.modules[id].<path> write.
- SET_USER_DATA_PATH({ path, value }) — generic typed mutation for
any state.user_data.<path> write.
- setData / addElementArray / removeElementArray: kept as deprecated
fallbacks with DEV console.warn showing the offending key path.
AppData.js:
- set() replaced the one direct commit("setData") with a routing
table. Known root keys dispatch named mutations; anything else
(dynamic paths from Popup.vue message relay, etc.) falls through to
setData with its deprecation warning.
- No behavioral change; popup sync code unchanged.
Result: commit("setData", ...) zero first-level callers outside
AppData.js itself. DevTools now shows meaningful mutation names.
Build + lint pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix critical normalization bug in Hotkeys.js: _comboFromEvent now lowercases all keys unconditionally, matching _normalizeCombo. This made every multi-char shortcut (F1, Space, Escape, ArrowLeft, Home, Delete, etc.) previously registered in main.js non-functional. - Remove vue3-shortkey: uninstall package, drop import/app.use from main.js, strip v-shortkey + @shortkey from PlayerControls.vue and PlayerActions.vue, remove shortkey fields from usePlayerState buttons. - Migrate Bible shortcuts (←, →, Del) to Hotkeys.register in mounted()/unmounted() of Bible/Index.vue. Handler refs stored on `this` for targeted unregister. Last-registered-wins gives Bible verse navigation priority over slide nav while module is mounted. - Add label field to Hotkeys.list(); HotkeysCheatsheet uses label for <kbd> rendering (shows "←" instead of "arrowleft"). Add "bible" group to GROUP_ORDER + i18n (pt/es). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove dead code from ModuleManager.js: the `modules` Map, `manifests`
Map, and `register()` method were never called externally or internally —
`modules` was always empty and `manifests` never populated.
Fix latent crash: moduleGroups[category].modules throws TypeError when
a module declares a category not pre-declared in state.js. Added
a dynamic guard that creates the category entry on first use.
Add explicit JSDoc responsibility headers to both files with cross-
references ("does NOT do X — that is Y.js") to make the boundary
unambiguous for future contributors.
No behavior changes; public API (Modules.open/close/get,
ModuleManager.init/installModule) is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Audit confirms Vuetify 4 VDialog provides focus-trap natively for all
modals in the project: retainFocus:true (Tab cycling trapped inside the
dialog) and captureFocus:true (focus moves to first element on open) are
both Vuetify 4 defaults. VBottomSheet inherits the same defaults via
makeVDialogProps(). No focus-trap-vue dependency needed.
The only gap found: LiturgyItemForm and LiturgySchedules used the
`persistent` prop without an Escape handler, leaving the close button
(Tab + Enter) as the only keyboard path to dismiss them. Added
@keydown.escape="$emit('update:modelValue', false)" matching the same
pattern used in Window.vue. Click-outside behavior remains unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard Path.db() against path traversal by validating the key matches [a-zA-Z0-9_-]+ before building the URL. Guard Path.file() against '..' segments and absolute URLs (SSRF-style). Critical for the upcoming Electron phase where louvorja:// resolves to real filesystem paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Workflow publica instaladores Windows/macOS/Linux automaticamente como GitHub Release quando uma tag v*.*.* é pushada. Pre-releases (`v*.*.*-*`, ex: v1.28.0-preview.1) viram releases marcadas como `prerelease`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A release 1.28.0-preview.1 falhou porque o workflow esperava artifacts/**/*.zip mas o mac target só produzia DMG. ZIP também é necessário para auto-update via electron-updater no macOS.
Feat/gerenciamento de abas modulo
…arra de controle.
…ador, para uma melhor usabilidade
feat/modulo-liturgia
Ribbon contextual: - Dividido em 4 sub-abas (Arquivo / Slides / Áudio/Gravação / Visualização) com paridade Delphi - Botões padronizados via token --lj-small-btn-width Layout: - Sidebar reorganizada em accordions (Texto, Cor & Tipografia, Imagem, Replicar) - Player customizado abaixo do preview com timeline e markers dos slides gravados - Preview usa o mesmo clamp da projeção real (useSlideStyle + cfg global) ModuleContainer: - Prop beforeClose para módulos cancelarem fechamento Bug fixes (slide_editor): - SljaConverter: encoding windows-1252 (Delphi ANSI) e interop JSZip via Vite - actProject: abre janela /projection e envia payload completo (cor, tamanho, imagem, posição) com live sync - actSave/SaveAs: materializa tokens pkg:// → lib:// para preservar mídia ao reabrir - recordRetroactive grava no slide atual com offset (paridade Delphi) - Sincronização total: lista, ribbon, player e timeline conversam (goSlide faz seek) - Feedback \$alert nos botões de áudio sem arquivo anexado
- recordAdvance: avança primeiro e grava no slide novo (paridade Delphi acaoSlide 'prox_grava'), evitando o syncSlideFromAudio puxar de volta - recordRetroactive: paridade btGravaRClick — primeiro slide faz seek para 0, demais usam o tempo já gravado menos offset e fazem seek no áudio - goSlide: ao voltar para o slide 1 sempre faz seek para 0 (mesmo sem tempo gravado) - syncSlideFromAudio: trabalha por delta — só atualiza current quando o tempo cruza um marker em playback contínuo; ignora saltos (seek/navegação manual), permitindo trocar para slides futuros sem tempo - togglePlay: removido alerta confuso que misturava "Nenhum áudio anexado" com aviso de tempo gravado; agora toca de onde estiver quando o slide não tem tempo - requireAudio: mensagem ajustada para refletir o que o usuário precisa fazer
Ajuste no css da tela de retorno com safeArea
Feat/modulo liturgia
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Porta todo o trabalho acumulado em
juanaleixo/louvorja:mainpara o repositório principal. Inclui a versão desktop (Electron), múltiplos refactors estruturais e novas features.Principais temas
louvorja://, AppMenu, splash, ícones, Service Worker cleanup.Escopo
7e93b00— 1.27.0)Inclui o merge
#1(Gerenciamento de abas de módulo) feito hoje.Test plan