vue-cli-plugin-electron-haunv 1.0.0 → 1.0.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/README.md +250 -0
- package/bin/cli.js +87 -0
- package/generator/template/index.html +15 -0
- package/generator/template/package.json +28 -0
- package/generator/template/src/App.vue +125 -0
- package/generator/template/src/background.js +88 -0
- package/generator/template/src/main.js +4 -0
- package/generator/template/src/preload.js +31 -0
- package/generator/template/vite.config.js +36 -0
- package/index.d.ts +180 -0
- package/index.js +41 -3
- package/lib/build.js +128 -0
- package/lib/config.js +25 -0
- package/lib/nativeDeps.js +85 -0
- package/lib/plugin.js +40 -0
- package/lib/protocol.js +55 -0
- package/lib/serve.js +99 -0
- package/lib/vitePlugin.js +65 -0
- package/package.json +56 -15
- package/generator/index.js +0 -46
- package/generator/template/electron/dev-runner.js +0 -56
- package/generator/template/electron/hot-reload.js +0 -17
- package/generator/template/electron/main.js +0 -50
- package/generator/template/electron/preload.js +0 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
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/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# vite-plugin-electron-builder
|
|
2
|
+
|
|
3
|
+
> Easily Build Your Vue 3 + Vite App For Desktop With Electron
|
|
4
|
+
|
|
5
|
+
[](https://github.com/your-username/vite-plugin-electron-builder/actions/workflows/nodeCI.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/vite-plugin-electron-builder)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
A modern replacement for `vue-cli-plugin-electron-builder`, built on top of **Vite 8**, **Electron 41**, and **electron-builder 26**. All dependencies are kept at their latest versions.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Features
|
|
14
|
+
|
|
15
|
+
- ⚡ **Vite-native** — instant HMR for the renderer process
|
|
16
|
+
- 🔄 **Auto-restart** — watches your main process files and restarts Electron on change
|
|
17
|
+
- 🔒 **Secure by default** — custom `app://` protocol with path traversal protection, context isolation enabled
|
|
18
|
+
- 🛠 **esbuild** — fast TypeScript/ESM compilation for the main process
|
|
19
|
+
- 🎭 **Playwright** — built-in E2E test helper for Electron windows
|
|
20
|
+
- 📦 **electron-builder** — full packaging for Linux, macOS, and Windows
|
|
21
|
+
- 🖥 **CLI** — scaffold a new project with one command
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Scaffold a new project
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx vite-plugin-electron-builder my-app
|
|
31
|
+
cd my-app
|
|
32
|
+
npm run electron:serve
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Add to an existing Vite + Vue project
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -D vite-plugin-electron-builder electron electron-builder esbuild
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then update your `vite.config.js`:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { defineConfig } from 'vite'
|
|
45
|
+
import vue from '@vitejs/plugin-vue'
|
|
46
|
+
import { createElectronPlugin } from 'vite-plugin-electron-builder'
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
plugins: [
|
|
50
|
+
vue(),
|
|
51
|
+
createElectronPlugin({
|
|
52
|
+
mainProcessFile: 'src/background.js',
|
|
53
|
+
outputDir: 'dist_electron',
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Add scripts to your `package.json`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"scripts": {
|
|
64
|
+
"electron:serve": "vite-electron serve",
|
|
65
|
+
"electron:build": "vite build && vite-electron build"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Project Structure
|
|
73
|
+
|
|
74
|
+
After scaffolding, your project will look like this:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
my-app/
|
|
78
|
+
├── src/
|
|
79
|
+
│ ├── App.vue # Vue renderer component
|
|
80
|
+
│ ├── main.js # Renderer entry point
|
|
81
|
+
│ ├── background.js # Electron main process ← edit this
|
|
82
|
+
│ └── preload.js # Preload script (contextBridge)
|
|
83
|
+
├── tests/
|
|
84
|
+
│ └── e2e/
|
|
85
|
+
│ └── app.spec.js # Playwright E2E tests
|
|
86
|
+
├── index.html
|
|
87
|
+
├── vite.config.js
|
|
88
|
+
└── package.json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Plugin Options
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
interface ElectronBuilderOptions {
|
|
97
|
+
/** Electron main process entry file. Default: 'src/background.js' */
|
|
98
|
+
mainProcessFile?: string
|
|
99
|
+
|
|
100
|
+
/** Output dir for compiled main process. Default: 'dist_electron' */
|
|
101
|
+
outputDir?: string
|
|
102
|
+
|
|
103
|
+
/** Preload script path. Optional. */
|
|
104
|
+
preload?: string
|
|
105
|
+
|
|
106
|
+
/** Enable Node integration in renderer. Default: false */
|
|
107
|
+
nodeIntegration?: boolean
|
|
108
|
+
|
|
109
|
+
/** Enable context isolation. Default: true */
|
|
110
|
+
contextIsolation?: boolean
|
|
111
|
+
|
|
112
|
+
/** Custom protocol scheme name. Default: 'app' */
|
|
113
|
+
customFileProtocol?: string
|
|
114
|
+
|
|
115
|
+
/** Extra Electron CLI flags for development. */
|
|
116
|
+
electronCliArgs?: string[]
|
|
117
|
+
|
|
118
|
+
/** electron-builder config overrides. */
|
|
119
|
+
builderOptions?: import('electron-builder').Configuration
|
|
120
|
+
|
|
121
|
+
/** Native modules to externalize from the bundle. */
|
|
122
|
+
externals?: string[]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
### Commands
|
|
131
|
+
|
|
132
|
+
| Command | Description |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `npm run electron:serve` | Start Vite dev server + Electron |
|
|
135
|
+
| `npm run electron:build` | Build renderer + package with electron-builder |
|
|
136
|
+
| `npm test` | Run Jest unit tests |
|
|
137
|
+
| `npm run test:e2e` | Run Playwright E2E tests |
|
|
138
|
+
| `npm run lint` | ESLint |
|
|
139
|
+
|
|
140
|
+
### How it works
|
|
141
|
+
|
|
142
|
+
**Development (`electron:serve`)**
|
|
143
|
+
|
|
144
|
+
1. Vite starts its dev server (default `:8080`)
|
|
145
|
+
2. The plugin compiles `src/background.js` with esbuild → `dist_electron/main.js`
|
|
146
|
+
3. Electron is launched pointing at the Vite dev server URL
|
|
147
|
+
4. chokidar watches `src/background.js` (and preload) — on change, recompiles and restarts Electron
|
|
148
|
+
|
|
149
|
+
**Production (`electron:build`)**
|
|
150
|
+
|
|
151
|
+
1. Vite builds the renderer to `dist/`
|
|
152
|
+
2. esbuild compiles and bundles the main process to `dist_electron/main.js`
|
|
153
|
+
3. electron-builder packages everything into a distributable (`.dmg`, `.exe`, `.AppImage`, etc.)
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Security
|
|
158
|
+
|
|
159
|
+
This plugin follows Electron security best practices:
|
|
160
|
+
|
|
161
|
+
- **Context isolation** is enabled by default (`contextIsolation: true`)
|
|
162
|
+
- **Node integration** is disabled by default in the renderer
|
|
163
|
+
- The custom `app://` protocol prevents path traversal attacks
|
|
164
|
+
- The preload template uses `contextBridge` to expose only whitelisted APIs
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## API Reference
|
|
169
|
+
|
|
170
|
+
### `createElectronPlugin(options?)`
|
|
171
|
+
|
|
172
|
+
Returns a Vite plugin. Add it to the `plugins` array in `vite.config.js`.
|
|
173
|
+
|
|
174
|
+
### `createProtocol(scheme, basePath?)`
|
|
175
|
+
|
|
176
|
+
Registers a secure Electron file protocol. Import in your main process:
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
import { createProtocol } from 'vite-plugin-electron-builder/lib/protocol'
|
|
180
|
+
|
|
181
|
+
app.whenReady().then(() => {
|
|
182
|
+
createProtocol('app')
|
|
183
|
+
win.loadURL('app://./index.html')
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `testWithPlaywright(options?)`
|
|
188
|
+
|
|
189
|
+
Launches Electron with Playwright for E2E tests:
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
import { testWithPlaywright } from 'vite-plugin-electron-builder'
|
|
193
|
+
|
|
194
|
+
let app, page, stop
|
|
195
|
+
|
|
196
|
+
beforeAll(async () => {
|
|
197
|
+
;({ app, page, stop } = await testWithPlaywright({
|
|
198
|
+
appPath: 'dist_electron/main.js',
|
|
199
|
+
}))
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
afterAll(() => stop())
|
|
203
|
+
|
|
204
|
+
test('renders app', async () => {
|
|
205
|
+
const el = await page.$('#app')
|
|
206
|
+
expect(el).not.toBeNull()
|
|
207
|
+
})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Package Versions
|
|
213
|
+
|
|
214
|
+
All dependencies are pinned to their latest major versions:
|
|
215
|
+
|
|
216
|
+
| Package | Version |
|
|
217
|
+
|---|---|
|
|
218
|
+
| `electron` | ^41.0.2 |
|
|
219
|
+
| `electron-builder` | ^26.8.1 |
|
|
220
|
+
| `vite` | ^8.0.0 |
|
|
221
|
+
| `@vitejs/plugin-vue` | ^6.0.5 |
|
|
222
|
+
| `esbuild` | ^0.27.4 |
|
|
223
|
+
| `@playwright/test` | ^1.58.2 |
|
|
224
|
+
| `chalk` | ^5.6.2 |
|
|
225
|
+
| `chokidar` | ^5.0.0 |
|
|
226
|
+
| `commander` | ^14.0.3 |
|
|
227
|
+
| `execa` | ^9.6.1 |
|
|
228
|
+
| `fs-extra` | ^11.3.4 |
|
|
229
|
+
| `semver` | ^7.7.4 |
|
|
230
|
+
| `concurrently` | ^9.2.1 |
|
|
231
|
+
| `wait-on` | ^9.0.4 |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Migrating from vue-cli-plugin-electron-builder
|
|
236
|
+
|
|
237
|
+
| vue-cli-plugin | vite-plugin-electron-builder |
|
|
238
|
+
|---|---|
|
|
239
|
+
| `vue add electron-builder` | `npx vite-plugin-electron-builder my-app` |
|
|
240
|
+
| `vue-cli-service electron:serve` | `vite-electron serve` |
|
|
241
|
+
| `vue-cli-service electron:build` | `vite-electron build` |
|
|
242
|
+
| `vue.config.js` plugin options | `vite.config.js` `createElectronPlugin({})` |
|
|
243
|
+
| `background.js` | `src/background.js` (unchanged) |
|
|
244
|
+
| `createProtocol` | `import { createProtocol } from 'vite-plugin-electron-builder/lib/protocol'` |
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict'
|
|
4
|
+
|
|
5
|
+
const { program } = require('commander')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const chalk = require('chalk')
|
|
8
|
+
const pkg = require('../package.json')
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('vite-electron')
|
|
12
|
+
.description('Vite Plugin Electron Builder CLI')
|
|
13
|
+
.version(pkg.version)
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.command('serve')
|
|
17
|
+
.description('Start the Electron development server')
|
|
18
|
+
.option('--main <file>', 'Path to main process file', 'src/background.js')
|
|
19
|
+
.option('--out <dir>', 'Output directory', 'dist_electron')
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const { createServer } = require('vite')
|
|
22
|
+
const { serveElectron } = require('../lib/build')
|
|
23
|
+
|
|
24
|
+
console.log(chalk.cyan('\n🚀 Starting Vite + Electron dev server...\n'))
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Start Vite dev server
|
|
28
|
+
const viteServer = await createServer({
|
|
29
|
+
base: './',
|
|
30
|
+
server: { port: 8080 }
|
|
31
|
+
})
|
|
32
|
+
await viteServer.listen()
|
|
33
|
+
viteServer.printUrls()
|
|
34
|
+
|
|
35
|
+
// Start Electron in watch mode
|
|
36
|
+
const { stop } = await serveElectron({
|
|
37
|
+
mainProcessFile: opts.main,
|
|
38
|
+
outputDir: opts.out
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Handle graceful shutdown
|
|
42
|
+
const shutdown = async () => {
|
|
43
|
+
console.log(chalk.yellow('\n[electron-builder] Shutting down...'))
|
|
44
|
+
await stop()
|
|
45
|
+
await viteServer.close()
|
|
46
|
+
process.exit(0)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
process.on('SIGINT', shutdown)
|
|
50
|
+
process.on('SIGTERM', shutdown)
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(chalk.red('[electron-builder] Serve failed:'), err)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
program
|
|
58
|
+
.command('build')
|
|
59
|
+
.description('Build the app for production')
|
|
60
|
+
.option('--main <file>', 'Path to main process file', 'src/background.js')
|
|
61
|
+
.option('--out <dir>', 'Output directory', 'dist_electron')
|
|
62
|
+
.option('--platform <platform>', 'Target platform (linux|mac|win)')
|
|
63
|
+
.action(async (opts) => {
|
|
64
|
+
const { buildElectron } = require('../lib/build')
|
|
65
|
+
|
|
66
|
+
console.log(chalk.cyan('\n📦 Building for production...\n'))
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await buildElectron({
|
|
70
|
+
mainProcessFile: opts.main,
|
|
71
|
+
outputDir: opts.out,
|
|
72
|
+
builderOptions: opts.platform
|
|
73
|
+
? { [opts.platform]: {} }
|
|
74
|
+
: {}
|
|
75
|
+
})
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error(chalk.red('[electron-builder] Build failed:'), err)
|
|
78
|
+
process.exit(1)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
program.parse(process.argv)
|
|
83
|
+
|
|
84
|
+
// Show help if no command given
|
|
85
|
+
if (!process.argv.slice(2).length) {
|
|
86
|
+
program.outputHelp()
|
|
87
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<!-- Required for Electron security -->
|
|
7
|
+
<meta http-equiv="Content-Security-Policy"
|
|
8
|
+
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" />
|
|
9
|
+
<title>Vue 3 + Vite + Electron</title>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
<script type="module" src="/src/main.js"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-electron-vue-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A Vue 3 + Vite + Electron desktop application",
|
|
5
|
+
"main": "dist_electron/main.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"electron:serve": "vite-electron serve",
|
|
10
|
+
"electron:build": "vite build && vite-electron build"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"vue": "^3.5.13"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@vitejs/plugin-vue": "^6.0.5",
|
|
23
|
+
"electron": "^41.0.2",
|
|
24
|
+
"electron-devtools-installer": "^4.0.0",
|
|
25
|
+
"vite": "^8.0.0",
|
|
26
|
+
"vite-plugin-electron-builder": "^1.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div id="app">
|
|
3
|
+
<header class="app-header">
|
|
4
|
+
<img src="./assets/logo.svg" alt="Vue + Electron" class="logo" />
|
|
5
|
+
<h1>Vue 3 + Vite + Electron</h1>
|
|
6
|
+
</header>
|
|
7
|
+
|
|
8
|
+
<main class="app-main">
|
|
9
|
+
<p class="tagline">
|
|
10
|
+
Edit <code>src/App.vue</code> and save to see hot-reload in action.
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<div class="info-cards">
|
|
14
|
+
<div class="card">
|
|
15
|
+
<h2>⚡ Vite</h2>
|
|
16
|
+
<p>Lightning-fast HMR during development.</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="card">
|
|
19
|
+
<h2>🖥 Electron</h2>
|
|
20
|
+
<p>Cross-platform desktop apps with web tech.</p>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="card">
|
|
23
|
+
<h2>💚 Vue 3</h2>
|
|
24
|
+
<p>Composition API, TypeScript, and more.</p>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<p class="version">Electron API available: {{ hasElectronAPI ? '✅' : '❌' }}</p>
|
|
29
|
+
</main>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup>
|
|
34
|
+
import { ref, onMounted } from 'vue'
|
|
35
|
+
|
|
36
|
+
const hasElectronAPI = ref(false)
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
hasElectronAPI.value = typeof window.electronAPI !== 'undefined'
|
|
40
|
+
})
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<style>
|
|
44
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
45
|
+
|
|
46
|
+
body {
|
|
47
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
48
|
+
background: #0d1117;
|
|
49
|
+
color: #c9d1d9;
|
|
50
|
+
min-height: 100vh;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#app {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
align-items: center;
|
|
57
|
+
padding: 40px 20px;
|
|
58
|
+
gap: 32px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.app-header {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 16px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.logo { width: 80px; height: 80px; }
|
|
69
|
+
|
|
70
|
+
h1 {
|
|
71
|
+
font-size: 2rem;
|
|
72
|
+
font-weight: 700;
|
|
73
|
+
background: linear-gradient(135deg, #42b883, #35495e);
|
|
74
|
+
-webkit-background-clip: text;
|
|
75
|
+
-webkit-text-fill-color: transparent;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.tagline {
|
|
79
|
+
font-size: 1.1rem;
|
|
80
|
+
color: #8b949e;
|
|
81
|
+
text-align: center;
|
|
82
|
+
margin-bottom: 24px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.tagline code {
|
|
86
|
+
background: #161b22;
|
|
87
|
+
padding: 2px 8px;
|
|
88
|
+
border-radius: 4px;
|
|
89
|
+
font-family: monospace;
|
|
90
|
+
color: #58a6ff;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.info-cards {
|
|
94
|
+
display: grid;
|
|
95
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
96
|
+
gap: 20px;
|
|
97
|
+
width: 100%;
|
|
98
|
+
max-width: 700px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.card {
|
|
102
|
+
background: #161b22;
|
|
103
|
+
border: 1px solid #30363d;
|
|
104
|
+
border-radius: 12px;
|
|
105
|
+
padding: 24px;
|
|
106
|
+
text-align: center;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.card h2 { font-size: 1.3rem; margin-bottom: 8px; }
|
|
110
|
+
.card p { color: #8b949e; font-size: 0.95rem; }
|
|
111
|
+
|
|
112
|
+
.version {
|
|
113
|
+
margin-top: 16px;
|
|
114
|
+
font-size: 0.9rem;
|
|
115
|
+
color: #8b949e;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.app-main {
|
|
119
|
+
display: flex;
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
align-items: center;
|
|
122
|
+
width: 100%;
|
|
123
|
+
max-width: 800px;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { app, protocol, BrowserWindow } from 'electron'
|
|
4
|
+
import { createProtocol } from 'vite-plugin-electron-builder/lib/protocol'
|
|
5
|
+
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
|
6
|
+
import path from 'path'
|
|
7
|
+
|
|
8
|
+
const isDevelopment = process.env.NODE_ENV !== 'production'
|
|
9
|
+
|
|
10
|
+
// Register the custom protocol before app is ready (required for security)
|
|
11
|
+
protocol.registerSchemesAsPrivileged([
|
|
12
|
+
{
|
|
13
|
+
scheme: 'app',
|
|
14
|
+
privileges: {
|
|
15
|
+
secure: true,
|
|
16
|
+
standard: true
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
let win
|
|
22
|
+
|
|
23
|
+
async function createWindow() {
|
|
24
|
+
win = new BrowserWindow({
|
|
25
|
+
width: 800,
|
|
26
|
+
height: 600,
|
|
27
|
+
webPreferences: {
|
|
28
|
+
// NOTE: Do NOT enable nodeIntegration in production for security
|
|
29
|
+
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION === 'true',
|
|
30
|
+
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
|
31
|
+
preload: path.join(__dirname, 'preload.js') // optional
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
if (isDevelopment) {
|
|
36
|
+
// Load from Vite dev server
|
|
37
|
+
await win.loadURL(process.env.VITE_DEV_SERVER_URL || 'http://localhost:8080')
|
|
38
|
+
win.webContents.openDevTools()
|
|
39
|
+
} else {
|
|
40
|
+
// Use custom protocol for production
|
|
41
|
+
createProtocol('app')
|
|
42
|
+
await win.loadURL('app://./index.html')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
win.on('closed', () => {
|
|
46
|
+
win = null
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Install Vue Devtools in development
|
|
51
|
+
app.whenReady().then(async () => {
|
|
52
|
+
if (isDevelopment && !process.env.IS_TEST) {
|
|
53
|
+
try {
|
|
54
|
+
await installExtension(VUEJS_DEVTOOLS)
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error('Vue Devtools failed to install:', e.toString())
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
await createWindow()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Quit when all windows are closed (except on macOS)
|
|
63
|
+
app.on('window-all-closed', () => {
|
|
64
|
+
if (process.platform !== 'darwin') {
|
|
65
|
+
app.quit()
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
app.on('activate', () => {
|
|
70
|
+
if (win === null) {
|
|
71
|
+
createWindow()
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Exit cleanly in development mode
|
|
76
|
+
if (isDevelopment) {
|
|
77
|
+
if (process.platform === 'win32') {
|
|
78
|
+
process.on('message', (data) => {
|
|
79
|
+
if (data === 'graceful-exit') {
|
|
80
|
+
app.quit()
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
} else {
|
|
84
|
+
process.on('SIGTERM', () => {
|
|
85
|
+
app.quit()
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Preload script - runs in a privileged context before the renderer.
|
|
5
|
+
* Use contextBridge to safely expose APIs to the renderer.
|
|
6
|
+
*
|
|
7
|
+
* Docs: https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
|
8
|
+
*/
|
|
9
|
+
const { contextBridge, ipcRenderer } = require('electron')
|
|
10
|
+
|
|
11
|
+
// Expose a safe API to the renderer process via window.electronAPI
|
|
12
|
+
contextBridge.exposeInMainWorld('electronAPI', {
|
|
13
|
+
// Example: send a message to the main process
|
|
14
|
+
sendMessage: (channel, data) => {
|
|
15
|
+
const allowedChannels = ['app:minimize', 'app:maximize', 'app:close', 'app:ready']
|
|
16
|
+
if (allowedChannels.includes(channel)) {
|
|
17
|
+
ipcRenderer.send(channel, data)
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Example: receive a message from the main process
|
|
22
|
+
onMessage: (channel, callback) => {
|
|
23
|
+
const allowedChannels = ['app:update-available', 'app:update-downloaded']
|
|
24
|
+
if (allowedChannels.includes(channel)) {
|
|
25
|
+
ipcRenderer.on(channel, (_event, ...args) => callback(...args))
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// Expose app version
|
|
30
|
+
getVersion: () => ipcRenderer.invoke('app:get-version'),
|
|
31
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import { createElectronPlugin } from 'vite-plugin-electron-builder'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
// https://vitejs.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
vue(),
|
|
10
|
+
createElectronPlugin({
|
|
11
|
+
// Path to Electron main process entry
|
|
12
|
+
mainProcessFile: 'src/background.js',
|
|
13
|
+
|
|
14
|
+
// Output directory for compiled main process
|
|
15
|
+
outputDir: 'dist_electron',
|
|
16
|
+
|
|
17
|
+
// Optional: path to preload script
|
|
18
|
+
preload: 'src/preload.js',
|
|
19
|
+
|
|
20
|
+
// Optional: electron-builder overrides
|
|
21
|
+
builderOptions: {
|
|
22
|
+
appId: 'com.example.myapp',
|
|
23
|
+
productName: 'My Vue Electron App',
|
|
24
|
+
linux: { target: ['AppImage', 'deb'] },
|
|
25
|
+
win: { target: ['nsis'] },
|
|
26
|
+
mac: { target: ['dmg'] },
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
],
|
|
30
|
+
|
|
31
|
+
resolve: {
|
|
32
|
+
alias: {
|
|
33
|
+
'@': path.resolve(__dirname, 'src'),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
})
|