Skip to content

Commit c4ea26d

Browse files
committed
Merge branch 'release/1.1.0'
2 parents 867908d + 6773902 commit c4ea26d

11 files changed

Lines changed: 267 additions & 238 deletions

package-lock.json

Lines changed: 168 additions & 168 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,30 @@
3333
"@eslint/js": "^10.0.1",
3434
"@stylistic/eslint-plugin": "^5.10.0",
3535
"@types/auto-launch": "^5.0.5",
36-
"@types/node": "^25.5.0",
36+
"@types/node": "^25.5.2",
3737
"clean-css": "^5.3.3",
3838
"concurrently": "^9.2.1",
3939
"copy-webpack-plugin": "^14.0.0",
40-
"electron": "^40.8.5",
40+
"electron": "^41.2.0",
4141
"electron-builder": "^26.8.1",
42-
"eslint": "^10.1.0",
42+
"eslint": "^10.2.0",
4343
"html-minifier-terser": "^7.2.0",
4444
"rimraf": "^6.1.3",
4545
"terser-webpack-plugin": "^5.4.0",
46-
"ts-loader": "^9.5.4",
46+
"ts-loader": "^9.5.7",
4747
"typescript": "^5.9.3",
48-
"typescript-eslint": "^8.58.0",
48+
"typescript-eslint": "^8.58.1",
4949
"webpack": "^5.105.4",
5050
"webpack-cli": "^7.0.2"
5151
},
5252
"dependencies": {
5353
"@ecromaneli/search-engine": "^3.0.0",
5454
"auto-launch": "^5.0.6",
5555
"electron-context-menu": "^4.1.2",
56-
"electron-draggable": "^1.5.2",
57-
"electron-findbar": "^3.6.0",
56+
"electron-draggable": "^1.5.3",
57+
"electron-findbar": "^3.7.0",
5858
"electron-store": "^11.0.2",
5959
"electron-updater": "^6.8.3",
60-
"vue": "^3.5.31"
60+
"vue": "^3.5.32"
6161
}
6262
}

src/AppState.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ export type SystemTheme = 'light' | 'dark';
1414
export type ResetBoundType = 'bounds' | 'position' | '';
1515

