2
2
mirror of https://github.com/Llewellynvdm/nativefier.git synced 2024-11-04 20:38:03 +00:00

Support defining a custom bookmarks menu (fix #1065) (PR #1155)

This PR adds an optional, customizable menu of predefined bookmarks. In addition to containing a list of bookmarks, this file customizes the name of the menu and (optionally) allows assigning keyboard shortcuts to bookmarks. It adds a new command-line flag, `--bookmarks-menu <string>`, which can be set as the path to a JSON file containing configuration for the bookmarks menu.

Example of such a JSON file:

```json
{
    "menuLabel": "Music",
    "bookmarks": [
        {
            "title": "lofi.cafe",
            "url": "https://lofi.cafe/",
            "type": "link",
            "shortcut": "Cmd+1"
        },
        {
            "title": "beats to relax/study to",
            "url": "https://www.youtube.com/watch?v=5qap5aO4i9A",
            "type": "link",
            "shortcut": "Cmd+2"
        },
        {
            "type": "separator"
        },
        {
            "title": "RÜFÜS DU SOL Live from Joshua Tree",
            "type": "link",
            "url": "https://www.youtube.com/watch?v=Zy4KtD98S2c"
        }
    ]
}
```

## Checks
- [x] `npm run ci` passes

## Notes

Compared to the fork linked in #1065, this PR:
- adds no dependencies
- doesn't currently support submenus (this should be easy enough to add, but I didn't need it)

## Screenshot

<img width="853" alt="screenshot" src="https://user-images.githubusercontent.com/102904/115882015-5493a800-a41a-11eb-85ef-a190f3dbfe76.png">
This commit is contained in:
Chris Dzombak 2021-04-23 21:46:34 -04:00 committed by GitHub
parent fa9bd2aba5
commit b4ddd6865c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 136 additions and 0 deletions

View File

