electron-findbar 1.1.0 → 2.0.1
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/README.md +164 -48
- package/index.js +160 -48
- package/package.json +1 -1
- package/remote.js +26 -4
- package/web/app.css +46 -20
- package/web/findbar.html +16 -9
- package/web/preload.js +36 -15
package/README.md
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
<p align='center'>
|
|
2
|
-
<a href="https://github.com/ECRomaneli/
|
|
3
|
-
<img src="https://i.postimg.cc/0QR0s0Z1/findbar-light.png" alt='Findbar Light Theme'>
|
|
4
|
-
<img src="https://i.postimg.cc/LXtB6g0Y/findbar-dark.png" alt='Findbar Dark Theme'>
|
|
5
|
-
</a>
|
|
2
|
+
<a href="https://github.com/ECRomaneli/electron-findbar" style='text-decoration:none'><img src="https://i.postimg.cc/sXwqJP59/findbar-v2-light.png" alt='Findbar Light Theme'><img src="https://i.postimg.cc/j26XXRVV/findbar-v2-dark.png" alt='Findbar Dark Theme'></a>
|
|
6
3
|
</p>
|
|
7
4
|
<p align='center'>
|
|
8
5
|
Chrome-like findbar for your Electron application
|
|
@@ -47,13 +44,35 @@ const Findbar = require('electron-findbar')
|
|
|
47
44
|
You can pass a `BrowserWindow` instance as a single parameter to use it as the parent window. The `BrowserWindow.WebContents` will be used as the findable content:
|
|
48
45
|
|
|
49
46
|
```js
|
|
50
|
-
|
|
47
|
+
// Create or retrieve the findbar associated to the browserWindow.webContents. If a new findbar is created, the browserWindow is used as parent.
|
|
48
|
+
const findbar = Findbar.from(browserWindow)
|
|
51
49
|
```
|
|
52
50
|
|
|
53
51
|
Alternatively, you can provide a custom `WebContents` as the second parameter. In this case, the first parameter can be any `BaseWindow`, and the second parameter will be the findable content:
|
|
54
52
|
|
|
55
53
|
```js
|
|
56
|
-
|
|
54
|
+
// Create or retrieve the findbar associated to the webContents. If a new findbar is created, the baseWindow is used as parent.
|
|
55
|
+
const findbar = Findbar.from(baseWindow, webContents)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Is also possible to create a findbar without a parent window (even though it is not recommended):
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
// Create or retrieve the findbar associated to the webContents. If a new findbar is created, it will be displayed in the middle of the screen without a parent to connect to.
|
|
62
|
+
const findbar = Findbar.from(webContents)
|
|
63
|
+
```
|
|
64
|
+
|
|
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.
|
|
66
|
+
|
|
67
|
+
#### Retrieve if exists
|
|
68
|
+
|
|
69
|
+
If there is no intention to create a new findbar in case it does not exist, use:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
// Get the existing findbar or undefined.
|
|
73
|
+
const existingFindbar = Findbar.fromIfExists(browserWindow)
|
|
74
|
+
/* OR */
|
|
75
|
+
const existingFindbar = Findbar.fromIfExists(webContents)
|
|
57
76
|
```
|
|
58
77
|
|
|
59
78
|
### Configuring the Findbar
|
|
@@ -61,7 +80,7 @@ const findbar = new Findbar(baseWindow, customWebContents)
|
|
|
61
80
|
You can customize the Findbar window options using the `setWindowOptions` method:
|
|
62
81
|
|
|
63
82
|
```js
|
|
64
|
-
findbar.setWindowOptions({
|
|
83
|
+
findbar.setWindowOptions({ resizable: true, alwaysOnTop: true, height: 100 })
|
|
65
84
|
```
|
|
66
85
|
|
|
67
86
|
To handle the Findbar window directly after it is opened, use the `setWindowHandler` method:
|
|
@@ -72,12 +91,14 @@ findbar.setWindowHandler(win => {
|
|
|
72
91
|
});
|
|
73
92
|
```
|
|
74
93
|
|
|
75
|
-
The findbar has a default position handler which moves the findbar to the top-right corner. To change the position handler, use the `
|
|
94
|
+
The findbar has a default position handler which moves the findbar to the top-right corner. To change the position handler, use the `setBoundsHandler` method. The bounds handler is called when the parent window moves or resizes and provides both the parent and findbar bounds as parameters.
|
|
76
95
|
|
|
77
96
|
```js
|
|
78
|
-
findbar.
|
|
97
|
+
findbar.setBoundsHandler((parentBounds, findbarBounds) => ({
|
|
79
98
|
x: parentBounds.x + parentBounds.width - findbarBounds.width - 20,
|
|
80
99
|
y: parentBounds.y - ((findbarBounds.height / 4) | 0)
|
|
100
|
+
/* width: OPTIONAL, current value will be used */
|
|
101
|
+
/* height: OPTIONAL, current value will be used */
|
|
81
102
|
}))
|
|
82
103
|
```
|
|
83
104
|
|
|
@@ -89,38 +110,6 @@ The Findbar is a child window of the `BaseWindow` passed during construction. To
|
|
|
89
110
|
findbar.open()
|
|
90
111
|
```
|
|
91
112
|
|
|
92
|
-
### Finding Text in the Page
|
|
93
|
-
|
|
94
|
-
Once open, the Findbar appears by default in the top-right corner of the parent window and can be used without additional coding. Alternatively, you can use the following methods to trigger `findInPage` and navigate through matches in the main process:
|
|
95
|
-
|
|
96
|
-
```js
|
|
97
|
-
/**
|
|
98
|
-
* Retrieve the last queried value.
|
|
99
|
-
*/
|
|
100
|
-
getLastValue()
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Initiate a request to find all matches for the specified text on the page.
|
|
104
|
-
* @param {string} text - The text to search for.
|
|
105
|
-
*/
|
|
106
|
-
startFind(text)
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Select the previous match, if available.
|
|
110
|
-
*/
|
|
111
|
-
findPrevious()
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Select the next match, if available.
|
|
115
|
-
*/
|
|
116
|
-
findNext()
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Stop the find request.
|
|
120
|
-
*/
|
|
121
|
-
stopFind()
|
|
122
|
-
```
|
|
123
|
-
|
|
124
113
|
### Closing the Findbar
|
|
125
114
|
|
|
126
115
|
When the Findbar is closed, its window is destroyed to free memory resources. Use the following method to close the Findbar:
|
|
@@ -144,21 +133,148 @@ app.whenReady().then(() => {
|
|
|
144
133
|
window.loadURL('https://github.com/ECRomaneli/electron-findbar')
|
|
145
134
|
|
|
146
135
|
// Create and configure the Findbar object
|
|
147
|
-
const findbar =
|
|
136
|
+
const findbar = Findbar.from(window)
|
|
148
137
|
|
|
149
138
|
// [OPTIONAL] Customize window options
|
|
150
139
|
findbar.setWindowOptions({ movable: true, resizable: true })
|
|
151
140
|
|
|
152
141
|
// [OPTIONAL] Handle the window object when the Findbar is opened
|
|
153
|
-
findbar.setWindowHandler(win => {
|
|
154
|
-
win.webContents.openDevTools()
|
|
155
|
-
})
|
|
142
|
+
findbar.setWindowHandler(win => { win.webContents.openDevTools() })
|
|
156
143
|
|
|
157
144
|
// Open the Findbar
|
|
158
145
|
findbar.open()
|
|
159
146
|
})
|
|
160
147
|
```
|
|
161
148
|
|
|
149
|
+
### Configuring Keyboard Shortcuts
|
|
150
|
+
|
|
151
|
+
The Findbar component can be controlled using keyboard shortcuts. Below are two implementation approaches to help you integrate search functionality seamlessly into your application's user experience.
|
|
152
|
+
|
|
153
|
+
**Note:** The following examples demonstrate only the ideal (happy path) scenarios. For production use, make sure to thoroughly validate all inputs and handle edge cases appropriately.
|
|
154
|
+
|
|
155
|
+
#### Using Before Input Event
|
|
156
|
+
|
|
157
|
+
The `before-input-event` approach allows you to capture keyboard events directly in the main process before they're processed by the web contents, giving you precise control:
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
window.webContents.on('before-input-event', (event, input) => {
|
|
161
|
+
// Detect Ctrl+F (Windows/Linux) or Command+F (macOS)
|
|
162
|
+
if ((input.control || input.meta) && input.key.toLowerCase() === 'f') {
|
|
163
|
+
// Prevent default browser behavior
|
|
164
|
+
event.preventDefault()
|
|
165
|
+
|
|
166
|
+
// Access and open the findbar
|
|
167
|
+
const findbar = Findbar.from(window)
|
|
168
|
+
if (findbar) {
|
|
169
|
+
findbar.open()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Handle Escape key to close the findbar
|
|
174
|
+
if (input.key === 'Escape') {
|
|
175
|
+
const findbar = Findbar.from(window)
|
|
176
|
+
if (findbar && findbar.isOpen()) {
|
|
177
|
+
event.preventDefault()
|
|
178
|
+
findbar.close()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Using Menu Accelerators
|
|
185
|
+
|
|
186
|
+
For a more integrated approach, you can modify your application's menu system to include findbar controls with keyboard accelerators. This method makes shortcuts available throughout your application:
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
// Get reference to the parent window
|
|
190
|
+
const parent = currentBrowserWindowOrWebContents
|
|
191
|
+
|
|
192
|
+
// Get or create application menu
|
|
193
|
+
const appMenu = Menu.getApplicationMenu() ?? new Menu()
|
|
194
|
+
|
|
195
|
+
// Add Findbar controls to menu
|
|
196
|
+
appMenu.append(new MenuItem({
|
|
197
|
+
label: 'Find',
|
|
198
|
+
submenu: [
|
|
199
|
+
{
|
|
200
|
+
label: 'Find in Page',
|
|
201
|
+
click: () => Findbar.from(parent)?.open(),
|
|
202
|
+
accelerator: 'CommandOrControl+F'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
label: 'Close Find',
|
|
206
|
+
click: () => Findbar.from(parent)?.close(),
|
|
207
|
+
accelerator: 'Esc'
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
}))
|
|
211
|
+
|
|
212
|
+
// Apply the updated menu
|
|
213
|
+
Menu.setApplicationMenu(appMenu)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Both approaches have their advantages - the first offers fine-grained control over exactly when shortcuts are activated, while the second provides better integration with standard application menu conventions.
|
|
217
|
+
|
|
218
|
+
### Finding Text using the main process
|
|
219
|
+
|
|
220
|
+
Once open, the Findbar appears by default in the top-right corner of the parent window and can be used without additional coding. Alternatively, you can use the following methods to trigger `findInPage` and navigate through matches in the main process:
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
/**
|
|
224
|
+
* Get the last state of the findbar.
|
|
225
|
+
* @returns {{ text: string, matchCase: boolean, movable: boolean }} Last state of the findbar.
|
|
226
|
+
*/
|
|
227
|
+
getLastState()
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Initiate a request to find all matches for the specified text on the page.
|
|
231
|
+
* @param {string} text - The text to search for.
|
|
232
|
+
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
|
|
233
|
+
*/
|
|
234
|
+
startFind(text, skipRendererEvent)
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Whether the search should be case-sensitive.
|
|
238
|
+
* @param {boolean} status - Whether the search should be case-sensitive. Default is false.
|
|
239
|
+
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
|
|
240
|
+
*/
|
|
241
|
+
matchCase(status, skipRendererEvent)
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Select the previous match, if available.
|
|
245
|
+
*/
|
|
246
|
+
findPrevious()
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Select the next match, if available.
|
|
250
|
+
*/
|
|
251
|
+
findNext()
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Stop the find request and clears selection.
|
|
255
|
+
*/
|
|
256
|
+
stopFind()
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Whether the findbar is opened.
|
|
260
|
+
* @returns {boolean} True if the findbar is open, otherwise false.
|
|
261
|
+
*/
|
|
262
|
+
isOpen()
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Whether the findbar is focused. If the findbar is closed, false will be returned.
|
|
266
|
+
* @returns {boolean} True if the findbar is focused, otherwise false.
|
|
267
|
+
*/
|
|
268
|
+
isFocused()
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Whether the findbar is visible to the user in the foreground of the app.
|
|
272
|
+
* If the findbar is closed, false will be returned.
|
|
273
|
+
* @returns {boolean} True if the findbar is visible, otherwise false.
|
|
274
|
+
*/
|
|
275
|
+
isVisible()
|
|
276
|
+
```
|
|
277
|
+
|
|
162
278
|
## IPC Events
|
|
163
279
|
|
|
164
280
|
As an alternative, the findbar can be controlled using IPC events in the `renderer` process of the `WebContents` provided during the findbar construction.
|
|
@@ -169,12 +285,12 @@ If the `contextIsolation` is enabled, the `electron-findbar/remote` will not be
|
|
|
169
285
|
|
|
170
286
|
```js
|
|
171
287
|
const $remote = (ipc => ({
|
|
172
|
-
|
|
288
|
+
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
|
|
173
289
|
inputChange: (value) => { ipc.send('electron-findbar/input-change', value) },
|
|
290
|
+
matchCase: (value) => { ipc.send('electron-findbar/match-case', value) },
|
|
174
291
|
previous: () => { ipc.send('electron-findbar/previous') },
|
|
175
292
|
next: () => { ipc.send('electron-findbar/next') },
|
|
176
|
-
|
|
177
|
-
close: () => { ipc.send('electron-findbar/close') },
|
|
293
|
+
close: () => { ipc.send('electron-findbar/close') }
|
|
178
294
|
})) (require('electron').ipcRenderer)
|
|
179
295
|
|
|
180
296
|
$remote.open()
|
package/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const { BaseWindow, BrowserWindow, WebContents, BrowserWindowConstructorOptions, Rectangle } = require('electron')
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Chrome-like findbar for Electron applications.
|
|
5
|
+
*/
|
|
3
6
|
class Findbar {
|
|
4
7
|
/** @type {BaseWindow} */
|
|
5
8
|
#parent
|
|
@@ -10,14 +13,14 @@ class Findbar {
|
|
|
10
13
|
/** @type {WebContents} */
|
|
11
14
|
#findableContents
|
|
12
15
|
|
|
13
|
-
/** */
|
|
16
|
+
/** @type { { active: number, total: number } } */
|
|
14
17
|
#matches
|
|
15
18
|
|
|
16
19
|
/** @type {(findbarWindow: BrowserWindow) => void} */
|
|
17
20
|
#windowHandler
|
|
18
21
|
|
|
19
|
-
/** @type {{parentBounds: Rectangle, findbarBounds: Rectangle} =>
|
|
20
|
-
#
|
|
22
|
+
/** @type {{parentBounds: Rectangle, findbarBounds: Rectangle} => Rectangle} */
|
|
23
|
+
#boundsHandler = Findbar.#setDefaultPosition
|
|
21
24
|
|
|
22
25
|
/** @type {BrowserWindowConstructorOptions} */
|
|
23
26
|
#customOptions
|
|
@@ -25,6 +28,12 @@ class Findbar {
|
|
|
25
28
|
/** @type {string} */
|
|
26
29
|
#lastText = ''
|
|
27
30
|
|
|
31
|
+
/** @type {boolean} */
|
|
32
|
+
#matchCase = false
|
|
33
|
+
|
|
34
|
+
/** @type {boolean} */
|
|
35
|
+
#isMovable = false
|
|
36
|
+
|
|
28
37
|
/**
|
|
29
38
|
* Workaround to fix "findInPage" bug - double-click to loop
|
|
30
39
|
* @type {boolean | null}
|
|
@@ -32,37 +41,56 @@ class Findbar {
|
|
|
32
41
|
#fixMove = null
|
|
33
42
|
|
|
34
43
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* @
|
|
38
|
-
*
|
|
44
|
+
* Configure the findbar and link to the web contents.
|
|
45
|
+
*
|
|
46
|
+
* @overload
|
|
47
|
+
* @param {BrowserWindow} browserWindow Parent window.
|
|
48
|
+
* @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the web contents of the BrowserWindow will be used.
|
|
49
|
+
* @returns {Findbar} The findbar instance if it exists.
|
|
50
|
+
*
|
|
51
|
+
* @overload
|
|
52
|
+
* @param {BaseWindow} baseWindow Parent window.
|
|
53
|
+
* @param {WebContents} webContents Findable web contents.
|
|
54
|
+
* @returns {Findbar} The findbar instance if it exists.
|
|
55
|
+
* @throws {Error} If no webContents is provided.
|
|
56
|
+
* *
|
|
57
|
+
* @overload
|
|
58
|
+
* @param {WebContents} webContents Findable web contents. The parent window will be undefined.
|
|
59
|
+
* @returns {Findbar} The findbar instance if it exists.
|
|
60
|
+
* @throws {Error} If no webContents is provided.
|
|
39
61
|
*/
|
|
40
62
|
constructor (parent, webContents) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
if (isFindable(parent)) {
|
|
64
|
+
this.#parent = void 0
|
|
65
|
+
this.#findableContents = parent
|
|
66
|
+
} else {
|
|
67
|
+
this.#parent = parent
|
|
68
|
+
this.#findableContents = webContents ?? parent.webContents
|
|
69
|
+
}
|
|
70
|
+
|
|
45
71
|
if (!this.#findableContents) {
|
|
46
72
|
throw new Error('There are no searchable web contents.')
|
|
47
73
|
}
|
|
74
|
+
|
|
75
|
+
this.#findableContents._findbar = this
|
|
48
76
|
}
|
|
49
77
|
|
|
50
78
|
/**
|
|
51
79
|
* Open the findbar. If the findbar is already opened, focus the input text.
|
|
80
|
+
* @returns {void}
|
|
52
81
|
*/
|
|
53
82
|
open() {
|
|
54
83
|
if (this.#window) {
|
|
55
84
|
this.#focusWindowAndHighlightInput()
|
|
56
85
|
return
|
|
57
86
|
}
|
|
58
|
-
|
|
87
|
+
const options = Findbar.#mergeStandardOptions(this.#customOptions, this.#parent)
|
|
88
|
+
this.#isMovable = options.movable
|
|
89
|
+
this.#window = new BrowserWindow(options)
|
|
59
90
|
this.#window.webContents._findbar = this
|
|
60
91
|
|
|
61
92
|
this.#registerListeners()
|
|
62
93
|
|
|
63
|
-
const pos = this.#positionHandler(this.#parent.getBounds(), this.#window.getBounds())
|
|
64
|
-
this.#window.setPosition(pos.x, pos.y)
|
|
65
|
-
|
|
66
94
|
this.#windowHandler && this.#windowHandler(this.#window)
|
|
67
95
|
|
|
68
96
|
this.#window.loadFile(`${__dirname}/web/findbar.html`)
|
|
@@ -70,6 +98,7 @@ class Findbar {
|
|
|
70
98
|
|
|
71
99
|
/**
|
|
72
100
|
* Close the findbar.
|
|
101
|
+
* @returns {void}
|
|
73
102
|
*/
|
|
74
103
|
close() {
|
|
75
104
|
if (!this.#window || this.#window.isDestroyed()) { return }
|
|
@@ -79,53 +108,72 @@ class Findbar {
|
|
|
79
108
|
}
|
|
80
109
|
|
|
81
110
|
/**
|
|
82
|
-
* Get last
|
|
111
|
+
* Get the last state of the findbar.
|
|
112
|
+
* @returns {{ text: string, matchCase: boolean, movable: boolean }} Last state of the findbar.
|
|
83
113
|
*/
|
|
84
|
-
|
|
85
|
-
return this.#lastText
|
|
114
|
+
getLastState() {
|
|
115
|
+
return { text: this.#lastText, matchCase: this.#matchCase, movable: this.#isMovable }
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
/**
|
|
89
119
|
* Starts a request to find all matches for the text in the page.
|
|
90
|
-
* @param {string} text Value to find in page.
|
|
91
|
-
* @param {boolean
|
|
120
|
+
* @param {string} text - Value to find in page.
|
|
121
|
+
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
|
|
122
|
+
* @returns {void}
|
|
92
123
|
*/
|
|
93
|
-
startFind(text,
|
|
94
|
-
|
|
124
|
+
startFind(text, skipRendererEvent) {
|
|
125
|
+
skipRendererEvent || this.#window?.webContents.send('electron-findbar/text-change', text)
|
|
95
126
|
if (this.#lastText = text) {
|
|
96
|
-
this.isOpen() && this.#
|
|
127
|
+
this.isOpen() && this.#findInContent({ findNext: true })
|
|
97
128
|
} else {
|
|
98
129
|
this.stopFind()
|
|
99
130
|
}
|
|
100
131
|
}
|
|
101
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Whether the search should be case-sensitive. If not set, the search will be case-insensitive.
|
|
135
|
+
* @param {boolean} status - Whether the search should be case-sensitive. Default is false.
|
|
136
|
+
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
|
|
137
|
+
* @returns {void}
|
|
138
|
+
*/
|
|
139
|
+
matchCase(status, skipRendererEvent) {
|
|
140
|
+
if (this.#matchCase === status) { return }
|
|
141
|
+
this.#matchCase = status
|
|
142
|
+
skipRendererEvent || this.#window?.webContents.send('electron-findbar/match-case-change', this.#matchCase)
|
|
143
|
+
this.#stopFindInContent()
|
|
144
|
+
this.startFind(this.#lastText, skipRendererEvent)
|
|
145
|
+
}
|
|
146
|
+
|
|
102
147
|
/**
|
|
103
148
|
* Select previous match if any.
|
|
149
|
+
* @returns {void}
|
|
104
150
|
*/
|
|
105
151
|
findPrevious() {
|
|
106
152
|
this.#matches.active === 1 && (this.#fixMove = false)
|
|
107
|
-
this.isOpen() && this.#
|
|
153
|
+
this.isOpen() && this.#findInContent({ forward: false })
|
|
108
154
|
}
|
|
109
155
|
|
|
110
156
|
/**
|
|
111
157
|
* Select next match if any.
|
|
158
|
+
* @returns {void}
|
|
112
159
|
*/
|
|
113
160
|
findNext() {
|
|
114
161
|
this.#matches.active === this.#matches.total && (this.#fixMove = true)
|
|
115
|
-
this.isOpen() && this.#
|
|
162
|
+
this.isOpen() && this.#findInContent({ forward: true })
|
|
116
163
|
}
|
|
117
164
|
|
|
118
165
|
/**
|
|
119
|
-
* Stops the find request.
|
|
166
|
+
* Stops the find request and clears selection.
|
|
167
|
+
* @returns {void}
|
|
120
168
|
*/
|
|
121
169
|
stopFind() {
|
|
122
170
|
this.isOpen() && this.#sendMatchesCount(0, 0)
|
|
123
|
-
this.#findableContents.isDestroyed() || this.#
|
|
171
|
+
this.#findableContents.isDestroyed() || this.#stopFindInContent()
|
|
124
172
|
}
|
|
125
173
|
|
|
126
174
|
/**
|
|
127
175
|
* Whether the findbar is opened.
|
|
128
|
-
* @returns {boolean} True
|
|
176
|
+
* @returns {boolean} True if the findbar is open, otherwise false.
|
|
129
177
|
*/
|
|
130
178
|
isOpen() {
|
|
131
179
|
return !!this.#window
|
|
@@ -133,15 +181,16 @@ class Findbar {
|
|
|
133
181
|
|
|
134
182
|
/**
|
|
135
183
|
* Whether the findbar is focused. If the findbar is closed, false will be returned.
|
|
136
|
-
* @returns {boolean} True
|
|
184
|
+
* @returns {boolean} True if the findbar is focused, otherwise false.
|
|
137
185
|
*/
|
|
138
186
|
isFocused() {
|
|
139
187
|
return !!this.#window?.isFocused()
|
|
140
188
|
}
|
|
141
189
|
|
|
142
190
|
/**
|
|
143
|
-
* Whether the findbar is visible to the user in the foreground of the app.
|
|
144
|
-
*
|
|
191
|
+
* Whether the findbar is visible to the user in the foreground of the app.
|
|
192
|
+
* If the findbar is closed, false will be returned.
|
|
193
|
+
* @returns {boolean} True if the findbar is visible, otherwise false.
|
|
145
194
|
*/
|
|
146
195
|
isVisible() {
|
|
147
196
|
return !!this.#window?.isVisible()
|
|
@@ -160,7 +209,9 @@ class Findbar {
|
|
|
160
209
|
* - options.fullscreenable (value: false)
|
|
161
210
|
* - options.webPreferences.nodeIntegration (value: true)
|
|
162
211
|
* - options.webPreferences.contextIsolation (value: false)
|
|
163
|
-
*
|
|
212
|
+
*
|
|
213
|
+
* @param {BrowserWindowConstructorOptions} customOptions - Custom window options.
|
|
214
|
+
* @returns {void}
|
|
164
215
|
*/
|
|
165
216
|
setWindowOptions(customOptions) {
|
|
166
217
|
this.#customOptions = customOptions
|
|
@@ -168,18 +219,32 @@ class Findbar {
|
|
|
168
219
|
|
|
169
220
|
/**
|
|
170
221
|
* Set a window handler capable of changing the findbar window settings after opening.
|
|
171
|
-
* @param {(findbarWindow: BrowserWindow) => void} windowHandler Window handler.
|
|
222
|
+
* @param {(findbarWindow: BrowserWindow) => void} windowHandler - Window handler function.
|
|
223
|
+
* @returns {void}
|
|
172
224
|
*/
|
|
173
225
|
setWindowHandler(windowHandler) {
|
|
174
226
|
this.#windowHandler = windowHandler
|
|
175
227
|
}
|
|
176
228
|
|
|
177
229
|
/**
|
|
178
|
-
* Set a bounds handler to calculate the findbar bounds when the parent resizes.
|
|
179
|
-
* @param {
|
|
230
|
+
* 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.
|
|
231
|
+
* @param {(parentBounds: Rectangle, findbarBounds: Rectangle) => Rectangle} boundsHandler - Bounds handler function.
|
|
232
|
+
* @returns {void}
|
|
180
233
|
*/
|
|
181
234
|
setBoundsHandler(boundsHandler) {
|
|
182
|
-
this.#
|
|
235
|
+
this.#boundsHandler = boundsHandler
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @param {Electron.FindInPageOptions} options
|
|
240
|
+
*/
|
|
241
|
+
#findInContent(options) {
|
|
242
|
+
options.matchCase = this.#matchCase
|
|
243
|
+
this.#findableContents.findInPage(this.#lastText, options)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#stopFindInContent() {
|
|
247
|
+
this.#findableContents.stopFindInPage('clearSelection')
|
|
183
248
|
}
|
|
184
249
|
|
|
185
250
|
/**
|
|
@@ -188,21 +253,29 @@ class Findbar {
|
|
|
188
253
|
#registerListeners() {
|
|
189
254
|
const showCascade = () => this.#window.isVisible() || this.#window.show()
|
|
190
255
|
const hideCascade = () => this.#window.isVisible() && this.#window.hide()
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
this.#
|
|
256
|
+
const boundsHandler = () => {
|
|
257
|
+
const currentBounds = this.#window.getBounds()
|
|
258
|
+
const newBounds = this.#boundsHandler(this.#parent.getBounds(), currentBounds)
|
|
259
|
+
if (!newBounds.width) { newBounds.width = currentBounds.width }
|
|
260
|
+
if (!newBounds.height) { newBounds.height = currentBounds.height }
|
|
261
|
+
this.#window.setBounds(newBounds, false)
|
|
194
262
|
}
|
|
195
263
|
|
|
196
|
-
this.#parent.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
264
|
+
if (this.#parent && !this.#parent.isDestroyed()) {
|
|
265
|
+
boundsHandler()
|
|
266
|
+
this.#parent.prependListener('show', showCascade)
|
|
267
|
+
this.#parent.prependListener('hide', hideCascade)
|
|
268
|
+
this.#parent.prependListener('resize', boundsHandler)
|
|
269
|
+
this.#parent.prependListener('move', boundsHandler)
|
|
270
|
+
}
|
|
200
271
|
|
|
201
272
|
this.#window.once('close', () => {
|
|
202
|
-
this.#parent.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
273
|
+
if (this.#parent && !this.#parent.isDestroyed()) {
|
|
274
|
+
this.#parent.off('show', showCascade)
|
|
275
|
+
this.#parent.off('hide', hideCascade)
|
|
276
|
+
this.#parent.off('resize', boundsHandler)
|
|
277
|
+
this.#parent.off('move', boundsHandler)
|
|
278
|
+
}
|
|
206
279
|
this.#window = null
|
|
207
280
|
this.stopFind()
|
|
208
281
|
})
|
|
@@ -279,14 +352,53 @@ class Findbar {
|
|
|
279
352
|
options.webPreferences.preload = options.webPreferences.preload ?? `${__dirname}/web/preload.js`
|
|
280
353
|
return options
|
|
281
354
|
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get the findbar instance for a given BrowserWindow or WebContents.
|
|
358
|
+
* If no findbar instance exists, it will return a new one linked to the web contents.
|
|
359
|
+
*
|
|
360
|
+
* @overload
|
|
361
|
+
* @param {BrowserWindow} browserWindow Parent window.
|
|
362
|
+
* @param {WebContents} [customWebContents] Custom findable web contents. If not provided, the web contents of the BrowserWindow will be used.
|
|
363
|
+
* @returns {Findbar} The findbar instance if it exists.
|
|
364
|
+
*
|
|
365
|
+
* @overload
|
|
366
|
+
* @param {WebContents} webContents Findable web contents. The parent window will be undefined.
|
|
367
|
+
* @returns {Findbar} The findbar instance if it exists.
|
|
368
|
+
* @throws {Error} If no webContents is provided.
|
|
369
|
+
*
|
|
370
|
+
* @overload
|
|
371
|
+
* @param {BaseWindow} baseWindow Parent window.
|
|
372
|
+
* @param {WebContents} webContents Findable web contents.
|
|
373
|
+
* @returns {Findbar} The findbar instance if it exists.
|
|
374
|
+
* @throws {Error} If no webContents is provided.
|
|
375
|
+
*/
|
|
376
|
+
static from(windowOrWebContents, customWebContents) {
|
|
377
|
+
let webContents = isFindable(windowOrWebContents) ?
|
|
378
|
+
windowOrWebContents : customWebContents ?? windowOrWebContents.webContents
|
|
379
|
+
|
|
380
|
+
return webContents._findbar || new Findbar(windowOrWebContents, customWebContents)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Get the findbar instance for a given BrowserWindow or WebContents.
|
|
385
|
+
* @param {BrowserWindow | WebContents} windowOrWebContents
|
|
386
|
+
* @returns {Findbar | undefined} The findbar instance if it exists, otherwise undefined.
|
|
387
|
+
*/
|
|
388
|
+
static fromIfExists(windowOrWebContents) {
|
|
389
|
+
return (isFindable(windowOrWebContents) ? windowOrWebContents : windowOrWebContents.webContents)._findbar
|
|
390
|
+
}
|
|
282
391
|
}
|
|
283
392
|
|
|
393
|
+
const isFindable = (obj) => obj && typeof obj.findInPage === 'function' && typeof obj.stopFindInPage === 'function';
|
|
394
|
+
|
|
284
395
|
/**
|
|
285
396
|
* Define IPC events.
|
|
286
397
|
*/
|
|
287
398
|
(ipc => {
|
|
288
|
-
ipc.handle('electron-findbar/last-
|
|
399
|
+
ipc.handle('electron-findbar/last-state', e => e.sender._findbar.getLastState())
|
|
289
400
|
ipc.on('electron-findbar/input-change', (e, text, skip) => e.sender._findbar.startFind(text, skip))
|
|
401
|
+
ipc.on('electron-findbar/match-case', (e, status, skip) => e.sender._findbar.matchCase(status, skip))
|
|
290
402
|
ipc.on('electron-findbar/previous', e => e.sender._findbar.findPrevious())
|
|
291
403
|
ipc.on('electron-findbar/next', e => e.sender._findbar.findNext())
|
|
292
404
|
ipc.on('electron-findbar/open', e => e.sender._findbar.open())
|
package/package.json
CHANGED
package/remote.js
CHANGED
|
@@ -3,19 +3,41 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const Remote = (ipc => ({
|
|
5
5
|
/**
|
|
6
|
-
* Get last queried text.
|
|
7
|
-
* @returns {string}
|
|
6
|
+
* Get last queried text and the "match case" status.
|
|
7
|
+
* @returns {Promise<{ text: string, matchCase: boolean, movable: boolean }>}
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Change the input value and find it.
|
|
13
|
-
* @param {string} text
|
|
13
|
+
* @param {string} text - The text to search for
|
|
14
14
|
*/
|
|
15
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
|
+
*/
|
|
16
26
|
previous: () => { ipc.send('electron-findbar/previous') },
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Navigate to the next match
|
|
30
|
+
*/
|
|
17
31
|
next: () => { ipc.send('electron-findbar/next') },
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Open the findbar
|
|
35
|
+
*/
|
|
18
36
|
open: () => { ipc.send('electron-findbar/open') },
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Close the findbar
|
|
40
|
+
*/
|
|
19
41
|
close: () => { ipc.send('electron-findbar/close') },
|
|
20
42
|
})) (require('electron').ipcRenderer)
|
|
21
43
|
|
package/web/app.css
CHANGED
|
@@ -11,28 +11,37 @@ body {
|
|
|
11
11
|
|
|
12
12
|
nav {
|
|
13
13
|
--bg-color: #fff;
|
|
14
|
-
--border: #
|
|
14
|
+
--border: #ddd;
|
|
15
15
|
--color: #626262;
|
|
16
|
-
--input-color: #
|
|
16
|
+
--input-color: #111;
|
|
17
17
|
--btn-hover-color: #ccc;
|
|
18
18
|
--btn-active-color: #bbb;
|
|
19
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
20
|
--font-size: .75rem;
|
|
21
|
-
--spacing: .
|
|
21
|
+
--spacing: .5rem;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
@media (prefers-color-scheme: dark) {
|
|
25
25
|
nav {
|
|
26
26
|
--bg-color: #1f1f1f;
|
|
27
|
-
--border: #
|
|
27
|
+
--border: #3f3f3f;
|
|
28
28
|
--color: #a9a9aa;
|
|
29
|
-
--input-color: #
|
|
29
|
+
--input-color: #eee;
|
|
30
30
|
--btn-hover-color: #333;
|
|
31
31
|
--btn-active-color: #444;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
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 {
|
|
36
45
|
fill: none;
|
|
37
46
|
stroke: var(--color);
|
|
38
47
|
stroke-width: 2;
|
|
@@ -48,15 +57,15 @@ nav {
|
|
|
48
57
|
padding: var(--spacing);
|
|
49
58
|
background-color: var(--bg-color);
|
|
50
59
|
color: var(--color);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.linux nav {
|
|
51
63
|
border-radius: 10px;
|
|
52
64
|
border: 1px solid var(--border);
|
|
53
|
-
-webkit-app-region: drag;
|
|
54
|
-
app-region: drag;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
nav
|
|
58
|
-
|
|
59
|
-
margin-right: var(--spacing);
|
|
67
|
+
nav, .btn-group {
|
|
68
|
+
gap: var(--spacing);
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
span, input {
|
|
@@ -65,6 +74,7 @@ span, input {
|
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
span {
|
|
77
|
+
color: var(--input-color);
|
|
68
78
|
user-select: none;
|
|
69
79
|
}
|
|
70
80
|
|
|
@@ -75,8 +85,6 @@ input {
|
|
|
75
85
|
font-weight: 500;
|
|
76
86
|
border: none;
|
|
77
87
|
outline: none;
|
|
78
|
-
-webkit-app-region: no-drag;
|
|
79
|
-
app-region: no-drag;
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
.divider {
|
|
@@ -89,27 +97,45 @@ input {
|
|
|
89
97
|
display: flex;
|
|
90
98
|
}
|
|
91
99
|
|
|
92
|
-
|
|
100
|
+
button {
|
|
101
|
+
background-color: transparent;
|
|
102
|
+
border: none;
|
|
93
103
|
border-radius: 50%;
|
|
94
104
|
cursor: default;
|
|
95
105
|
width: 26px;
|
|
96
106
|
height: 26px;
|
|
97
107
|
padding: 3px;
|
|
98
108
|
text-align: center;
|
|
99
|
-
transition: .
|
|
100
|
-
-webkit-app-region: no-drag;
|
|
101
|
-
app-region: no-drag;
|
|
109
|
+
transition: .2s linear all;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
|
|
112
|
+
button:hover {
|
|
105
113
|
background-color: var(--btn-hover-color);
|
|
106
114
|
}
|
|
107
115
|
|
|
108
|
-
|
|
116
|
+
button:active {
|
|
109
117
|
background-color: var(--btn-active-color);
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
|
|
120
|
+
button:focus {
|
|
121
|
+
outline: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
button.disabled {
|
|
113
125
|
opacity: .4;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#previous.disabled, #next.disabled {
|
|
114
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
|
+
|
|
115
141
|
}
|
package/web/findbar.html
CHANGED
|
@@ -5,30 +5,37 @@
|
|
|
5
5
|
<meta charset="utf-8">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
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>
|
|
8
|
+
<title>Find in page</title>
|
|
9
9
|
<link rel="stylesheet" href="app.css">
|
|
10
|
-
<body>
|
|
10
|
+
<body class="movable">
|
|
11
11
|
<nav>
|
|
12
|
-
<input id=
|
|
12
|
+
<input id="input" type="text" spellcheck="false">
|
|
13
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>
|
|
14
20
|
<div class="divider"></div>
|
|
15
21
|
<div class="btn-group">
|
|
16
|
-
|
|
22
|
+
|
|
23
|
+
<button id="previous" class="disabled" title="Previous">
|
|
17
24
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
18
25
|
<path d="M7 15L12 10L17 15"/>
|
|
19
26
|
</svg>
|
|
20
|
-
</
|
|
21
|
-
<
|
|
27
|
+
</button>
|
|
28
|
+
<button id="next" class="disabled" title="Next">
|
|
22
29
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
23
30
|
<path d="M7 10L12 15L17 10"/>
|
|
24
31
|
</svg>
|
|
25
|
-
</
|
|
26
|
-
<
|
|
32
|
+
</button>
|
|
33
|
+
<button id="close" title="Close">
|
|
27
34
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
28
35
|
<path d="M7 17.5L12 12.5L17 17.5"/>
|
|
29
36
|
<path d="M7 7.5L12 12.5L17 7.5"/>
|
|
30
37
|
</svg>
|
|
31
|
-
</
|
|
38
|
+
</button>
|
|
32
39
|
</div>
|
|
33
40
|
</nav>
|
|
34
41
|
</body>
|
package/web/preload.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
const $remote = (ipc => ({
|
|
2
|
-
|
|
2
|
+
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
|
|
3
3
|
inputChange: (value) => { ipc.send('electron-findbar/input-change', value, true) },
|
|
4
|
+
matchCase: (value) => { ipc.send('electron-findbar/match-case', value, true) },
|
|
4
5
|
previous: () => { ipc.send('electron-findbar/previous') },
|
|
5
6
|
next: () => { ipc.send('electron-findbar/next') },
|
|
6
7
|
close: () => { ipc.send('electron-findbar/close') },
|
|
7
8
|
onMatchesChange: (listener) => { ipc.on('electron-findbar/matches', listener) },
|
|
8
9
|
onInputFocus: (listener) => { ipc.on('electron-findbar/input-focus', listener) },
|
|
9
|
-
onTextChange: (listener) => { ipc.on('electron-findbar/text-change', listener) }
|
|
10
|
+
onTextChange: (listener) => { ipc.on('electron-findbar/text-change', listener) },
|
|
11
|
+
onMatchCaseChange: (listener) => { ipc.on('electron-findbar/match-case-change', listener) }
|
|
10
12
|
})) (require('electron').ipcRenderer)
|
|
11
13
|
|
|
12
14
|
let canRequest = true, canMove = false
|
|
@@ -16,6 +18,12 @@ function inputChange(e) {
|
|
|
16
18
|
$remote.inputChange(e.target.value)
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
function matchCaseChange(btn) {
|
|
22
|
+
const newStatus = buttonIsDisabled(btn)
|
|
23
|
+
toggleButton(btn, newStatus)
|
|
24
|
+
$remote.matchCase(newStatus)
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
function move(next) {
|
|
20
28
|
if (canRequest && canMove) {
|
|
21
29
|
canRequest = false
|
|
@@ -23,38 +31,51 @@ function move(next) {
|
|
|
23
31
|
}
|
|
24
32
|
}
|
|
25
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
|
+
|
|
26
42
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
27
43
|
const inputEl = document.getElementById('input')
|
|
44
|
+
const matchCaseBtn = document.getElementById('match-case')
|
|
28
45
|
const previousBtn = document.getElementById('previous')
|
|
29
46
|
const nextBtn = document.getElementById('next')
|
|
30
47
|
const closeBtn = document.getElementById('close')
|
|
31
48
|
const matchesEl = document.getElementById('matches')
|
|
32
|
-
const moveBtns = [
|
|
49
|
+
const moveBtns = [previousBtn, nextBtn]
|
|
33
50
|
|
|
51
|
+
matchCaseBtn.addEventListener('click', () => matchCaseChange(matchCaseBtn))
|
|
34
52
|
previousBtn.addEventListener('click', () => move(false))
|
|
35
53
|
nextBtn.addEventListener('click', () => move(true))
|
|
36
54
|
closeBtn.addEventListener('click', () => $remote.close())
|
|
37
55
|
inputEl.addEventListener('input', inputChange)
|
|
38
56
|
|
|
39
|
-
$remote.
|
|
40
|
-
canRequest = true
|
|
41
|
-
matchesEl.innerText = inputEl.value ? m.active + '/' + m.total : ''
|
|
42
|
-
|
|
43
|
-
for (var moveBtn of moveBtns) {
|
|
44
|
-
(canMove = m.total > 1) ?
|
|
45
|
-
moveBtn.classList.remove('disabled') :
|
|
46
|
-
moveBtn.classList.add('disabled')
|
|
47
|
-
}
|
|
48
|
-
})
|
|
57
|
+
$remote.onTextChange((_, text) => { inputEl.value = text })
|
|
49
58
|
|
|
50
59
|
$remote.onInputFocus(() => {
|
|
51
60
|
inputEl.setSelectionRange(0, inputEl.value.length)
|
|
52
61
|
inputEl.focus()
|
|
53
62
|
})
|
|
54
63
|
|
|
55
|
-
$remote.
|
|
64
|
+
$remote.onMatchCaseChange((_, status) => { console.log('Match case:', status);toggleButton(matchCaseBtn, status) })
|
|
56
65
|
|
|
57
|
-
|
|
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)
|
|
58
79
|
$remote.inputChange(inputEl.value)
|
|
59
80
|
inputEl.setSelectionRange(0, inputEl.value.length)
|
|
60
81
|
inputEl.focus()
|