feat(audit): Batch 20 — Windows/main-process (UI23, W12, W15, L131)
UI23: cookie sign-in + PO-token windows open with the app's resolved theme background (passed from the IPC layer), no more white flash. W12: multi-resolution fallback tray icon (16/24/32/48 via addRepresentation dataURL, embedded in trayFallbackIcons.ts); decode + scale factors verified in an Electron probe. W15: hardwareAcceleration setting (default false = software rendering); index.ts gates disableHardwareAcceleration on it after the portable redirect; Appearance card switch with restart note; boot path verified. L131: resolved by-design — clearing the queue acknowledges failures; flashFrame (W10) covers attention, Diagnostics persists the record. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+7
-1
@@ -216,7 +216,10 @@ function exportAndResolve(): void {
|
||||
*/
|
||||
export function openCookieLoginWindow(
|
||||
url: string,
|
||||
parent?: BrowserWindow
|
||||
parent?: BrowserWindow,
|
||||
/** app-theme window background (UI23) — supplied by the IPC layer so the
|
||||
* sign-in chrome matches the app's Light/Dark instead of flashing white. */
|
||||
backgroundColor?: string
|
||||
): Promise<CookiesLoginResult> {
|
||||
return new Promise((resolve) => {
|
||||
let validUrl: string
|
||||
@@ -241,6 +244,9 @@ export function openCookieLoginWindow(
|
||||
width: 480,
|
||||
height: 720,
|
||||
title: 'Sign in — AeroFetch',
|
||||
// Match the app's resolved theme (UI23) so the window doesn't flash a
|
||||
// default-white background before the remote page paints.
|
||||
backgroundColor,
|
||||
autoHideMenuBar: true,
|
||||
// Group under the app window instead of taking its own taskbar button
|
||||
// (W6); a child window stays above its parent without a modal block.
|
||||
|
||||
+19
-14
@@ -29,23 +29,28 @@ import { initialWindowState, saveWindowState } from './windowState'
|
||||
const isPrimaryInstance = app.requestSingleInstanceLock()
|
||||
if (!isPrimaryInstance) app.quit()
|
||||
|
||||
// Force software compositing (GPU acceleration off). On this dev machine's old GeForce
|
||||
// GT 625 — and plausibly on the old / locked-down public PCs this app targets —
|
||||
// Chromium's GPU compositor renders blank: under hardware acceleration the WHOLE window
|
||||
// paints white (popup overlays alone were blank earlier). Updating the NVIDIA driver to
|
||||
// the last Fermi release did not fix it. Software compositing renders correctly and
|
||||
// costs no meaningful performance for this lightweight UI, so it is the safe default for
|
||||
// this app's target hardware. The only downside is a minor overlay-repaint flicker that
|
||||
// appears on the most broken GPUs. The window backgroundColor is theme-matched (see
|
||||
// createWindow); native window-occlusion tracking is disabled (a separate documented
|
||||
// Windows blank/flicker cause). Must run before the app is ready.
|
||||
app.disableHardwareAcceleration()
|
||||
app.commandLine.appendSwitch('disable-features', 'CalculateNativeWinOcclusion')
|
||||
|
||||
// Redirect user data next to the exe when possible (portable, no-admin). Must run
|
||||
// before the app is ready and before any user path is read.
|
||||
// before the app is ready and before any user path is read — including the
|
||||
// settings read for the hardware-acceleration gate just below.
|
||||
setupPortableData()
|
||||
|
||||
// Software compositing (GPU acceleration off) is the DEFAULT, now behind an
|
||||
// opt-in setting (W15). On this dev machine's old GeForce GT 625 — and plausibly
|
||||
// on the old / locked-down public PCs this app targets — Chromium's GPU
|
||||
// compositor renders blank: under hardware acceleration the WHOLE window paints
|
||||
// white (popup overlays alone were blank earlier). Updating the NVIDIA driver to
|
||||
// the last Fermi release did not fix it. Software compositing renders correctly
|
||||
// and costs no meaningful performance for this lightweight UI, so it stays the
|
||||
// safe default; users on modern GPUs can flip Settings → Appearance →
|
||||
// "Hardware acceleration" (applies on next launch — this must run before the
|
||||
// app is ready). The window backgroundColor is theme-matched (see createWindow).
|
||||
if (!getSettings().hardwareAcceleration) {
|
||||
app.disableHardwareAcceleration()
|
||||
}
|
||||
// Native window-occlusion tracking stays off in BOTH modes — it's a separate
|
||||
// documented Windows blank/flicker cause, independent of GPU compositing.
|
||||
app.commandLine.appendSwitch('disable-features', 'CalculateNativeWinOcclusion')
|
||||
|
||||
// Register aerofetch:// so a browser/another app can hand AeroFetch a link
|
||||
// (?url=<encoded>) the way Android's share sheet hands Seal one. The NSIS
|
||||
// installer also declares this scheme (electron-builder.yml's `protocols`)
|
||||
|
||||
+10
-3
@@ -196,8 +196,13 @@ export function registerIpcHandlers(getMainWindow: () => BrowserWindow | null):
|
||||
|
||||
ipcMain.handle(IpcChannels.cookiesLogin, (e, url: string) =>
|
||||
// Parent the sign-in window to the app window (W6) so it groups under
|
||||
// AeroFetch instead of spawning a second taskbar button.
|
||||
openCookieLoginWindow(url, BrowserWindow.fromWebContents(e.sender) ?? undefined)
|
||||
// AeroFetch instead of spawning a second taskbar button; theme its
|
||||
// background to the app's Light/Dark (UI23).
|
||||
openCookieLoginWindow(
|
||||
url,
|
||||
BrowserWindow.fromWebContents(e.sender) ?? undefined,
|
||||
PAGE_BACKGROUND[resolveBackgroundMode(getSettings().theme)]
|
||||
)
|
||||
)
|
||||
ipcMain.handle(IpcChannels.cookiesStatus, () => getCookiesStatus())
|
||||
ipcMain.handle(IpcChannels.cookiesClear, () => clearCookies())
|
||||
@@ -296,7 +301,9 @@ export function registerIpcHandlers(getMainWindow: () => BrowserWindow | null):
|
||||
|
||||
// Open a YouTube WebView and extract a PO token for bot-check bypass (Phase P).
|
||||
ipcMain.handle(IpcChannels.youtubePoTokenMint, async () => {
|
||||
const token = await openPoTokenWindow()
|
||||
const token = await openPoTokenWindow(
|
||||
PAGE_BACKGROUND[resolveBackgroundMode(getSettings().theme)]
|
||||
)
|
||||
if (token) await setSettings({ youtubePoToken: token })
|
||||
return token
|
||||
})
|
||||
|
||||
+7
-1
@@ -80,7 +80,11 @@ let mintWindow: BrowserWindow | null = null
|
||||
* Resolves with the token string on success, or null if the window was closed
|
||||
* by the user before extraction completed or if the field was not found.
|
||||
*/
|
||||
export function openPoTokenWindow(): Promise<string | null> {
|
||||
export function openPoTokenWindow(
|
||||
/** app-theme window background (UI23) — supplied by the IPC layer so this
|
||||
* window's chrome matches the app's Light/Dark instead of flashing white. */
|
||||
backgroundColor?: string
|
||||
): Promise<string | null> {
|
||||
// If a minting window is already open, bring it to the front rather than
|
||||
// opening a second one.
|
||||
if (mintWindow && !mintWindow.isDestroyed()) {
|
||||
@@ -105,6 +109,8 @@ export function openPoTokenWindow(): Promise<string | null> {
|
||||
width: 960,
|
||||
height: 640,
|
||||
title: 'AeroFetch — Fetch YouTube token',
|
||||
// Match the app's resolved theme (UI23), like the cookie sign-in window.
|
||||
backgroundColor,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
partition: LOGIN_PARTITION,
|
||||
|
||||
@@ -289,6 +289,7 @@ function applySettings(s: Store<Settings>, partial: Partial<Settings>): void {
|
||||
case 'hasCompletedOnboarding':
|
||||
case 'minimizeToTray':
|
||||
case 'sidebarCollapsed':
|
||||
case 'hardwareAcceleration':
|
||||
if (typeof value === 'boolean') s.set(key, value)
|
||||
break
|
||||
case 'launchAtStartup':
|
||||
|
||||
+13
-14
@@ -6,16 +6,7 @@
|
||||
*/
|
||||
import { app, Tray, Menu, nativeImage, type BrowserWindow } from 'electron'
|
||||
import { getAppIconPath } from './binaries'
|
||||
|
||||
// Fallback tray glyph (32×32 teal disc + white download arrow) used when no
|
||||
// build/icon.ico is present. Without this the tray is skipped (an empty image
|
||||
// makes an invisible/unclickable Windows tray entry), which would strand a
|
||||
// minimized-to-tray window with no way back. Embedded as base64 so it needs no
|
||||
// asset-bundling step.
|
||||
const FALLBACK_TRAY_PNG =
|
||||
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAl0lEQVR4nO3TQQ6AIAxEUU7giV17bd0i' +
|
||||
'aaDTmdqY0ISl/peirf11jvO6+/N5cHXKwlIIG6cQqngIoY5DiKy4C4G8aBwJohSArpIBmIgNKAWgDys' +
|
||||
'AL8QGlANmCHZc8dUW1HEYEEFA6/d+B6q4CVAhwnHkb2DiUwCDkMRRBHpccQsRnXB8RLCAULxHMAAqbm0j5b4VoPRg1jzWxpzrS/JA7QAAAABJRU5ErkJggg=='
|
||||
import { FALLBACK_TRAY_PNGS } from './trayFallbackIcons'
|
||||
|
||||
let tray: Tray | null = null
|
||||
// True once the user has chosen to really quit (tray menu, or app.quit from the
|
||||
@@ -41,11 +32,19 @@ function show(getWindow: () => BrowserWindow | null): void {
|
||||
/** Create the tray icon + menu once. No-op if it already exists or the icon is missing. */
|
||||
export function createTray(getWindow: () => BrowserWindow | null): void {
|
||||
if (tray) return
|
||||
// Prefer the real app icon; fall back to the embedded glyph when no icon.ico
|
||||
// ships, so minimize-to-tray always has a tray to restore from.
|
||||
// Prefer the real app icon; fall back to the embedded multi-resolution glyph
|
||||
// when no icon.ico ships, so minimize-to-tray always has a tray to restore
|
||||
// from — and stays crisp at 150%/200% DPI (W12). Without any icon the tray is
|
||||
// skipped (an empty image makes an invisible/unclickable Windows tray entry),
|
||||
// which would strand a minimized-to-tray window with no way back.
|
||||
let icon = nativeImage.createFromPath(getAppIconPath())
|
||||
if (icon.isEmpty())
|
||||
icon = nativeImage.createFromDataURL(`data:image/png;base64,${FALLBACK_TRAY_PNG}`)
|
||||
if (icon.isEmpty()) {
|
||||
icon = nativeImage.createEmpty()
|
||||
for (const [scaleFactor, b64] of FALLBACK_TRAY_PNGS) {
|
||||
// dataURL (not `buffer`) so the PNG is decoded — `buffer` is raw bitmap data.
|
||||
icon.addRepresentation({ scaleFactor, dataURL: `data:image/png;base64,${b64}` })
|
||||
}
|
||||
}
|
||||
if (icon.isEmpty()) return
|
||||
|
||||
tray = new Tray(icon)
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Multi-resolution fallback tray glyphs (W12), rendered from build/icon.svg at
|
||||
* 16/24/32/48 px (scale factors 1/1.5/2/3) so the fallback tray icon stays
|
||||
* crisp on high-DPI displays even when the bundled icon.ico is missing.
|
||||
* Regenerate with ImageMagick:
|
||||
* magick -background none build/icon.svg -resize NxN -strip out.png
|
||||
* (base64-embedded so the fallback needs no asset-bundling step).
|
||||
*/
|
||||
export const FALLBACK_TRAY_PNGS: ReadonlyArray<readonly [number, string]> = [
|
||||
// 16×16 — scaleFactor 1
|
||||
[
|
||||
1,
|
||||
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAFEElEQVRIx52Ua1CVVRSGn/WdczhwAAnRgCPgDfGGihCZiqXj' +
|
||||
'bdS0EqyZSsUUMsy71ahlZU5W2miWOWkmYc14BxW18VaD5oji/QoqircwQUsEzne+c77dDxAx6o/vnz2z9157ve+711rS3Z29' +
|
||||
'aavXJ0CepL90Su8hrbQICZ7xK5N4i/Sow/hjw1+68LioxKBSHeNrlrL86mvqmlmsyhZOwCBfFSyfK4kt183YlPt2qnSUGOn8' +
|
||||
'6UK6kSQ9AkO4zz0qAAUoTDRA0LhSs++dp66Zf9UmUWB5XyK1YKAFjQgEzNo4ATQ0AmhEAJDPPnWgwq2Oq7Pq7KyREtf9Z7+1' +
|
||||
'va8IT5NE7+Ym1VRRRRUgCI46JVdrEst7rJUseGZi+LbQrwALGgIHl/wx+NYkUPN5WY0ComqJPIRCUYUfDhw4OMhv5F25Lh3S' +
|
||||
'fvg8a4u5H8HElJ61mhtAmXzBIghebV9lHwqZsYPCBrgBK4JA6okdpTttcPd1PVXfCqLxLlP/81Oklo4gapfVHak31WOkJyaq' +
|
||||
'zjLQ6q678eIFtUXdxgNmX4tDewl8mmrlWs6DDwAzxJNkvAjufFe1ng0yTFYRDfhgwfKIDwAmGoJIf6vbrf+pl9Qda3Uc71BN' +
|
||||
'NahilUYGqBDS1Megeyx5Wh6ovapAHX34rt7BtU9vDHqInuYaA3KEeTIQpJWs4FugMX741ZGoE2h1t3BN1mfWY/hAcTUT1BQY' +
|
||||
'cr9tWkwodCuJmNUsBLJzz3Y+nwXeNeZe0/dhWOhg306+62B8SvzouLGQ3/H6+BtO2OYoDCm6A+JkqSxu6IjVk+X+SH+jHoG/' +
|
||||
'0XGBilUr+QAS+4R3DD0H6XOfSooPgIRqZ0z4KAga5BtrzwSq8eCBeaH9xvROgYRS522nBuZS7wxvCmw+eHLz6Tkgu2Q6k4Eg' +
|
||||
'7NQjblW3jCijvGGlmNNVscqBZcf3jzjgguiKxtGNB0K/wDYXo+8BvR693wQHDmBP1wsDL46CZYv3j/xdgdnduOguAO1VaSPb' +
|
||||
'ARdQ+TBOU4lGnBEE/15lheee5xCUdLp9uaw/TEvZuC/HF3ZnFuYWHWpIePelwsKicpjaa+OOHANK/G4fKosD+d5T4Tnc8P0H' +
|
||||
'q1W1M7oYQf/RLDVdAWNVe2WHSwtLKc2AiYlrt60vh08WPB82pCnQihCawJyM3DXbfoKSrDtpd5PBNs7S3rIQ1AhpK75AM6Su' +
|
||||
'COv35BO/TI6YtkEpBEEwqUTHjYYVG1aIXeLc5WwCMfawZ0M3gKmbx9Q5cFzwKbJtp2ZcAVWt3W2MQaD5al2lAxTppXm3UuD0' +
|
||||
'pJv9bpYBnppawR87PrVzUqFZVYIRbgSqrYCGJkPZzDGOg22TVm65AFOX9T3ZZxEMn5YwO/4EeEvMXLM7qHR+5IV6SjIZzViw' +
|
||||
'tNNma2Ww6Zsj5UcT4M2pmedXTwcj2WzijQF60ZU4tBqP1RoJikybP7748nZcnON0i0FUIQhVWAHB0XFzxMZm/hATFRYfth5Y' +
|
||||
'rHayG3BhYNTz0hcbNmCKDKAfFF0tPVo6As4Mu558oxLwAooqHCgUDux0IPbKSmmUPuazcbsnlFGkolTk/LN4OMXJwF5QMzi8' +
|
||||
'q9VctQBTJZiX1DU0ArBj5/9xHx0d5IjWWiIxLSNljryDBjWDDSud6VxxnJZyXW7MdErgqZEZqR/a7mJSTHl6PntUJ9VyRgF3' +
|
||||
'yGZd83Yo3BiSzONCwwcftZZghpNcYuM5OSOXvnwFRTzO7+7/A1vMJNSBLSZHAAAAAElFTkSuQmCC'
|
||||
],
|
||||
// 24×24 — scaleFactor 1.5
|
||||
[
|
||||
1.5,
|
||||
'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'
|
||||
],
|
||||
// 32×32 — scaleFactor 2
|
||||
[
|
||||
2,
|
||||
'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'
|
||||
],
|
||||
// 48×48 — scaleFactor 3
|
||||
[
|
||||
3,
|
||||
'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' +
|
||||
'YII='
|
||||
]
|
||||
]
|
||||
@@ -62,7 +62,11 @@ type CardComponent = () => React.JSX.Element
|
||||
* state instead of mutating the card's own DOM `style` (M14).
|
||||
*/
|
||||
const SECTIONS: { id: string; title: string; cards: CardComponent[] }[] = [
|
||||
{ id: 'downloads', title: 'Downloads', cards: [DownloadsCard, PostProcessingCard, FilenamesCard] },
|
||||
{
|
||||
id: 'downloads',
|
||||
title: 'Downloads',
|
||||
cards: [DownloadsCard, PostProcessingCard, FilenamesCard]
|
||||
},
|
||||
{ id: 'appearance', title: 'Appearance', cards: [AppearanceCard] },
|
||||
{ id: 'network', title: 'Network & accounts', cards: [NetworkCard, CookiesCard] },
|
||||
{ id: 'advanced', title: 'Advanced', cards: [CustomCommandsCard] },
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { useRef } from 'react'
|
||||
import { Field, Button, Card, Subtitle2, Caption1, mergeClasses } from '@fluentui/react-components'
|
||||
import {
|
||||
Field,
|
||||
Button,
|
||||
Card,
|
||||
Subtitle2,
|
||||
Caption1,
|
||||
Switch,
|
||||
mergeClasses
|
||||
} from '@fluentui/react-components'
|
||||
import { PaintBucketRegular, AccessibilityRegular } from '@fluentui/react-icons'
|
||||
import { type ThemeMode, type AccentColor } from '@shared/ipc'
|
||||
import { useSettings } from '../../store/settings'
|
||||
@@ -20,6 +28,7 @@ export function AppearanceCard(): React.JSX.Element {
|
||||
const focus = useFocusStyles()
|
||||
const theme = useSettings((s) => s.theme)
|
||||
const accentColor = useSettings((s) => s.accentColor)
|
||||
const hardwareAcceleration = useSettings((s) => s.hardwareAcceleration)
|
||||
const highContrast = useSystemTheme((s) => s.shouldUseHighContrastColors)
|
||||
const update = useSettings((s) => s.update)
|
||||
// Store action wraps the IPC call (L93/CC13: no window.api in components).
|
||||
@@ -91,6 +100,16 @@ export function AppearanceCard(): React.JSX.Element {
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
label="Hardware acceleration"
|
||||
hint="Uses the GPU to draw the window (takes effect after you restart AeroFetch). Off by default: some older GPUs render the window blank with this on — turn it back off if that happens."
|
||||
>
|
||||
<Switch
|
||||
checked={hardwareAcceleration}
|
||||
onChange={(_, d) => update({ hardwareAcceleration: d.checked })}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Caption1 className={styles.hint}>
|
||||
{highContrast
|
||||
? 'A Windows high-contrast theme is active -- AeroFetch follows your system colors.'
|
||||
|
||||
@@ -624,6 +624,14 @@ export interface Settings {
|
||||
launchAtStartup: boolean
|
||||
/** whether the navigation sidebar is collapsed to icon-only mode */
|
||||
sidebarCollapsed: boolean
|
||||
/**
|
||||
* Opt in to GPU compositing (W15; takes effect on next launch). Off by
|
||||
* default: software rendering is the safe choice for this app's target
|
||||
* hardware — on some old/locked-down GPUs (the documented GeForce GT 625
|
||||
* case) Chromium's GPU compositor paints the whole window blank. Users on
|
||||
* modern GPUs can turn this on for smoother high-DPI/large-window rendering.
|
||||
*/
|
||||
hardwareAcceleration: boolean
|
||||
/**
|
||||
* Optional Gitea access token for the in-app updater. Empty = anonymous (works
|
||||
* only where the release repo allows anonymous access). When the release repo
|
||||
@@ -689,6 +697,7 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
minimizeToTray: false,
|
||||
launchAtStartup: false,
|
||||
sidebarCollapsed: false,
|
||||
hardwareAcceleration: false,
|
||||
updateToken: ''
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user