@witchcraft/nuxt-electron 0.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.
Files changed (82) hide show
  1. package/README.md +299 -0
  2. package/dist/module.d.mts +117 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +362 -0
  5. package/dist/runtime/components/ElectronWindowControls.d.vue.ts +31 -0
  6. package/dist/runtime/components/ElectronWindowControls.vue +67 -0
  7. package/dist/runtime/components/ElectronWindowControls.vue.d.ts +31 -0
  8. package/dist/runtime/components/WindowControls/CloseButton.d.vue.ts +16 -0
  9. package/dist/runtime/components/WindowControls/CloseButton.vue +54 -0
  10. package/dist/runtime/components/WindowControls/CloseButton.vue.d.ts +16 -0
  11. package/dist/runtime/components/WindowControls/MaximizeButton.d.vue.ts +16 -0
  12. package/dist/runtime/components/WindowControls/MaximizeButton.vue +33 -0
  13. package/dist/runtime/components/WindowControls/MaximizeButton.vue.d.ts +16 -0
  14. package/dist/runtime/components/WindowControls/MinimizeButton.d.vue.ts +16 -0
  15. package/dist/runtime/components/WindowControls/MinimizeButton.vue +40 -0
  16. package/dist/runtime/components/WindowControls/MinimizeButton.vue.d.ts +16 -0
  17. package/dist/runtime/components/WindowControls/PinButton.d.vue.ts +27 -0
  18. package/dist/runtime/components/WindowControls/PinButton.vue +51 -0
  19. package/dist/runtime/components/WindowControls/PinButton.vue.d.ts +27 -0
  20. package/dist/runtime/electron/apiBuilder.d.ts +8 -0
  21. package/dist/runtime/electron/apiBuilder.js +9 -0
  22. package/dist/runtime/electron/createBroadcastHandlers.d.ts +4 -0
  23. package/dist/runtime/electron/createBroadcastHandlers.js +30 -0
  24. package/dist/runtime/electron/createBroadcaster.d.ts +2 -0
  25. package/dist/runtime/electron/createBroadcaster.js +7 -0
  26. package/dist/runtime/electron/createNuxtFileProtocolHandler.d.ts +14 -0
  27. package/dist/runtime/electron/createNuxtFileProtocolHandler.js +20 -0
  28. package/dist/runtime/electron/createWindowControlsApi.d.ts +19 -0
  29. package/dist/runtime/electron/createWindowControlsApi.js +6 -0
  30. package/dist/runtime/electron/createWindowControlsApiHandler.d.ts +4 -0
  31. package/dist/runtime/electron/createWindowControlsApiHandler.js +24 -0
  32. package/dist/runtime/electron/getEventWindow.d.ts +11 -0
  33. package/dist/runtime/electron/getEventWindow.js +8 -0
  34. package/dist/runtime/electron/getPaths.d.ts +27 -0
  35. package/dist/runtime/electron/getPaths.js +33 -0
  36. package/dist/runtime/electron/getPreloadMeta.d.ts +25 -0
  37. package/dist/runtime/electron/getPreloadMeta.js +7 -0
  38. package/dist/runtime/electron/index.d.ts +16 -0
  39. package/dist/runtime/electron/index.js +16 -0
  40. package/dist/runtime/electron/promisifyApi.d.ts +40 -0
  41. package/dist/runtime/electron/promisifyApi.js +41 -0
  42. package/dist/runtime/electron/promisifyReply.d.ts +8 -0
  43. package/dist/runtime/electron/promisifyReply.js +27 -0
  44. package/dist/runtime/electron/registerDevtoolsShortcuts.d.ts +2 -0
  45. package/dist/runtime/electron/registerDevtoolsShortcuts.js +10 -0
  46. package/dist/runtime/electron/static.d.ts +12 -0
  47. package/dist/runtime/electron/static.js +8 -0
  48. package/dist/runtime/electron/types.d.ts +1 -0
  49. package/dist/runtime/electron/types.js +0 -0
  50. package/dist/runtime/electron/useDevDataDir.d.ts +1 -0
  51. package/dist/runtime/electron/useDevDataDir.js +8 -0
  52. package/dist/runtime/electron/useNuxtRuntimeConfig.d.ts +2 -0
  53. package/dist/runtime/electron/useNuxtRuntimeConfig.js +4 -0
  54. package/dist/runtime/utils/isElectron.d.ts +9 -0
  55. package/dist/runtime/utils/isElectron.js +3 -0
  56. package/dist/types.d.mts +3 -0
  57. package/genDevDesktop.js +47 -0
  58. package/package.json +93 -0
  59. package/src/module.ts +549 -0
  60. package/src/runtime/components/ElectronWindowControls.vue +94 -0
  61. package/src/runtime/components/WindowControls/CloseButton.vue +56 -0
  62. package/src/runtime/components/WindowControls/MaximizeButton.vue +35 -0
  63. package/src/runtime/components/WindowControls/MinimizeButton.vue +42 -0
  64. package/src/runtime/components/WindowControls/PinButton.vue +56 -0
  65. package/src/runtime/electron/apiBuilder.ts +27 -0
  66. package/src/runtime/electron/createBroadcastHandlers.ts +36 -0
  67. package/src/runtime/electron/createBroadcaster.ts +11 -0
  68. package/src/runtime/electron/createNuxtFileProtocolHandler.ts +42 -0
  69. package/src/runtime/electron/createWindowControlsApi.ts +27 -0
  70. package/src/runtime/electron/createWindowControlsApiHandler.ts +34 -0
  71. package/src/runtime/electron/getEventWindow.ts +19 -0
  72. package/src/runtime/electron/getPaths.ts +68 -0
  73. package/src/runtime/electron/getPreloadMeta.ts +36 -0
  74. package/src/runtime/electron/index.ts +17 -0
  75. package/src/runtime/electron/promisifyApi.ts +102 -0
  76. package/src/runtime/electron/promisifyReply.ts +49 -0
  77. package/src/runtime/electron/registerDevtoolsShortcuts.ts +12 -0
  78. package/src/runtime/electron/static.ts +14 -0
  79. package/src/runtime/electron/types.ts +1 -0
  80. package/src/runtime/electron/useDevDataDir.ts +8 -0
  81. package/src/runtime/electron/useNuxtRuntimeConfig.ts +7 -0
  82. package/src/runtime/utils/isElectron.ts +11 -0