@ -230,6 +230,9 @@ export function createMainWindow(
const getCurrentUrl = (): void =>
withFocusedWindow((focusedWindow) => focusedWindow.webContents.getURL());
const gotoUrl = (url: string): void =>
withFocusedWindow((focusedWindow) => void focusedWindow.loadURL(url));
const onBlockedExternalUrl = (url: string) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
dialog.showMessageBox(mainWindow, {
@ -342,6 +345,7 @@ export function createMainWindow(
goBack: onGoBack,
goForward: onGoForward,
getCurrentUrl,
gotoUrl,
clearAppData,
disableDevTools: options.disableDevTools,
};

View File

@ -1,5 +1,23 @@
import * as fs from 'fs';
import path from 'path';
import { Menu, clipboard, shell, MenuItemConstructorOptions } from 'electron';
type BookmarksLink = {
type: 'link';
title: string;
url: string;
shortcut?: string;
};
type BookmarksSeparator = {
type: 'separator';
};
type BookmarkConfig = BookmarksLink | BookmarksSeparator;
type BookmarksMenuConfig = {
menuLabel: string;
bookmarks: BookmarkConfig[];
};
export function createMenu({
nativefierVersion,
appQuit,
@ -10,6 +28,7 @@ export function createMenu({
goBack,
goForward,
getCurrentUrl,
gotoUrl,
clearAppData,
disableDevTools,
}): void {
@ -286,6 +305,58 @@ export function createMenu({
menuTemplate = [editMenu, viewMenu, windowMenu, helpMenu];
}
try {
const bookmarkConfigPath = path.join(__dirname, '..', 'bookmarks.json');
if (fs.existsSync(bookmarkConfigPath)) {
const bookmarksMenuConfig: BookmarksMenuConfig = JSON.parse(
fs.readFileSync(bookmarkConfigPath, 'utf-8'),
);
const bookmarksMenu: MenuItemConstructorOptions = {
label: bookmarksMenuConfig.menuLabel,
submenu: bookmarksMenuConfig.bookmarks.map((bookmark) => {
if (bookmark.type === 'link') {
if (!('title' in bookmark && 'url' in bookmark)) {
throw Error(
'All links in the bookmarks menu must have a title and url.',
);
}
try {
new URL(bookmark.url);
} catch (_) {
throw Error('Bookmark URL "' + bookmark.url + '"is invalid.');
}
let accelerator = null;
if ('shortcut' in bookmark) {
accelerator = bookmark.shortcut;
}
return {
label: bookmark.title,
click: () => {
gotoUrl(bookmark.url);
},
accelerator: accelerator,
};
} else if (bookmark.type === 'separator') {
return {
type: 'separator',
};
} else {
throw Error(
'A bookmarks menu entry has an invalid type; type must be one of "link", "separator".',
);
}
}),
};
// Insert custom bookmarks menu between menus "View" and "Window"
menuTemplate.splice(menuTemplate.length - 2, 0, bookmarksMenu);
}
} catch (err) {
console.warn(
'Failed to load & parse bookmarks configuration JSON file.',
err,
);
}
const menu = Menu.buildFromTemplate(menuTemplate);
Menu.setApplicationMenu(menu);
}

View File

@ -68,6 +68,7 @@
- [[browserwindow-options]](#browserwindow-options)
- [[darwin-dark-mode-support]](#darwin-dark-mode-support)
- [[background-color]](#background-color)
- [[bookmarks-menu]](#bookmarks-menu)
- [Deprecated](#deprecated)
- [[flash] and [flash-path]](#flash) (DEPRECATED)
- [Programmatic API](#programmatic-api)
@ -794,6 +795,51 @@ Enables Dark Mode support on macOS 10.14+.
See https://electronjs.org/docs/api/browser-window#setting-backgroundcolor
#### [bookmarks-menu]
```
--bookmarks-menu <string>
```
Path to a JSON file defining a bookmarks menu. In addition to containing a list of bookmarks, this file customizes the name of the menu and (optionally) allows assigning keyboard shortcuts to bookmarks.
This menu is a simple list; folders are not supported.
Your `menuLabel` can be bound to a `Alt + letter` shortcut using the letter `&` before the `letter` you want. Be careful to not conflict with the letter of other menus!
Keyboard shortcuts can use the modifier keys `Cmd`, `Ctrl`, `CmdOrCtrl`, `Alt`, `Option`, `AltGr`, `Shift`, and `Super`. See [the Electron documentation](https://www.electronjs.org/docs/api/accelerator) for more information.
Example of such a JSON file:
```json
{
"menuLabel": "&Music",
"bookmarks": [
{
"title": "lofi.cafe",
"url": "https://lofi.cafe/",
"type": "link",
"shortcut": "CmdOrCtrl+1"
},
{
"title": "beats to relax/study to",
"url": "https://www.youtube.com/watch?v=5qap5aO4i9A",
"type": "link",
"shortcut": "CmdOrCtrl+2"
},
{
"type": "separator"
},
{
"title": "RÜFÜS DU SOL Live from Joshua Tree",
"type": "link",
"url": "https://www.youtube.com/watch?v=Zy4KtD98S2c"
}
]
}
```
### Deprecated
#### [flash]

View File

@ -156,6 +156,15 @@ export async function prepareElectronApp(
JSON.stringify(pickElectronAppArgs(options)),
);
if (options.nativefier.bookmarksMenu) {
const bookmarksJsonPath = path.join(dest, '/bookmarks.json');
try {
await copyFileOrDir(options.nativefier.bookmarksMenu, bookmarksJsonPath);
} catch (err) {
log.error('Error copying bookmarks menu config file.', err);
}
}
try {
await maybeCopyScripts(options.nativefier.inject, dest);
} catch (err) {

View File

@ -274,6 +274,10 @@ if (require.main === module) {
.option(
'--darwin-dark-mode-support',
'(macOS only) enable Dark Mode support on macOS 10.14+',
)
.option(
'--bookmarks-menu <value>',
'Path to JSON configuration file for the bookmarks menu.',
);
try {

View File

@ -13,6 +13,7 @@ export interface AppOptions {
backgroundColor: string;
basicAuthPassword: string;
basicAuthUsername: string;
bookmarksMenu: string;
bounce: boolean;
browserwindowOptions: any;
clearCache: boolean;

View File

@ -52,6 +52,7 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
backgroundColor: rawOptions.backgroundColor || null,
basicAuthPassword: rawOptions.basicAuthPassword || null,
basicAuthUsername: rawOptions.basicAuthUsername || null,
bookmarksMenu: rawOptions.bookmarksMenu || null,
bounce: rawOptions.bounce || false,
browserwindowOptions: rawOptions.browserwindowOptions,
clearCache: rawOptions.clearCache || false,