From 72ba2e7cc7f7726819aa4a1f056329bb8d3ee336 Mon Sep 17 00:00:00 2001 From: BurntToasters <61037367+BurntToasters@users.noreply.github.com> Date: Sat, 25 Jul 2026 20:51:13 -0700 Subject: [PATCH 1/2] B2 --- CHANGELOG.md | 14 +- com.burnttoasters.rosi.metainfo.xml | 1 + package-lock.json | 4 +- package.json | 2 +- src/main/downloader.ts | 1 + src/main/main.ts | 32 +- src/main/preload.ts | 1 + src/renderer/css/05-overlays.css | 13 + src/renderer/css/07-queue.css | 99 +++--- src/renderer/css/08-wizard.css | 72 +++- src/renderer/css/splash.css | 256 ++++++++++++++ src/renderer/index.html | 21 +- src/renderer/modules/queue.ts | 10 - src/renderer/modules/ui.ts | 1 + src/renderer/renderer-globals.d.ts | 1 + src/renderer/rosiEngine.ts | 14 +- src/renderer/splash.html | 313 +----------------- src/tests/buildScripts.updateMetainfo.test.ts | 13 +- src/tests/ipc.contract.test.ts | 28 ++ src/tests/jobProgressReporter.test.ts | 74 +++++ src/tests/main.ipc.test.ts | 29 +- src/tests/packaging.contract.test.ts | 3 + src/tests/preload.test.ts | 2 + src/tests/queue.test.ts | 5 + src/tests/rosiEngine.dom.test.ts | 1 + src/types.ts | 1 + src/utils/ipcValidation.ts | 2 + vitest.config.ts | 3 + 28 files changed, 595 insertions(+), 421 deletions(-) create mode 100644 src/renderer/css/splash.css create mode 100644 src/tests/jobProgressReporter.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 11cfdca..d5376f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ | Windows | macOS | Linux | | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **EXE:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Windows-x64.exe) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Windows-arm64.exe) | **[Universal DMG](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-MacOS-universal.dmg)** | **AppImage:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Linux-x86_64.AppImage) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Linux-arm64.AppImage) | -|
| **[Universal ZIP](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-MacOS-universal.zip)** | **DEB:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Linux-amd64.deb) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Linux-arm64.deb) | -| | | **RPM:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Linux-x86_64.rpm) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.1/ROSI-Linux-aarch64.rpm) | +| **EXE:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Windows-x64.exe) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Windows-arm64.exe) | **[Universal DMG](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-MacOS-universal.dmg)** | **AppImage:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Linux-x86_64.AppImage) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Linux-arm64.AppImage) | +|
| **[Universal ZIP](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-MacOS-universal.zip)** | **DEB:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Linux-amd64.deb) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Linux-arm64.deb) | +| | | **RPM:** [x64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Linux-x86_64.rpm) / [arm64](https://github.com/BurntToasters/ROSI/releases/download/v4.3.0-beta.2/ROSI-Linux-aarch64.rpm) | > [!IMPORTANT] > The `.sig` files in this repo are NOT normal GPG signatures — they are for ROSI's built-in updater to verify the integrity of updates before downloading and installing. @@ -18,6 +18,14 @@ --- +## Changes in `v4.3.0-beta.2:` + +- **Splash:** Loading screen now follows your saved theme tokens, improved contrast on status text, and shows the live app version at runtime. +- **UI / a11y:** Toast dismiss buttons, wizard toggles, and modal placeholders were polished for keyboard focus and cleaner first paint. +- **Queue:** Status indicators use theme-colored dots instead of emoji for clearer light/dark/purple rendering. +- **Linux:** Taskbar / Dock progress setting is hidden with a note that the feature is Windows and macOS only. +- **Lifecycle:** Quitting on Windows and Linux now stops active downloads and the queue the same way as closing the window on macOS. + ## Changes in `v4.3.0-beta.1:` - **macOS:** Added a native application menu (About, Settings, Check for Updates, Edit, View, Window, Help) with actions wired into the existing UI. diff --git a/com.burnttoasters.rosi.metainfo.xml b/com.burnttoasters.rosi.metainfo.xml index de3e019..c68e9ca 100644 --- a/com.burnttoasters.rosi.metainfo.xml +++ b/com.burnttoasters.rosi.metainfo.xml @@ -34,6 +34,7 @@ + diff --git a/package-lock.json b/package-lock.json index c26cee5..bd0fc46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rosi", - "version": "4.3.0-beta.1", + "version": "4.3.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rosi", - "version": "4.3.0-beta.1", + "version": "4.3.0-beta.2", "license": "MPL-2.0", "dependencies": { "electron-log": "^5.3.4", diff --git a/package.json b/package.json index d60a99b..2f8f5df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rosi", - "version": "4.3.0-beta.1", + "version": "4.3.0-beta.2", "private": true, "description": "Electron GUI for yt-dlp", "desktopName": "com.burnttoasters.rosi.desktop", diff --git a/src/main/downloader.ts b/src/main/downloader.ts index 3aca9db..48c46d4 100644 --- a/src/main/downloader.ts +++ b/src/main/downloader.ts @@ -82,6 +82,7 @@ function safeSend(sender: Electron.WebContents, channel: string, message: unknow } } +/** Console log lines for the download panel; structured UI uses `job-progress` via JobProgressReporter. */ function sendProgress(session: DownloadSession | null, message: string) { if (!session || !shouldEmitTerminalEvent(session.lifecycle)) return; if (!isActiveSession(session)) return; diff --git a/src/main/main.ts b/src/main/main.ts index 8eb6386..1c80fb3 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -138,6 +138,13 @@ function createSplashWindow() { ...(process.platform === 'darwin' ? { roundedCorners: true } : {}), }); void splashWindow.loadFile(path.join(__dirname, '..', '..', 'src', 'renderer', 'splash.html')); + splashWindow.webContents.once('did-finish-load', () => { + if (!splashWindow || splashWindow.isDestroyed()) return; + const versionLiteral = JSON.stringify(app.getVersion()); + void splashWindow.webContents.executeJavaScript( + `(function(){var el=document.getElementById('version-display');if(el)el.textContent='v'+${versionLiteral};})()` + ); + }); splashWindow.center(); setTimeout(() => { if (splashWindow && !splashWindow.isDestroyed()) { @@ -219,6 +226,7 @@ async function runRendererSmokeChecks(windowRef: BrowserWindow): Promise { return; } - if (!isSmokeRun) { + if (isPackaged && !isSmokeRun && !process.env.VITEST) { createSplashWindow(); } verifyBundledFfmpeg(); @@ -500,29 +508,18 @@ app.on('activate', () => { app.on('before-quit', () => { appQuitting = true; + stopActiveDownloadsAndQueue(); flushQueueOnShutdown(); - try { - killAllProcesses(); - } catch (error) { - log.error('Error killing processes on quit:', error); - } - try { - cancelFormats(); - } catch (error) { - log.error('Error cancelling formats on quit:', error); - } - try { - cancelVideoInfo(); - } catch (error) { - log.error('Error cancelling video info on quit:', error); - } }); if (!process.windowsStore) { setupAutoUpdater(getMainWindow, loadSettings); } -ipcMain.on('log-error', (_, message) => { +ipcMain.on('log-error', (event, message) => { + if (!assertMainWindowSender(event)) { + return; + } if (typeof message === 'string') { const truncated = message.length > 2000 ? message.slice(0, 2000) + '...(truncated)' : message; log.error(`[renderer] ${truncated}`); @@ -545,6 +542,7 @@ ipcMain.on('settings-flush-complete', (event) => { }); ipcMain.handle('get-app-version', () => app.getVersion()); +ipcMain.handle('get-app-platform', () => process.platform); ipcMain.handle('is-packaged', () => isPackaged); if (!process.windowsStore) { ipcMain.handle('check-for-updates', () => checkForUpdates(isPackaged, loadSettings)); diff --git a/src/main/preload.ts b/src/main/preload.ts index 32f52bf..e8159e1 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -29,6 +29,7 @@ const api: RendererApi = { cancelDownload: () => ipcRenderer.send('cancel-download'), cancelFormats: () => ipcRenderer.send('cancel-formats'), getAppVersion: () => ipcRenderer.invoke('get-app-version'), + getAppPlatform: () => ipcRenderer.invoke('get-app-platform'), checkDenoInstalled: () => ipcRenderer.invoke('check-deno-installed'), installDeno: () => ipcRenderer.invoke('install-deno'), detectGpu: () => ipcRenderer.invoke('detect-gpu'), diff --git a/src/renderer/css/05-overlays.css b/src/renderer/css/05-overlays.css index 4e8e37c..b0f0362 100644 --- a/src/renderer/css/05-overlays.css +++ b/src/renderer/css/05-overlays.css @@ -1,3 +1,8 @@ +.modal-overlay:not(.active) .modal-box h3, +.modal-overlay:not(.active) .modal-message { + visibility: hidden; +} + .modal-overlay { position: fixed; inset: 0; @@ -371,6 +376,14 @@ background: var(--surface-3); } +.toast-dismiss:focus-visible { + outline: none; + color: var(--text-primary); + box-shadow: + 0 0 0 2px var(--accent-subtle), + 0 0 0 4px var(--accent); +} + .toast-warning { border-left: 3px solid var(--warning); } diff --git a/src/renderer/css/07-queue.css b/src/renderer/css/07-queue.css index 409cd48..80cfe2e 100644 --- a/src/renderer/css/07-queue.css +++ b/src/renderer/css/07-queue.css @@ -247,8 +247,43 @@ .queue-item-status { flex-shrink: 0; - width: 20px; - text-align: center; + width: 10px; + height: 10px; + border-radius: var(--radius-full); + background: var(--text-muted); +} + +.queue-pending .queue-item-status { + background: var(--text-muted); +} + +.queue-downloading .queue-item-status { + background: var(--accent); + box-shadow: 0 0 0 2px var(--accent-subtle); + animation: queueStatusPulse 1.2s ease-in-out infinite; +} + +.queue-completed .queue-item-status { + background: var(--success); +} + +.queue-failed .queue-item-status { + background: var(--danger); +} + +.queue-cancelled .queue-item-status { + background: var(--text-muted); + opacity: 0.55; +} + +@keyframes queueStatusPulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.45; + } } .queue-item-url { @@ -306,63 +341,3 @@ .queue-cancelled .queue-item-url { color: var(--text-muted); } - -.wizard-overlay { - position: fixed; - inset: 0; - display: none; - align-items: center; - justify-content: center; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(8px); - z-index: 2000; -} - -.wizard-overlay.active { - display: flex; -} - -.wizard-card { - width: 90%; - max-width: 520px; - background: color-mix(in srgb, var(--surface-1) 32%, transparent); - backdrop-filter: blur(var(--blur-lg)); - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: var(--radius-xl); - box-shadow: - var(--shadow-lg), - 0 16px 48px rgba(0, 0, 0, 0.4); - padding: var(--spacing-2xl) var(--spacing-xl) var(--spacing-xl); - display: flex; - flex-direction: column; - animation: cardEntrance 0.5s cubic-bezier(0.16, 1, 0.3, 1) both; -} - -.wizard-progress { - height: 3px; - background: var(--surface-3); - border-radius: 2px; - margin-bottom: var(--spacing-xl); - overflow: hidden; -} - -.wizard-progress-bar { - height: 100%; - width: 25%; - background: var(--accent); - border-radius: 2px; - transition: width var(--transition-normal); -} - -.wizard-step { - display: none; - flex-direction: column; - align-items: center; - text-align: center; - min-height: 280px; -} - -.wizard-step.active { - display: flex; - animation: wizardFadeIn 0.3s ease both; -} diff --git a/src/renderer/css/08-wizard.css b/src/renderer/css/08-wizard.css index 23160eb..7fbbd15 100644 --- a/src/renderer/css/08-wizard.css +++ b/src/renderer/css/08-wizard.css @@ -9,6 +9,66 @@ } } +.wizard-overlay { + position: fixed; + inset: 0; + display: none; + align-items: center; + justify-content: center; + background: var(--overlay-bg-heavy); + backdrop-filter: blur(var(--blur-md)); + z-index: var(--z-modal); +} + +.wizard-overlay.active { + display: flex; +} + +.wizard-card { + width: 90%; + max-width: 520px; + background: color-mix(in srgb, var(--surface-1) 32%, transparent); + backdrop-filter: blur(var(--blur-lg)); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: var(--radius-xl); + box-shadow: + var(--shadow-lg), + 0 16px 48px rgba(0, 0, 0, 0.4); + padding: var(--spacing-2xl) var(--spacing-xl) var(--spacing-xl); + display: flex; + flex-direction: column; + animation: cardEntrance 0.5s cubic-bezier(0.16, 1, 0.3, 1) both; +} + +.wizard-progress { + height: 3px; + background: var(--surface-3); + border-radius: 2px; + margin-bottom: var(--spacing-xl); + overflow: hidden; +} + +.wizard-progress-bar { + height: 100%; + width: 25%; + background: var(--accent); + border-radius: 2px; + transition: width var(--transition-normal); +} + +.wizard-step { + display: none; + flex-direction: column; + align-items: center; + text-align: center; + min-height: 280px; +} + +.wizard-step.active { + display: flex; + animation: wizardFadeIn 0.3s ease both; +} + .wizard-icon { margin-bottom: var(--spacing-lg); } @@ -134,13 +194,17 @@ .wizard-toggle input { position: absolute; opacity: 0; - pointer-events: none; + width: var(--toggle-width); + height: var(--toggle-height); + margin: 0; + cursor: pointer; + z-index: 1; } -/* Focus ring on wizard toggle slider proxy. */ .wizard-toggle input:focus-visible + .toggle-slider { - outline: 2px solid var(--accent); - outline-offset: 2px; + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 4px var(--accent-subtle); } .wizard-toggle input:checked + .toggle-slider { diff --git a/src/renderer/css/splash.css b/src/renderer/css/splash.css new file mode 100644 index 0000000..5637f68 --- /dev/null +++ b/src/renderer/css/splash.css @@ -0,0 +1,256 @@ +body.splash-screen { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; + overflow: hidden; + background: var(--bg-gradient-mid); +} + +.splash-container { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 2rem; +} + +.logo-wrapper { + position: relative; + margin-bottom: 2rem; + animation: splashLogoEntrance 0.8s cubic-bezier(0.16, 1, 0.3, 1) both; +} + +@keyframes splashLogoEntrance { + from { + opacity: 0; + transform: scale(0.8) translateY(10px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.logo { + width: 100px; + height: 100px; + filter: none; + animation: splashLogoPulse 3s ease-in-out infinite; + position: relative; + z-index: 2; +} + +.logo-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 160px; + height: 160px; + background: radial-gradient(circle, var(--accent-glow) 0%, transparent 70%); + border-radius: var(--radius-full); + animation: splashGlowPulse 3s ease-in-out infinite; + z-index: 0; + opacity: 0.5; +} + +.logo-ring { + position: absolute; + top: 50%; + left: 50%; + width: 140px; + height: 140px; + margin-top: -70px; + margin-left: -70px; + border: 1px solid var(--accent-subtle); + border-radius: var(--radius-full); + animation: + splashRingRotate 8s linear infinite, + splashRingFade 3s ease-in-out infinite; + z-index: 1; +} + +.logo-ring::before { + content: ''; + position: absolute; + top: -3px; + left: 50%; + width: 6px; + height: 6px; + margin-left: -3px; + background: var(--accent-light); + border-radius: var(--radius-full); +} + +.logo-ring-2 { + position: absolute; + top: 50%; + left: 50%; + width: 170px; + height: 170px; + margin-top: -85px; + margin-left: -85px; + border: 1px solid color-mix(in srgb, var(--accent-subtle) 55%, transparent); + border-radius: var(--radius-full); + animation: + splashRingRotate 12s linear infinite reverse, + splashRingFade 4s ease-in-out infinite 1s; + z-index: 0; +} + +@keyframes splashRingRotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes splashRingFade { + 0%, + 100% { + opacity: 0.4; + } + 50% { + opacity: 0.8; + } +} + +@keyframes splashLogoPulse { + 0%, + 100% { + transform: translateY(0) scale(1); + } + 50% { + transform: translateY(-6px) scale(1.02); + } +} + +@keyframes splashGlowPulse { + 0%, + 100% { + opacity: 0.4; + transform: translate(-50%, -50%) scale(1); + } + 50% { + opacity: 0.7; + transform: translate(-50%, -50%) scale(1.15); + } +} + +.app-title { + font-size: 2.2rem; + font-weight: 700; + letter-spacing: 0.12em; + margin-bottom: 0.4rem; + color: var(--text-primary); + animation: splashTitleEntrance 0.6s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both; +} + +@keyframes splashTitleEntrance { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.app-subtitle { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 400; + letter-spacing: 0.04em; + margin-bottom: 2.5rem; + animation: splashTitleEntrance 0.6s cubic-bezier(0.16, 1, 0.3, 1) 0.35s both; +} + +.loading-container { + width: 180px; + animation: splashTitleEntrance 0.6s cubic-bezier(0.16, 1, 0.3, 1) 0.5s both; +} + +.loading-track { + height: 3px; + background: var(--surface-2); + border-radius: 2px; + overflow: hidden; + position: relative; +} + +.loading-track::after { + content: ''; + position: absolute; + top: 0; + left: -50%; + width: 50%; + height: 100%; + background: linear-gradient(90deg, transparent, var(--accent), transparent); + border-radius: 2px; + animation: splashLoadingSlide 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite; +} + +@keyframes splashLoadingSlide { + 0% { + left: -50%; + } + 100% { + left: 100%; + } +} + +.loading-label { + display: flex; + justify-content: center; + margin-top: 1rem; + font-size: 0.7rem; + color: var(--text-muted); + letter-spacing: 0.15em; + text-transform: uppercase; + animation: splashLoadingFade 2s ease-in-out infinite; +} + +@keyframes splashLoadingFade { + 0%, + 100% { + opacity: 0.55; + } + 50% { + opacity: 1; + } +} + +.version { + position: absolute; + bottom: 1.2rem; + right: 1.2rem; + font-size: 0.7rem; + font-family: var(--font-mono); + color: var(--text-muted); + letter-spacing: 0.05em; + opacity: 0; + animation: splashVersionFade 0.5s ease 0.8s forwards; +} + +@keyframes splashVersionFade { + to { + opacity: 1; + } +} + +@media (prefers-reduced-motion: reduce) { + .splash-screen *, + .splash-screen *::before, + .splash-screen *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/src/renderer/index.html b/src/renderer/index.html index d3dc4eb..e1a74c7 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -348,11 +348,14 @@

Settings

Download notifications -