Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

Commit

Permalink
refactor(in-app-menu): refactor in-app-menu plugin
Browse files Browse the repository at this point in the history
resolve #13
  • Loading branch information
Su-Yong committed Oct 2, 2023
1 parent a5fe8bc commit 5a7774e
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 223 deletions.
13 changes: 13 additions & 0 deletions assets/youtube-music.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ function createMainWindow() {
sandbox: false,
}),
},
frame: !is.macOS() && !useInlineMenu,
frame: !is.macOS() && !is.linux() && !useInlineMenu,
titleBarOverlay: {
color: '#00000000',
symbolColor: '#ffffff',
height: 36,
},
titleBarStyle: useInlineMenu
? 'hidden'
: (is.macOS()
Expand Down
9 changes: 0 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@
"butterchurn-presets": "2.4.7",
"conf": "10.2.0",
"custom-electron-prompt": "1.5.7",
"custom-electron-titlebar": "4.1.6",
"electron-better-web-request": "1.0.1",
"electron-debug": "3.2.0",
"electron-is": "3.0.0",
Expand Down
53 changes: 42 additions & 11 deletions plugins/in-app-menu/back.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,58 @@
import path from 'node:path';

import { register } from 'electron-localshortcut';
// eslint-disable-next-line import/no-unresolved
import { attachTitlebarToWindow, setupTitlebar } from 'custom-electron-titlebar/main';

import { BrowserWindow } from 'electron';
import { BrowserWindow, Menu, MenuItem, ipcMain } from 'electron';

import { injectCSS } from '../utils';


setupTitlebar();

// Tracks menu visibility

export default (win: BrowserWindow) => {
// Css for custom scrollbar + disable drag area(was causing bugs)
injectCSS(win.webContents, path.join(__dirname, 'style.css'));
injectCSS(win.webContents, path.join(__dirname, 'titlebar.css'));

win.once('ready-to-show', () => {
attachTitlebarToWindow(win);

register(win, '`', () => {
win.webContents.send('toggleMenu');
});
});

ipcMain.handle(
'get-menu',
() => JSON.parse(JSON.stringify(
Menu.getApplicationMenu(),
(key: string, value: unknown) => (key !== 'commandsMap' && key !== 'menu') ? value : undefined),
),
);

const getMenuItemById = (commandId: number): MenuItem | null => {
const menu = Menu.getApplicationMenu();

let target: MenuItem | null = null;
const stack = [...menu?.items ?? []];
while (stack.length > 0) {
const now = stack.shift();
now?.submenu?.items.forEach((item) => stack.push(item));

if (now?.commandId === commandId) {
target = now;
break;
}
}

return target;
};

ipcMain.handle('menu-event', (event, commandId: number) => {
const target = getMenuItemById(commandId);
if (target) target.click(undefined, BrowserWindow.fromWebContents(event.sender), event.sender);
});

ipcMain.handle('get-menu-by-id', (_, commandId: number) => {
const result = getMenuItemById(commandId);

return JSON.parse(JSON.stringify(
result,
(key: string, value: unknown) => (key !== 'commandsMap' && key !== 'menu') ? value : undefined),
);
});
};
9 changes: 0 additions & 9 deletions plugins/in-app-menu/custom-electron-titlebar.d.ts

This file was deleted.

118 changes: 44 additions & 74 deletions plugins/in-app-menu/front.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import path from 'node:path';

import { ipcRenderer, Menu } from 'electron';
// eslint-disable-next-line import/no-unresolved
import { Color, Titlebar } from 'custom-electron-titlebar';

import config from '../../config';
import { isEnabled } from '../../config/plugins';
import { createPanel } from './menu/panel';

import type { FastAverageColorResult } from 'fast-average-color';
import { ElementFromFile } from '../utils';
import { isEnabled } from '../../config/plugins';

type ElectronCSSStyleDeclaration = CSSStyleDeclaration & { webkitAppRegion: 'drag' | 'no-drag' };
type ElectronHTMLElement = HTMLElement & { style: ElectronCSSStyleDeclaration };
Expand All @@ -15,60 +15,60 @@ function $<E extends Element = Element>(selector: string) {
}

export default () => {
const visible = () => !!($('.cet-menubar')?.firstChild);
const bar = new Titlebar({
icon: 'https://cdn-icons-png.flaticon.com/512/5358/5358672.png',
backgroundColor: Color.fromHex('#050505'),
itemBackgroundColor: Color.fromHex('#1d1d1d') ,
svgColor: Color.WHITE,
menu: config.get('options.hideMenu') ? null as unknown as Menu : undefined,
});
bar.updateTitle(' ');
document.title = 'Youtube Music';
const titleBar = document.createElement('title-bar');
const navBar = document.querySelector<HTMLDivElement>('#nav-bar-background');

const toggleMenu = () => {
if (visible()) {
bar.updateMenu(null as unknown as Menu);
} else {
bar.refreshMenu();
}
const logo = ElementFromFile(path.join(__dirname, '../../assets/youtube-music.svg'));
logo.classList.add('title-bar-icon');

titleBar.appendChild(logo);
document.body.appendChild(titleBar);

if (navBar) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(() => {
titleBar.style.setProperty('--titlebar-background-color', navBar.style.backgroundColor);
document.querySelector('html')!.style.setProperty('--titlebar-background-color', navBar.style.backgroundColor);
});
});

observer.observe(navBar, { attributes : true, attributeFilter : ['style'] });
}

