electron-findbar 3.1.1 → 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +59 -8
- package/dist/index.html +1 -0
- package/dist/main.d.ts +150 -0
- package/dist/main.js +1 -0
- package/dist/preload.js +1 -0
- package/dist/remote.d.ts +65 -0
- package/dist/remote.js +1 -0
- package/package.json +36 -7
- package/index.js +0 -484
- package/remote.js +0 -44
- package/web/app.css +0 -141
- package/web/findbar.html +0 -43
- package/web/preload.js +0 -82
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -19,6 +19,23 @@ Install the `electron-findbar` package via [npm](https://www.npmjs.com/package/e
|
|
|
19
19
|
npm install electron-findbar
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Development & Build
|
|
23
|
+
|
|
24
|
+
This project is written in **TypeScript** and uses **Webpack** for bundling and minification.
|
|
25
|
+
|
|
26
|
+
### Prerequisites
|
|
27
|
+
|
|
28
|
+
- Node.js >= 12.0.0
|
|
29
|
+
- npm
|
|
30
|
+
|
|
31
|
+
### Setup
|
|
32
|
+
|
|
33
|
+
Install dependencies:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install
|
|
37
|
+
```
|
|
38
|
+
|
|
22
39
|
## Overview
|
|
23
40
|
|
|
24
41
|
The `electron-findbar` package creates a `BrowserWindow`-based component designed to emulate the Chrome findbar layout, leveraging the `webContents.findInPage` method to navigate through matches. Inter-process communication (IPC) is used for interaction between the `main` and `renderer` processes.
|
|
@@ -62,7 +79,7 @@ It is also possible to create a findbar providing only the web contents. The Bas
|
|
|
62
79
|
const findbar = Findbar.from(webContents)
|
|
63
80
|
```
|
|
64
81
|
|
|
65
|
-
**Note:** The findbar is ALWAYS linked to the webContents, not the window. The parent is only the window to connect the events and stay on top. If the `.from(webContents)` is used to retrieve an existing findbar previously created with a parent, the findbar will stay connected to the parent.
|
|
82
|
+
**Note:** The findbar is ALWAYS linked to the webContents, not the window. The parent is only the window to connect the events and stay on top. If the `.from(webContents)` is used to retrieve an existing findbar previously created with a parent, the findbar will stay connected to the parent. If a different parent is used, the parent window is updated automatically.
|
|
66
83
|
|
|
67
84
|
#### Retrieve if exists
|
|
68
85
|
|
|
@@ -241,7 +258,7 @@ Once open, the Findbar appears by default in the top-right corner of the parent
|
|
|
241
258
|
```js
|
|
242
259
|
/**
|
|
243
260
|
* Get the last state of the findbar.
|
|
244
|
-
* @returns {{ text: string, matchCase: boolean, movable: boolean }} Last state of the findbar.
|
|
261
|
+
* @returns {{ text: string, matchCase: boolean, movable: boolean, theme: 'light' | 'dark' | 'system' }} Last state of the findbar.
|
|
245
262
|
*/
|
|
246
263
|
getLastState()
|
|
247
264
|
|
|
@@ -292,6 +309,36 @@ isFocused()
|
|
|
292
309
|
* @returns {boolean} True if the findbar is visible, otherwise false.
|
|
293
310
|
*/
|
|
294
311
|
isVisible()
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get the current theme of this findbar instance.
|
|
315
|
+
* @returns {'light' | 'dark' | 'system'} The current theme setting.
|
|
316
|
+
*/
|
|
317
|
+
getTheme()
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Update the theme of the findbar. Only affects the current instance.
|
|
321
|
+
* @param {'light' | 'dark' | 'system'} theme - The theme to set. If not provided, uses the default theme.
|
|
322
|
+
*/
|
|
323
|
+
updateTheme(theme)
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Set whether the findbar will follow the parent window visibility events. Default is true.
|
|
327
|
+
* If false, the findbar will not hide with the parent window automatically.
|
|
328
|
+
*/
|
|
329
|
+
followVisibilityEvents(shouldFollow: boolean = true)
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get the default theme for new findbar instances.
|
|
333
|
+
* @returns {'light' | 'dark' | 'system'} The default theme setting.
|
|
334
|
+
*/
|
|
335
|
+
static getDefaultTheme()
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Set the default theme for new findbar instances.
|
|
339
|
+
* @param {'light' | 'dark' | 'system'} theme - The theme to set as default.
|
|
340
|
+
*/
|
|
341
|
+
static setDefaultTheme(theme)
|
|
295
342
|
```
|
|
296
343
|
|
|
297
344
|
## IPC Events
|
|
@@ -305,13 +352,17 @@ If the `contextIsolation` is enabled, the `electron-findbar/remote` will not be
|
|
|
305
352
|
```js
|
|
306
353
|
const $remote = (ipc => ({
|
|
307
354
|
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
|
|
308
|
-
inputChange: (
|
|
309
|
-
matchCase: (value) => { ipc.send('electron-findbar/match-case', value) },
|
|
355
|
+
inputChange: (value: string) => { ipc.send('electron-findbar/input-change', value, true) },
|
|
356
|
+
matchCase: (value: boolean) => { ipc.send('electron-findbar/match-case', value, true) },
|
|
310
357
|
previous: () => { ipc.send('electron-findbar/previous') },
|
|
311
358
|
next: () => { ipc.send('electron-findbar/next') },
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
359
|
+
close: () => { ipc.send('electron-findbar/close') },
|
|
360
|
+
onMatchesChange: (listener: Function) => { ipc.on('electron-findbar/matches', listener) },
|
|
361
|
+
onInputFocus: (listener: Function) => { ipc.on('electron-findbar/input-focus', listener) },
|
|
362
|
+
onTextChange: (listener: Function) => { ipc.on('electron-findbar/text-change', listener) },
|
|
363
|
+
onMatchCaseChange: (listener: Function) => { ipc.on('electron-findbar/match-case-change', listener) },
|
|
364
|
+
onForceTheme: (listener: Function) => { ipc.on('electron-findbar/force-theme', listener) },
|
|
365
|
+
})) (require('electron').ipcRenderer);
|
|
315
366
|
|
|
316
367
|
$remote.open()
|
|
317
368
|
$remote.inputChange('findIt')
|
|
@@ -338,7 +389,7 @@ The `updateParentWindow` method allows you to change the parent window while pre
|
|
|
338
389
|
|
|
339
390
|
```javascript
|
|
340
391
|
// Create a findbar for the initial window
|
|
341
|
-
const findbar = Findbar.from(oldWindow, webContents)
|
|
392
|
+
const findbar = Findbar.from([oldWindow, ]webContents)
|
|
342
393
|
|
|
343
394
|
// Later, when you need to change the parent:
|
|
344
395
|
findbar.updateParentWindow(newWindow)
|
package/dist/index.html
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src 'self' 'sha256-xegrQ38nadcaxMgXTqofPqPhLTW4XM5uk6u7cigFZb4='; default-src 'self'; connect-src 'none'; frame-src 'none'; object-src 'none'; font-src 'self'; img-src 'self'"><title>Find in page</title><style>*,:after,:before{box-sizing:border-box}body{margin:0;padding:0;font-weight:400;height:100vh;--bg-color:#fff;--border:#ddd;--color:#626262;--input-color:#111;--btn-hover-color:#ccc;--btn-active-color:#bbb;--font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-size:.75rem;--spacing:.5rem}body.dark{--bg-color:#1f1f1f;--border:#3f3f3f;--color:#a9a9aa;--input-color:#eee;--btn-hover-color:#333;--btn-active-color:#444}@media (prefers-color-scheme:dark){body:not(.light){--bg-color:#1f1f1f;--border:#3f3f3f;--color:#a9a9aa;--input-color:#eee;--btn-hover-color:#333;--btn-active-color:#444}}#match-case{fill:var(--color);user-select:none}#match-case>svg{width:20px}#close,#next,#previous{fill:none;stroke:var(--color);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}nav{display:flex;align-items:center;width:100%;height:100%;padding:var(--spacing);background-color:var(--bg-color);color:var(--color)}.linux nav{border-radius:10px;border:1px solid var(--border)}.btn-group,nav{gap:var(--spacing)}input,span{font-family:var(--font-family);font-size:var(--font-size)}span{color:var(--input-color);user-select:none}input{width:100%;background-color:transparent;color:var(--input-color);font-weight:500;border:none;outline:0}.divider{width:2px;height:100%;background-color:var(--btn-hover-color)}.btn-group{display:flex}button{background-color:transparent;border:none;border-radius:50%;cursor:default;width:26px;height:26px;padding:3px;text-align:center;transition:.2s linear all}button:hover{background-color:var(--btn-hover-color)}button:active{background-color:var(--btn-active-color)}button:focus{outline:0}button.disabled{opacity:.4}#next.disabled,#previous.disabled{background-color:transparent!important}.movable nav{-webkit-app-region:drag;app-region:drag}button,input{-webkit-app-region:no-drag;app-region:no-drag}</style></head><body class="movable"><nav><input id="input" spellcheck="false"> <span id="matches"></span> <button id="match-case" class="disabled" title="Match case"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" stroke="none"><text x="4" y="15" font-size="14" font-family="monospace" font-weight="bold">A</text><text x="12" y="19" font-size="14" font-family="monospace">a</text></svg></button><div class="divider"></div><div class="btn-group"><button id="previous" class="disabled" title="Previous"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M7 15L12 10L17 15"/></svg></button> <button id="next" class="disabled" title="Next"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10L12 15L17 10"/></svg></button> <button id="close" title="Close"><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M7 17.5L12 12.5L17 17.5"/><path d="M7 7.5L12 12.5L17 7.5"/></svg></button></div></nav></body></html>
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { BaseWindow, BrowserWindow, WebContents, BrowserWindowConstructorOptions, Rectangle } from 'electron';
|
|
2
|
+
interface LastState {
|
|
3
|
+
text: string;
|
|
4
|
+
matchCase: boolean;
|
|
5
|
+
movable: boolean;
|
|
6
|
+
theme: 'light' | 'dark' | 'system';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Chrome-like findbar for Electron applications.
|
|
10
|
+
*/
|
|
11
|
+
declare class Findbar {
|
|
12
|
+
private static defaultTheme;
|
|
13
|
+
private static defaultWindowHandler?;
|
|
14
|
+
private static defaultBoundsHandler;
|
|
15
|
+
private parent?;
|
|
16
|
+
private window?;
|
|
17
|
+
private findableContents;
|
|
18
|
+
private followVisibilityEventsFlag;
|
|
19
|
+
private matches;
|
|
20
|
+
private windowHandler;
|
|
21
|
+
private boundsHandler;
|
|
22
|
+
private customOptions?;
|
|
23
|
+
private lastText;
|
|
24
|
+
private theme;
|
|
25
|
+
private matchCaseFlag;
|
|
26
|
+
private isMovableFlag;
|
|
27
|
+
private fixMove?;
|
|
28
|
+
/**
|
|
29
|
+
* Configure the findbar and link to the web contents.
|
|
30
|
+
*
|
|
31
|
+
* @param parent - Parent window or web contents
|
|
32
|
+
* @param webContents - Custom findable web contents (optional)
|
|
33
|
+
*/
|
|
34
|
+
constructor(parent: BaseWindow | BrowserWindow | WebContents, webContents?: WebContents);
|
|
35
|
+
/**
|
|
36
|
+
* Open the findbar. If the findbar is already opened, focus the input text.
|
|
37
|
+
*/
|
|
38
|
+
open(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Close the findbar.
|
|
41
|
+
*/
|
|
42
|
+
close(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Detach the findbar from the web contents and close it if opened.
|
|
45
|
+
*/
|
|
46
|
+
detach(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Update the parent window of the findbar.
|
|
49
|
+
*/
|
|
50
|
+
updateParentWindow(newParent?: BaseWindow): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get the last state of the findbar.
|
|
53
|
+
*/
|
|
54
|
+
getLastState(): LastState;
|
|
55
|
+
/**
|
|
56
|
+
* Starts a request to find all matches for the text in the page.
|
|
57
|
+
*/
|
|
58
|
+
startFind(text: string, skipRendererEvent?: boolean): void;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the search should be case-sensitive.
|
|
61
|
+
*/
|
|
62
|
+
matchCase(status: boolean, skipRendererEvent?: boolean): void;
|
|
63
|
+
/**
|
|
64
|
+
* Select previous match if any.
|
|
65
|
+
*/
|
|
66
|
+
findPrevious(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Select next match if any.
|
|
69
|
+
*/
|
|
70
|
+
findNext(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Stops the find request and clears selection.
|
|
73
|
+
*/
|
|
74
|
+
stopFind(): void;
|
|
75
|
+
/**
|
|
76
|
+
* Whether the findbar is opened.
|
|
77
|
+
*/
|
|
78
|
+
isOpen(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Whether the findbar is focused.
|
|
81
|
+
*/
|
|
82
|
+
isFocused(): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Whether the findbar is visible to the user.
|
|
85
|
+
*/
|
|
86
|
+
isVisible(): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Set custom options for the findbar window.
|
|
89
|
+
*/
|
|
90
|
+
setWindowOptions(customOptions: BrowserWindowConstructorOptions): void;
|
|
91
|
+
/**
|
|
92
|
+
* Set a window handler for the findbar window.
|
|
93
|
+
*/
|
|
94
|
+
setWindowHandler(windowHandler: (findbarWindow: BrowserWindow) => void): void;
|
|
95
|
+
/**
|
|
96
|
+
* Set a bounds handler to calculate findbar bounds.
|
|
97
|
+
*/
|
|
98
|
+
setBoundsHandler(boundsHandler: (parentBounds: Rectangle, findbarBounds: Rectangle) => Rectangle): void;
|
|
99
|
+
/**
|
|
100
|
+
* Set whether the findbar will follow the parent window "show" and "hide" events. Default is true.
|
|
101
|
+
* If false, the findbar will not hide automatically with the parent window.
|
|
102
|
+
*/
|
|
103
|
+
followVisibilityEvents(shouldFollow: boolean): void;
|
|
104
|
+
/**
|
|
105
|
+
* Get the current theme of this findbar instance.
|
|
106
|
+
* @returns The current theme setting ('light', 'dark', or 'system').
|
|
107
|
+
*/
|
|
108
|
+
getTheme(): 'light' | 'dark' | 'system';
|
|
109
|
+
/**
|
|
110
|
+
* Update the theme of the findbar. Only affects the current instance.
|
|
111
|
+
* @param theme - The theme to set. If not provided, uses the default theme.
|
|
112
|
+
*/
|
|
113
|
+
updateTheme(theme?: 'light' | 'dark' | 'system'): void;
|
|
114
|
+
/**
|
|
115
|
+
* Get the default theme.
|
|
116
|
+
*/
|
|
117
|
+
static getDefaultTheme(): 'light' | 'dark' | 'system';
|
|
118
|
+
/**
|
|
119
|
+
* Set the default theme for new findbar instances.
|
|
120
|
+
*/
|
|
121
|
+
static setDefaultTheme(theme: 'light' | 'dark' | 'system'): void;
|
|
122
|
+
/**
|
|
123
|
+
* Set the default window handler for new findbar instances.
|
|
124
|
+
*/
|
|
125
|
+
static setDefaultWindowHandler(windowHandler: (findbarWindow: BrowserWindow) => void): void;
|
|
126
|
+
/**
|
|
127
|
+
* Set the default bounds handler for new findbar instances.
|
|
128
|
+
*/
|
|
129
|
+
static setDefaultBoundsHandler(boundsHandler: (parentBounds: Rectangle, findbarBounds: Rectangle) => Rectangle): void;
|
|
130
|
+
private registerKeyboardShortcuts;
|
|
131
|
+
private removeParent;
|
|
132
|
+
private findInContent;
|
|
133
|
+
private stopFindInContent;
|
|
134
|
+
private registerListeners;
|
|
135
|
+
private sendMatchesCount;
|
|
136
|
+
private focusWindowAndHighlightInput;
|
|
137
|
+
private static retrieveWebContents;
|
|
138
|
+
private static getBaseWindowFromWebContents;
|
|
139
|
+
private static setDefaultPosition;
|
|
140
|
+
private static mergeStandardOptions;
|
|
141
|
+
/**
|
|
142
|
+
* Get the findbar instance for a given BrowserWindow or WebContents.
|
|
143
|
+
*/
|
|
144
|
+
static from(windowOrWebContents: BaseWindow | BrowserWindow | WebContents, customWebContents?: WebContents): Findbar;
|
|
145
|
+
/**
|
|
146
|
+
* Get the findbar instance for a given BrowserWindow or WebContents if it exists.
|
|
147
|
+
*/
|
|
148
|
+
static fromIfExists(windowOrWebContents: BaseWindow | BrowserWindow | WebContents): Findbar | undefined;
|
|
149
|
+
}
|
|
150
|
+
export = Findbar;
|
package/dist/main.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var e={288(e){e.exports=require("electron")},927(e,t,i){const n=i(288);class s{constructor(e,t){var i;if(this.followVisibilityEventsFlag=!0,this.matches={active:0,total:0},this.windowHandler=s.defaultWindowHandler,this.boundsHandler=s.defaultBoundsHandler,this.lastText="",this.theme=s.defaultTheme,this.matchCaseFlag=!1,this.isMovableFlag=!1,o(e)?(this.findableContents=e,e=s.getBaseWindowFromWebContents(this.findableContents)):this.findableContents=null!==(i=t)&&void 0!==i?i:s.retrieveWebContents(e),!this.findableContents)throw new Error("There are no searchable web contents.");this.findableContents._findbar=this,this.findableContents.once("destroyed",()=>{this.detach()}),this.updateParentWindow(e)}open(){var e,t;if(this.window)return void this.focusWindowAndHighlightInput();this.parent||this.updateParentWindow();const i=s.mergeStandardOptions(this.customOptions,this.parent);this.isMovableFlag=null!==(e=i.movable)&&void 0!==e&&e,this.window=new n.BrowserWindow(i),this.window.webContents._findbar=this,this.registerListeners(),null===(t=this.windowHandler)||void 0===t||t.call(this,this.window),this.window.loadFile(`${__dirname}/index.html`)}close(){this.window&&!this.window.isDestroyed()&&this.window.close()}detach(){this.close(),this.findableContents._findbar=void 0,this.window&&(this.window.webContents._findbar=void 0)}updateParentWindow(e){var t;this.parent!==e&&(this.close(),this.parent=null!==(t=e)&&void 0!==t?t:s.getBaseWindowFromWebContents(this.findableContents),this.parent&&!this.parent.isDestroyed()&&this.parent.once("closed",()=>{this.removeParent()}))}getLastState(){return{text:this.lastText,matchCase:this.matchCaseFlag,movable:this.isMovableFlag,theme:this.theme}}startFind(e,t){var i;t||null===(i=this.window)||void 0===i||i.webContents.send("electron-findbar/text-change",e),(this.lastText=e)?this.isOpen()&&this.findInContent({findNext:!0}):this.stopFind()}matchCase(e,t){var i;this.matchCaseFlag!==e&&(this.matchCaseFlag=e,t||null===(i=this.window)||void 0===i||i.webContents.send("electron-findbar/match-case-change",this.matchCaseFlag),this.stopFindInContent(),this.startFind(this.lastText,t))}findPrevious(){this.matches.total<2||(1===this.matches.active&&(this.fixMove=!1),this.isOpen()&&this.findInContent({forward:!1}))}findNext(){this.matches.total<2||(this.matches.active===this.matches.total&&(this.fixMove=!0),this.isOpen()&&this.findInContent({forward:!0}))}stopFind(){this.isOpen()&&this.sendMatchesCount(0,0),this.findableContents.isDestroyed()||this.stopFindInContent()}isOpen(){return!!this.window}isFocused(){var e;return!!(null===(e=this.window)||void 0===e?void 0:e.isFocused())}isVisible(){var e;return!!(null===(e=this.window)||void 0===e?void 0:e.isVisible())}setWindowOptions(e){this.customOptions=e}setWindowHandler(e){this.windowHandler=e}setBoundsHandler(e){this.boundsHandler=e}followVisibilityEvents(e){this.followVisibilityEventsFlag=e}getTheme(){return this.theme}updateTheme(e=s.defaultTheme){this.theme=e,this.window&&!this.window.isDestroyed()&&this.window.webContents.send("electron-findbar/force-theme",e)}static getDefaultTheme(){return s.defaultTheme}static setDefaultTheme(e){s.defaultTheme=e}static setDefaultWindowHandler(e){s.defaultWindowHandler=e}static setDefaultBoundsHandler(e){s.defaultBoundsHandler=e}registerKeyboardShortcuts(e,t){if(t.meta||t.control||t.alt)return;const i=t.key.toLowerCase();if(!t.shift)return"enter"===i?(this.findNext(),void e.preventDefault()):void("escape"===i&&this.isOpen()&&(this.close(),e.preventDefault()));"enter"===i&&(this.findPrevious(),e.preventDefault())}removeParent(){this.close(),this.parent=void 0}findInContent(e){e.matchCase=this.matchCaseFlag,this.findableContents.findInPage(this.lastText,e)}stopFindInContent(){this.findableContents.stopFindInPage("clearSelection")}registerListeners(){const e=()=>{this.window.isVisible()||this.window.show()},t=()=>{this.window.isVisible()&&this.window.hide()},i=()=>{const e=this.window.getBounds(),t=this.boundsHandler(this.parent.getBounds(),e);t.width||(t.width=e.width),t.height||(t.height=e.height),this.window.setBounds(t,!1)};this.parent&&!this.parent.isDestroyed()&&(i(),this.followVisibilityEventsFlag&&(this.parent.prependListener("show",e),this.parent.prependListener("hide",t)),this.parent.prependListener("resize",i),this.parent.prependListener("move",i)),this.window.once("closed",()=>{this.parent&&!this.parent.isDestroyed()&&(this.followVisibilityEventsFlag&&(this.parent.off("show",e),this.parent.off("hide",t)),this.parent.off("resize",i),this.parent.off("move",i)),this.window=void 0,this.stopFind()}),this.window.prependOnceListener("ready-to-show",()=>{this.window.show()}),this.window.webContents.prependListener("before-input-event",(e,t)=>{this.registerKeyboardShortcuts(e,t)}),this.findableContents.prependOnceListener("destroyed",()=>{this.close()}),this.findableContents.prependListener("found-in-page",(e,t)=>{this.sendMatchesCount(t.activeMatchOrdinal,t.matches)})}sendMatchesCount(e,t){void 0!==this.fixMove&&(this.fixMove?this.findNext():this.findPrevious(),this.fixMove=void 0),this.matches.active=e,this.matches.total=t,this.window.webContents.send("electron-findbar/matches",this.matches)}focusWindowAndHighlightInput(){this.window.focus(),this.window.webContents.send("electron-findbar/input-focus")}static retrieveWebContents(e){var t,i,n;return null!==(t=e.webContents)&&void 0!==t?t:null===(n=null===(i=e.contentView)||void 0===i?void 0:i.children[0])||void 0===n?void 0:n.webContents}static getBaseWindowFromWebContents(e){return n.BaseWindow.getAllWindows().find(t=>{var i;return t.webContents===e||(null===(i=t.contentView)||void 0===i?void 0:i.children.some(t=>t.webContents===e))})}static setDefaultPosition(e,t){return{x:e.x+e.width-t.width-20,y:e.y-(t.height/4|0),width:t.width,height:t.height}}static mergeStandardOptions(e,t){var i,n,s,o,r,a;return e||(e={}),e.width=null!==(i=e.width)&&void 0!==i?i:372,e.height=null!==(n=e.height)&&void 0!==n?n:52,e.resizable=null!==(s=e.resizable)&&void 0!==s&&s,e.movable=null!==(o=e.movable)&&void 0!==o&&o,e.acceptFirstMouse=null===(r=e.acceptFirstMouse)||void 0===r||r,e.parent=t,e.show=!1,e.frame=!1,e.roundedCorners=!0,e.transparent="linux"===process.platform,e.maximizable=!1,e.minimizable=!1,e.skipTaskbar=!0,e.fullscreenable=!1,e.autoHideMenuBar=!0,e.webPreferences||(e.webPreferences={}),e.webPreferences.nodeIntegration=!1,e.webPreferences.contextIsolation=!0,e.webPreferences.preload=null!==(a=e.webPreferences.preload)&&void 0!==a?a:`${__dirname}/preload.js`,e}static from(e,t){const i=o(e)?e:null!=t?t:s.retrieveWebContents(e);if(!i)throw new Error("[Findbar] There are no searchable web contents.");return i._findbar||new s(e,t)}static fromIfExists(e){const t=o(e)?e:s.retrieveWebContents(e);if(!t)throw new Error("[Findbar] There are no searchable web contents.");return t._findbar}}s.defaultTheme="system",s.defaultBoundsHandler=s.setDefaultPosition;const o=e=>e&&"function"==typeof e.findInPage&&"function"==typeof e.stopFindInPage;n.ipcMain.handle("electron-findbar/last-state",e=>{var t;return null===(t=s.fromIfExists(e.sender))||void 0===t?void 0:t.getLastState()}),n.ipcMain.on("electron-findbar/input-change",(e,t,i)=>{var n;return null===(n=s.fromIfExists(e.sender))||void 0===n?void 0:n.startFind(t,i)}),n.ipcMain.on("electron-findbar/match-case",(e,t,i)=>{var n;return null===(n=s.fromIfExists(e.sender))||void 0===n?void 0:n.matchCase(t,i)}),n.ipcMain.on("electron-findbar/previous",e=>{var t;return null===(t=s.fromIfExists(e.sender))||void 0===t?void 0:t.findPrevious()}),n.ipcMain.on("electron-findbar/next",e=>{var t;return null===(t=s.fromIfExists(e.sender))||void 0===t?void 0:t.findNext()}),n.ipcMain.on("electron-findbar/open",e=>s.from(e.sender).open()),n.ipcMain.on("electron-findbar/close",e=>{const t=s.fromIfExists(e.sender);t&&(t.stopFind(),t.close())}),e.exports=s}},t={},i=function i(n){var s=t[n];if(void 0!==s)return s.exports;var o=t[n]={exports:{}};return e[n](o,o.exports,i),o.exports}(927);module.exports=i})();
|
package/dist/preload.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var e={213(e,n,t){var o=this&&this.__awaiter||function(e,n,t,o){return new(t||(t=Promise))(function(c,a){function s(e){try{r(o.next(e))}catch(e){a(e)}}function i(e){try{r(o.throw(e))}catch(e){a(e)}}function r(e){var n;e.done?c(e.value):(n=e.value,n instanceof t?n:new t(function(e){e(n)})).then(s,i)}r((o=o.apply(e,n||[])).next())})};const c=(a=t(288).ipcRenderer,{getLastState:()=>o(void 0,void 0,void 0,function*(){return a.invoke("electron-findbar/last-state")}),inputChange:e=>{a.send("electron-findbar/input-change",e,!0)},matchCase:e=>{a.send("electron-findbar/match-case",e,!0)},previous:()=>{a.send("electron-findbar/previous")},next:()=>{a.send("electron-findbar/next")},close:()=>{a.send("electron-findbar/close")},onMatchesChange:e=>{a.on("electron-findbar/matches",e)},onInputFocus:e=>{a.on("electron-findbar/input-focus",e)},onTextChange:e=>{a.on("electron-findbar/text-change",e)},onMatchCaseChange:e=>{a.on("electron-findbar/match-case-change",e)},onForceTheme:e=>{a.on("electron-findbar/force-theme",e)}});var a;let s=!0,i=!1;function r(e){const n=e.target;s=!1,c.inputChange(n.value)}function d(e){s&&i&&(s=!1,e?c.next():c.previous())}function l(e,n){e.classList[n?"remove":"add"]("disabled")}function u(e){document.body.classList.remove("light","dark"),"system"!==e&&document.body.classList.add(e)}document.addEventListener("DOMContentLoaded",()=>o(void 0,void 0,void 0,function*(){const e=document.getElementById("input"),n=document.getElementById("match-case"),t=document.getElementById("previous"),o=document.getElementById("next"),a=document.getElementById("close"),v=document.getElementById("matches"),f=[t,o].filter(e=>null!==e);if(!(e&&n&&t&&o&&a&&v))return void console.error("Required elements not found in the DOM");n.addEventListener("click",()=>function(e){const n=function(e){return e.classList.contains("disabled")}(e);l(e,n),c.matchCase(n)}(n)),t.addEventListener("click",()=>d(!1)),o.addEventListener("click",()=>d(!0)),a.addEventListener("click",()=>c.close()),e.addEventListener("input",r),c.onTextChange((n,t)=>{e.value=t}),c.onInputFocus(()=>{e.setSelectionRange(0,e.value.length),e.focus()}),c.onMatchCaseChange((e,t)=>l(n,t)),c.onMatchesChange((n,t)=>{s=!0,v.innerText=e.value?t.active+"/"+t.total:"";for(const e of f)l(e,i=t.total>1)}),c.onForceTheme((e,n)=>{u(n)});const h=yield c.getLastState();e.value=h.text||"",h.movable||document.body.classList.remove("movable"),"linux"===process.platform&&document.body.classList.add("linux"),u(h.theme),l(n,h.matchCase),c.inputChange(e.value),e.setSelectionRange(0,e.value.length),e.focus()}))},288(e){e.exports=require("electron")}},n={};!function t(o){var c=n[o];if(void 0!==c)return c.exports;var a=n[o]={exports:{}};return e[o].call(a.exports,a,a.exports,t),a.exports}(213)})();
|
package/dist/remote.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { IpcRendererEvent } from 'electron';
|
|
2
|
+
/**
|
|
3
|
+
* Remote IPC events to control the findbar through the renderer.
|
|
4
|
+
*/
|
|
5
|
+
interface Remote {
|
|
6
|
+
/**
|
|
7
|
+
* Get last queried text and the "match case" status.
|
|
8
|
+
*/
|
|
9
|
+
getLastState(): Promise<{
|
|
10
|
+
text: string;
|
|
11
|
+
matchCase: boolean;
|
|
12
|
+
movable: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Change the input value and find it.
|
|
16
|
+
* @param text - The text to search for
|
|
17
|
+
*/
|
|
18
|
+
inputChange(text: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Toggle case sensitive search
|
|
21
|
+
* @param value - Whether to match case or not
|
|
22
|
+
*/
|
|
23
|
+
matchCase(value: boolean): void;
|
|
24
|
+
/**
|
|
25
|
+
* Navigate to the previous match
|
|
26
|
+
*/
|
|
27
|
+
previous(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Navigate to the next match
|
|
30
|
+
*/
|
|
31
|
+
next(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Open the findbar
|
|
34
|
+
*/
|
|
35
|
+
open(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Close the findbar
|
|
38
|
+
*/
|
|
39
|
+
close(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Listen for matches change event
|
|
42
|
+
*/
|
|
43
|
+
onMatchesChange(listener: (event: IpcRendererEvent, matches: {
|
|
44
|
+
active: number;
|
|
45
|
+
total: number;
|
|
46
|
+
}) => void): void;
|
|
47
|
+
/**
|
|
48
|
+
* Listen for input focus event
|
|
49
|
+
*/
|
|
50
|
+
onInputFocus(listener: (event: IpcRendererEvent) => void): void;
|
|
51
|
+
/**
|
|
52
|
+
* Listen for text change event
|
|
53
|
+
*/
|
|
54
|
+
onTextChange(listener: (event: IpcRendererEvent, text: string) => void): void;
|
|
55
|
+
/**
|
|
56
|
+
* Listen for match case change event
|
|
57
|
+
*/
|
|
58
|
+
onMatchCaseChange(listener: (event: IpcRendererEvent, status: boolean) => void): void;
|
|
59
|
+
/**
|
|
60
|
+
* Listen for force theme change event
|
|
61
|
+
*/
|
|
62
|
+
onForceTheme(listener: (event: IpcRendererEvent, theme: 'light' | 'dark' | 'system') => void): void;
|
|
63
|
+
}
|
|
64
|
+
declare const Remote: Remote;
|
|
65
|
+
export = Remote;
|
package/dist/remote.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var e={288(e){e.exports=require("electron")},684(e,n,r){var t=this&&this.__awaiter||function(e,n,r,t){return new(r||(r=Promise))(function(o,c){function i(e){try{d(t.next(e))}catch(e){c(e)}}function a(e){try{d(t.throw(e))}catch(e){c(e)}}function d(e){var n;e.done?o(e.value):(n=e.value,n instanceof r?n:new r(function(e){e(n)})).then(i,a)}d((t=t.apply(e,n||[])).next())})};const o=r(288),c={getLastState:()=>t(void 0,void 0,void 0,function*(){return o.ipcRenderer.invoke("electron-findbar/last-state")}),inputChange:e=>{o.ipcRenderer.send("electron-findbar/input-change",e)},matchCase:e=>{o.ipcRenderer.send("electron-findbar/match-case",e)},previous:()=>{o.ipcRenderer.send("electron-findbar/previous")},next:()=>{o.ipcRenderer.send("electron-findbar/next")},open:()=>{o.ipcRenderer.send("electron-findbar/open")},close:()=>{o.ipcRenderer.send("electron-findbar/close")},onMatchesChange:e=>{o.ipcRenderer.on("electron-findbar/matches",e)},onInputFocus:e=>{o.ipcRenderer.on("electron-findbar/input-focus",e)},onTextChange:e=>{o.ipcRenderer.on("electron-findbar/text-change",e)},onMatchCaseChange:e=>{o.ipcRenderer.on("electron-findbar/match-case-change",e)},onForceTheme:e=>{o.ipcRenderer.on("electron-findbar/force-theme",e)}};e.exports=c}},n={},r=function r(t){var o=n[t];if(void 0!==o)return o.exports;var c=n[t]={exports:{}};return e[t].call(c.exports,c,c.exports,r),c.exports}(684);module.exports=r})();
|
package/package.json
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-findbar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "Chrome-like findbar for your Electron app.",
|
|
5
|
-
"
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/main.d.ts",
|
|
8
|
+
"default": "./dist/main.js"
|
|
9
|
+
},
|
|
10
|
+
"./remote": {
|
|
11
|
+
"types": "./dist/remote.d.ts",
|
|
12
|
+
"default": "./dist/remote.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
6
15
|
"files": [
|
|
7
|
-
"
|
|
8
|
-
"remote.js",
|
|
16
|
+
"dist/**/*",
|
|
9
17
|
"LICENSE",
|
|
10
|
-
"
|
|
18
|
+
"!dist/preload.d.ts"
|
|
11
19
|
],
|
|
12
20
|
"scripts": {
|
|
13
|
-
"
|
|
21
|
+
"clean": "rm -rf dist",
|
|
22
|
+
"build": "npm run clean && webpack",
|
|
23
|
+
"build:dev": "npm run clean && NODE_ENV=development webpack",
|
|
24
|
+
"dev": "webpack --mode development --watch",
|
|
25
|
+
"sample": "electron test/sample.js",
|
|
26
|
+
"publish": "npm run build && npm publish --access public"
|
|
14
27
|
},
|
|
15
28
|
"repository": {
|
|
16
29
|
"type": "git",
|
|
17
30
|
"url": "git+https://github.com/ECRomaneli/electron-findbar.git"
|
|
18
31
|
},
|
|
19
32
|
"keywords": [
|
|
33
|
+
"find",
|
|
20
34
|
"findbar",
|
|
21
35
|
"find-bar",
|
|
22
36
|
"find bar",
|
|
37
|
+
"electron-find",
|
|
23
38
|
"electron-findbar",
|
|
24
39
|
"electron-find-bar",
|
|
25
40
|
"electron",
|
|
41
|
+
"chrome-find",
|
|
26
42
|
"chrome-findbar",
|
|
27
43
|
"chrome-find-bar",
|
|
28
44
|
"search",
|
|
@@ -38,6 +54,19 @@
|
|
|
38
54
|
"node": ">=12.0.0"
|
|
39
55
|
},
|
|
40
56
|
"devDependencies": {
|
|
41
|
-
"electron": "
|
|
57
|
+
"@types/electron": "^1.6.12",
|
|
58
|
+
"@types/node": "^25.1.0",
|
|
59
|
+
"clean-css": "^5.3.3",
|
|
60
|
+
"css-loader": "^7.1.3",
|
|
61
|
+
"cssnano": "^7.1.2",
|
|
62
|
+
"electron": ">=12.0.0",
|
|
63
|
+
"html-webpack-plugin": "^5.6.6",
|
|
64
|
+
"mini-css-extract-plugin": "^2.10.0",
|
|
65
|
+
"postcss-loader": "^8.2.0",
|
|
66
|
+
"style-loader": "^4.0.0",
|
|
67
|
+
"ts-loader": "^9.5.4",
|
|
68
|
+
"typescript": "^5.9.3",
|
|
69
|
+
"webpack": "^5.104.1",
|
|
70
|
+
"webpack-cli": "^6.0.1"
|
|
42
71
|
}
|
|
43
72
|
}
|
package/index.js
DELETED
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
const { BaseWindow, BrowserWindow, WebContents, BrowserWindowConstructorOptions, Rectangle } = require('electron')
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Chrome-like findbar for Electron applications.
|
|
5
|
-
*/
|
|
6
|
-
class Findbar {
|
|
7
|
-
/** @type {BaseWindow} */
|
|
8
|
-
#parent
|
|
9
|
-
|
|
10
|
-
/** @type {BrowserWindow} */
|
|
11
|
-
#window
|
|
12
|
-
|
|
13
|
-
/** @type {WebContents} */
|
|
14
|
-
#findableContents
|
|
15
|
-
|
|
16
|
-
/** @type { { active: number, total: number } } */
|
|
17
|
-
#matches = { active: 0, total: 0 }
|
|
18
|
-
|
|
19
|
-
/** @type {(findbarWindow: BrowserWindow) => void} */
|
|
20
|
-
#windowHandler
|
|
21
|
-
|
|
22
|
-
/** @type {{parentBounds: Rectangle, findbarBounds: Rectangle} => Rectangle} */
|
|
23
|
-
#boundsHandler = Findbar.#setDefaultPosition
|
|
24
|
-
|
|
25
|
-
/** @type {BrowserWindowConstructorOptions} */
|
|
26
|
-
#customOptions
|
|
27
|
-
|
|
28
|
-
/** @type {string} */
|
|
29
|
-
#lastText = ''
|
|
30
|
-
|
|
31
|
-
/** @type {boolean} */
|
|
32
|
-
#matchCase = false
|
|
33
|
-
|
|
34
|
-
/** @type {boolean} */
|
|
35
|
-
#isMovable = false
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Workaround to fix "findInPage" bug - double-click to loop
|
|
39
|
-
* @type {boolean | null}
|
|
40
|
-
*/
|
|
41
|
-
#fixMove = null
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Configure the findbar and link to the web contents.
|
|
45
|
-
*
|
|
46
|
-
* @overload
|
|
47
|
-
* @param {WebContents} webContents Findable web contents. The parent window will be defined by using BaseWindow.getAllWindows() and
|
|
48
|
-
* matching the webContents with the webContents of the window or its contentView children.
|
|
49
|
-
* @returns {Findbar} The findbar instance if it exists.
|
|
50
|
-
* @throws {Error} If no webContents is provided.
|
|
51
|
-
*
|
|
52
|
-
* @overload
|
|
53
|
-
* @param {BrowserWindow} browserWindow Parent window.
|
|
54
|
-
* @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the browserWindow.webContents will be used.
|
|
55
|
-
* @returns {Findbar} The findbar instance if it exists.
|
|
56
|
-
*
|
|
57
|
-
* @overload
|
|
58
|
-
* @param {BaseWindow} baseWindow Parent window.
|
|
59
|
-
* @param {WebContents} [webContents] Custom findable web contents. If not provided, the win.contentView.children[0] will be used.
|
|
60
|
-
* @returns {Findbar} The findbar instance if it exists.
|
|
61
|
-
* @throws {Error} If no webContents is provided.
|
|
62
|
-
*/
|
|
63
|
-
constructor (parent, webContents) {
|
|
64
|
-
if (isFindable(parent)) {
|
|
65
|
-
this.#findableContents = parent
|
|
66
|
-
this.#parent = Findbar.#getBaseWindowFromWebContents(this.#findableContents)
|
|
67
|
-
} else {
|
|
68
|
-
this.#parent = parent
|
|
69
|
-
this.#findableContents = webContents ?? Findbar.#retrieveWebContents(parent)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!this.#findableContents) {
|
|
73
|
-
throw new Error('There are no searchable web contents.')
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.#findableContents._findbar = this
|
|
77
|
-
|
|
78
|
-
this.#findableContents.once('destroyed', () => { this.detach() })
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Open the findbar. If the findbar is already opened, focus the input text.
|
|
83
|
-
* @returns {void}
|
|
84
|
-
*/
|
|
85
|
-
open() {
|
|
86
|
-
if (this.#window) {
|
|
87
|
-
this.#focusWindowAndHighlightInput()
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
const options = Findbar.#mergeStandardOptions(this.#customOptions, this.#parent)
|
|
91
|
-
this.#isMovable = options.movable
|
|
92
|
-
this.#window = new BrowserWindow(options)
|
|
93
|
-
this.#window.webContents._findbar = this
|
|
94
|
-
|
|
95
|
-
this.#registerListeners()
|
|
96
|
-
|
|
97
|
-
this.#windowHandler && this.#windowHandler(this.#window)
|
|
98
|
-
this.#window.loadFile(`${__dirname}/web/findbar.html`)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Close the findbar.
|
|
103
|
-
* @returns {void}
|
|
104
|
-
*/
|
|
105
|
-
close() {
|
|
106
|
-
if (this.#window && !this.#window.isDestroyed()) {
|
|
107
|
-
this.#window.close()
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Detach the findbar from the web contents and close it if opened. After detaching, the findbar instance will be unusable.
|
|
113
|
-
* @returns {void}
|
|
114
|
-
*/
|
|
115
|
-
detach() {
|
|
116
|
-
this.close()
|
|
117
|
-
this.#findableContents._findbar = void 0
|
|
118
|
-
if (this.#window) { this.#window.webContents._findbar = void 0 }
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Update the parent window of the findbar.
|
|
123
|
-
* @param {BaseWindow} [newParent] - The new parent window. If not provided, the parent will be set to the window containing the web contents.
|
|
124
|
-
* @returns {void}
|
|
125
|
-
*/
|
|
126
|
-
updateParentWindow(newParent) {
|
|
127
|
-
if (this.#parent === newParent) { return }
|
|
128
|
-
this.close()
|
|
129
|
-
this.#parent = newParent ?? Findbar.#getBaseWindowFromWebContents(this.#findableContents)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Get the last state of the findbar.
|
|
134
|
-
* @returns {{ text: string, matchCase: boolean, movable: boolean }} Last state of the findbar.
|
|
135
|
-
*/
|
|
136
|
-
getLastState() {
|
|
137
|
-
return { text: this.#lastText, matchCase: this.#matchCase, movable: this.#isMovable }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Starts a request to find all matches for the text in the page.
|
|
142
|
-
* @param {string} text - Value to find in page.
|
|
143
|
-
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
|
|
144
|
-
* @returns {void}
|
|
145
|
-
*/
|
|
146
|
-
startFind(text, skipRendererEvent) {
|
|
147
|
-
skipRendererEvent || this.#window?.webContents.send('electron-findbar/text-change', text)
|
|
148
|
-
if (this.#lastText = text) {
|
|
149
|
-
this.isOpen() && this.#findInContent({ findNext: true })
|
|
150
|
-
} else {
|
|
151
|
-
this.stopFind()
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Whether the search should be case-sensitive. If not set, the search will be case-insensitive.
|
|
157
|
-
* @param {boolean} status - Whether the search should be case-sensitive. Default is false.
|
|
158
|
-
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
|
|
159
|
-
* @returns {void}
|
|
160
|
-
*/
|
|
161
|
-
matchCase(status, skipRendererEvent) {
|
|
162
|
-
if (this.#matchCase === status) { return }
|
|
163
|
-
this.#matchCase = status
|
|
164
|
-
skipRendererEvent || this.#window?.webContents.send('electron-findbar/match-case-change', this.#matchCase)
|
|
165
|
-
this.#stopFindInContent()
|
|
166
|
-
this.startFind(this.#lastText, skipRendererEvent)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Select previous match if any.
|
|
171
|
-
* @returns {void}
|
|
172
|
-
*/
|
|
173
|
-
findPrevious() {
|
|
174
|
-
if (this.#matches.total < 2) { return }
|
|
175
|
-
this.#matches.active === 1 && (this.#fixMove = false)
|
|
176
|
-
this.isOpen() && this.#findInContent({ forward: false })
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Select next match if any.
|
|
181
|
-
* @returns {void}
|
|
182
|
-
*/
|
|
183
|
-
findNext() {
|
|
184
|
-
if (this.#matches.total < 2) { return }
|
|
185
|
-
this.#matches.active === this.#matches.total && (this.#fixMove = true)
|
|
186
|
-
this.isOpen() && this.#findInContent({ forward: true })
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Stops the find request and clears selection.
|
|
191
|
-
* @returns {void}
|
|
192
|
-
*/
|
|
193
|
-
stopFind() {
|
|
194
|
-
this.isOpen() && this.#sendMatchesCount(0, 0)
|
|
195
|
-
this.#findableContents.isDestroyed() || this.#stopFindInContent()
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Whether the findbar is opened.
|
|
200
|
-
* @returns {boolean} True if the findbar is open, otherwise false.
|
|
201
|
-
*/
|
|
202
|
-
isOpen() {
|
|
203
|
-
return !!this.#window
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Whether the findbar is focused. If the findbar is closed, false will be returned.
|
|
208
|
-
* @returns {boolean} True if the findbar is focused, otherwise false.
|
|
209
|
-
*/
|
|
210
|
-
isFocused() {
|
|
211
|
-
return !!this.#window?.isFocused()
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Whether the findbar is visible to the user in the foreground of the app.
|
|
216
|
-
* If the findbar is closed, false will be returned.
|
|
217
|
-
* @returns {boolean} True if the findbar is visible, otherwise false.
|
|
218
|
-
*/
|
|
219
|
-
isVisible() {
|
|
220
|
-
return !!this.#window?.isVisible()
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Provides a customized set of options to findbar window before open. Note
|
|
225
|
-
* that the options below are necessary for the correct functioning and cannot
|
|
226
|
-
* be overridden:
|
|
227
|
-
* - options.parent (value: parentWindow)
|
|
228
|
-
* - options.frame (value: false)
|
|
229
|
-
* - options.transparent (value: true)
|
|
230
|
-
* - options.maximizable (value: false)
|
|
231
|
-
* - options.minimizable (value: false)
|
|
232
|
-
* - options.skipTaskbar (value: true)
|
|
233
|
-
* - options.fullscreenable (value: false)
|
|
234
|
-
* - options.webPreferences.nodeIntegration (value: true)
|
|
235
|
-
* - options.webPreferences.contextIsolation (value: false)
|
|
236
|
-
*
|
|
237
|
-
* @param {BrowserWindowConstructorOptions} customOptions - Custom window options.
|
|
238
|
-
* @returns {void}
|
|
239
|
-
*/
|
|
240
|
-
setWindowOptions(customOptions) {
|
|
241
|
-
this.#customOptions = customOptions
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Set a window handler capable of changing the findbar window settings after opening.
|
|
246
|
-
* @param {(findbarWindow: BrowserWindow) => void} windowHandler - Window handler function.
|
|
247
|
-
* @returns {void}
|
|
248
|
-
*/
|
|
249
|
-
setWindowHandler(windowHandler) {
|
|
250
|
-
this.#windowHandler = windowHandler
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Set a bounds handler to calculate the findbar bounds when the parent window resizes. If width and/or height are not provided, the current value will be used.
|
|
255
|
-
* @param {(parentBounds: Rectangle, findbarBounds: Rectangle) => Rectangle} boundsHandler - Bounds handler function.
|
|
256
|
-
* @returns {void}
|
|
257
|
-
*/
|
|
258
|
-
setBoundsHandler(boundsHandler) {
|
|
259
|
-
this.#boundsHandler = boundsHandler
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
#registerKeyboardShortcuts(event, input) {
|
|
263
|
-
if (input.meta || input.control || input.alt) { return }
|
|
264
|
-
|
|
265
|
-
const key = input.key.toLowerCase()
|
|
266
|
-
|
|
267
|
-
if (input.shift) {
|
|
268
|
-
if (key === 'enter') {
|
|
269
|
-
this.findPrevious()
|
|
270
|
-
event.preventDefault()
|
|
271
|
-
}
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (key === 'enter') {
|
|
276
|
-
this.findNext()
|
|
277
|
-
event.preventDefault()
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (key === 'escape') {
|
|
282
|
-
if (this.isOpen()) {
|
|
283
|
-
this.close()
|
|
284
|
-
event.preventDefault()
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* @param {Electron.FindInPageOptions} options
|
|
290
|
-
*/
|
|
291
|
-
#findInContent(options) {
|
|
292
|
-
options.matchCase = this.#matchCase
|
|
293
|
-
this.#findableContents.findInPage(this.#lastText, options)
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
#stopFindInContent() {
|
|
297
|
-
this.#findableContents.stopFindInPage('clearSelection')
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Register all event listeners.
|
|
302
|
-
*/
|
|
303
|
-
#registerListeners() {
|
|
304
|
-
const showCascade = () => this.#window.isVisible() || this.#window.show()
|
|
305
|
-
const hideCascade = () => this.#window.isVisible() && this.#window.hide()
|
|
306
|
-
const boundsHandler = () => {
|
|
307
|
-
const currentBounds = this.#window.getBounds()
|
|
308
|
-
const newBounds = this.#boundsHandler(this.#parent.getBounds(), currentBounds)
|
|
309
|
-
if (!newBounds.width) { newBounds.width = currentBounds.width }
|
|
310
|
-
if (!newBounds.height) { newBounds.height = currentBounds.height }
|
|
311
|
-
this.#window.setBounds(newBounds, false)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (this.#parent && !this.#parent.isDestroyed()) {
|
|
315
|
-
boundsHandler()
|
|
316
|
-
this.#parent.prependListener('show', showCascade)
|
|
317
|
-
this.#parent.prependListener('hide', hideCascade)
|
|
318
|
-
this.#parent.prependListener('resize', boundsHandler)
|
|
319
|
-
this.#parent.prependListener('move', boundsHandler)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
this.#window.once('closed', () => {
|
|
323
|
-
if (this.#parent && !this.#parent.isDestroyed()) {
|
|
324
|
-
this.#parent.off('show', showCascade)
|
|
325
|
-
this.#parent.off('hide', hideCascade)
|
|
326
|
-
this.#parent.off('resize', boundsHandler)
|
|
327
|
-
this.#parent.off('move', boundsHandler)
|
|
328
|
-
}
|
|
329
|
-
this.#window = null
|
|
330
|
-
this.stopFind()
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
this.#window.prependOnceListener('ready-to-show', () => { this.#window.show() })
|
|
334
|
-
this.#window.webContents.prependListener('before-input-event', this.#registerKeyboardShortcuts.bind(this))
|
|
335
|
-
|
|
336
|
-
this.#findableContents.prependOnceListener('destroyed', () => { this.close() })
|
|
337
|
-
this.#findableContents.prependListener('found-in-page', (_e, result) => { this.#sendMatchesCount(result.activeMatchOrdinal, result.matches) })
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Send to renderer the active match and the total.
|
|
342
|
-
* @param {number} active Active match.
|
|
343
|
-
* @param {number} total Total matches.
|
|
344
|
-
*/
|
|
345
|
-
#sendMatchesCount(active, total) {
|
|
346
|
-
if (this.#fixMove !== null) {
|
|
347
|
-
this.#fixMove ? this.findNext() : this.findPrevious()
|
|
348
|
-
this.#fixMove = null
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
this.#matches.active = active
|
|
352
|
-
this.#matches.total = total
|
|
353
|
-
|
|
354
|
-
this.#window.webContents.send('electron-findbar/matches', this.#matches)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Focus the findbar and highlight the input text.
|
|
359
|
-
*/
|
|
360
|
-
#focusWindowAndHighlightInput() {
|
|
361
|
-
this.#window.focus()
|
|
362
|
-
this.#window.webContents.send('electron-findbar/input-focus')
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Retrieve web contents from a BrowserWindow or BaseWindow.
|
|
367
|
-
* @param {BrowserWindow | BaseWindow} window
|
|
368
|
-
* @returns {WebContents | undefined} The web contents if any.
|
|
369
|
-
*/
|
|
370
|
-
static #retrieveWebContents(window) {
|
|
371
|
-
return window.webContents ?? window.contentView?.children[0]?.webContents
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Get the parent window from web contents.
|
|
376
|
-
* @param {WebContents} w
|
|
377
|
-
* @returns {BaseWindow | undefined} Parent window if any.
|
|
378
|
-
*/
|
|
379
|
-
static #getBaseWindowFromWebContents(w) {
|
|
380
|
-
return BaseWindow.getAllWindows().find(win => win.webContents === w || win.contentView.children.some(child => child.webContents === w))
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Set default findbar position.
|
|
385
|
-
* @param {Rectangle} parentBounds
|
|
386
|
-
* @param {Rectangle} findbarBounds
|
|
387
|
-
* @returns {x: number, y: number} position.
|
|
388
|
-
*/
|
|
389
|
-
static #setDefaultPosition(parentBounds, findbarBounds) {
|
|
390
|
-
return {
|
|
391
|
-
x: parentBounds.x + parentBounds.width - findbarBounds.width - 20,
|
|
392
|
-
y: parentBounds.y - ((findbarBounds.height / 4) | 0)
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Merge custom, defaults, and fixed options.
|
|
398
|
-
* @param {Electron.BrowserWindowConstructorOptions} options Custom options.
|
|
399
|
-
* @param {BaseWindow | void} parent Parent window, if any.
|
|
400
|
-
* @returns {Electron.BrowserWindowConstructorOptions} Merged options.
|
|
401
|
-
*/
|
|
402
|
-
static #mergeStandardOptions(options, parent) {
|
|
403
|
-
if (!options) { options = {} }
|
|
404
|
-
options.width = options.width ?? 372
|
|
405
|
-
options.height = options.height ?? 52
|
|
406
|
-
options.resizable = options.resizable ?? false
|
|
407
|
-
options.movable = options.movable ?? false
|
|
408
|
-
options.acceptFirstMouse = options.acceptFirstMouse ?? true
|
|
409
|
-
options.parent = parent
|
|
410
|
-
options.show = false
|
|
411
|
-
options.frame = false
|
|
412
|
-
options.roundedCorners = true
|
|
413
|
-
options.transparent = process.platform === 'linux'
|
|
414
|
-
options.maximizable = false
|
|
415
|
-
options.minimizable = false
|
|
416
|
-
options.skipTaskbar = true
|
|
417
|
-
options.fullscreenable = false
|
|
418
|
-
options.autoHideMenuBar = true
|
|
419
|
-
if (!options.webPreferences) { options.webPreferences = {} }
|
|
420
|
-
options.webPreferences.nodeIntegration = false
|
|
421
|
-
options.webPreferences.contextIsolation = true
|
|
422
|
-
options.webPreferences.preload = options.webPreferences.preload ?? `${__dirname}/web/preload.js`
|
|
423
|
-
return options
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Get the findbar instance for a given BrowserWindow or WebContents.
|
|
428
|
-
* If no findbar instance exists, it will return a new one linked to the web contents.
|
|
429
|
-
*
|
|
430
|
-
* @overload
|
|
431
|
-
* @param {WebContents} webContents Findable web contents. The parent window will be defined by using BaseWindow.getAllWindows() and
|
|
432
|
-
* matching the webContents with the webContents of the window or its contentView children.
|
|
433
|
-
* @returns {Findbar} The findbar instance if it exists.
|
|
434
|
-
* @throws {Error} If no webContents is provided.
|
|
435
|
-
*
|
|
436
|
-
* @overload
|
|
437
|
-
* @param {BrowserWindow} browserWindow Parent window.
|
|
438
|
-
* @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the browserWindow.webContents will be used.
|
|
439
|
-
* @returns {Findbar} The findbar instance if it exists.
|
|
440
|
-
*
|
|
441
|
-
* @overload
|
|
442
|
-
* @param {BaseWindow} baseWindow Parent window.
|
|
443
|
-
* @param {WebContents} [webContents] Custom findable web contents. If not provided, the win.contentView.children[0] will be used.
|
|
444
|
-
* @returns {Findbar} The findbar instance if it exists.
|
|
445
|
-
* @throws {Error} If no webContents is provided.
|
|
446
|
-
*/
|
|
447
|
-
static from(windowOrWebContents, customWebContents) {
|
|
448
|
-
const webContents = isFindable(windowOrWebContents) ? windowOrWebContents : customWebContents ?? Findbar.#retrieveWebContents(windowOrWebContents)
|
|
449
|
-
if (!webContents) { throw new Error('[Findbar] There are no searchable web contents.') }
|
|
450
|
-
return webContents._findbar || new Findbar(windowOrWebContents, customWebContents)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Get the findbar instance for a given BrowserWindow or WebContents.
|
|
455
|
-
* @param {BrowserWindow | WebContents} windowOrWebContents
|
|
456
|
-
* @returns {Findbar | undefined} The findbar instance if it exists, otherwise undefined.
|
|
457
|
-
*/
|
|
458
|
-
static fromIfExists(windowOrWebContents) {
|
|
459
|
-
const webContents = isFindable(windowOrWebContents) ? windowOrWebContents : Findbar.#retrieveWebContents(windowOrWebContents)
|
|
460
|
-
if (!webContents) { throw new Error('[Findbar] There are no searchable web contents.') }
|
|
461
|
-
return webContents._findbar
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const isFindable = (obj) => obj && typeof obj.findInPage === 'function' && typeof obj.stopFindInPage === 'function';
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Define IPC events.
|
|
469
|
-
*/
|
|
470
|
-
(ipc => {
|
|
471
|
-
ipc.handle('electron-findbar/last-state', e => e.sender._findbar.getLastState())
|
|
472
|
-
ipc.on('electron-findbar/input-change', (e, text, skip) => e.sender._findbar.startFind(text, skip))
|
|
473
|
-
ipc.on('electron-findbar/match-case', (e, status, skip) => e.sender._findbar.matchCase(status, skip))
|
|
474
|
-
ipc.on('electron-findbar/previous', e => e.sender._findbar.findPrevious())
|
|
475
|
-
ipc.on('electron-findbar/next', e => e.sender._findbar.findNext())
|
|
476
|
-
ipc.on('electron-findbar/open', e => e.sender._findbar.open())
|
|
477
|
-
ipc.on('electron-findbar/close', e => {
|
|
478
|
-
const findbar = e.sender._findbar
|
|
479
|
-
findbar.stopFind()
|
|
480
|
-
findbar.close()
|
|
481
|
-
})
|
|
482
|
-
}) (require('electron').ipcMain)
|
|
483
|
-
|
|
484
|
-
module.exports = Findbar
|
package/remote.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote IPC events to control the findbar through the renderer.
|
|
3
|
-
*/
|
|
4
|
-
const Remote = (ipc => ({
|
|
5
|
-
/**
|
|
6
|
-
* Get last queried text and the "match case" status.
|
|
7
|
-
* @returns {Promise<{ text: string, matchCase: boolean, movable: boolean }>}
|
|
8
|
-
*/
|
|
9
|
-
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Change the input value and find it.
|
|
13
|
-
* @param {string} text - The text to search for
|
|
14
|
-
*/
|
|
15
|
-
inputChange: (text) => { ipc.send('electron-findbar/input-change', text) },
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Toggle case sensitive search
|
|
19
|
-
* @param {boolean} value - Whether to match case or not
|
|
20
|
-
*/
|
|
21
|
-
matchCase: (value) => { ipc.send('electron-findbar/match-case', value) },
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Navigate to the previous match
|
|
25
|
-
*/
|
|
26
|
-
previous: () => { ipc.send('electron-findbar/previous') },
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Navigate to the next match
|
|
30
|
-
*/
|
|
31
|
-
next: () => { ipc.send('electron-findbar/next') },
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Open the findbar
|
|
35
|
-
*/
|
|
36
|
-
open: () => { ipc.send('electron-findbar/open') },
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Close the findbar
|
|
40
|
-
*/
|
|
41
|
-
close: () => { ipc.send('electron-findbar/close') },
|
|
42
|
-
})) (require('electron').ipcRenderer)
|
|
43
|
-
|
|
44
|
-
module.exports = Remote
|
package/web/app.css
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
*, *:before, *:after {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
body {
|
|
6
|
-
margin: 0;
|
|
7
|
-
padding: 0;
|
|
8
|
-
font-weight: normal;
|
|
9
|
-
height: 100vh;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
nav {
|
|
13
|
-
--bg-color: #fff;
|
|
14
|
-
--border: #ddd;
|
|
15
|
-
--color: #626262;
|
|
16
|
-
--input-color: #111;
|
|
17
|
-
--btn-hover-color: #ccc;
|
|
18
|
-
--btn-active-color: #bbb;
|
|
19
|
-
--font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
|
20
|
-
--font-size: .75rem;
|
|
21
|
-
--spacing: .5rem;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@media (prefers-color-scheme: dark) {
|
|
25
|
-
nav {
|
|
26
|
-
--bg-color: #1f1f1f;
|
|
27
|
-
--border: #3f3f3f;
|
|
28
|
-
--color: #a9a9aa;
|
|
29
|
-
--input-color: #eee;
|
|
30
|
-
--btn-hover-color: #333;
|
|
31
|
-
--btn-active-color: #444;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
#match-case {
|
|
36
|
-
fill: var(--color);
|
|
37
|
-
user-select: none;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
#match-case > svg {
|
|
41
|
-
width: 20px;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
#previous, #next, #close {
|
|
45
|
-
fill: none;
|
|
46
|
-
stroke: var(--color);
|
|
47
|
-
stroke-width: 2;
|
|
48
|
-
stroke-linecap: round;
|
|
49
|
-
stroke-linejoin: round;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
nav {
|
|
53
|
-
display: flex;
|
|
54
|
-
align-items: center;
|
|
55
|
-
width: 100%;
|
|
56
|
-
height: 100%;
|
|
57
|
-
padding: var(--spacing);
|
|
58
|
-
background-color: var(--bg-color);
|
|
59
|
-
color: var(--color);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.linux nav {
|
|
63
|
-
border-radius: 10px;
|
|
64
|
-
border: 1px solid var(--border);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
nav, .btn-group {
|
|
68
|
-
gap: var(--spacing);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
span, input {
|
|
72
|
-
font-family: var(--font-family);
|
|
73
|
-
font-size: var(--font-size);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
span {
|
|
77
|
-
color: var(--input-color);
|
|
78
|
-
user-select: none;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
input {
|
|
82
|
-
width: 100%;
|
|
83
|
-
background-color: transparent;
|
|
84
|
-
color: var(--input-color);
|
|
85
|
-
font-weight: 500;
|
|
86
|
-
border: none;
|
|
87
|
-
outline: none;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.divider {
|
|
91
|
-
width: 2px;
|
|
92
|
-
height: 100%;
|
|
93
|
-
background-color: var(--btn-hover-color);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.btn-group {
|
|
97
|
-
display: flex;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
button {
|
|
101
|
-
background-color: transparent;
|
|
102
|
-
border: none;
|
|
103
|
-
border-radius: 50%;
|
|
104
|
-
cursor: default;
|
|
105
|
-
width: 26px;
|
|
106
|
-
height: 26px;
|
|
107
|
-
padding: 3px;
|
|
108
|
-
text-align: center;
|
|
109
|
-
transition: .2s linear all;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
button:hover {
|
|
113
|
-
background-color: var(--btn-hover-color);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
button:active {
|
|
117
|
-
background-color: var(--btn-active-color);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
button:focus {
|
|
121
|
-
outline: none;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
button.disabled {
|
|
125
|
-
opacity: .4;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
#previous.disabled, #next.disabled {
|
|
129
|
-
background-color: transparent !important;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.movable nav {
|
|
133
|
-
-webkit-app-region: drag;
|
|
134
|
-
app-region: drag;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
input, button {
|
|
138
|
-
-webkit-app-region: no-drag;
|
|
139
|
-
app-region: no-drag;
|
|
140
|
-
|
|
141
|
-
}
|
package/web/findbar.html
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="utf-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
-
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; style-src 'self'; default-src 'self'; connect-src 'none'; frame-src 'none'; object-src 'none'; font-src 'self'; img-src 'self'">
|
|
8
|
-
<title>Find in page</title>
|
|
9
|
-
<link rel="stylesheet" href="app.css">
|
|
10
|
-
<body class="movable">
|
|
11
|
-
<nav>
|
|
12
|
-
<input id="input" type="text" spellcheck="false">
|
|
13
|
-
<span id="matches"></span>
|
|
14
|
-
<button id="match-case" class="disabled" title="Match case">
|
|
15
|
-
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" stroke="none">
|
|
16
|
-
<text x="4" y="15" font-size="14" font-family="monospace" font-weight="bold">A</text>
|
|
17
|
-
<text x="12" y="19" font-size="14" font-family="monospace">a</text>
|
|
18
|
-
</svg>
|
|
19
|
-
</button>
|
|
20
|
-
<div class="divider"></div>
|
|
21
|
-
<div class="btn-group">
|
|
22
|
-
|
|
23
|
-
<button id="previous" class="disabled" title="Previous">
|
|
24
|
-
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
25
|
-
<path d="M7 15L12 10L17 15"/>
|
|
26
|
-
</svg>
|
|
27
|
-
</button>
|
|
28
|
-
<button id="next" class="disabled" title="Next">
|
|
29
|
-
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
30
|
-
<path d="M7 10L12 15L17 10"/>
|
|
31
|
-
</svg>
|
|
32
|
-
</button>
|
|
33
|
-
<button id="close" title="Close">
|
|
34
|
-
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
35
|
-
<path d="M7 17.5L12 12.5L17 17.5"/>
|
|
36
|
-
<path d="M7 7.5L12 12.5L17 7.5"/>
|
|
37
|
-
</svg>
|
|
38
|
-
</button>
|
|
39
|
-
</div>
|
|
40
|
-
</nav>
|
|
41
|
-
</body>
|
|
42
|
-
|
|
43
|
-
</html>
|
package/web/preload.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const $remote = (ipc => ({
|
|
2
|
-
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
|
|
3
|
-
inputChange: (value) => { ipc.send('electron-findbar/input-change', value, true) },
|
|
4
|
-
matchCase: (value) => { ipc.send('electron-findbar/match-case', value, true) },
|
|
5
|
-
previous: () => { ipc.send('electron-findbar/previous') },
|
|
6
|
-
next: () => { ipc.send('electron-findbar/next') },
|
|
7
|
-
close: () => { ipc.send('electron-findbar/close') },
|
|
8
|
-
onMatchesChange: (listener) => { ipc.on('electron-findbar/matches', listener) },
|
|
9
|
-
onInputFocus: (listener) => { ipc.on('electron-findbar/input-focus', listener) },
|
|
10
|
-
onTextChange: (listener) => { ipc.on('electron-findbar/text-change', listener) },
|
|
11
|
-
onMatchCaseChange: (listener) => { ipc.on('electron-findbar/match-case-change', listener) }
|
|
12
|
-
})) (require('electron').ipcRenderer)
|
|
13
|
-
|
|
14
|
-
let canRequest = true, canMove = false
|
|
15
|
-
|
|
16
|
-
function inputChange(e) {
|
|
17
|
-
canRequest = false
|
|
18
|
-
$remote.inputChange(e.target.value)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function matchCaseChange(btn) {
|
|
22
|
-
const newStatus = buttonIsDisabled(btn)
|
|
23
|
-
toggleButton(btn, newStatus)
|
|
24
|
-
$remote.matchCase(newStatus)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function move(next) {
|
|
28
|
-
if (canRequest && canMove) {
|
|
29
|
-
canRequest = false
|
|
30
|
-
next ? $remote.next() : $remote.previous()
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function toggleButton(btn, status) {
|
|
35
|
-
btn.classList[status ? 'remove' : 'add']('disabled')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function buttonIsDisabled(btn) {
|
|
39
|
-
return btn.classList.contains('disabled')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
document.addEventListener('DOMContentLoaded', async () => {
|
|
43
|
-
const inputEl = document.getElementById('input')
|
|
44
|
-
const matchCaseBtn = document.getElementById('match-case')
|
|
45
|
-
const previousBtn = document.getElementById('previous')
|
|
46
|
-
const nextBtn = document.getElementById('next')
|
|
47
|
-
const closeBtn = document.getElementById('close')
|
|
48
|
-
const matchesEl = document.getElementById('matches')
|
|
49
|
-
const moveBtns = [previousBtn, nextBtn]
|
|
50
|
-
|
|
51
|
-
matchCaseBtn.addEventListener('click', () => matchCaseChange(matchCaseBtn))
|
|
52
|
-
previousBtn.addEventListener('click', () => move(false))
|
|
53
|
-
nextBtn.addEventListener('click', () => move(true))
|
|
54
|
-
closeBtn.addEventListener('click', () => $remote.close())
|
|
55
|
-
inputEl.addEventListener('input', inputChange)
|
|
56
|
-
|
|
57
|
-
$remote.onTextChange((_, text) => { inputEl.value = text })
|
|
58
|
-
|
|
59
|
-
$remote.onInputFocus(() => {
|
|
60
|
-
inputEl.setSelectionRange(0, inputEl.value.length)
|
|
61
|
-
inputEl.focus()
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
$remote.onMatchCaseChange((_, status) => { console.log('Match case:', status);toggleButton(matchCaseBtn, status) })
|
|
65
|
-
|
|
66
|
-
$remote.onMatchesChange((_, m) => {
|
|
67
|
-
canRequest = true
|
|
68
|
-
matchesEl.innerText = inputEl.value ? m.active + '/' + m.total : ''
|
|
69
|
-
for (var moveBtn of moveBtns) { toggleButton(moveBtn, canMove = m.total > 1) }
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
const lastState = await $remote.getLastState()
|
|
73
|
-
inputEl.value = lastState.text || ''
|
|
74
|
-
lastState.movable || document.body.classList.remove('movable')
|
|
75
|
-
if (process.platform === 'linux') {
|
|
76
|
-
document.body.classList.add('linux')
|
|
77
|
-
}
|
|
78
|
-
toggleButton(matchCaseBtn, lastState.matchCase)
|
|
79
|
-
$remote.inputChange(inputEl.value)
|
|
80
|
-
inputEl.setSelectionRange(0, inputEl.value.length)
|
|
81
|
-
inputEl.focus()
|
|
82
|
-
})
|