1616
class AppState {
17-
private _autoLauncher = new AutoLaunch({ name: 'Handbook' });
17+
private readonly _autoLauncher = new AutoLaunch({ name: 'Handbook' });
1818
private _strings: Strings = getLanguageStrings(Storage.getSettings(Settings.APP_LANGUAGE) || app.getLocale());
1919
private _defaultAppMenu?: MenuItem[];
2020
private _globalShortcut = '';
2121
private _resetBoundsType: ResetBoundType = Storage.getSettings(Settings.RESET_BOUNDS);
2222
private _systemTheme = this.getSystemTheme();
2323
private _tray?: Tray;
2424
private _preferences?: BrowserWindow;
25-
private _fromClipboardPage: Page = new Page(void 0, this.strings.menu.fromClipboard);
2625
private _pages: Page[] = [];
27-
private _appMenuTemplate: MenuItemConstructorOptions[] = [];
26+
private readonly _fromClipboardPage: Page = new Page(void 0, this.strings.menu.fromClipboard);
2827
private readonly currentStack: { frame?: BaseWindow, navbar?: WebContentsView, page?: Page } = {};
28+
private readonly onViewChangeHandler = function (this: Page) { ViewPropagator.propagate(this.view); };
2929
private readonly contextMenu: {
3030
tray?: MenuItemConstructorOptions[],
3131
view?: MenuItemConstructorOptions[],
@@ -63,14 +63,14 @@ class AppState {
6363
this.currentStack.page = page;
6464
if (page) {
6565
ViewPropagator.propagate(page.view);
66-
page.setViewChangeHandler(() => ViewPropagator.propagate(page.view));
66+
if (!page.hasViewChangeHandler()) {
67+
page.setViewChangeHandler(this.onViewChangeHandler);
68+
}
6769
}
6870
}
6971
get currentPage(): Page | undefined { return this.currentStack.page; }
7072
set pages(pages: Page[]) { this._pages = pages; }
7173
get pages(): Page[] { return this._pages; }
72-
get appMenuTemplate(): MenuItemConstructorOptions[] { return this._appMenuTemplate; }
73-
set appMenuTemplate(template: MenuItemConstructorOptions[]) { this._appMenuTemplate = template; }
7474

7575
set googleApiKey(key: string) { process.env.GOOGLE_API_KEY = key; }
7676
set themeSource(theme: 'light' | 'dark' | 'system') { nativeTheme.themeSource = theme; }
@@ -98,9 +98,10 @@ class AppState {
9898
if (process.env.NODE_ENV !== 'development') { return []; }
9999
const allPages = () => [this._fromClipboardPage, ...this._pages];
100100
const pageType = (current?: Page) => {
101-
return !current ? '' :
102-
!current.view ? 'no view' :
103-
current.view.webContents.isDestroyed() ? 'view: destroyed' : 'view: alive';
101+
if (!current) { return ''; }
102+
if (!current.view) { return 'no view'; }
103+
return current.label + (current.hasView ?
104+
current.view.webContents && !current.view.webContents.isDestroyed() ? ': alive' : ': destroyed' : '');
104105
};
105106

106107
return [

src/data/Constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const DefaultSettings: Record<string, any> = {};
5353
DefaultSettings[Settings.SHOW_FRAME] = true;
5454
DefaultSettings[Settings.BACKGROUND_COLOR] = '#171717';
5555
DefaultSettings[Settings.FOCUS_OPACITY] = 100;
56-
DefaultSettings[Settings.BLUR_OPACITY] = 70;
56+
DefaultSettings[Settings.BLUR_OPACITY] = 90;
5757
DefaultSettings[Settings.KEEP_OPACITY_WHEN_MAXIMIZED] = false;
5858
DefaultSettings[Settings.ALLOW_FULLSCREEN] = false;
5959
DefaultSettings[Settings.RESET_BOUNDS] = 'position';

src/model/Page.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ export class Page {
114114
this._onViewChange = handler;
115115
}
116116

117+
public hasViewChangeHandler(): boolean {
118+
return !!this._onViewChange;
119+
}
120+
117121
get hasView(): boolean {
118122
return !!this._view;
119123
}

src/service/ApplicationService.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import SyncService from '@/service/SyncService';
1212
import TrayService from '@/service/TrayService';
1313
import ViewService from '@/service/ViewService';
1414
import { parseToAccelerator } from '@/util/EventKeyCapture';
15-
import Dialog from '@/util/modal/Dialog';
16-
import { app, globalShortcut, Menu, MenuItemConstructorOptions, Session, session, WebContentsView } from 'electron';
15+
import Dialog, { DialogOptions } from '@/util/modal/Dialog';
16+
import { app, BrowserWindow, globalShortcut, Menu, MenuItemConstructorOptions, Session, session, WebContentsView } from 'electron';
1717

1818
class ApplicationService {
1919
private static readonly ACCEPT_LANGUAGE_HEADER = 'Accept-Language';
2020

2121
public initialize() {
22+
this.setupExitDialog();
2223
this.setupAutoLaunch();
2324
this.registerGlobalShortcut();
2425
this.setupAccelerators();
@@ -93,6 +94,54 @@ class ApplicationService {
9394
}
9495
}
9596

97+
private setupExitDialog() {
98+
let quitting = false;
99+
app.on('before-quit', (e) => {
100+
if (quitting) { return; }
101+
e.preventDefault();
102+
const d = AppState.strings.exitDialog;
103+
return this.showConfirmationDialog({
104+
title: d.title,
105+
message: d.message,
106+
confirmBtn: d.confirm,
107+
cancelBtn: AppState.strings.dialog.cancel,
108+
parent: null,
109+
confirmAction: () => { quitting = true; app.quit(); },
110+
});
111+
});
112+
}
113+
114+
private async showConfirmationDialog(
115+
data: DialogOptions & {
116+
parent: BrowserWindow | null,
117+
confirmBtn?: string,
118+
cancelBtn?: string,
119+
confirmAction?: () => void,
120+
cancelAction?: () => void,
121+
},
122+
): Promise<void> {
123+
const d = AppState.strings.dialog;
124+
const result = await Dialog.show(
125+
data.parent ?? null,
126+
{
127+
type: data.type || 'question',
128+
title: data.title || d.confirmation,
129+
message: data.message || d.areYouSure,
130+
buttons: [data.confirmBtn ?? d.ok, data.cancelBtn ?? d.cancel],
131+
defaultId: 1,
132+
cancelId: 1,
133+
},
134+
);
135+
136+
setTimeout(() => {
137+
if (result.response === 0) {
138+
data.confirmAction && data.confirmAction();
139+
} else {
140+
data.cancelAction && data.cancelAction();
141+
}
142+
});
143+
}
144+
96145
private setupAccelerators() {
97146
AppState.defaultAppMenu = (Menu.getApplicationMenu()?.items || []);
98147
this.buildApplicationMenu();

src/service/FrameService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class FrameService {
4949

5050
public toggleVisibility(): void {
5151
const frame = this.getFrame()!;
52-
frame.isVisible() ? frame.hide() : frame.show();
52+
frame.isVisible() ? this.hide() : this.show();
5353
}
5454

5555
public toggleMaximize(): void {
@@ -310,6 +310,7 @@ class FrameService {
310310

311311
public show(): void {
312312
this.getFrame()!.show();
313+
ViewService.focus();
313314
}
314315

315316
public hide(): void {

src/service/MenuService.ts

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import PageService from '@/service/PageService';
88
import PreferencesService from '@/service/PreferencesService';
99
import TrayService from '@/service/TrayService';
1010
import ViewService from '@/service/ViewService';
11-
import Dialog, { DialogOptions } from '@/util/modal/Dialog';
1211
import QuickMenuModal, { QuickMenuItem } from '@/util/modal/QuickMenuModal';
1312
import SearchEngine from '@ecromaneli/search-engine';
14-
import { app, BrowserWindow, clipboard, Menu, MenuItemConstructorOptions, shell } from 'electron';
13+
import { app, clipboard, Menu, MenuItemConstructorOptions, shell } from 'electron';
1514

1615
type MenuItem = MenuItemConstructorOptions & { submenu: MenuItemConstructorOptions[] };
1716

@@ -115,19 +114,7 @@ class MenuService {
115114
windowMenuItems.push({ label: s.preferences, click: () => PreferencesService.open() });
116115

117116
menuItems.push(...windowMenuItems);
118-
menuItems.push({
119-
label: s.exit, click: () => {
120-
const d = AppState.strings.exitDialog;
121-
this.showConfirmationDialog({
122-
title: d.title,
123-
message: d.message,
124-
confirmBtn: d.confirm,
125-
cancelBtn: AppState.strings.dialog.cancel,
126-
parent: null,
127-
confirmAction: () => app.quit(),
128-
});
129-
},
130-
});
117+
menuItems.push({ label: s.exit, click: () => app.quit() });
131118

132119
if (currentPageSubmenu) {
133120
AppState.viewContextMenu = [
@@ -186,37 +173,6 @@ class MenuService {
186173
}
187174
}
188175

189-
private async showConfirmationDialog(
190-
data: DialogOptions & {
191-
parent: BrowserWindow | null,
192-
confirmBtn?: string,
193-
cancelBtn?: string,
194-
confirmAction?: () => void,
195-
cancelAction?: () => void,
196-
},
197-
): Promise<void> {
198-
const d = AppState.strings.dialog;
199-
const result = await Dialog.show(
200-
data.parent ?? null,
201-
{
202-
type: data.type || 'question',
203-
title: data.title || d.confirmation,
204-
message: data.message || d.areYouSure,
205-
buttons: [data.confirmBtn ?? d.ok, data.cancelBtn ?? d.cancel],
206-
defaultId: 1,
207-
cancelId: 1,
208-
},
209-
);
210-
211-
setTimeout(() => {
212-
if (result.response === 0) {
213-
data.confirmAction && data.confirmAction();
214-
} else {
215-
data.cancelAction && data.cancelAction();
216-
}
217-
});
218-
}
219-
220176
private createPageSubmenu(page: Page): MenuItemConstructorOptions[] {
221177
const view = page.view!;
222178
const wc = view.webContents;

src/service/NavbarService.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@ import MenuService, { ContextMenuType } from '@/service/MenuService';
77
import PageService from '@/service/PageService';
88
import PreferencesService from '@/service/PreferencesService';
99
import ViewService from '@/service/ViewService';
10-
import { clipboard, WebContentsView } from 'electron';
10+
import { clipboard, MouseInputEvent, WebContents, WebContentsView } from 'electron';
1111
import path from 'node:path';
1212

1313
class NavbarService {
1414
public readonly NAVBAR_HEIGHT = 40;
1515
public readonly NAVBAR_WEB_FOLDER = path.join(Path.WEB, 'navigation-bar');
16+
private static readonly FOCUS_CASCADE_HANDLER =
17+
(_: unknown, i: MouseInputEvent) => i.type === 'mouseUp' && ViewService.focus();
18+
private static readonly REMOVE_FOCUS_CASCADE = function (this: WebContents) {
19+
this.removeListener('before-mouse-event', NavbarService.FOCUS_CASCADE_HANDLER);
20+
};
1621

1722
constructor() {
1823
this.registerStateListeners();
@@ -32,6 +37,8 @@ class NavbarService {
3237
},
3338
});
3439
AppState.navbar = navbar;
40+
navbar.webContents.on('before-mouse-event', NavbarService.FOCUS_CASCADE_HANDLER);
41+
navbar.webContents.once('destroyed', NavbarService.REMOVE_FOCUS_CASCADE);
3542
navbar.webContents.loadFile(path.join(this.NAVBAR_WEB_FOLDER, 'index.html'));
3643
return navbar;
3744
}

src/service/PageService.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ class PageService {
4040
AppState.currentPage = page;
4141
this.setupCurrentPage();
4242

43+
if (!Storage.getSettings<boolean>(Settings.SHARE_BOUNDS)) {
44+
FrameService.getFrame()?.isMaximized() && FrameService.toggleMaximize();
45+
}
46+
4347
if (previousPage?.view && !previousPage.persist) {
44-
FrameService.getFrame()!.isMaximized() && FrameService.toggleMaximize();
4548
this.closePageView(previousPage);
4649
}
4750
}
@@ -206,7 +209,7 @@ class PageService {
206209
const view = page.view;
207210
page.view = undefined;
208211
const wc = view.webContents;
209-
!wc.isDestroyed() && wc.close();
212+
wc && !wc.isDestroyed() && wc.close();
210213
}
211214

212215
public recreateView(page = this.getCurrentPage()!): void {

0 commit comments

Comments
 (0)