@@ -0,0 +1,362 @@
1
+ import { crop } from '@alanscodelog/utils/crop';
2
+ import { run } from '@alanscodelog/utils/run';
3
+ import { defineNuxtModule, useLogger, createResolver, addComponentsDir, installModule, addTemplate, extendRouteRules, addImportsDir } from '@nuxt/kit';
4
+ import { nuxtRemoveUneededPages, nuxtFileBasedRouting, nuxtRerouteOutputTo, createConstantCaseVariables } from '@witchcraft/nuxt-utils/utils';
5
+ import { defu } from 'defu';
6
+ import fs from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import { startup, build } from 'vite-plugin-electron';
9
+ import { notBundle } from 'vite-plugin-electron/plugin';
10
+ import { externalizeDeps } from 'vite-plugin-externalize-deps';
11
+
12
+ startup.exit = async () => {
13
+ if ("electronApp" in process) {
14
+ try {
15
+ const app = process.electronApp;
16
+ app.removeAllListeners();
17
+ process.kill(app.pid);
18
+ } catch (e) {
19
+ console.error("Could not kill electron instance/s.");
20
+ console.error(e);
21
+ }
22
+ }
23
+ };
24
+ const module = defineNuxtModule({
25
+ meta: {
26
+ name: "electron",
27
+ configKey: "electron"
28
+ },
29
+ // Default configuration options of the Nuxt module
30
+ defaults: {
31
+ srcDir: "~~/app-electron",
32
+ electronBuildDir: "~~/.dist/electron",
33
+ nonElectronNuxtBuildDir: "~~/.dist/web/.output",
34
+ devUserDataDir: "~~/.user-data-dir",
35
+ electronRoute: "/app",
36
+ autoOpen: process.env.AUTO_OPEN?.includes("electron"),
37
+ electronBuildPackScript: "npm run build:electron:pack",
38
+ additionalRoutes: [],
39
+ extraCliArgs: [],
40
+ enable: true,
41
+ electronOnlyRuntimeConfig: {},
42
+ usePreloadScript: true,
43
+ electronViteOptions: {},
44
+ additionalElectronVariables: {},
45
+ additionalViteDefinesToCopy: [],
46
+ notBundleOptions: {}
47
+ },
48
+ async setup(options, nuxt) {
49
+ if (!options.enable) {
50
+ return;
51
+ }
52
+ const moduleName = "@witchcraft/nuxt-electron";
53
+ const logger = useLogger(moduleName);
54
+ const { resolvePath, resolve } = createResolver(import.meta.url);
55
+ addComponentsDir({
56
+ global: true,
57
+ path: resolve("runtime/components")
58
+ });
59
+ await installModule("@witchcraft/ui/nuxt", nuxt.options.witchcraftUi);
60
+ addTemplate({
61
+ filename: "witchcraft-electron.css",
62
+ write: true,
63
+ getContents: () => crop`
64
+ @source "${resolve("runtime/components")}";
65
+ `
66
+ });
67
+ const isDev = nuxt.options.dev;
68
+ const srcDir = await resolvePath(options.srcDir, nuxt.options.alias);
69
+ const nonElectronNuxtBuildDir = await resolvePath(options.nonElectronNuxtBuildDir, nuxt.options.alias);
70
+ const electronRootBuildDir = await resolvePath(options.electronBuildDir, nuxt.options.alias);
71
+ const relativeElectronDir = path.relative(nuxt.options.rootDir, electronRootBuildDir);
72
+ const electronNuxtDir = path.join(relativeElectronDir, ".output");
73
+ const electronBuildDir = path.join(relativeElectronDir, "build");
74
+ const electronProdUrl = `${options.electronRoute}/index.html`;
75
+ const electronRoute = options.electronRoute;
76
+ const electronNuxtPublicDir = path.join(electronNuxtDir, "public");
77
+ const mainScriptPath = path.join(srcDir, "main.ts");
78
+ const preloadScriptPath = path.join(srcDir, "preload.ts");
79
+ const hasMainScript = await fs.stat(mainScriptPath).then(() => true).catch(() => false);
80
+ const hasPreloadScript = !options.usePreloadScript || await fs.stat(preloadScriptPath).then(() => true).catch(() => false);
81
+ const hasScripts = hasMainScript && hasPreloadScript;
82
+ if (!hasScripts) {
83
+ logger.warn(`Missing electron scripts: ${[hasMainScript ? "" : "main.ts", hasPreloadScript ? "" : "preload.ts"].join(", ")}. Skipping electron build.`);
84
+ }
85
+ const isElectronBuild = process.env.BUILD_ELECTRON === "true" && hasScripts;
86
+ const skipElectronPack = process.env.SKIP_ELECTRON_PACK === "true";
87
+ const autoOpen = !!(options.autoOpen && hasScripts && isDev);
88
+ const useWatch = nuxt.options.dev;
89
+ const devUserDataDir = options.devUserDataDir && await resolvePath(options.devUserDataDir, nuxt.options.alias);
90
+ if (devUserDataDir) {
91
+ if (!await fs.stat(devUserDataDir).then(() => true).catch(() => false)) {
92
+ await fs.mkdir(devUserDataDir, {
93
+ recursive: true
94
+ });
95
+ }
96
+ }
97
+ logger.debug({
98
+ isDev,
99
+ useWatch,
100
+ srcDir,
101
+ prodNonElectronNuxtDir: nonElectronNuxtBuildDir,
102
+ electronRootBuildDir,
103
+ relativeElectronDir,
104
+ electronNuxtDir,
105
+ electronBuildDir,
106
+ electronProdUrl,
107
+ electronRoute,
108
+ electronNuxtPublicDir,
109
+ mainScriptPath,
110
+ preloadScriptPath,
111
+ isElectronBuild,
112
+ skipElectronPack,
113
+ autoOpen,
114
+ devUserDataDir
115
+ });
116
+ let resolveGetViteServer;
117
+ const viteServerPromise = new Promise((resolve2) => {
118
+ resolveGetViteServer = resolve2;
119
+ });
120
+ nuxt.hook("vite:serverCreated", (server) => {
121
+ logger.info(`Resolved vite server.`);
122
+ resolveGetViteServer(server);
123
+ });
124
+ const viteServerUrl = new Promise((resolve2) => {
125
+ nuxt.hook("build:before", () => {
126
+ resolve2(void 0);
127
+ });
128
+ nuxt.hook("listen", (_server, listener) => {
129
+ logger.info(`Resolved server url.`, listener.url);
130
+ resolve2(listener.url);
131
+ });
132
+ });
133
+ const viteConfigPromise = new Promise((resolve2) => {
134
+ nuxt.hook("vite:configResolved", (config) => {
135
+ resolve2(config);
136
+ });
137
+ });
138
+ let maybeWatchers;
139
+ let started = false;
140
+ nuxt.hook("vite:extendConfig", (config) => {
141
+ config.define ??= {};
142
+ config.define["import.meta.electron"] = "false";
143
+ config.define["process.electron"] = "false";
144
+ });
145
+ const buildElectron = async () => {
146
+ if (maybeWatchers || started) return;
147
+ started = true;
148
+ const viteConfig = await viteConfigPromise;
149
+ const electronRuntimeConfig = defu(
150
+ options.electronOnlyRuntimeConfig,
151
+ nuxt.options.runtimeConfig.public
152
+ );
153
+ const additionalElectronVariables = defu(
154
+ options.additionalElectronVariables,
155
+ nuxt.options.electron.additionalElectronVariables
156
+ );
157
+ const additionalViteDefinesToCopy = [
158
+ ...options.additionalViteDefinesToCopy,
159
+ ...nuxt.options.electron.additionalViteDefinesToCopy ?? []
160
+ ];
161
+ const copyFromVite = [
162
+ "__NUXT_VERSION__",
163
+ "process.dev",
164
+ "import.meta.dev",
165
+ "process.test",
166
+ "import.meta.test",
167
+ ...additionalViteDefinesToCopy
168
+ ];
169
+ const electronVariables = {
170
+ "process.electron": true,
171
+ "import.meta.electron": true,
172
+ ...Object.fromEntries(
173
+ copyFromVite.map((v) => [v, viteConfig.define[v]])
174
+ ),
175
+ ...createConstantCaseVariables({
176
+ electronRoute,
177
+ electronProdUrl,
178
+ electronNuxtDir,
179
+ electronNuxtPublicDir,
180
+ electronBuildDir,
181
+ // ...options.additionalElectronVariables,
182
+ // nuxt's runtimeConfig cannot be used in electron's main since it's built seperately
183
+ // also we must stringify ourselves since escaped double quotes are not preserved in the final output :/
184
+ electronRuntimeConfig: JSON.stringify(electronRuntimeConfig).replaceAll("\\", "\\\\")
185
+ }, "process.env."),
186
+ // wut am having issue with just using STATIC. directly ???
187
+ ...createConstantCaseVariables(
188
+ additionalElectronVariables,
189
+ "process.env.",
190
+ { autoquote: false }
191
+ )
192
+ };
193
+ const electronViteOptions = defu(
194
+ {
195
+ build: {
196
+ // must be false or preload can get deleted when vite rebuilds
197
+ emptyOutDir: false
198
+ }
199
+ },
200
+ options.electronViteOptions,
201
+ {
202
+ build: {
203
+ outDir: electronBuildDir,
204
+ minify: false
205
+ },
206
+ define: electronVariables,
207
+ resolve: {
208
+ alias: nuxt.options.alias,
209
+ extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"]
210
+ },
211
+ plugins: [externalizeDeps()]
212
+ }
213
+ );
214
+ const electronCliArgs = [
215
+ ".",
216
+ ...process.env.NODE_ENV !== "production" && devUserDataDir ? [
217
+ "--user-data-dir",
218
+ devUserDataDir
219
+ ] : [],
220
+ ...options.extraCliArgs ?? []
221
+ ];
222
+ const builds = [
223
+ {
224
+ entry: mainScriptPath,
225
+ onStart: autoOpen ? async () => {
226
+ await startup([...electronCliArgs]);
227
+ } : void 0
228
+ },
229
+ ...options.usePreloadScript ? [{
230
+ entry: preloadScriptPath,
231
+ onStart: async () => {
232
+ (await viteServerPromise).hot.send({ type: "full-reload" });
233
+ },
234
+ build: {
235
+ rollupOptions: {
236
+ output: {
237
+ inlineDynamicImports: true
238
+ }
239
+ }
240
+ }
241
+ }] : []
242
+ ].map((entry) => ({
243
+ vite: {
244
+ mode: process.env.NODE_ENV,
245
+ ...electronViteOptions,
246
+ build: {
247
+ watch: useWatch ? {
248
+ include: [`${srcDir}/**/*`]
249
+ } : null,
250
+ lib: {
251
+ entry: entry.entry,
252
+ formats: entry.entry.includes("preload") ? ["cjs"] : ["es"],
253
+ fileName: () => entry.entry.includes("preload") ? "[name].cjs" : "[name].mjs"
254
+ },
255
+ ...electronViteOptions.build
256
+ },
257
+ plugins: [
258
+ autoOpen ? {
259
+ name: "plugin-start-electron",
260
+ async closeBundle() {
261
+ void entry.onStart?.();
262
+ }
263
+ } : void 0,
264
+ // not bundle breaks preload because it tries to required from node_modules
265
+ // and sandboxed windows can't do that
266
+ ...!isElectronBuild && !entry.entry.includes("preload") ? [notBundle(options.notBundleOptions)] : [],
267
+ ...electronViteOptions.plugins ?? []
268
+ ]
269
+ }
270
+ }));
271
+ logger.debug(builds);
272
+ const devUrl = await viteServerUrl;
273
+ if (devUrl) {
274
+ Object.assign(process.env, {
275
+ VITE_DEV_SERVER_URL: devUrl.slice(0, -1)
276
+ });
277
+ }
278
+ logger.debug({
279
+ electronViteOptions,
280
+ prodNonElectronNuxtDir: nonElectronNuxtBuildDir,
281
+ electronVariables,
282
+ electronRuntimeConfig,
283
+ electronCliArgs,
284
+ devUrl
285
+ });
286
+ maybeWatchers = await Promise.all(builds.map(
287
+ async (config) => build(config).then((res) => {
288
+ logger.info(`Build done.`);
289
+ return res;
290
+ }).catch((err) => {
291
+ logger.error(`Build failed.`, err);
292
+ process.exit(1);
293
+ })
294
+ ));
295
+ if (useWatch) {
296
+ for (const maybeWatcher of maybeWatchers) {
297
+ if (maybeWatcher && "on" in maybeWatcher) {
298
+ maybeWatcher.on("change", (e) => {
299
+ logger.info(`Detected change in: ${e}`);
300
+ });
301
+ }
302
+ }
303
+ }
304
+ };
305
+ if (autoOpen) {
306
+ nuxt.hook("close", async () => {
307
+ logger.info(`Killing`);
308
+ await startup?.exit();
309
+ });
310
+ nuxt.hook("restart", async () => {
311
+ logger.info(`Killing and Restarting`);
312
+ await startup?.exit();
313
+ });
314
+ }
315
+ logger.info(`Building Electron: ${isElectronBuild}`);
316
+ if (isElectronBuild) {
317
+ nuxt.hook("build:manifest", (manifest) => {
318
+ for (const key of Object.keys(manifest)) {
319
+ manifest[key].dynamicImports = [];
320
+ }
321
+ });
322
+ nuxtRemoveUneededPages(nuxt, ["/", electronRoute, ...options.additionalRoutes]);
323
+ extendRouteRules(electronRoute, { ssr: false, prerender: true }, { override: true });
324
+ nuxt.options.router = defu(
325
+ nuxtFileBasedRouting().router,
326
+ nuxt.options.router ?? {}
327
+ );
328
+ nuxtRerouteOutputTo(nuxt, electronNuxtDir);
329
+ nuxt.hook("close", async () => {
330
+ logger.info(`Building Electron`);
331
+ await buildElectron();
332
+ if (!skipElectronPack) {
333
+ logger.info(`Packing Electron`);
334
+ const buildCommand = run(options.electronBuildPackScript, {
335
+ stdio: "inherit"
336
+ });
337
+ await buildCommand.promise.catch((err) => {
338
+ logger.error("Error building electron.", err);
339
+ process.exit(1);
340
+ });
341
+ } else {
342
+ logger.info(`Skipping Electron Pack`);
343
+ }
344
+ logger.info(`Done Building Electron`);
345
+ });
346
+ } else {
347
+ if (isDev) {
348
+ nuxt.hook("ready", async () => {
349
+ logger.info("electron - ready");
350
+ void buildElectron();
351
+ });
352
+ logger.info(`Watching Electron`);
353
+ } else {
354
+ logger.info(`Skipping Electron Build`);
355
+ }
356
+ nuxtRerouteOutputTo(nuxt, nonElectronNuxtBuildDir);
357
+ }
358
+ addImportsDir(resolve("runtime/utils"));
359
+ }
360
+ });
361
+
362
+ export { module as default };
@@ -0,0 +1,31 @@
1
+ import { type Component } from "vue";
2
+ import type { WindowControlsApi } from "../electron/types.js.js";
3
+ type __VLS_Props = {
4
+ borderWidth?: string;
5
+ buttonSize?: string;
6
+ borderRadius?: string;
7
+ /**
8
+ *
9
+ * Replace any of the default buttons with your own components.
10
+ *
11
+ * Note that you can get the existing button components (import from `/components/WIndowControls/[name]`) and pass a slot to change the icon then pass your new component here.
12
+ *
13
+ * If using a completely custom component, it must emit an `action` event with the action name as the value.
14
+ *
15
+ * This wrapper component sets the following css variables if you need them:
16
+ * `--electron-wc-size`
17
+ * `--electron-wc-border`
18
+ * `--electron-wc-rounded`
19
+ * `--electron-wc-diagonal` (useful for creating the close cross)
20
+ */
21
+ components?: Partial<Record<"CloseButton" | "MinimizeButton" | "MaximizeButton" | "PinButton", Component>>;
22
+ buttonsOrder?: ("CloseButton" | "MinimizeButton" | "MaximizeButton" | "PinButton")[];
23
+ handler?: WindowControlsApi;
24
+ };
25
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
26
+ borderWidth: string;
27
+ buttonSize: string;
28
+ borderRadius: string;
29
+ buttonsOrder: ("CloseButton" | "MinimizeButton" | "MaximizeButton" | "PinButton")[];
30
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ export default _default;
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <ClientOnly>
3
+ <div
4
+ v-if="isElectron()"
5
+ :class="twMerge(`
6
+ flex
7
+ items-center
8
+ gap-2
9
+ `, $attrs.class)"
10
+ v-bind="{ ...$attrs, class: void 0 }"
11
+ :style="`
12
+ --electron-wc-size:${props.buttonSize};
13
+ --electron-wc-border:${props.borderWidth};
14
+ --electron-wc-rounded:${props.borderRadius};
15
+ --electron-wc-diagonal:calc((var(--electron-wc-size) - var(--electron-wc-border)/2)*sqrt(2));
16
+ `"
17
+ >
18
+ <template
19
+ v-for="button in buttonsOrder"
20
+ :key="button"
21
+ >
22
+ <component
23
+ :is="componentsMap[button]"
24
+ @action="actionHandler($event)"
25
+ />
26
+ </template>
27
+ </div>
28
+ </ClientOnly>
29
+ </template>
30
+
31
+ <script setup>
32
+ import { computed, useAttrs } from "vue";
33
+ import CloseButton from "./WindowControls/CloseButton.vue";
34
+ import MaximizeButton from "./WindowControls/MaximizeButton.vue";
35
+ import MinimizeButton from "./WindowControls/MinimizeButton.vue";
36
+ import PinButton from "./WindowControls/PinButton.vue";
37
+ import { twMerge } from "#imports";
38
+ import { isElectron } from "../utils/isElectron.js";
39
+ const $attrs = useAttrs();
40
+ const props = defineProps({
41
+ borderWidth: { type: String, required: false, default: "2px" },
42
+ buttonSize: { type: String, required: false, default: "15px" },
43
+ borderRadius: { type: String, required: false, default: "1.5px" },
44
+ components: { type: Object, required: false },
45
+ buttonsOrder: { type: Array, required: false, default: () => ["PinButton", "MinimizeButton", "MaximizeButton", "CloseButton"] },
46
+ handler: { type: Function, required: false }
47
+ });
48
+ const componentsMap = computed(() => ({
49
+ CloseButton,
50
+ MinimizeButton,
51
+ MaximizeButton,
52
+ PinButton,
53
+ ...props.components
54
+ }));
55
+ const actionHandler = computed(() => {
56
+ if (!isElectron()) return void 0;
57
+ if (!props.handler) {
58
+ const defaultHandlerPath = window.electron?.api?.ui?.windowAction;
59
+ if (defaultHandlerPath) {
60
+ return defaultHandlerPath;
61
+ } else {
62
+ console.warn("No ElectronWindowControls handler specified and could not find default handler at `window.electron.api.ui.windowAction`");
63
+ }
64
+ }
65
+ return props.handler;
66
+ });
67
+ </script>
@@ -0,0 +1,31 @@
1
+ import { type Component } from "vue";
2
+ import type { WindowControlsApi } from "../electron/types.js.js";
3
+ type __VLS_Props = {
4
+ borderWidth?: string;
5
+ buttonSize?: string;
6
+ borderRadius?: string;
7
+ /**
8
+ *
9
+ * Replace any of the default buttons with your own components.
10
+ *
11
+ * Note that you can get the existing button components (import from `/components/WIndowControls/[name]`) and pass a slot to change the icon then pass your new component here.
12
+ *
13
+ * If using a completely custom component, it must emit an `action` event with the action name as the value.
14
+ *
15
+ * This wrapper component sets the following css variables if you need them:
16
+ * `--electron-wc-size`
17
+ * `--electron-wc-border`
18
+ * `--electron-wc-rounded`
19
+ * `--electron-wc-diagonal` (useful for creating the close cross)
20
+ */
21
+ components?: Partial<Record<"CloseButton" | "MinimizeButton" | "MaximizeButton" | "PinButton", Component>>;
22
+ buttonsOrder?: ("CloseButton" | "MinimizeButton" | "MaximizeButton" | "PinButton")[];
23
+ handler?: WindowControlsApi;
24
+ };
25
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
26
+ borderWidth: string;
27
+ buttonSize: string;
28
+ borderRadius: string;
29
+ buttonsOrder: ("CloseButton" | "MinimizeButton" | "MaximizeButton" | "PinButton")[];
30
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ export default _default;
@@ -0,0 +1,16 @@
1
+ declare var __VLS_10: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_10) => any;
4
+ };
5
+ declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ action: (action: "close") => any;
7
+ }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
+ onAction?: ((action: "close") => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <WButton
3
+ :border="false"
4
+ aria-label="Close"
5
+ class="
6
+ p-0
7
+ [&:hover_.default-icon:after]:bg-accent-500
8
+ [&:hover_.default-icon:before]:bg-accent-500
9
+ [&:hover_.default-icon:after]:shadow-xs
10
+ [&:hover_.default-icon:after]:shadow-fg/50
11
+ [&:hover_.default-icon:before]:shadow-xs
12
+ [&:hover_.default-icon:before]:shadow-fg/50
13
+
14
+ "
15
+ @click="emit('action', 'close')"
16
+ >
17
+ <slot>
18
+ <div
19
+ class="
20
+ default-icon
21
+ relative
22
+ w-[calc(var(--electron-wc-size)-var(--electron-wc-border)/2)]
23
+ h-[calc(var(--electron-wc-size)-var(--electron-wc-border)/2)]
24
+ before:absolute
25
+ before:content-['']
26
+ before:left-0
27
+ before:rotate-45
28
+ before:origin-left
29
+ before:top-[calc(var(--electron-wc-border)/-2)]
30
+ before:w-[var(--electron-wc-diagonal)]
31
+ before:h-[var(--electron-wc-border)]
32
+ before:rounded-(--electron-wc-rounded)
33
+ before:bg-fg
34
+ dark:before:bg-bg
35
+ after:absolute
36
+ after:content-['']
37
+ after:origin-top
38
+ after:right-[calc(var(--electron-wc-border)/-2)]
39
+ after:top-0
40
+ after:rotate-[45deg]
41
+ after:h-[var(--electron-wc-diagonal)]
42
+ after:w-[var(--electron-wc-border)]
43
+ after:bg-fg
44
+ after:rounded-(--electron-wc-rounded)
45
+ dark:after:bg-bg
46
+ "
47
+ />
48
+ </slot>
49
+ </WButton>
50
+ </template>
51
+
52
+ <script setup>
53
+ const emit = defineEmits(["action"]);
54
+ </script>
@@ -0,0 +1,16 @@
1
+ declare var __VLS_10: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_10) => any;
4
+ };
5
+ declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ action: (action: "close") => any;
7
+ }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
+ onAction?: ((action: "close") => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -0,0 +1,16 @@
1
+ declare var __VLS_10: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_10) => any;
4
+ };
5
+ declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ action: (action: "toggleMaximize") => any;
7
+ }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
+ onAction?: ((action: "toggleMaximize") => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <WButton
3
+ :border="false"
4
+ aria-label="Toggle Maximize"
5
+ class="
6
+ p-0
7
+ [&:hover_.default-icon]:border-accent-500
8
+ [&:hover_.default-icon]:shadow-xs
9
+ [&:hover_.default-icon]:shadow-fg/50
10
+
11
+ "
12
+ @click="emit('action', 'toggleMaximize')"
13
+ >
14
+ <slot>
15
+ <div
16
+ class="
17
+ default-icon
18
+ border-fg
19
+ dark:border-bg
20
+ hover:border-accent-500
21
+ border-[length:var(--electron-wc-border)]
22
+ rounded-(--electron-wc-rounded)
23
+ w-[var(--electron-wc-size)]
24
+ h-[var(--electron-wc-size)]
25
+ "
26
+ />
27
+ </slot>
28
+ </WButton>
29
+ </template>
30
+
31
+ <script setup>
32
+ const emit = defineEmits(["action"]);
33
+ </script>
@@ -0,0 +1,16 @@
1
+ declare var __VLS_10: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_10) => any;
4
+ };
5
+ declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ action: (action: "toggleMaximize") => any;
7
+ }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
+ onAction?: ((action: "toggleMaximize") => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };
@@ -0,0 +1,16 @@
1
+ declare var __VLS_10: {};
2
+ type __VLS_Slots = {} & {
3
+ default?: (props: typeof __VLS_10) => any;
4
+ };
5
+ declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
6
+ action: (action: "minimize") => any;
7
+ }, string, import("vue").PublicProps, Readonly<{}> & Readonly<{
8
+ onAction?: ((action: "minimize") => any) | undefined;
9
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
10
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
11
+ export default _default;
12
+ type __VLS_WithSlots<T, S> = T & {
13
+ new (): {
14
+ $slots: S;
15
+ };
16
+ };