diff --git a/eslint.config.mjs b/eslint.config.mjs index f6eea5dc..f2d7231e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,7 +12,7 @@ export default tsEslint.config( tsEslint.configs.eslintRecommended, ...tsEslint.configs.recommendedTypeChecked, prettier, - { ignores: ['dist', 'node_modules', '*.config.*js'] }, + { ignores: ['dist', 'node_modules', '*.config.*js', '*.test.*js'] }, { plugins: { stylistic, @@ -54,7 +54,7 @@ export default tsEslint.config( afterLineComment: false, }], 'stylistic/max-len': 'off', - 'stylistic/no-mixed-operators': 'error', + 'stylistic/no-mixed-operators': 'warn', // prettier does not support no-mixed-operators 'stylistic/no-multi-spaces': ['error', { ignoreEOLComments: true }], 'stylistic/no-tabs': 'error', 'no-void': 'error', diff --git a/src/custom-electron-prompt.d.ts b/src/custom-electron-prompt.d.ts index bdbdc87f..a48ba5e5 100644 --- a/src/custom-electron-prompt.d.ts +++ b/src/custom-electron-prompt.d.ts @@ -64,29 +64,29 @@ declare module 'custom-electron-prompt' { export type PromptOptions = T extends 'input' ? InputPromptOptions : T extends 'select' - ? SelectPromptOptions - : T extends 'counter' - ? CounterPromptOptions - : T extends 'keybind' - ? KeybindPromptOptions - : T extends 'multiInput' - ? MultiInputPromptOptions - : never; + ? SelectPromptOptions + : T extends 'counter' + ? CounterPromptOptions + : T extends 'keybind' + ? KeybindPromptOptions + : T extends 'multiInput' + ? MultiInputPromptOptions + : never; type PromptResult = T extends 'input' ? string : T extends 'select' - ? string - : T extends 'counter' - ? number - : T extends 'keybind' - ? { - value: string; - accelerator: string; - }[] - : T extends 'multiInput' - ? string[] - : never; + ? string + : T extends 'counter' + ? number + : T extends 'keybind' + ? { + value: string; + accelerator: string; + }[] + : T extends 'multiInput' + ? string[] + : never; const prompt: ( options?: PromptOptions & { type: T }, diff --git a/src/index.ts b/src/index.ts index 18bbda6f..a2020fbf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -334,7 +334,9 @@ async function createMainWindow() { const display = screen.getDisplayNearestPoint(windowPosition); const primaryDisplay = screen.getPrimaryDisplay(); - const scaleFactor = is.windows() ? primaryDisplay.scaleFactor / display.scaleFactor : 1; + const scaleFactor = is.windows() + ? primaryDisplay.scaleFactor / display.scaleFactor + : 1; const scaledWidth = Math.floor(windowSize.width * scaleFactor); const scaledHeight = Math.floor(windowSize.height * scaleFactor); @@ -342,10 +344,10 @@ async function createMainWindow() { const scaledY = windowY; if ( - scaledX + (scaledWidth / 2) < display.bounds.x - 8 || // Left - scaledX + (scaledWidth / 2) > display.bounds.x + display.bounds.width || // Right + scaledX + scaledWidth / 2 < display.bounds.x - 8 || // Left + scaledX + scaledWidth / 2 > display.bounds.x + display.bounds.width || // Right scaledY < display.bounds.y - 8 || // Top - scaledY + (scaledHeight / 2) > display.bounds.y + display.bounds.height // Bottom + scaledY + scaledHeight / 2 > display.bounds.y + display.bounds.height // Bottom ) { // Window is offscreen if (is.dev()) { @@ -442,7 +444,7 @@ async function createMainWindow() { ...defaultTitleBarOverlayOptions, height: Math.floor( defaultTitleBarOverlayOptions.height! * - win.webContents.getZoomFactor(), + win.webContents.getZoomFactor(), ), }); } @@ -455,7 +457,7 @@ async function createMainWindow() { event.preventDefault(); win.webContents.loadURL( - 'https://accounts.google.com/ServiceLogin?ltmpl=music&service=youtube&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26next%3Dhttps%253A%252F%252Fmusic.youtube.com%252F' + 'https://accounts.google.com/ServiceLogin?ltmpl=music&service=youtube&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26next%3Dhttps%253A%252F%252Fmusic.youtube.com%252F', ); } }); @@ -479,8 +481,8 @@ app.once('browser-window-created', (_event, win) => { const updatedUserAgent = is.macOS() ? userAgents.mac : is.windows() - ? userAgents.windows - : userAgents.linux; + ? userAgents.windows + : userAgents.linux; win.webContents.userAgent = updatedUserAgent; app.userAgentFallback = updatedUserAgent; @@ -642,7 +644,9 @@ app.whenReady().then(async () => { // In dev mode, get string from process.env.VITE_DEV_SERVER_URL, else use fs.readFileSync if (is.dev() && process.env.ELECTRON_RENDERER_URL) { // HACK: to make vite work with electron renderer (supports hot reload) - event.returnValue = [null, ` + event.returnValue = [ + null, + ` console.log('${LoggerPrefix}', 'Loading vite from dev server'); (async () => { await new Promise((resolve) => { @@ -663,7 +667,8 @@ app.whenReady().then(async () => { document.body.appendChild(rendererScript); })(); 0 - `]; + `, + ]; } else { const rendererPath = path.join(__dirname, '..', 'renderer'); const indexHTML = parse( @@ -675,7 +680,10 @@ app.whenReady().then(async () => { scriptSrc.getAttribute('src')!, ); const scriptString = fs.readFileSync(scriptPath, 'utf-8'); - event.returnValue = [url.pathToFileURL(scriptPath).toString(), scriptString + ';0']; + event.returnValue = [ + url.pathToFileURL(scriptPath).toString(), + scriptString + ';0', + ]; } }); diff --git a/src/loader/main.ts b/src/loader/main.ts index 58807fe3..c763bc31 100644 --- a/src/loader/main.ts +++ b/src/loader/main.ts @@ -34,11 +34,12 @@ const createContext = ( win.webContents.send(event, ...args); }, handle: (event: string, listener: CallableFunction) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return + // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call ipcMain.handle(event, (_, ...args: unknown[]) => listener(...args)); }, on: (event: string, listener: CallableFunction) => { ipcMain.on(event, (_, ...args: unknown[]) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call listener(...args); }); }, @@ -75,11 +76,11 @@ export const forceUnloadMainPlugin = async ( ); return; } else { - console.log( - LoggerPrefix, - t('common.console.plugins.unload-failed', { pluginName: id }), - ); - return Promise.reject(); + const message = t('common.console.plugins.unload-failed', { + pluginName: id, + }); + console.log(LoggerPrefix, message); + return Promise.reject(new Error(message)); } } catch (err) { console.error( @@ -87,7 +88,7 @@ export const forceUnloadMainPlugin = async ( t('common.console.plugins.unload-failed', { pluginName: id }), ); console.trace(err); - return Promise.reject(err); + return Promise.reject(err as Error); } }; @@ -111,11 +112,11 @@ export const forceLoadMainPlugin = async ( ) { loadedPluginMap[id] = plugin; } else { - console.log( - LoggerPrefix, - t('common.console.plugins.load-failed', { pluginName: id }), - ); - return Promise.reject(); + const message = t('common.console.plugins.load-failed', { + pluginName: id, + }); + console.log(LoggerPrefix, message); + return Promise.reject(new Error(message)); } } catch (err) { console.error( @@ -123,7 +124,7 @@ export const forceLoadMainPlugin = async ( t('common.console.plugins.initialize-failed', { pluginName: id }), ); console.trace(err); - return Promise.reject(err); + return Promise.reject(err as Error); } }; diff --git a/src/loader/renderer.ts b/src/loader/renderer.ts index bf9d5cd5..a8ce8ed4 100644 --- a/src/loader/renderer.ts +++ b/src/loader/renderer.ts @@ -18,7 +18,8 @@ const loadedPluginMap: Record< export const createContext = ( id: string, ): RendererContext => ({ - getConfig: async () => window.ipcRenderer.invoke('ytmd:get-config', id), + getConfig: async () => + window.ipcRenderer.invoke('ytmd:get-config', id) as Promise, setConfig: async (newConfig) => { await window.ipcRenderer.invoke('ytmd:set-config', id, newConfig); }, @@ -30,6 +31,7 @@ export const createContext = ( window.ipcRenderer.invoke(event, ...args), on: (event: string, listener: CallableFunction) => { window.ipcRenderer.on(event, (_, ...args: unknown[]) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call listener(...args); }); }, diff --git a/src/menu.ts b/src/menu.ts index 22bde9af..61fd5466 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -1,5 +1,13 @@ import is from 'electron-is'; -import { app, BrowserWindow, clipboard, dialog, Menu, MenuItem, shell, } from 'electron'; +import { + app, + BrowserWindow, + clipboard, + dialog, + Menu, + MenuItem, + shell, +} from 'electron'; import prompt from 'custom-electron-prompt'; import { satisfies } from 'semver'; @@ -68,12 +76,21 @@ export const mainMenuTemplate = async ( const plugin = allPlugins[id]; const pluginLabel = plugin?.name?.() ?? id; const pluginDescription = plugin?.description?.() ?? undefined; - const isNew = plugin?.addedVersion ? satisfies(packageJson.version, plugin.addedVersion) : false; + const isNew = plugin?.addedVersion + ? satisfies(packageJson.version, plugin.addedVersion) + : false; if (!config.plugins.isEnabled(id)) { return [ id, - pluginEnabledMenu(id, pluginLabel, pluginDescription, isNew, true, innerRefreshMenu), + pluginEnabledMenu( + id, + pluginLabel, + pluginDescription, + isNew, + true, + innerRefreshMenu, + ), ] as const; } @@ -115,9 +132,18 @@ export const mainMenuTemplate = async ( const plugin = allPlugins[id]; const pluginLabel = plugin?.name?.() ?? id; const pluginDescription = plugin?.description?.() ?? undefined; - const isNew = plugin?.addedVersion ? satisfies(packageJson.version, plugin.addedVersion) : false; + const isNew = plugin?.addedVersion + ? satisfies(packageJson.version, plugin.addedVersion) + : false; - return pluginEnabledMenu(id, pluginLabel, pluginDescription, isNew, true, innerRefreshMenu); + return pluginEnabledMenu( + id, + pluginLabel, + pluginDescription, + isNew, + true, + innerRefreshMenu, + ); }); const availableLanguages = Object.keys(languageResources); @@ -229,12 +255,12 @@ export const mainMenuTemplate = async ( submenu: [ ...((config.get('options.themes')?.length ?? 0) === 0 ? [ - { - label: t( - 'main.menu.options.submenu.visual-tweaks.submenu.theme.submenu.no-theme', - ), - } - ] + { + label: t( + 'main.menu.options.submenu.visual-tweaks.submenu.theme.submenu.no-theme', + ), + }, + ] : []), ...(config.get('options.themes')?.map((theme: string) => ({ type: 'normal' as const, @@ -251,16 +277,25 @@ export const mainMenuTemplate = async ( { theme }, ), buttons: [ - t('main.menu.options.submenu.visual-tweaks.submenu.theme.dialog.button.cancel'), - t('main.menu.options.submenu.visual-tweaks.submenu.theme.dialog.button.remove'), + t( + 'main.menu.options.submenu.visual-tweaks.submenu.theme.dialog.button.cancel', + ), + t( + 'main.menu.options.submenu.visual-tweaks.submenu.theme.dialog.button.remove', + ), ], }); if (response === 1) { - config.set('options.themes', config.get('options.themes')?.filter((t) => t !== theme) ?? []); + config.set( + 'options.themes', + config + .get('options.themes') + ?.filter((t) => t !== theme) ?? [], + ); innerRefreshMenu(); } - } + }, })) ?? []), { type: 'separator' }, { @@ -306,40 +341,40 @@ export const mainMenuTemplate = async ( }, ...((is.windows() || is.linux() ? [ - { - label: t('main.menu.options.submenu.hide-menu.label'), - type: 'checkbox', - checked: config.get('options.hideMenu'), - click(item) { - config.setMenuOption('options.hideMenu', item.checked); - if (item.checked && !config.get('options.hideMenuWarned')) { - dialog.showMessageBox(win, { - type: 'info', - title: t( - 'main.menu.options.submenu.hide-menu.dialog.title', - ), - message: t( - 'main.menu.options.submenu.hide-menu.dialog.message', - ), - }); - } + { + label: t('main.menu.options.submenu.hide-menu.label'), + type: 'checkbox', + checked: config.get('options.hideMenu'), + click(item) { + config.setMenuOption('options.hideMenu', item.checked); + if (item.checked && !config.get('options.hideMenuWarned')) { + dialog.showMessageBox(win, { + type: 'info', + title: t( + 'main.menu.options.submenu.hide-menu.dialog.title', + ), + message: t( + 'main.menu.options.submenu.hide-menu.dialog.message', + ), + }); + } + }, }, - }, - ] + ] : []) satisfies Electron.MenuItemConstructorOptions[]), ...((is.windows() || is.macOS() ? // Only works on Win/Mac // https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windows - [ - { - label: t('main.menu.options.submenu.start-at-login'), - type: 'checkbox', - checked: config.get('options.startAtLogin'), - click(item) { - config.setMenuOption('options.startAtLogin', item.checked); + [ + { + label: t('main.menu.options.submenu.start-at-login'), + type: 'checkbox', + checked: config.get('options.startAtLogin'), + click(item) { + config.setMenuOption('options.startAtLogin', item.checked); + }, }, - }, - ] + ] : []) satisfies Electron.MenuItemConstructorOptions[]), { label: t('main.menu.options.submenu.tray.label'), @@ -493,25 +528,25 @@ export const mainMenuTemplate = async ( { type: 'separator' }, is.macOS() ? { - label: t( - 'main.menu.options.submenu.advanced-options.submenu.toggle-dev-tools', - ), - // Cannot use "toggleDevTools" role in macOS - click() { - const { webContents } = win; - if (webContents.isDevToolsOpened()) { - webContents.closeDevTools(); - } else { - webContents.openDevTools(); - } - }, - } + label: t( + 'main.menu.options.submenu.advanced-options.submenu.toggle-dev-tools', + ), + // Cannot use "toggleDevTools" role in macOS + click() { + const { webContents } = win; + if (webContents.isDevToolsOpened()) { + webContents.closeDevTools(); + } else { + webContents.openDevTools(); + } + }, + } : { - label: t( - 'main.menu.options.submenu.advanced-options.submenu.toggle-dev-tools', - ), - role: 'toggleDevTools', - }, + label: t( + 'main.menu.options.submenu.advanced-options.submenu.toggle-dev-tools', + ), + role: 'toggleDevTools', + }, { label: t( 'main.menu.options.submenu.advanced-options.submenu.edit-config-json', diff --git a/src/plugins/adblocker/adSpeedup.ts b/src/plugins/adblocker/adSpeedup.ts index dbe2ea35..acbfd5ef 100644 --- a/src/plugins/adblocker/adSpeedup.ts +++ b/src/plugins/adblocker/adSpeedup.ts @@ -1,5 +1,7 @@ function skipAd(target: Element) { - const skipButton = target.querySelector('button.ytp-ad-skip-button-modern'); + const skipButton = target.querySelector( + 'button.ytp-ad-skip-button-modern', + ); if (skipButton) { skipButton.click(); } @@ -17,7 +19,7 @@ function speedUpAndMute(player: Element, isAdShowing: boolean) { } } -export const loadAdSpeedup = async () => { +export const loadAdSpeedup = () => { const player = document.querySelector('#movie_player'); if (!player) return; @@ -53,4 +55,4 @@ export const loadAdSpeedup = async () => { player.classList.contains('ad-interrupting'); speedUpAndMute(player, isAdShowing); skipAd(player); -} +}; diff --git a/src/plugins/adblocker/index.ts b/src/plugins/adblocker/index.ts index 8469a62c..c52faf79 100644 --- a/src/plugins/adblocker/index.ts +++ b/src/plugins/adblocker/index.ts @@ -79,7 +79,7 @@ export default createPlugin({ if (config.blocker === blockers.AdSpeedup) { await loadAdSpeedup(); } - } + }, }, backend: { mainWindow: null as BrowserWindow | null, diff --git a/src/plugins/album-actions/index.ts b/src/plugins/album-actions/index.ts index bc27c9e3..1542332a 100644 --- a/src/plugins/album-actions/index.ts +++ b/src/plugins/album-actions/index.ts @@ -104,21 +104,28 @@ export default createPlugin< buttons.splice(i, 1); i--; } else { - (buttons[i].children[0].children[0] as HTMLElement).style.setProperty( + ( + buttons[i].children[0].children[0] as HTMLElement + ).style.setProperty( '-webkit-mask-size', - `100% ${100 - ((count / listsLength) * 100)}%`, + `100% ${100 - (count / listsLength) * 100}%`, ); } i++; } } - const menuParent = document.querySelector('#action-buttons')?.parentElement; + const menuParent = + document.querySelector('#action-buttons')?.parentElement; if (menuParent && !document.querySelector('.like-menu')) { const menu = document.createElement('div'); menu.id = 'ytmd-album-action-buttons'; - menu.className = 'action-buttons style-scope ytmusic-responsive-header-renderer'; + menu.className = + 'action-buttons style-scope ytmusic-responsive-header-renderer'; - menuParent.insertBefore(menu, menuParent.children[menuParent.children.length - 1]); + menuParent.insertBefore( + menu, + menuParent.children[menuParent.children.length - 1], + ); for (const button of buttons) { menu.appendChild(button); button.addEventListener('click', this.loadFullList); diff --git a/src/plugins/album-color-theme/index.ts b/src/plugins/album-color-theme/index.ts index ce02f5f7..b48117fb 100644 --- a/src/plugins/album-color-theme/index.ts +++ b/src/plugins/album-color-theme/index.ts @@ -25,7 +25,12 @@ export default createPlugin< sidebarSmall: HTMLElement | null; ytmusicAppLayout: HTMLElement | null; - getMixedColor(color: string, key: string, alpha?: number, ratioMultiply?: number): string; + getMixedColor( + color: string, + key: string, + alpha?: number, + ratioMultiply?: number, + ): string; updateColor(): void; }, { @@ -91,7 +96,10 @@ export default createPlugin< this.ytmusicAppLayout = document.querySelector('#layout'); const config = await getConfig(); - document.documentElement.style.setProperty(RATIO_KEY, `${~~(config.ratio * 100)}%`); + document.documentElement.style.setProperty( + RATIO_KEY, + `${~~(config.ratio * 100)}%`, + ); }, onPlayerApiReady(playerApi) { const fastAverageColor = new FastAverageColor(); @@ -100,10 +108,12 @@ export default createPlugin< if (event.detail.name !== 'dataloaded') return; const playerResponse = playerApi.getPlayerResponse(); - const thumbnail = playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); + const thumbnail = + playerResponse?.videoDetails?.thumbnail?.thumbnails?.at(0); if (!thumbnail) return; - const albumColor = await fastAverageColor.getColorAsync(thumbnail.url) + const albumColor = await fastAverageColor + .getColorAsync(thumbnail.url) .catch((err) => { console.error(err); return null; @@ -120,8 +130,14 @@ export default createPlugin< this.darkColor = this.darkColor?.darken(0.05); } - document.documentElement.style.setProperty(COLOR_KEY, `${~~this.color.red()}, ${~~this.color.green()}, ${~~this.color.blue()}`); - document.documentElement.style.setProperty(DARK_COLOR_KEY, `${~~this.darkColor.red()}, ${~~this.darkColor.green()}, ${~~this.darkColor.blue()}`); + document.documentElement.style.setProperty( + COLOR_KEY, + `${~~this.color.red()}, ${~~this.color.green()}, ${~~this.color.blue()}`, + ); + document.documentElement.style.setProperty( + DARK_COLOR_KEY, + `${~~this.darkColor.red()}, ${~~this.darkColor.green()}, ${~~this.darkColor.blue()}`, + ); } else { document.documentElement.style.setProperty(COLOR_KEY, '0, 0, 0'); document.documentElement.style.setProperty(DARK_COLOR_KEY, '0, 0, 0'); @@ -131,7 +147,10 @@ export default createPlugin< }); }, onConfigChange(config) { - document.documentElement.style.setProperty(RATIO_KEY, `${~~(config.ratio * 100)}%`); + document.documentElement.style.setProperty( + RATIO_KEY, + `${~~(config.ratio * 100)}%`, + ); }, getMixedColor(color: string, key: string, alpha = 1, ratioMultiply) { const keyColor = `rgba(var(${key}), ${alpha})`; @@ -181,11 +200,23 @@ export default createPlugin< '--yt-spec-black-1-alpha-95': 'rgba(40,40,40,0.95)', }; Object.entries(variableMap).map(([variable, color]) => { - document.documentElement.style.setProperty(variable, this.getMixedColor(color, COLOR_KEY), 'important'); + document.documentElement.style.setProperty( + variable, + this.getMixedColor(color, COLOR_KEY), + 'important', + ); }); - document.body.style.setProperty('background', this.getMixedColor('#030303', COLOR_KEY), 'important'); - document.documentElement.style.setProperty('--ytmusic-background', this.getMixedColor('#030303', DARK_COLOR_KEY), 'important'); + document.body.style.setProperty( + 'background', + this.getMixedColor('#030303', COLOR_KEY), + 'important', + ); + document.documentElement.style.setProperty( + '--ytmusic-background', + this.getMixedColor('#030303', DARK_COLOR_KEY), + 'important', + ); }, }, }); diff --git a/src/plugins/ambient-mode/index.ts b/src/plugins/ambient-mode/index.ts index 5dfd2023..a36feb5f 100644 --- a/src/plugins/ambient-mode/index.ts +++ b/src/plugins/ambient-mode/index.ts @@ -53,10 +53,16 @@ export default createPlugin({ const songImage = document.querySelector('#song-image'); const songVideo = document.querySelector('#song-video'); - const image = songImage?.querySelector('yt-img-shadow > img'); - const video = await waitForElement('.html5-video-container > video'); + const image = songImage?.querySelector( + 'yt-img-shadow > img', + ); + const video = await waitForElement( + '.html5-video-container > video', + ); - const videoWrapper = document.querySelector('#song-video > .player-wrapper'); + const videoWrapper = document.querySelector( + '#song-video > .player-wrapper', + ); const injectBlurImage = () => { if (!songImage || !image) return null; @@ -95,7 +101,9 @@ export default createPlugin({ const blurCanvas = document.createElement('canvas'); blurCanvas.classList.add('html5-blur-canvas'); - const context = blurCanvas.getContext('2d', { willReadFrequently: true }); + const context = blurCanvas.getContext('2d', { + willReadFrequently: true, + }); /* effect */ let lastEffectWorkId: number | null = null; @@ -109,14 +117,18 @@ export default createPlugin({ if (!context) return; const width = this.qualityRatio; - let height = Math.max(Math.floor((blurCanvas.height / blurCanvas.width) * width), 1,); + let height = Math.max( + Math.floor((blurCanvas.height / blurCanvas.width) * width), + 1, + ); if (!Number.isFinite(height)) height = width; if (!height) return; context.globalAlpha = 1; if (lastImageData) { - const frameOffset = (1 / this.buffer) * (1000 / this.interpolationTime); - context.globalAlpha = 1 - (frameOffset * 2); // because of alpha value must be < 1 + const frameOffset = + (1 / this.buffer) * (1000 / this.interpolationTime); + context.globalAlpha = 1 - frameOffset * 2; // because of alpha value must be < 1 context.putImageData(lastImageData, 0, 0); context.globalAlpha = frameOffset; } @@ -137,7 +149,9 @@ export default createPlugin({ if (newWidth === 0 || newHeight === 0) return; blurCanvas.width = this.qualityRatio; - blurCanvas.height = Math.floor((newHeight / newWidth) * this.qualityRatio); + blurCanvas.height = Math.floor( + (newHeight / newWidth) * this.qualityRatio, + ); if (this.isFullscreen) blurCanvas.classList.add('fullscreen'); else blurCanvas.classList.remove('fullscreen'); @@ -151,7 +165,10 @@ export default createPlugin({ /* hooking */ let canvasInterval: NodeJS.Timeout | null = null; - canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / this.buffer))); + canvasInterval = setInterval( + onSync, + Math.max(1, Math.ceil(1000 / this.buffer)), + ); const onPause = () => { if (canvasInterval) clearInterval(canvasInterval); @@ -159,7 +176,10 @@ export default createPlugin({ }; const onPlay = () => { if (canvasInterval) clearInterval(canvasInterval); - canvasInterval = setInterval(onSync, Math.max(1, Math.ceil(1000 / this.buffer))); + canvasInterval = setInterval( + onSync, + Math.max(1, Math.ceil(1000 / this.buffer)), + ); }; songVideo.addEventListener('pause', onPause); songVideo.addEventListener('play', onPlay); @@ -198,11 +218,20 @@ export default createPlugin({ if (isPageOpen) { const isVideo = isVideoMode(); if (!force) { - if (this.lastMediaType === 'video' && this.lastVideoSource === video?.src) return false; - if (this.lastMediaType === 'image' && this.lastImageSource === image?.src) return false; + if ( + this.lastMediaType === 'video' && + this.lastVideoSource === video?.src + ) + return false; + if ( + this.lastMediaType === 'image' && + this.lastImageSource === image?.src + ) + return false; } this.unregister?.(); - this.unregister = (isVideo ? injectBlurVideo() : injectBlurImage()) ?? null; + this.unregister = + (isVideo ? injectBlurVideo() : injectBlurImage()) ?? null; } else { this.unregister?.(); this.unregister = null; diff --git a/src/plugins/ambient-mode/menu.ts b/src/plugins/ambient-mode/menu.ts index 14fcf4fa..3c2ae97e 100644 --- a/src/plugins/ambient-mode/menu.ts +++ b/src/plugins/ambient-mode/menu.ts @@ -1,14 +1,24 @@ -import { t } from "@/i18n"; -import { MenuContext } from "@/types/contexts"; -import { MenuItemConstructorOptions } from "electron"; -import { AmbientModePluginConfig } from "./types"; +import { MenuItemConstructorOptions } from 'electron'; + +import { t } from '@/i18n'; +import { MenuContext } from '@/types/contexts'; +import { AmbientModePluginConfig } from './types'; export interface menuParameters { getConfig: () => AmbientModePluginConfig | Promise; - setConfig: (conf: Partial>) => void | Promise; + setConfig: ( + conf: Partial>, + ) => void | Promise; } -export const menu: (ctx: MenuContext) => MenuItemConstructorOptions[] | Promise = async ({ getConfig, setConfig }: menuParameters) => { +export const menu: ( + ctx: MenuContext, +) => + | MenuItemConstructorOptions[] + | Promise = async ({ + getConfig, + setConfig, +}: menuParameters) => { const interpolationTimeList = [0, 500, 1000, 1500, 2000, 3000, 4000, 5000]; const qualityList = [10, 25, 50, 100, 200, 500, 1000]; const sizeList = [100, 110, 125, 150, 175, 200, 300]; @@ -107,4 +117,4 @@ export const menu: (ctx: MenuContext) => MenuItemConstr }, }, ]; -} \ No newline at end of file +}; diff --git a/src/plugins/ambient-mode/types.ts b/src/plugins/ambient-mode/types.ts index 6bb3b37c..befca0d3 100644 --- a/src/plugins/ambient-mode/types.ts +++ b/src/plugins/ambient-mode/types.ts @@ -7,4 +7,4 @@ export type AmbientModePluginConfig = { size: number; opacity: number; fullscreen: boolean; -}; \ No newline at end of file +}; diff --git a/src/plugins/api-server/backend/main.ts b/src/plugins/api-server/backend/main.ts index 83c63767..409f8d7a 100644 --- a/src/plugins/api-server/backend/main.ts +++ b/src/plugins/api-server/backend/main.ts @@ -28,7 +28,10 @@ export const backend = createBackend({ this.end(); }, onConfigChange(config) { - if (this.oldConfig?.hostname === config.hostname && this.oldConfig?.port === config.port) { + if ( + this.oldConfig?.hostname === config.hostname && + this.oldConfig?.port === config.port + ) { this.oldConfig = config; return; } @@ -55,7 +58,8 @@ export const backend = createBackend({ this.app.use('/api/*', async (ctx, next) => { const result = await JWTPayloadSchema.spa(await ctx.get('jwtPayload')); - const isAuthorized = result.success && config.authorizedClients.includes(result.data.id); + const isAuthorized = + result.success && config.authorizedClients.includes(result.data.id); if (!isAuthorized) { ctx.status(401); return ctx.body('Unauthorized'); diff --git a/src/plugins/api-server/backend/routes/auth.ts b/src/plugins/api-server/backend/routes/auth.ts index b3a3e64b..54e034ff 100644 --- a/src/plugins/api-server/backend/routes/auth.ts +++ b/src/plugins/api-server/backend/routes/auth.ts @@ -2,9 +2,10 @@ import { createRoute, z } from '@hono/zod-openapi'; import { dialog } from 'electron'; import { sign } from 'hono/jwt'; -import { t } from '@/i18n'; import { getConnInfo } from '@hono/node-server/conninfo'; +import { t } from '@/i18n'; + import { APIServerConfig } from '../../config'; import { JWTPayload } from '../scheme'; @@ -20,7 +21,7 @@ const routes = { request: { params: z.object({ id: z.string(), - }) + }), }, responses: { 200: { @@ -40,7 +41,10 @@ const routes = { }), }; -export const register = (app: HonoApp, { getConfig, setConfig }: BackendContext) => { +export const register = ( + app: HonoApp, + { getConfig, setConfig }: BackendContext, +) => { app.openapi(routes.request, async (ctx) => { const config = await getConfig(); const { id } = ctx.req.param(); @@ -54,7 +58,10 @@ export const register = (app: HonoApp, { getConfig, setConfig }: BackendContext< origin: getConnInfo(ctx).remote.address, id, }), - buttons: [t('plugins.api-server.dialog.request.buttons.allow'), t('plugins.api-server.dialog.request.deny')], + buttons: [ + t('plugins.api-server.dialog.request.buttons.allow'), + t('plugins.api-server.dialog.request.deny'), + ], defaultId: 1, cancelId: 1, }); @@ -68,10 +75,7 @@ export const register = (app: HonoApp, { getConfig, setConfig }: BackendContext< } setConfig({ - authorizedClients: [ - ...config.authorizedClients, - id, - ], + authorizedClients: [...config.authorizedClients, id], }); const token = await sign( diff --git a/src/plugins/api-server/backend/routes/control.ts b/src/plugins/api-server/backend/routes/control.ts index f8231e29..45e6b24e 100644 --- a/src/plugins/api-server/backend/routes/control.ts +++ b/src/plugins/api-server/backend/routes/control.ts @@ -84,7 +84,8 @@ const routes = { method: 'post', path: `/api/${API_VERSION}/toggle-play`, summary: 'Toggle play/pause', - description: 'Change the state of the player to play if paused, or pause if playing', + description: + 'Change the state of the player to play if paused, or pause if playing', request: { headers: AuthHeadersSchema, }, @@ -280,7 +281,7 @@ const routes = { schema: z.object({ state: z.boolean(), }), - } + }, }, }, }, @@ -299,7 +300,7 @@ const routes = { content: { 'application/json': { schema: z.object({}), - } + }, }, }, 204: { @@ -321,7 +322,7 @@ const routes = { content: { 'application/json': { schema: SongInfoSchema, - } + }, }, }, 204: { @@ -331,7 +332,11 @@ const routes = { }), }; -export const register = (app: HonoApp, { window }: BackendContext, songInfoGetter: () => SongInfo | undefined) => { +export const register = ( + app: HonoApp, + { window }: BackendContext, + songInfoGetter: () => SongInfo | undefined, +) => { const controller = getSongControls(window); app.openapi(routes.previous, (ctx) => { @@ -426,9 +431,12 @@ export const register = (app: HonoApp, { window }: BackendContext { const stateResponsePromise = new Promise((resolve) => { - ipcMain.once('ytmd:set-fullscreen', (_, isFullscreen: boolean | undefined) => { - return resolve(!!isFullscreen); - }); + ipcMain.once( + 'ytmd:set-fullscreen', + (_, isFullscreen: boolean | undefined) => { + return resolve(!!isFullscreen); + }, + ); controller.requestFullscreenInformation(); }); diff --git a/src/plugins/api-server/backend/scheme/index.ts b/src/plugins/api-server/backend/scheme/index.ts index 305f55af..1cbb91e8 100644 --- a/src/plugins/api-server/backend/scheme/index.ts +++ b/src/plugins/api-server/backend/scheme/index.ts @@ -5,4 +5,3 @@ export * from './go-forward'; export * from './switch-repeat'; export * from './set-volume'; export * from './set-fullscreen'; - diff --git a/src/plugins/api-server/menu.ts b/src/plugins/api-server/menu.ts index 4273877f..24a68d7a 100644 --- a/src/plugins/api-server/menu.ts +++ b/src/plugins/api-server/menu.ts @@ -22,17 +22,20 @@ export const onMenu = async ({ async click() { const config = await getConfig(); - const newHostname = await prompt( - { - title: t('plugins.api-server.prompt.hostname.title'), - label: t('plugins.api-server.prompt.hostname.label'), - value: config.hostname, - type: 'input', - width: 380, - ...promptOptions(), - }, - window, - ) ?? (config.hostname ?? defaultAPIServerConfig.hostname); + const newHostname = + (await prompt( + { + title: t('plugins.api-server.prompt.hostname.title'), + label: t('plugins.api-server.prompt.hostname.label'), + value: config.hostname, + type: 'input', + width: 380, + ...promptOptions(), + }, + window, + )) ?? + config.hostname ?? + defaultAPIServerConfig.hostname; setConfig({ ...config, hostname: newHostname }); }, @@ -43,18 +46,21 @@ export const onMenu = async ({ async click() { const config = await getConfig(); - const newPort = await prompt( - { - title: t('plugins.api-server.prompt.port.title'), - label: t('plugins.api-server.prompt.port.label'), - value: config.port, - type: 'counter', - counterOptions: { minimum: 0, maximum: 65565, }, - width: 380, - ...promptOptions(), - }, - window, - ) ?? (config.port ?? defaultAPIServerConfig.port); + const newPort = + (await prompt( + { + title: t('plugins.api-server.prompt.port.title'), + label: t('plugins.api-server.prompt.port.label'), + value: config.port, + type: 'counter', + counterOptions: { minimum: 0, maximum: 65565 }, + width: 380, + ...promptOptions(), + }, + window, + )) ?? + config.port ?? + defaultAPIServerConfig.port; setConfig({ ...config, port: newPort }); }, @@ -64,7 +70,9 @@ export const onMenu = async ({ type: 'submenu', submenu: [ { - label: t('plugins.api-server.menu.auth-strategy.submenu.auth-at-first.label'), + label: t( + 'plugins.api-server.menu.auth-strategy.submenu.auth-at-first.label', + ), type: 'radio', checked: config.authStrategy === 'AUTH_AT_FIRST', click() { diff --git a/src/plugins/blur-nav-bar/index.ts b/src/plugins/blur-nav-bar/index.ts index 7c21c219..9a8db6a4 100644 --- a/src/plugins/blur-nav-bar/index.ts +++ b/src/plugins/blur-nav-bar/index.ts @@ -15,7 +15,10 @@ export default createPlugin({ this.styleSheet = new CSSStyleSheet(); await this.styleSheet.replace(style); - document.adoptedStyleSheets = [...document.adoptedStyleSheets, this.styleSheet]; + document.adoptedStyleSheets = [ + ...document.adoptedStyleSheets, + this.styleSheet, + ]; }, async stop() { await this.styleSheet?.replace(''); diff --git a/src/plugins/captions-selector/index.ts b/src/plugins/captions-selector/index.ts index b7ef9c58..7ef9b22e 100644 --- a/src/plugins/captions-selector/index.ts +++ b/src/plugins/captions-selector/index.ts @@ -34,7 +34,7 @@ export default createPlugin< { label: t('plugins.captions-selector.menu.autoload'), type: 'checkbox', - checked: config.autoload as boolean, + checked: config.autoload, click(item) { setConfig({ autoload: item.checked }); }, @@ -42,7 +42,7 @@ export default createPlugin< { label: t('plugins.captions-selector.menu.disable-captions'), type: 'checkbox', - checked: config.disableCaptions as boolean, + checked: config.disableCaptions, click(item) { setConfig({ disableCaptions: item.checked }); }, diff --git a/src/plugins/crossfade/fader.ts b/src/plugins/crossfade/fader.ts index 5e8630d1..f4e30a4b 100644 --- a/src/plugins/crossfade/fader.ts +++ b/src/plugins/crossfade/fader.ts @@ -64,7 +64,7 @@ interface VolumeFade { // Main class export class VolumeFader { private readonly media: HTMLMediaElement; - private readonly logger: VolumeLogger | false; + private readonly logger: VolumeLogger | null; private scale: { internalToVolume: (level: number) => number; volumeToInternal: (level: number) => number; @@ -100,7 +100,7 @@ export class VolumeFader { this.logger = options.logger; } else { // Set log function explicitly to false - this.logger = false; + this.logger = null; } // Linear volume fading? @@ -112,7 +112,7 @@ export class VolumeFader { }; // Log setting - this.logger && this.logger('Using linear fading.'); + this.logger?.('Using linear fading.'); } // No linear, but logarithmic fading… else { @@ -152,9 +152,8 @@ export class VolumeFader { }; // Log setting if not default - options.fadeScaling && - this.logger && - this.logger( + if (options.fadeScaling) + this.logger?.( 'Using logarithmic fading with ' + String(10 * dynamicRange) + ' dB dynamic range.', @@ -170,8 +169,7 @@ export class VolumeFader { this.media.volume = options.initialVolume; // Log setting - this.logger && - this.logger('Set initial volume to ' + String(this.media.volume) + '.'); + this.logger?.('Set initial volume to ' + String(this.media.volume) + '.'); } // Fade duration given? @@ -187,7 +185,7 @@ export class VolumeFader { this.active = false; // Initialization done - this.logger && this.logger('Initialized for', this.media); + this.logger?.('Initialized for', this.media); } /** @@ -236,8 +234,7 @@ export class VolumeFader { this.fadeDuration = fadeDuration; // Log setting - this.logger && - this.logger('Set fade duration to ' + String(fadeDuration) + ' ms.'); + this.logger?.('Set fade duration to ' + String(fadeDuration) + ' ms.'); } else { // Abort and throw an exception throw new TypeError('Positive number expected as fade duration!'); @@ -279,7 +276,7 @@ export class VolumeFader { this.start(); // Log new fade - this.logger && this.logger('New fade started:', this.fade); + this.logger?.('New fade started:', this.fade); // Return instance for chaining return this; @@ -313,7 +310,7 @@ export class VolumeFader { // Compute current level on internal scale const level = - (progress * (this.fade.volume.end - this.fade.volume.start)) + + progress * (this.fade.volume.end - this.fade.volume.start) + this.fade.volume.start; // Map fade level to volume level and apply it to media element @@ -323,8 +320,7 @@ export class VolumeFader { window.requestAnimationFrame(this.updateVolume.bind(this)); } else { // Log end of fade - this.logger && - this.logger('Fade to ' + String(this.fade.volume.end) + ' complete.'); + this.logger?.('Fade to ' + String(this.fade.volume.end) + ' complete.'); // Time is up, jump to target volume this.media.volume = this.scale.internalToVolume(this.fade.volume.end); @@ -333,7 +329,7 @@ export class VolumeFader { this.active = false; // Done, call back (if callable) - typeof this.fade.callback === 'function' && this.fade.callback(); + if (typeof this.fade.callback === 'function') this.fade.callback(); // Clear fade this.fade = undefined; @@ -382,7 +378,7 @@ export class VolumeFader { input = Math.log10(input); // Scale minus something × 10 dB to 0…1 (clipping at 0) - return Math.max(1 + (input / dynamicRange), 0); + return Math.max(1 + input / dynamicRange, 0); } } diff --git a/src/plugins/crossfade/index.ts b/src/plugins/crossfade/index.ts index 79af8b0f..b663c9e9 100644 --- a/src/plugins/crossfade/index.ts +++ b/src/plugins/crossfade/index.ts @@ -191,7 +191,7 @@ export default createPlugin< let waitForTransition: Promise; const getStreamURL = async (videoID: string): Promise => - this.ipc?.invoke('audio-url', videoID); + this.ipc?.invoke('audio-url', videoID) as Promise; const getVideoIDFromURL = (url: string) => new URLSearchParams(url.split('?')?.at(-1)).get('v'); diff --git a/src/plugins/discord/main.ts b/src/plugins/discord/main.ts index 8bca907a..5b62353c 100644 --- a/src/plugins/discord/main.ts +++ b/src/plugins/discord/main.ts @@ -202,9 +202,9 @@ export const backend = createBackend< } } else if (!config.hideDurationLeft) { // Add the start and end time of the song - const songStartTime = Date.now() - ((songInfo.elapsedSeconds ?? 0) * 1000); + const songStartTime = Date.now() - (songInfo.elapsedSeconds ?? 0) * 1000; activityInfo.startTimestamp = songStartTime; - activityInfo.endTimestamp = songStartTime + (songInfo.songDuration * 1000); + activityInfo.endTimestamp = songStartTime + songInfo.songDuration * 1000; } info.rpc.user?.setActivity(activityInfo).catch(console.error); diff --git a/src/plugins/downloader/main/index.ts b/src/plugins/downloader/main/index.ts index d4b66729..1d30fe75 100644 --- a/src/plugins/downloader/main/index.ts +++ b/src/plugins/downloader/main/index.ts @@ -183,12 +183,18 @@ function downloadSongOnFinishSetup({ config.downloadOnFinish.mode === 'seconds' && duration - time <= config.downloadOnFinish.seconds ) { - downloadSong(currentUrl, config.downloadOnFinish.folder ?? config.downloadFolder); + downloadSong( + currentUrl, + config.downloadOnFinish.folder ?? config.downloadFolder, + ); } else if ( config.downloadOnFinish.mode === 'percent' && time >= duration * (config.downloadOnFinish.percent / 100) ) { - downloadSong(currentUrl, config.downloadOnFinish.folder ?? config.downloadFolder); + downloadSong( + currentUrl, + config.downloadOnFinish.folder ?? config.downloadFolder, + ); } } @@ -438,7 +444,7 @@ async function iterableStreamToProcessedUint8Array( }), ratio, ); - increasePlaylistProgress(0.15 + (ratio * 0.85)); + increasePlaylistProgress(0.15 + ratio * 0.85); }); const safeVideoNameWithExtension = `${safeVideoName}.${extension}`; @@ -566,7 +572,13 @@ export async function downloadPlaylist(givenUrl?: string | URL) { return; } - if (!playlist || !playlist.items || playlist.items.length === 0 || !playlist.header || !('title' in playlist.header)) { + if ( + !playlist || + !playlist.items || + playlist.items.length === 0 || + !playlist.header || + !('title' in playlist.header) + ) { sendError( new Error(t('plugins.downloader.backend.feedback.playlist-is-empty')), ); @@ -660,7 +672,7 @@ export async function downloadPlaylist(givenUrl?: string | URL) { const increaseProgress = (itemPercentage: number) => { const currentProgress = (counter - 1) / (items.length ?? 1); - const newProgress = currentProgress + (progressStep * itemPercentage); + const newProgress = currentProgress + progressStep * itemPercentage; win.setProgressBar(newProgress); }; diff --git a/src/plugins/downloader/menu.ts b/src/plugins/downloader/menu.ts index e075fb0e..d4320abb 100644 --- a/src/plugins/downloader/menu.ts +++ b/src/plugins/downloader/menu.ts @@ -35,7 +35,10 @@ export const onMenu = async ({ click(item) { setConfig({ downloadOnFinish: { - ...deepmerge(defaultConfig.downloadOnFinish, config.downloadOnFinish)!, + ...deepmerge( + defaultConfig.downloadOnFinish, + config.downloadOnFinish, + )!, enabled: item.checked, }, }); @@ -49,14 +52,19 @@ export const onMenu = async ({ click() { const result = dialog.showOpenDialogSync({ properties: ['openDirectory', 'createDirectory'], - defaultPath: getFolder(config.downloadOnFinish?.folder ?? config.downloadFolder), + defaultPath: getFolder( + config.downloadOnFinish?.folder ?? config.downloadFolder, + ), }); if (result) { setConfig({ downloadOnFinish: { - ...deepmerge(defaultConfig.downloadOnFinish, config.downloadOnFinish)!, + ...deepmerge( + defaultConfig.downloadOnFinish, + config.downloadOnFinish, + )!, folder: result[0], - } + }, }); } }, @@ -76,7 +84,10 @@ export const onMenu = async ({ click() { setConfig({ downloadOnFinish: { - ...deepmerge(defaultConfig.downloadOnFinish, config.downloadOnFinish)!, + ...deepmerge( + defaultConfig.downloadOnFinish, + config.downloadOnFinish, + )!, mode: 'seconds', }, }); @@ -91,7 +102,10 @@ export const onMenu = async ({ click() { setConfig({ downloadOnFinish: { - ...deepmerge(defaultConfig.downloadOnFinish, config.downloadOnFinish)!, + ...deepmerge( + defaultConfig.downloadOnFinish, + config.downloadOnFinish, + )!, mode: 'percent', }, }); @@ -120,7 +134,9 @@ export const onMenu = async ({ min: '0', step: '1', }, - value: config.downloadOnFinish?.seconds ?? defaultConfig.downloadOnFinish!.seconds, + value: + config.downloadOnFinish?.seconds ?? + defaultConfig.downloadOnFinish!.seconds, }, { label: t( @@ -133,7 +149,9 @@ export const onMenu = async ({ max: '100', step: '1', }, - value: config.downloadOnFinish?.percent ?? defaultConfig.downloadOnFinish!.percent, + value: + config.downloadOnFinish?.percent ?? + defaultConfig.downloadOnFinish!.percent, }, ], ...promptOptions(), @@ -147,7 +165,10 @@ export const onMenu = async ({ setConfig({ downloadOnFinish: { - ...deepmerge(defaultConfig.downloadOnFinish, config.downloadOnFinish)!, + ...deepmerge( + defaultConfig.downloadOnFinish, + config.downloadOnFinish, + )!, seconds: Number(res[0]), percent: Number(res[1]), }, diff --git a/src/plugins/downloader/renderer.ts b/src/plugins/downloader/renderer.ts index 99e2dde5..dc63b3ba 100644 --- a/src/plugins/downloader/renderer.ts +++ b/src/plugins/downloader/renderer.ts @@ -39,7 +39,9 @@ const menuObserver = new MutationObserver(() => { if (!menuUrl?.includes('watch?')) { menuUrl = undefined; // check for podcast - for (const it of document.querySelectorAll('tp-yt-paper-listbox [tabindex="-1"] #navigation-endpoint')) { + for (const it of document.querySelectorAll( + 'tp-yt-paper-listbox [tabindex="-1"] #navigation-endpoint', + )) { if (it.getAttribute('href')?.includes('podcast/')) { menuUrl = it.getAttribute('href')!; break; @@ -72,7 +74,9 @@ export const onRendererLoad = ({ ?.getAttribute('href'); if (!videoUrl && songMenu) { - for (const it of songMenu.querySelectorAll('ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint')) { + for (const it of songMenu.querySelectorAll( + 'ytmusic-menu-navigation-item-renderer[tabindex="-1"] #navigation-endpoint', + )) { if (it.getAttribute('href')?.includes('podcast/')) { videoUrl = it.getAttribute('href'); break; @@ -86,7 +90,8 @@ export const onRendererLoad = ({ } if (videoUrl.startsWith('podcast/')) { - videoUrl = defaultConfig.url + '/watch?' + videoUrl.replace('podcast/', 'v='); + videoUrl = + defaultConfig.url + '/watch?' + videoUrl.replace('podcast/', 'v='); } if (videoUrl.includes('?playlist=')) { diff --git a/src/plugins/in-app-menu/constants.ts b/src/plugins/in-app-menu/constants.ts index 631dc0b9..ad159c5b 100644 --- a/src/plugins/in-app-menu/constants.ts +++ b/src/plugins/in-app-menu/constants.ts @@ -4,24 +4,12 @@ export interface InAppMenuConfig { } export const defaultInAppMenuConfig: InAppMenuConfig = { enabled: - ( - ( - typeof window !== 'undefined' && - !window.navigator?.userAgent?.toLowerCase().includes('mac') - ) || - ( - typeof global !== 'undefined' && - global.process?.platform !== 'darwin' - ) - ) && ( - ( - typeof window !== 'undefined' && - !window.navigator?.userAgent?.toLowerCase().includes('linux') - ) || - ( - typeof global !== 'undefined' && - global.process?.platform !== 'linux' - ) - ), + ((typeof window !== 'undefined' && + !window.navigator?.userAgent?.toLowerCase().includes('mac')) || + (typeof global !== 'undefined' && + global.process?.platform !== 'darwin')) && + ((typeof window !== 'undefined' && + !window.navigator?.userAgent?.toLowerCase().includes('linux')) || + (typeof global !== 'undefined' && global.process?.platform !== 'linux')), hideDOMWindowControls: false, }; diff --git a/src/plugins/in-app-menu/main.ts b/src/plugins/in-app-menu/main.ts index 400558ea..71479fab 100644 --- a/src/plugins/in-app-menu/main.ts +++ b/src/plugins/in-app-menu/main.ts @@ -1,6 +1,13 @@ import { register } from 'electron-localshortcut'; -import { BrowserWindow, Menu, MenuItem, ipcMain, nativeImage } from 'electron'; +import { + BrowserWindow, + Menu, + MenuItem, + ipcMain, + nativeImage, + WebContents, +} from 'electron'; import type { BackendContext } from '@/types/contexts'; import type { InAppMenuConfig } from './constants'; @@ -50,11 +57,13 @@ export const onMainLoad = ({ ipcMain.handle('ytmd:menu-event', (event, commandId: number) => { const target = getMenuItemById(commandId); if (target) - target.click( - undefined, - BrowserWindow.fromWebContents(event.sender), - event.sender, - ); + ( + target.click as ( + args0: unknown, + args1: BrowserWindow | null, + args3: WebContents, + ) => void + )(undefined, BrowserWindow.fromWebContents(event.sender), event.sender); }); handle('get-menu-by-id', (commandId: number) => { diff --git a/src/plugins/in-app-menu/renderer.tsx b/src/plugins/in-app-menu/renderer.tsx index 3fdb928a..522d0efe 100644 --- a/src/plugins/in-app-menu/renderer.tsx +++ b/src/plugins/in-app-menu/renderer.tsx @@ -16,8 +16,9 @@ const isMacOS = navigator.userAgent.includes('Macintosh'); const isNotWindowsOrMacOS = !navigator.userAgent.includes('Windows') && !isMacOS; - -const [config, setConfig] = createSignal(defaultInAppMenuConfig); +const [config, setConfig] = createSignal( + defaultInAppMenuConfig, +); export const onRendererLoad = async ({ getConfig, ipc, @@ -29,14 +30,19 @@ export const onRendererLoad = async ({ stylesheet.replaceSync(scrollStyle); document.adoptedStyleSheets = [...document.adoptedStyleSheets, stylesheet]; - render(() => ( - - ), document.body); + render( + () => ( + + ), + document.body, + ); }; export const onPlayerApiReady = () => { diff --git a/src/plugins/in-app-menu/renderer/IconButton.tsx b/src/plugins/in-app-menu/renderer/IconButton.tsx index b3301b81..4b705fad 100644 --- a/src/plugins/in-app-menu/renderer/IconButton.tsx +++ b/src/plugins/in-app-menu/renderer/IconButton.tsx @@ -3,36 +3,38 @@ import { css } from 'solid-styled-components'; import { cacheNoArgs } from '@/providers/decorators'; -const iconButton = cacheNoArgs(() => css` - -webkit-app-region: none; +const iconButton = cacheNoArgs( + () => css` + -webkit-app-region: none; - background: transparent; + background: transparent; - width: 24px; - height: 24px; + width: 24px; + height: 24px; - padding: 2px; - border-radius: 2px; + padding: 2px; + border-radius: 2px; - display: flex; - justify-content: center; - align-items: center; + display: flex; + justify-content: center; + align-items: center; - color: white; + color: white; - outline: none; - border: none; + outline: none; + border: none; - transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); - &:hover { - background: rgba(255, 255, 255, 0.1); - } + &:hover { + background: rgba(255, 255, 255, 0.1); + } - &:active { - scale: 0.9; - } -`); + &:active { + scale: 0.9; + } + `, +); type CollapseIconButtonProps = JSX.HTMLAttributes; export const IconButton = (props: CollapseIconButtonProps) => { diff --git a/src/plugins/in-app-menu/renderer/MenuButton.tsx b/src/plugins/in-app-menu/renderer/MenuButton.tsx index c208b332..7d9f6a1d 100644 --- a/src/plugins/in-app-menu/renderer/MenuButton.tsx +++ b/src/plugins/in-app-menu/renderer/MenuButton.tsx @@ -3,31 +3,33 @@ import { css } from 'solid-styled-components'; import { cacheNoArgs } from '@/providers/decorators'; -const menuStyle = cacheNoArgs(() => css` - -webkit-app-region: none; +const menuStyle = cacheNoArgs( + () => css` + -webkit-app-region: none; - display: flex; - justify-content: center; - align-items: center; - align-self: stretch; + display: flex; + justify-content: center; + align-items: center; + align-self: stretch; - padding: 2px 8px; - border-radius: 4px; + padding: 2px 8px; + border-radius: 4px; - cursor: pointer; - transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); + cursor: pointer; + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); - &:hover { - background-color: rgba(255, 255, 255, 0.1); - } - &:active { - scale: 0.9; - } + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } + &:active { + scale: 0.9; + } - &[data-selected="true"] { - background-color: rgba(255, 255, 255, 0.2); - } -`); + &[data-selected='true'] { + background-color: rgba(255, 255, 255, 0.2); + } + `, +); export type MenuButtonProps = JSX.HTMLAttributes & { text?: string; diff --git a/src/plugins/in-app-menu/renderer/Panel.tsx b/src/plugins/in-app-menu/renderer/Panel.tsx index f1f4f26c..b5cba590 100644 --- a/src/plugins/in-app-menu/renderer/Panel.tsx +++ b/src/plugins/in-app-menu/renderer/Panel.tsx @@ -2,39 +2,48 @@ import { createSignal, JSX, Show, splitProps } from 'solid-js'; import { mergeProps, Portal } from 'solid-js/web'; import { css } from 'solid-styled-components'; import { Transition } from 'solid-transition-group'; -import { autoUpdate, flip, offset, OffsetOptions, size } from '@floating-ui/dom'; +import { + autoUpdate, + flip, + offset, + OffsetOptions, + size, +} from '@floating-ui/dom'; import { useFloating } from 'solid-floating-ui'; import { cacheNoArgs } from '@/providers/decorators'; -const panelStyle = cacheNoArgs(() => css` - position: fixed; - top: var(--offset-y, 0); - left: var(--offset-x, 0); +const panelStyle = cacheNoArgs( + () => css` + position: fixed; + top: var(--offset-y, 0); + left: var(--offset-x, 0); - max-width: var(--max-width, 100%); - max-height: var(--max-height, 100%); + max-width: var(--max-width, 100%); + max-height: var(--max-height, 100%); - z-index: 10000; - width: fit-content; - height: fit-content; + z-index: 10000; + width: fit-content; + height: fit-content; - padding: 4px; - box-sizing: border-box; - border-radius: 8px; - overflow: auto; + padding: 4px; + box-sizing: border-box; + border-radius: 8px; + overflow: auto; - background-color: color-mix( - in srgb, - var(--titlebar-background-color, #030303) 50%, - rgba(0, 0, 0, 0.1) - ); - backdrop-filter: blur(8px); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), - 0 2px 8px rgba(0, 0, 0, 0.2); + background-color: color-mix( + in srgb, + var(--titlebar-background-color, #030303) 50%, + rgba(0, 0, 0, 0.1) + ); + backdrop-filter: blur(8px); + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.05), + 0 2px 8px rgba(0, 0, 0, 0.2); - transform-origin: var(--origin-x, 50%) var(--origin-y, 50%); -`); + transform-origin: var(--origin-x, 50%) var(--origin-y, 50%); + `, +); const animationStyle = cacheNoArgs(() => ({ enter: css` @@ -42,19 +51,23 @@ const animationStyle = cacheNoArgs(() => ({ transform: scale(0.9); `, enterActive: css` - transition: opacity 0.225s cubic-bezier(0.33, 1, 0.68, 1), transform 0.225s cubic-bezier(0.33, 1, 0.68, 1); + transition: + opacity 0.225s cubic-bezier(0.33, 1, 0.68, 1), + transform 0.225s cubic-bezier(0.33, 1, 0.68, 1); `, exitTo: css` opacity: 0; transform: scale(0.9); `, exitActive: css` - transition: opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), transform 0.225s cubic-bezier(0.32, 0, 0.67, 0); + transition: + opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), + transform 0.225s cubic-bezier(0.32, 0, 0.67, 0); `, })); export type Placement = - 'top' + | 'top' | 'bottom' | 'left' | 'right' @@ -92,9 +105,15 @@ export const Panel = (props: PanelProps) => { size({ padding: 8, apply({ elements, availableWidth, availableHeight }) { - elements.floating.style.setProperty('--max-width', `${Math.max(200, availableWidth)}px`); - elements.floating.style.setProperty('--max-height', `${Math.max(200, availableHeight)}px`); - } + elements.floating.style.setProperty( + '--max-width', + `${Math.max(200, availableWidth)}px`, + ); + elements.floating.style.setProperty( + '--max-height', + `${Math.max(200, availableHeight)}px`, + ); + }, }), flip({ fallbackStrategy: 'initialPlacement' }), ], @@ -103,7 +122,10 @@ export const Panel = (props: PanelProps) => { const originX = () => { if (position.placement.includes('left')) return '100%'; if (position.placement.includes('right')) return '0'; - if (position.placement.includes('top') || position.placement.includes('bottom')) { + if ( + position.placement.includes('top') || + position.placement.includes('bottom') + ) { if (position.placement.includes('start')) return '0'; if (position.placement.includes('end')) return '100%'; } @@ -113,7 +135,10 @@ export const Panel = (props: PanelProps) => { const originY = () => { if (position.placement.includes('top')) return '100%'; if (position.placement.includes('bottom')) return '0'; - if (position.placement.includes('left') || position.placement.includes('right')) { + if ( + position.placement.includes('left') || + position.placement.includes('right') + ) { if (position.placement.includes('start')) return '0'; if (position.placement.includes('end')) return '100%'; } diff --git a/src/plugins/in-app-menu/renderer/PanelItem.tsx b/src/plugins/in-app-menu/renderer/PanelItem.tsx index c54a0ae4..583b5f95 100644 --- a/src/plugins/in-app-menu/renderer/PanelItem.tsx +++ b/src/plugins/in-app-menu/renderer/PanelItem.tsx @@ -10,100 +10,111 @@ import { autoUpdate, offset, size } from '@floating-ui/dom'; import { Panel } from './Panel'; import { cacheNoArgs } from '@/providers/decorators'; -const itemStyle = cacheNoArgs(() => css` - position: relative; +const itemStyle = cacheNoArgs( + () => css` + position: relative; - -webkit-app-region: none; - min-height: 32px; - height: 32px; + -webkit-app-region: none; + min-height: 32px; + height: 32px; - display: grid; - grid-template-columns: 32px 1fr auto minmax(32px, auto); - justify-content: flex-start; - align-items: center; + display: grid; + grid-template-columns: 32px 1fr auto minmax(32px, auto); + justify-content: flex-start; + align-items: center; - border-radius: 4px; - cursor: pointer; - box-sizing: border-box; - user-select: none; - -webkit-user-drag: none; - - transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); - - &:hover { - background-color: rgba(255, 255, 255, 0.1); - } - - &:active { - background-color: rgba(255, 255, 255, 0.2); - } - - &[data-selected="true"] { - background-color: rgba(255, 255, 255, 0.2); - } - - & * { + border-radius: 4px; + cursor: pointer; box-sizing: border-box; - } -`); + user-select: none; + -webkit-user-drag: none; -const itemIconStyle = cacheNoArgs(() => css` - height: 32px; - padding: 4px; - color: white; -`); + transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); -const itemLabelStyle = cacheNoArgs(() => css` - font-size: 12px; - color: white; -`); + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } -const itemChipStyle = cacheNoArgs(() => css` - display: flex; - justify-content: center; - align-items: center; + &:active { + background-color: rgba(255, 255, 255, 0.2); + } - min-width: 16px; - height: 16px; - padding: 0 4px; - margin-left: 8px; + &[data-selected='true'] { + background-color: rgba(255, 255, 255, 0.2); + } - border-radius: 4px; - background-color: rgba(255, 255, 255, 0.2); - color: #f1f1f1; - font-size: 10px; - font-weight: 500; - line-height: 1; -`); + & * { + box-sizing: border-box; + } + `, +); -const toolTipStyle = cacheNoArgs(() => css` - min-width: 32px; - width: 100%; - height: 100%; +const itemIconStyle = cacheNoArgs( + () => css` + height: 32px; + padding: 4px; + color: white; + `, +); - padding: 4px; +const itemLabelStyle = cacheNoArgs( + () => css` + font-size: 12px; + color: white; + `, +); - max-width: calc(var(--max-width, 100%) - 8px); - max-height: calc(var(--max-height, 100%) - 8px); +const itemChipStyle = cacheNoArgs( + () => css` + display: flex; + justify-content: center; + align-items: center; - border-radius: 4px; - background-color: rgba(25, 25, 25, 0.8); - color: #f1f1f1; - font-size: 10px; -`); + min-width: 16px; + height: 16px; + padding: 0 4px; + margin-left: 8px; -const popupStyle = cacheNoArgs(() => css` - position: fixed; - top: var(--offset-y, 0); - left: var(--offset-x, 0); + border-radius: 4px; + background-color: rgba(255, 255, 255, 0.2); + color: #f1f1f1; + font-size: 10px; + font-weight: 500; + line-height: 1; + `, +); - max-width: var(--max-width, 100%); - max-height: var(--max-height, 100%); +const toolTipStyle = cacheNoArgs( + () => css` + min-width: 32px; + width: 100%; + height: 100%; - z-index: 100000000; - pointer-events: none; + padding: 4px; -`); + max-width: calc(var(--max-width, 100%) - 8px); + max-height: calc(var(--max-height, 100%) - 8px); + + border-radius: 4px; + background-color: rgba(25, 25, 25, 0.8); + color: #f1f1f1; + font-size: 10px; + `, +); + +const popupStyle = cacheNoArgs( + () => css` + position: fixed; + top: var(--offset-y, 0); + left: var(--offset-x, 0); + + max-width: var(--max-width, 100%); + max-height: var(--max-height, 100%); + + z-index: 100000000; + pointer-events: none; + `, +); const animationStyle = cacheNoArgs(() => ({ enter: css` @@ -111,14 +122,18 @@ const animationStyle = cacheNoArgs(() => ({ transform: scale(0.9); `, enterActive: css` - transition: opacity 0.225s cubic-bezier(0.33, 1, 0.68, 1), transform 0.225s cubic-bezier(0.33, 1, 0.68, 1); + transition: + opacity 0.225s cubic-bezier(0.33, 1, 0.68, 1), + transform 0.225s cubic-bezier(0.33, 1, 0.68, 1); `, exitTo: css` opacity: 0; transform: scale(0.9); `, exitActive: css` - transition: opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), transform 0.225s cubic-bezier(0.32, 0, 0.67, 0); + transition: + opacity 0.225s cubic-bezier(0.32, 0, 0.67, 0), + transform 0.225s cubic-bezier(0.32, 0, 0.67, 0); `, })); @@ -160,7 +175,11 @@ type CheckboxPanelItemProps = BasePanelItemProps & { checked: boolean; onChange?: (checked: boolean) => void; }; -export type PanelItemProps = NormalPanelItemProps | SubmenuItemProps | RadioPanelItemProps | CheckboxPanelItemProps; +export type PanelItemProps = + | NormalPanelItemProps + | SubmenuItemProps + | RadioPanelItemProps + | CheckboxPanelItemProps; export const PanelItem = (props: PanelItemProps) => { const [open, setOpen] = createSignal(false); const [toolTipOpen, setToolTipOpen] = createSignal(false); @@ -176,17 +195,24 @@ export const PanelItem = (props: PanelItemProps) => { offset({ mainAxis: 8 }), size({ apply({ rects, elements }) { - elements.floating.style.setProperty('--max-width', `${rects.reference.width}px`); - } + elements.floating.style.setProperty( + '--max-width', + `${rects.reference.width}px`, + ); + }, }), ], }); const handleHover = (event: MouseEvent) => { setToolTipOpen(true); - event.target?.addEventListener('mouseleave', () => { - setToolTipOpen(false); - }, { once: true }); + event.target?.addEventListener( + 'mouseleave', + () => { + setToolTipOpen(false); + }, + { once: true }, + ); if (props.type === 'submenu') { const timer = setTimeout(() => { @@ -200,36 +226,54 @@ export const PanelItem = (props: PanelItemProps) => { }; document.addEventListener('mousemove', onMouseMove); - event.target?.addEventListener('mouseleave', () => { - setTimeout(() => { - document.removeEventListener('mousemove', onMouseMove); - const parents = getParents(document.elementFromPoint(mouseX, mouseY)); + event.target?.addEventListener( + 'mouseleave', + () => { + setTimeout(() => { + document.removeEventListener('mousemove', onMouseMove); + const parents = getParents( + document.elementFromPoint(mouseX, mouseY), + ); - if (!parents.includes(child())) { - setOpen(false); - } else { - const onOtherHover = (event: MouseEvent) => { - const parents = getParents(event.target as HTMLElement); - const closestLevel = parents.find((it) => it?.dataset?.level)?.dataset.level ?? ''; - const path = event.composedPath(); + if (!parents.includes(child())) { + setOpen(false); + } else { + const onOtherHover = (event: MouseEvent) => { + const parents = getParents(event.target as HTMLElement); + const closestLevel = + parents.find((it) => it?.dataset?.level)?.dataset.level ?? + ''; + const path = event.composedPath(); - const isOtherItem = path.some((it) => it instanceof HTMLElement && it.classList.contains(itemStyle())); - const isChild = closestLevel.startsWith(props.level.join('/')); + const isOtherItem = path.some( + (it) => + it instanceof HTMLElement && + it.classList.contains(itemStyle()), + ); + const isChild = closestLevel.startsWith( + props.level.join('/'), + ); - if (isOtherItem && !isChild) { - setOpen(false); - document.removeEventListener('mousemove', onOtherHover); - } - }; - document.addEventListener('mousemove', onOtherHover); - } - }, 225); - }, { once: true }); + if (isOtherItem && !isChild) { + setOpen(false); + document.removeEventListener('mousemove', onOtherHover); + } + }; + document.addEventListener('mousemove', onOtherHover); + } + }, 225); + }, + { once: true }, + ); }, 225); - event.target?.addEventListener('mouseleave', () => { - clearTimeout(timer); - }, { once: true }); + event.target?.addEventListener( + 'mouseleave', + () => { + clearTimeout(timer); + }, + { once: true }, + ); } }; @@ -244,7 +288,6 @@ export const PanelItem = (props: PanelItemProps) => { } }; - return (
  • { onClick={handleClick} data-selected={open()} > - }> + }> - - - + + + - - + + - - + + - - {props.name} - - }> - - {props.chip} - + {props.name} + }> + {props.chip} - - - + + + { exitActiveClass={animationStyle().exitActive} > -
    - {props.toolTip} -
    +
    {props.toolTip}
    diff --git a/src/plugins/in-app-menu/renderer/TitleBar.tsx b/src/plugins/in-app-menu/renderer/TitleBar.tsx index f9208922..61f9f17f 100644 --- a/src/plugins/in-app-menu/renderer/TitleBar.tsx +++ b/src/plugins/in-app-menu/renderer/TitleBar.tsx @@ -1,5 +1,15 @@ import { Menu, MenuItem } from 'electron'; -import { createEffect, createResource, createSignal, Index, Match, onCleanup, onMount, Show, Switch } from 'solid-js'; +import { + createEffect, + createResource, + createSignal, + Index, + Match, + onCleanup, + onMount, + Show, + Switch, +} from 'solid-js'; import { css } from 'solid-styled-components'; import { TransitionGroup } from 'solid-transition-group'; @@ -14,49 +24,55 @@ import { cacheNoArgs } from '@/providers/decorators'; import type { RendererContext } from '@/types/contexts'; import type { InAppMenuConfig } from '../constants'; -const titleStyle = cacheNoArgs(() => css` - -webkit-app-region: drag; - box-sizing: border-box; +const titleStyle = cacheNoArgs( + () => css` + -webkit-app-region: drag; + box-sizing: border-box; - position: fixed; - top: 0; - z-index: 10000000; + position: fixed; + top: 0; + z-index: 10000000; - width: 100%; - height: var(--menu-bar-height, 32px); + width: 100%; + height: var(--menu-bar-height, 32px); - display: flex; - flex-flow: row; - justify-content: flex-start; - align-items: center; - gap: 4px; + display: flex; + flex-flow: row; + justify-content: flex-start; + align-items: center; + gap: 4px; - color: #f1f1f1; - font-size: 12px; - padding: 4px 4px 4px var(--offset-left, 4px); - background-color: var(--titlebar-background-color, #030303); - user-select: none; + color: #f1f1f1; + font-size: 12px; + padding: 4px 4px 4px var(--offset-left, 4px); + background-color: var(--titlebar-background-color, #030303); + user-select: none; - transition: opacity 200ms ease 0s, - transform 300ms cubic-bezier(0.2, 0, 0.6, 1) 0s, - background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) 0s; + transition: + opacity 200ms ease 0s, + transform 300ms cubic-bezier(0.2, 0, 0.6, 1) 0s, + background-color 300ms cubic-bezier(0.2, 0, 0.6, 1) 0s; - &[data-macos="true"] { - padding: 4px 4px 4px 74px; - } + &[data-macos='true'] { + padding: 4px 4px 4px 74px; + } - ytmusic-app:has(ytmusic-player[player-ui-state=FULLSCREEN]) ~ &:not([data-show="true"]) { - transform: translateY(calc(-1 * var(--menu-bar-height, 32px))); - } -`); + ytmusic-app:has(ytmusic-player[player-ui-state='FULLSCREEN']) + ~ &:not([data-show='true']) { + transform: translateY(calc(-1 * var(--menu-bar-height, 32px))); + } + `, +); -const separatorStyle = cacheNoArgs(() => css` - min-height: 1px; - height: 1px; - margin: 4px 0; +const separatorStyle = cacheNoArgs( + () => css` + min-height: 1px; + height: 1px; + margin: 4px 0; - background-color: rgba(255, 255, 255, 0.2); -`); + background-color: rgba(255, 255, 255, 0.2); + `, +); const animationStyle = cacheNoArgs(() => ({ enter: css` @@ -64,14 +80,18 @@ const animationStyle = cacheNoArgs(() => ({ transform: translateX(-50%) scale(0.8); `, enterActive: css` - transition: opacity 0.1s cubic-bezier(0.33, 1, 0.68, 1), transform 0.1s cubic-bezier(0.33, 1, 0.68, 1); + transition: + opacity 0.1s cubic-bezier(0.33, 1, 0.68, 1), + transform 0.1s cubic-bezier(0.33, 1, 0.68, 1); `, exitTo: css` opacity: 0; transform: translateX(-50%) scale(0.8); `, exitActive: css` - transition: opacity 0.1s cubic-bezier(0.32, 0, 0.67, 0), transform 0.1s cubic-bezier(0.32, 0, 0.67, 0); + transition: + opacity 0.1s cubic-bezier(0.32, 0, 0.67, 0), + transform 0.1s cubic-bezier(0.32, 0, 0.67, 0); `, move: css` transition: all 0.1s cubic-bezier(0.65, 0, 0.35, 1); @@ -89,7 +109,7 @@ export type PanelRendererProps = { items: Electron.Menu['items']; level?: number[]; onClick?: (commandId: number, radioGroup?: MenuItem[]) => void; -} +}; const PanelRenderer = (props: PanelRendererProps) => { const radioGroup = () => props.items.filter((it) => it.type === 'radio'); @@ -114,12 +134,12 @@ const PanelRenderer = (props: PanelRendererProps) => { name={subItem().label} chip={subItem().sublabel} toolTip={subItem().toolTip} - level={[...props.level ?? [], subItem().commandId]} + level={[...(props.level ?? []), subItem().commandId]} commandId={subItem().commandId} > @@ -143,11 +163,13 @@ const PanelRenderer = (props: PanelRendererProps) => { chip={subItem().sublabel} toolTip={subItem().toolTip} commandId={subItem().commandId} - onChange={() => props.onClick?.(subItem().commandId, radioGroup())} + onChange={() => + props.onClick?.(subItem().commandId, radioGroup()) + } /> -
    +
    @@ -169,8 +191,13 @@ export const TitleBar = (props: TitleBarProps) => { const [menu, setMenu] = createSignal(null); const [mouseY, setMouseY] = createSignal(0); - const [data, { refetch }] = createResource(async () => await props.ipc.invoke('get-menu') as Promise); - const [isMaximized, { refetch: refetchMaximize }] = createResource(async () => await props.ipc.invoke('window-is-maximized') as Promise); + const [data, { refetch }] = createResource( + async () => (await props.ipc.invoke('get-menu')) as Promise, + ); + const [isMaximized, { refetch: refetchMaximize }] = createResource( + async () => + (await props.ipc.invoke('window-is-maximized')) as Promise, + ); const handleToggleMaximize = async () => { if (isMaximized()) { @@ -194,10 +221,12 @@ export const TitleBar = (props: TitleBarProps) => { )) as MenuItem | null; const newMenu = structuredClone(originalMenu); - const stack = [...newMenu?.items ?? []]; + const stack = [...(newMenu?.items ?? [])]; let now: MenuItem | undefined = stack.pop(); while (now) { - const index = now?.submenu?.items?.findIndex((it) => it.commandId === commandId) ?? -1; + const index = + now?.submenu?.items?.findIndex((it) => it.commandId === commandId) ?? + -1; if (index >= 0) { if (menuItem) now?.submenu?.items?.splice(index, 1, menuItem); @@ -213,13 +242,16 @@ export const TitleBar = (props: TitleBarProps) => { return newMenu; }; - const handleItemClick = async (commandId: number, radioGroup?: MenuItem[]) => { + const handleItemClick = async ( + commandId: number, + radioGroup?: MenuItem[], + ) => { const menuData = menu(); if (!menuData) return; if (Array.isArray(radioGroup)) { let newMenu = menuData; - for await (const item of radioGroup) { + for (const item of radioGroup) { newMenu = await refreshMenuItem(newMenu, item.commandId); } @@ -272,18 +304,15 @@ export const TitleBar = (props: TitleBarProps) => { window.addEventListener('mousemove', listener); const ytmusicAppLayout = document.querySelector('#layout'); ytmusicAppLayout?.addEventListener('scroll', () => { - const scrollValue = ytmusicAppLayout.scrollTop; - if (scrollValue > 20){ - ytmusicAppLayout.classList.add('content-scrolled'); - } - else{ - ytmusicAppLayout.classList.remove('content-scrolled'); - } + const scrollValue = ytmusicAppLayout.scrollTop; + if (scrollValue > 20) { + ytmusicAppLayout.classList.add('content-scrolled'); + } else { + ytmusicAppLayout.classList.remove('content-scrolled'); + } }); }); - - createEffect(() => { if (!menu() && data()) { setMenu(data() ?? null); @@ -295,7 +324,12 @@ export const TitleBar = (props: TitleBarProps) => { }); return ( -