@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 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.
@@ -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
+ }