const updateMenu = async () => {
const children = [...titleBar.children];
children.forEach((child) => {
if (child !== logo) child.remove();
});

const menu = await ipcRenderer.invoke('get-menu') as Menu | null;
if (!menu) return;

menu.items.forEach((menuItem) => {
const menu = document.createElement('menu-button');
createPanel(titleBar, menu, menuItem.submenu?.items ?? []);

menu.append(menuItem.label);
titleBar.appendChild(menu);
});
};
updateMenu();

$('.cet-window-icon')?.addEventListener('click', toggleMenu);
ipcRenderer.on('toggleMenu', toggleMenu);
document.title = 'Youtube Music';

ipcRenderer.on('refreshMenu', () => {
if (visible()) {
bar.refreshMenu();
}
updateMenu();
});

if (isEnabled('album-color-theme')) {
ipcRenderer.on('album-color-changed', (_, albumColor: FastAverageColorResult) => {
if (albumColor) {
bar.updateBackground(Color.fromHex(albumColor.hexa));
} else {
bar.updateBackground(Color.fromHex('#050505'));
}
});
}

if (isEnabled('picture-in-picture')) {
ipcRenderer.on('pip-toggle', () => {
bar.refreshMenu();
updateMenu();
});
}

// Increases the right margin of Navbar background when the scrollbar is visible to avoid blocking it (z-index doesn't affect it)
document.addEventListener('apiLoaded', () => {
setNavbarMargin();
const playPageObserver = new MutationObserver(setNavbarMargin);
const appLayout = $('ytmusic-app-layout');
if (appLayout) {
playPageObserver.observe(appLayout, { attributeFilter: ['player-page-open_', 'playerPageOpen_'] });
}
setupSearchOpenObserver();
setupMenuOpenObserver();
}, { once: true, passive: true });
};

Expand All @@ -84,33 +84,3 @@ function setupSearchOpenObserver() {
searchOpenObserver.observe(searchBox, { attributeFilter: ['opened'] });
}
}

function setupMenuOpenObserver() {
const cetMenubar = $('.cet-menubar');
if (cetMenubar) {
const menuOpenObserver = new MutationObserver(() => {
let isOpen = false;
for (const child of cetMenubar.children) {
if (child.classList.contains('open')) {
isOpen = true;
break;
}
}
const navBarBackground = $<ElectronHTMLElement>('#nav-bar-background');
if (navBarBackground) {
navBarBackground.style.webkitAppRegion = isOpen ? 'no-drag' : 'drag';
}
});
menuOpenObserver.observe(cetMenubar, { subtree: true, attributeFilter: ['class'] });
}
}

function setNavbarMargin() {
const navBarBackground = $<HTMLElement>('#nav-bar-background');
if (navBarBackground) {
navBarBackground.style.right
= $<HTMLElement & { playerPageOpen_: boolean }>('ytmusic-app-layout')?.playerPageOpen_
? '0px'
: '12px';
}
}
10 changes: 10 additions & 0 deletions plugins/in-app-menu/menu/icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const Icons = {
submenu: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><polyline points="9 6 15 12 9 18" /></svg>',
checkbox: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l5 5l10 -10" /></svg>',
radio: {
checked: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" style="padding: 2px"><path fill="currentColor" d="M10,5 C7.2,5 5,7.2 5,10 C5,12.8 7.2,15 10,15 C12.8,15 15,12.8 15,10 C15,7.2 12.8,5 10,5 L10,5 Z M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z" /></svg>',
unchecked: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" style="padding: 2px"><path fill="currentColor" d="M10,0 C4.5,0 0,4.5 0,10 C0,15.5 4.5,20 10,20 C15.5,20 20,15.5 20,10 C20,4.5 15.5,0 10,0 L10,0 Z M10,18 C5.6,18 2,14.4 2,10 C2,5.6 5.6,2 10,2 C14.4,2 18,5.6 18,10 C18,14.4 14.4,18 10,18 L10,18 Z" /></svg>',
},
};

export default Icons;
Loading

0 comments on commit 5a7774e

Please sign in to comment.