@reause/electron 0.1.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 +21 -0
- package/dist/index.d.ts +315 -0
- package/dist/index.js +206 -0
- package/package.json +29 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hairyf
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { RefOrValue } from "@reause/shared";
|
|
2
|
+
import { IpcRenderer, IpcRendererEvent, WebFrame } from "electron";
|
|
3
|
+
//#region _types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* An `ipcRenderer` message listener — `listener(event, ...args)`.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors upstream `source/vueuse/packages/electron/_types.ts`.
|
|
8
|
+
*/
|
|
9
|
+
type IpcRendererListener = (event: IpcRendererEvent, ...args: any[]) => void;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region useIpcRenderer/index.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* Result from useIpcRenderer
|
|
14
|
+
*
|
|
15
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer
|
|
16
|
+
*/
|
|
17
|
+
export interface UseIpcRendererReturn {
|
|
18
|
+
/**
|
|
19
|
+
* Listens to channel, when a new message arrives listener would be called with listener(event, args...).
|
|
20
|
+
* [ipcRenderer.removeListener](https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererremovelistenerchannel-listener) automatically on unmounted.
|
|
21
|
+
*
|
|
22
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendereronchannel-listener
|
|
23
|
+
*/
|
|
24
|
+
on: (channel: string, listener: IpcRendererListener) => IpcRenderer;
|
|
25
|
+
/**
|
|
26
|
+
* Adds a one time listener function for the event. This listener is invoked only the next time a message is sent to channel, after which it is removed.
|
|
27
|
+
*
|
|
28
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendereroncechannel-listener
|
|
29
|
+
*/
|
|
30
|
+
once: (channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void) => IpcRenderer;
|
|
31
|
+
/**
|
|
32
|
+
* Removes the specified listener from the listener array for the specified channel.
|
|
33
|
+
*
|
|
34
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererremovelistenerchannel-listener
|
|
35
|
+
*/
|
|
36
|
+
removeListener: (channel: string, listener: (...args: any[]) => void) => IpcRenderer;
|
|
37
|
+
/**
|
|
38
|
+
* Removes all listeners, or those of the specified channel.
|
|
39
|
+
*
|
|
40
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererremovealllistenerschannel
|
|
41
|
+
*/
|
|
42
|
+
removeAllListeners: (channel: string) => IpcRenderer;
|
|
43
|
+
/**
|
|
44
|
+
* Send an asynchronous message to the main process via channel, along with arguments.
|
|
45
|
+
*
|
|
46
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendchannel-args
|
|
47
|
+
*/
|
|
48
|
+
send: (channel: string, ...args: any[]) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Returns `Promise<any>` — resolves with the response from the main process.
|
|
51
|
+
* Send a message to the main process via channel and expect a result ~~asynchronously~~.
|
|
52
|
+
*
|
|
53
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererinvokechannel-args
|
|
54
|
+
*/
|
|
55
|
+
invoke: <T>(channel: string, ...args: any[]) => Promise<T>;
|
|
56
|
+
/**
|
|
57
|
+
* Returns `any` — the value sent back by the `ipcMain` handler.
|
|
58
|
+
* Send a message to the main process via channel and expect a result synchronously.
|
|
59
|
+
*
|
|
60
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendsyncchannel-args
|
|
61
|
+
*/
|
|
62
|
+
sendSync: <T>(channel: string, ...args: any[]) => T;
|
|
63
|
+
/**
|
|
64
|
+
* Send a message to the main process, optionally transferring ownership of zero or more MessagePort objects.
|
|
65
|
+
*
|
|
66
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererpostmessagechannel-message-transfer
|
|
67
|
+
*/
|
|
68
|
+
postMessage: (channel: string, message: any, transfer?: MessagePort[]) => void;
|
|
69
|
+
/**
|
|
70
|
+
* Sends a message to a window with webContentsId via channel.
|
|
71
|
+
*
|
|
72
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendtowebcontentsid-channel-args
|
|
73
|
+
*/
|
|
74
|
+
sendTo: (webContentsId: number, channel: string, ...args: any[]) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Like `ipcRenderer.send` but the event will be sent to the `<webview>` element in the host page instead of the main process.
|
|
77
|
+
*
|
|
78
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendtohostchannel-args
|
|
79
|
+
*/
|
|
80
|
+
sendToHost: (channel: string, ...args: any[]) => void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the `ipcRenderer` module with all APIs.
|
|
84
|
+
*
|
|
85
|
+
* Map from @vueuse/electron `useIpcRenderer`
|
|
86
|
+
* (`source/vueuse/packages/electron/useIpcRenderer/`).
|
|
87
|
+
*
|
|
88
|
+
* React deviations:
|
|
89
|
+
* - upstream implements `on` by calling the `useIpcRendererOn` composable
|
|
90
|
+
* inside the method. Hooks cannot be called from callbacks, so `on`
|
|
91
|
+
* registers directly and tracks `{ ipc, channel, listener }` pairs; a mount
|
|
92
|
+
* effect's cleanup removes every tracked listener from **its captured
|
|
93
|
+
* instance** on unmount (the same auto-cleanup guarantee upstream gets from
|
|
94
|
+
* the effect scope). Like upstream, listeners are not re-registered when the
|
|
95
|
+
* instance changes — each stays on the instance it was registered with;
|
|
96
|
+
* - upstream `invoke` returns a `ShallowRef<T | null>`; this port returns the
|
|
97
|
+
* raw `Promise<T>`. Declarative async state is the job of the
|
|
98
|
+
* `useIpcRendererInvoke` hook — a method on a returned object cannot own
|
|
99
|
+
* component state;
|
|
100
|
+
* - upstream `sendSync` wraps the synchronous return in a `ShallowRef`; this
|
|
101
|
+
* port returns the value `T` directly.
|
|
102
|
+
*
|
|
103
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendtohostchannel-args
|
|
104
|
+
* @see https://vueuse.org/useIpcRenderer
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* const ipcRenderer = useIpcRenderer()
|
|
108
|
+
* ipcRenderer.on('custom-event', (event, ...args) => console.log(args))
|
|
109
|
+
* const result = await ipcRenderer.invoke<string>('custom-channel', 'some data')
|
|
110
|
+
*
|
|
111
|
+
* @__NO_SIDE_EFFECTS__
|
|
112
|
+
*/
|
|
113
|
+
export declare function useIpcRenderer(ipcRenderer?: IpcRenderer): UseIpcRendererReturn;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region useIpcRendererInvoke/index.d.ts
|
|
116
|
+
/**
|
|
117
|
+
* Returns `T | null` — the response from the main process once the
|
|
118
|
+
* asynchronous `invoke` call resolves.
|
|
119
|
+
*
|
|
120
|
+
* Send a message to the main process via channel and expect a result asynchronously.
|
|
121
|
+
*
|
|
122
|
+
* You need to provide `ipcRenderer` to this function.
|
|
123
|
+
*
|
|
124
|
+
* Map from @vueuse/electron `useIpcRendererInvoke`
|
|
125
|
+
* (`source/vueuse/packages/electron/useIpcRendererInvoke/`).
|
|
126
|
+
*
|
|
127
|
+
* React deviations:
|
|
128
|
+
* - upstream returns a `ShallowRef<T | null>`; a React hook cannot return a Vue
|
|
129
|
+
* ref, so this port returns the plain value `T | null` (same non-tuple
|
|
130
|
+
* precedent as `useQRCode`'s plain `string`);
|
|
131
|
+
* - the invoke runs in an effect, so changing `channel` / `args` re-invokes
|
|
132
|
+
* (upstream runs once per setup). The result is unmount-safe: a late
|
|
133
|
+
* resolution after unmount does not update state;
|
|
134
|
+
* - upstream leaves rejections unhandled and so does this port (no `.catch`).
|
|
135
|
+
* Callers that need error handling should call `ipcRenderer.invoke` directly,
|
|
136
|
+
* or use `useIpcRenderer().invoke`, which returns the raw `Promise<T>`.
|
|
137
|
+
*
|
|
138
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererinvokechannel-args
|
|
139
|
+
* @see https://vueuse.org/useIpcRendererInvoke
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* const result = useIpcRendererInvoke<string>('custom-channel', 'some data')
|
|
143
|
+
*
|
|
144
|
+
* @__NO_SIDE_EFFECTS__
|
|
145
|
+
*/
|
|
146
|
+
export declare function useIpcRendererInvoke<T>(ipcRenderer: IpcRenderer, channel: string, ...args: any[]): T | null;
|
|
147
|
+
/**
|
|
148
|
+
* Returns `T | null` — the response from the main process once the
|
|
149
|
+
* asynchronous `invoke` call resolves.
|
|
150
|
+
*
|
|
151
|
+
* Send a message to the main process via channel and expect a result asynchronously.
|
|
152
|
+
*
|
|
153
|
+
* `ipcRenderer` will be automatically gotten (`window.require('electron')`, i.e. `nodeIntegration`).
|
|
154
|
+
*
|
|
155
|
+
* Map from @vueuse/electron `useIpcRendererInvoke`
|
|
156
|
+
* (`source/vueuse/packages/electron/useIpcRendererInvoke/`).
|
|
157
|
+
*
|
|
158
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererinvokechannel-args
|
|
159
|
+
* @see https://vueuse.org/useIpcRendererInvoke
|
|
160
|
+
*
|
|
161
|
+
* @__NO_SIDE_EFFECTS__
|
|
162
|
+
*/
|
|
163
|
+
export declare function useIpcRendererInvoke<T>(channel: string, ...args: any[]): T | null;
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region useIpcRendererOn/index.d.ts
|
|
166
|
+
/**
|
|
167
|
+
* Listens to channel, when a new message arrives listener would be called with `listener(event, args...)`.
|
|
168
|
+
* [`ipcRenderer.removeListener`](https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererremovelistenerchannel-listener) automatically on unmounted.
|
|
169
|
+
*
|
|
170
|
+
* You need to provide `ipcRenderer` to this function.
|
|
171
|
+
*
|
|
172
|
+
* Map from @vueuse/electron `useIpcRendererOn`
|
|
173
|
+
* (`source/vueuse/packages/electron/useIpcRendererOn/`).
|
|
174
|
+
*
|
|
175
|
+
* React deviations:
|
|
176
|
+
* - upstream registers the listener at setup and removes it in
|
|
177
|
+
* `tryOnScopeDispose`; here a mount effect registers the **actual**
|
|
178
|
+
* `listener` you passed (no wrapper) and removes it by the same identity on
|
|
179
|
+
* unmount, so `removeListener(channel, listener)` always matches;
|
|
180
|
+
* - because `listener` is an effect dependency, a new listener identity
|
|
181
|
+
* re-registers (remove + add). Stabilise it with `useCallback`, or use the
|
|
182
|
+
* `useListener` protocol where it fits;
|
|
183
|
+
* - the instance is resolved at render, so a missing `ipcRenderer` throws
|
|
184
|
+
* synchronously instead of at effect time.
|
|
185
|
+
*
|
|
186
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendereronchannel-listener
|
|
187
|
+
* @see https://vueuse.org/useIpcRendererOn
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* useIpcRendererOn('custom-event', (event, ...args) => {
|
|
191
|
+
* console.log(args)
|
|
192
|
+
* })
|
|
193
|
+
*
|
|
194
|
+
* @__NO_SIDE_EFFECTS__
|
|
195
|
+
*/
|
|
196
|
+
export declare function useIpcRendererOn(ipcRenderer: IpcRenderer, channel: string, listener: IpcRendererListener): IpcRenderer;
|
|
197
|
+
/**
|
|
198
|
+
* Listens to channel, when a new message arrives listener would be called with `listener(event, args...)`.
|
|
199
|
+
* [`ipcRenderer.removeListener`](https://www.electronjs.org/docs/api/ipc-renderer#ipcrendererremovelistenerchannel-listener) automatically on unmounted.
|
|
200
|
+
*
|
|
201
|
+
* `ipcRenderer` will be automatically gotten (`window.require('electron')`, i.e. `nodeIntegration`).
|
|
202
|
+
*
|
|
203
|
+
* Map from @vueuse/electron `useIpcRendererOn`
|
|
204
|
+
* (`source/vueuse/packages/electron/useIpcRendererOn/`).
|
|
205
|
+
*
|
|
206
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrendereronchannel-listener
|
|
207
|
+
* @see https://vueuse.org/useIpcRendererOn
|
|
208
|
+
*
|
|
209
|
+
* @__NO_SIDE_EFFECTS__
|
|
210
|
+
*/
|
|
211
|
+
export declare function useIpcRendererOn(channel: string, listener: IpcRendererListener): IpcRenderer;
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region useZoomFactor/index.d.ts
|
|
214
|
+
/**
|
|
215
|
+
* Setter returned by `useZoomFactor`: validates the factor, writes it to
|
|
216
|
+
* `WebFrame.setZoomFactor` and updates the value returned by the hook.
|
|
217
|
+
*/
|
|
218
|
+
export type ZoomFactorSetter = (value: number) => void;
|
|
219
|
+
/**
|
|
220
|
+
* Reactive `WebFrame` zoom factor — React port of VueUse's `useZoomFactor`.
|
|
221
|
+
*
|
|
222
|
+
* Map from @vueuse/electron `useZoomFactor`
|
|
223
|
+
* (`source/vueuse/packages/electron/useZoomFactor/`). Upstream returns a
|
|
224
|
+
* writable Vue `Ref<number>` whose setter writes to
|
|
225
|
+
* `WebFrame.setZoomFactor`; this port follows the repo's state-like writable
|
|
226
|
+
* rule and returns the React tuple `[factor, setFactor]` instead.
|
|
227
|
+
*
|
|
228
|
+
* Adjustment for React:
|
|
229
|
+
* - the writable ref becomes `const [factor, setFactor] = useZoomFactor()` —
|
|
230
|
+
* `setFactor(value)` validates the value, calls
|
|
231
|
+
* `webFrame.setZoomFactor(value)` and updates the returned factor;
|
|
232
|
+
* - upstream's `watch(factor, cb, { immediate: true })` maps to a single
|
|
233
|
+
* effect keyed on `[webFrame, external factor]`: because the last-written ref
|
|
234
|
+
* starts as `null`, the immediate run is covered by the first effect run,
|
|
235
|
+
* which applies an explicitly passed factor once on mount and re-applies
|
|
236
|
+
* whenever the source value changes. The last factor written to `webFrame`
|
|
237
|
+
* is tracked in a ref, so a redundant render never re-writes the same
|
|
238
|
+
* factor;
|
|
239
|
+
* - upstream's `0` guard is kept verbatim — `useZoomFactor(webFrame, 0)` and
|
|
240
|
+
* `setFactor(0)` both throw `the factor must be greater than 0.0.`;
|
|
241
|
+
* - the `WebFrame` instance is resolved once per render through the internal
|
|
242
|
+
* `resolveWebFrame` helper: pass it explicitly, or enable `nodeIntegration`
|
|
243
|
+
* so it can be read from `window.require('electron').webFrame`;
|
|
244
|
+
* - `useZoomFactor()` reads the current factor from `getZoomFactor()`, while
|
|
245
|
+
* `useZoomFactor(2)` / `useZoomFactor(webFrame, 2)` apply the factor given
|
|
246
|
+
* as a plain number or a React ref.
|
|
247
|
+
*
|
|
248
|
+
* @see https://www.electronjs.org/docs/api/web-frame#webframesetzoomfactorfactor
|
|
249
|
+
* @see https://vueuse.org/useZoomFactor
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* const [factor, setFactor] = useZoomFactor()
|
|
253
|
+
* console.log(factor) // current zoom factor
|
|
254
|
+
* setFactor(2) // webFrame.setZoomFactor(2)
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* const [factor] = useZoomFactor(webFrame, 2) // apply an explicit factor on mount
|
|
258
|
+
*
|
|
259
|
+
* @__NO_SIDE_EFFECTS__
|
|
260
|
+
*/
|
|
261
|
+
export declare function useZoomFactor(factor?: RefOrValue<number>): [number, ZoomFactorSetter];
|
|
262
|
+
export declare function useZoomFactor(webFrame: WebFrame, factor?: RefOrValue<number>): [number, ZoomFactorSetter];
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region useZoomLevel/index.d.ts
|
|
265
|
+
/**
|
|
266
|
+
* Setter returned by `useZoomLevel`: writes the level to
|
|
267
|
+
* `WebFrame.setZoomLevel` and updates the value returned by the hook.
|
|
268
|
+
*/
|
|
269
|
+
export type ZoomLevelSetter = (value: number) => void;
|
|
270
|
+
/**
|
|
271
|
+
* Reactive `WebFrame` zoom level — React port of VueUse's `useZoomLevel`.
|
|
272
|
+
*
|
|
273
|
+
* Map from @vueuse/electron `useZoomLevel`
|
|
274
|
+
* (`source/vueuse/packages/electron/useZoomLevel/`). Upstream returns a
|
|
275
|
+
* writable Vue `Ref<number>` whose setter writes to `WebFrame.setZoomLevel`;
|
|
276
|
+
* this port follows the repo's state-like writable rule and returns the React
|
|
277
|
+
* tuple `[level, setLevel]` instead.
|
|
278
|
+
*
|
|
279
|
+
* Adjustment for React:
|
|
280
|
+
* - the writable ref becomes `const [level, setLevel] = useZoomLevel()` —
|
|
281
|
+
* `setLevel(value)` calls `webFrame.setZoomLevel(value)` and updates the
|
|
282
|
+
* returned level;
|
|
283
|
+
* - upstream's `watch(level, cb, { immediate: true })` maps to a single sync
|
|
284
|
+
* effect keyed on `[webFrame, external level]` whose first run applies an
|
|
285
|
+
* explicitly passed level (upstream's immediate run) and re-applies when
|
|
286
|
+
* the source value changes. The last level written to `webFrame` is tracked
|
|
287
|
+
* in a ref, so a redundant render never re-writes the same level;
|
|
288
|
+
* - a ref-like level source stays the single source of truth (upstream's
|
|
289
|
+
* `deepRef` passthrough): `setLevel` writes back to `ref.current`, so later
|
|
290
|
+
* renders re-read the updated value instead of a stale one;
|
|
291
|
+
* - upstream has no range guard for zoom levels, so neither has this port —
|
|
292
|
+
* `0` is a valid level (upstream's `useZoomFactor` guard does not apply);
|
|
293
|
+
* - the `WebFrame` instance is resolved once per render through the internal
|
|
294
|
+
* `resolveWebFrame` helper: pass it explicitly, or enable `nodeIntegration`
|
|
295
|
+
* so it can be read from `window.require('electron').webFrame`;
|
|
296
|
+
* - `useZoomLevel()` reads the current level from `getZoomLevel()`, while
|
|
297
|
+
* `useZoomLevel(2)` / `useZoomLevel(webFrame, 2)` apply the level given as a
|
|
298
|
+
* plain number or a React ref.
|
|
299
|
+
*
|
|
300
|
+
* @see https://www.electronjs.org/docs/api/web-frame#webframesetzoomlevellevel
|
|
301
|
+
* @see https://vueuse.org/useZoomLevel
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* const [level, setLevel] = useZoomLevel()
|
|
305
|
+
* console.log(level) // current zoom level
|
|
306
|
+
* setLevel(2) // webFrame.setZoomLevel(2)
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* const [level] = useZoomLevel(webFrame, 2) // apply an explicit level on mount
|
|
310
|
+
*
|
|
311
|
+
* @__NO_SIDE_EFFECTS__
|
|
312
|
+
*/
|
|
313
|
+
export declare function useZoomLevel(level?: RefOrValue<number>): [number, ZoomLevelSetter];
|
|
314
|
+
export declare function useZoomLevel(webFrame: WebFrame, level?: RefOrValue<number>): [number, ZoomLevelSetter];
|
|
315
|
+
//#endregion
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { isRefLike, toValue } from "@reause/shared";
|
|
3
|
+
//#region _resolve/index.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Resolve an `ipcRenderer` instance from the explicit argument or, when
|
|
6
|
+
* `nodeIntegration` is enabled, from `window.require('electron')`.
|
|
7
|
+
*
|
|
8
|
+
* Internal helper (not exported from `index.ts`). Resolution happens at
|
|
9
|
+
* render time — not inside an effect — so a missing instance throws
|
|
10
|
+
* synchronously, exactly like upstream.
|
|
11
|
+
*
|
|
12
|
+
* Each caller passes its own upstream-verbatim error message, because
|
|
13
|
+
* upstream `useIpcRenderer` and the `useIpcRendererInvoke` /
|
|
14
|
+
* `useIpcRendererOn` pair word it differently.
|
|
15
|
+
*/
|
|
16
|
+
function resolveIpcRenderer(ipcRenderer, message = "provide IpcRenderer module or enable nodeIntegration") {
|
|
17
|
+
var _require;
|
|
18
|
+
if (ipcRenderer) return ipcRenderer;
|
|
19
|
+
const resolved = typeof window !== "undefined" && typeof window.require === "function" ? (_require = window.require("electron")) === null || _require === void 0 ? void 0 : _require.ipcRenderer : void 0;
|
|
20
|
+
if (!resolved) throw new Error(message);
|
|
21
|
+
return resolved;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a `webFrame` instance from the explicit argument or, when
|
|
25
|
+
* `nodeIntegration` is enabled, from `window.require('electron')`.
|
|
26
|
+
*
|
|
27
|
+
* Internal helper (not exported from `index.ts`), reserved for the zoom
|
|
28
|
+
* hooks (`useZoomFactor` / `useZoomLevel`, #256 / #257).
|
|
29
|
+
*/
|
|
30
|
+
function resolveWebFrame(webFrame, message = "provide WebFrame module or enable nodeIntegration") {
|
|
31
|
+
var _require2;
|
|
32
|
+
if (webFrame) return webFrame;
|
|
33
|
+
const resolved = typeof window !== "undefined" && typeof window.require === "function" ? (_require2 = window.require("electron")) === null || _require2 === void 0 ? void 0 : _require2.webFrame : void 0;
|
|
34
|
+
if (!resolved) throw new Error(message);
|
|
35
|
+
return resolved;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region useIpcRenderer/index.tsx
|
|
39
|
+
/**
|
|
40
|
+
* Get the `ipcRenderer` module with all APIs.
|
|
41
|
+
*
|
|
42
|
+
* Map from @vueuse/electron `useIpcRenderer`
|
|
43
|
+
* (`source/vueuse/packages/electron/useIpcRenderer/`).
|
|
44
|
+
*
|
|
45
|
+
* React deviations:
|
|
46
|
+
* - upstream implements `on` by calling the `useIpcRendererOn` composable
|
|
47
|
+
* inside the method. Hooks cannot be called from callbacks, so `on`
|
|
48
|
+
* registers directly and tracks `{ ipc, channel, listener }` pairs; a mount
|
|
49
|
+
* effect's cleanup removes every tracked listener from **its captured
|
|
50
|
+
* instance** on unmount (the same auto-cleanup guarantee upstream gets from
|
|
51
|
+
* the effect scope). Like upstream, listeners are not re-registered when the
|
|
52
|
+
* instance changes — each stays on the instance it was registered with;
|
|
53
|
+
* - upstream `invoke` returns a `ShallowRef<T | null>`; this port returns the
|
|
54
|
+
* raw `Promise<T>`. Declarative async state is the job of the
|
|
55
|
+
* `useIpcRendererInvoke` hook — a method on a returned object cannot own
|
|
56
|
+
* component state;
|
|
57
|
+
* - upstream `sendSync` wraps the synchronous return in a `ShallowRef`; this
|
|
58
|
+
* port returns the value `T` directly.
|
|
59
|
+
*
|
|
60
|
+
* @see https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendtohostchannel-args
|
|
61
|
+
* @see https://vueuse.org/useIpcRenderer
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* const ipcRenderer = useIpcRenderer()
|
|
65
|
+
* ipcRenderer.on('custom-event', (event, ...args) => console.log(args))
|
|
66
|
+
* const result = await ipcRenderer.invoke<string>('custom-channel', 'some data')
|
|
67
|
+
*
|
|
68
|
+
* @__NO_SIDE_EFFECTS__
|
|
69
|
+
*/
|
|
70
|
+
function useIpcRenderer(ipcRenderer) {
|
|
71
|
+
const resolved = resolveIpcRenderer(ipcRenderer);
|
|
72
|
+
const trackedRef = useRef([]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
return () => {
|
|
75
|
+
trackedRef.current.forEach(({ ipc, channel, listener }) => {
|
|
76
|
+
ipc.removeListener(channel, listener);
|
|
77
|
+
});
|
|
78
|
+
trackedRef.current = [];
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
return {
|
|
82
|
+
on: (channel, listener) => {
|
|
83
|
+
resolved.on(channel, listener);
|
|
84
|
+
trackedRef.current.push({
|
|
85
|
+
ipc: resolved,
|
|
86
|
+
channel,
|
|
87
|
+
listener
|
|
88
|
+
});
|
|
89
|
+
return resolved;
|
|
90
|
+
},
|
|
91
|
+
once: resolved.once.bind(resolved),
|
|
92
|
+
removeListener: resolved.removeListener.bind(resolved),
|
|
93
|
+
removeAllListeners: resolved.removeAllListeners.bind(resolved),
|
|
94
|
+
send: resolved.send,
|
|
95
|
+
invoke: (channel, ...args) => resolved.invoke(channel, ...args),
|
|
96
|
+
sendSync: (channel, ...args) => resolved.sendSync(channel, ...args),
|
|
97
|
+
postMessage: resolved.postMessage,
|
|
98
|
+
sendTo: resolved.sendTo,
|
|
99
|
+
sendToHost: resolved.sendToHost
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region useIpcRendererInvoke/index.tsx
|
|
104
|
+
function useIpcRendererInvoke(...args) {
|
|
105
|
+
let ipcRenderer;
|
|
106
|
+
let channel;
|
|
107
|
+
let invokeArgs;
|
|
108
|
+
if (typeof args[0] === "string") [channel, ...invokeArgs] = args;
|
|
109
|
+
else [ipcRenderer, channel, ...invokeArgs] = args;
|
|
110
|
+
const resolved = resolveIpcRenderer(ipcRenderer, "please provide IpcRenderer module or enable nodeIntegration");
|
|
111
|
+
const [result, setResult] = useState(null);
|
|
112
|
+
const effectDeps = [
|
|
113
|
+
resolved,
|
|
114
|
+
channel,
|
|
115
|
+
...invokeArgs
|
|
116
|
+
];
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
let cancelled = false;
|
|
119
|
+
resolved.invoke(channel, ...invokeArgs).then((response) => {
|
|
120
|
+
if (!cancelled) setResult(response);
|
|
121
|
+
});
|
|
122
|
+
return () => {
|
|
123
|
+
cancelled = true;
|
|
124
|
+
};
|
|
125
|
+
}, effectDeps);
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region useIpcRendererOn/index.tsx
|
|
130
|
+
function useIpcRendererOn(...args) {
|
|
131
|
+
let ipcRenderer;
|
|
132
|
+
let channel;
|
|
133
|
+
let listener;
|
|
134
|
+
if (typeof args[0] === "string") [channel, listener] = args;
|
|
135
|
+
else [ipcRenderer, channel, listener] = args;
|
|
136
|
+
const resolved = resolveIpcRenderer(ipcRenderer, "please provide IpcRenderer module or enable nodeIntegration");
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
resolved.on(channel, listener);
|
|
139
|
+
return () => {
|
|
140
|
+
resolved.removeListener(channel, listener);
|
|
141
|
+
};
|
|
142
|
+
}, [
|
|
143
|
+
resolved,
|
|
144
|
+
channel,
|
|
145
|
+
listener
|
|
146
|
+
]);
|
|
147
|
+
return resolved;
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region useZoomFactor/index.tsx
|
|
151
|
+
const ZOOM_FACTOR_ERROR = "the factor must be greater than 0.0.";
|
|
152
|
+
function assertZoomFactor(value) {
|
|
153
|
+
if (value === 0) throw new Error(ZOOM_FACTOR_ERROR);
|
|
154
|
+
}
|
|
155
|
+
function isFactorArgument(value) {
|
|
156
|
+
return typeof value === "number" || isRefLike(value);
|
|
157
|
+
}
|
|
158
|
+
function useZoomFactor(webFrameOrFactor, factor) {
|
|
159
|
+
const webFrame = isFactorArgument(webFrameOrFactor) ? void 0 : webFrameOrFactor;
|
|
160
|
+
const externalFactor = isFactorArgument(webFrameOrFactor) ? webFrameOrFactor : factor;
|
|
161
|
+
const instance = resolveWebFrame(webFrame);
|
|
162
|
+
const resolvedFactor = externalFactor === void 0 ? void 0 : toValue(externalFactor);
|
|
163
|
+
if (resolvedFactor !== void 0) assertZoomFactor(resolvedFactor);
|
|
164
|
+
const [value, setValue] = useState(() => resolvedFactor === void 0 ? instance.getZoomFactor() : resolvedFactor);
|
|
165
|
+
const lastAppliedRef = useRef(null);
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (resolvedFactor === void 0 || resolvedFactor === lastAppliedRef.current) return;
|
|
168
|
+
assertZoomFactor(resolvedFactor);
|
|
169
|
+
instance.setZoomFactor(resolvedFactor);
|
|
170
|
+
lastAppliedRef.current = resolvedFactor;
|
|
171
|
+
setValue(resolvedFactor);
|
|
172
|
+
}, [instance, resolvedFactor]);
|
|
173
|
+
return [value, useCallback((nextFactor) => {
|
|
174
|
+
assertZoomFactor(nextFactor);
|
|
175
|
+
instance.setZoomFactor(nextFactor);
|
|
176
|
+
lastAppliedRef.current = nextFactor;
|
|
177
|
+
setValue(nextFactor);
|
|
178
|
+
}, [instance])];
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region useZoomLevel/index.tsx
|
|
182
|
+
function isLevelArgument(value) {
|
|
183
|
+
return typeof value === "number" || isRefLike(value);
|
|
184
|
+
}
|
|
185
|
+
function useZoomLevel(webFrameOrLevel, level) {
|
|
186
|
+
const webFrame = isLevelArgument(webFrameOrLevel) ? void 0 : webFrameOrLevel;
|
|
187
|
+
const externalLevel = isLevelArgument(webFrameOrLevel) ? webFrameOrLevel : level;
|
|
188
|
+
const instance = resolveWebFrame(webFrame);
|
|
189
|
+
const resolvedLevel = externalLevel === void 0 ? void 0 : toValue(externalLevel);
|
|
190
|
+
const [value, setValue] = useState(() => resolvedLevel === void 0 ? instance.getZoomLevel() : resolvedLevel);
|
|
191
|
+
const lastAppliedRef = useRef(null);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (resolvedLevel === void 0 || resolvedLevel === lastAppliedRef.current) return;
|
|
194
|
+
instance.setZoomLevel(resolvedLevel);
|
|
195
|
+
lastAppliedRef.current = resolvedLevel;
|
|
196
|
+
setValue(resolvedLevel);
|
|
197
|
+
}, [instance, resolvedLevel]);
|
|
198
|
+
return [value, useCallback((nextLevel) => {
|
|
199
|
+
instance.setZoomLevel(nextLevel);
|
|
200
|
+
lastAppliedRef.current = nextLevel;
|
|
201
|
+
setValue(nextLevel);
|
|
202
|
+
if (isRefLike(externalLevel)) externalLevel.current = nextLevel;
|
|
203
|
+
}, [instance, externalLevel])];
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
export { useIpcRenderer, useIpcRendererInvoke, useIpcRendererOn, useZoomFactor, useZoomLevel };
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reause/electron",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"description": "Electron renderer process modules — React port of @vueuse/electron",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/index.js",
|
|
10
|
+
"./*": "./dist/*",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"electron": ">=9.0.0",
|
|
21
|
+
"react": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@reause/shared": "0.1.2"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown"
|
|
28
|
+
}
|
|
29
|
+
}
|