Compare commits
210 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d51d45b38c |
|
|
![]() |
767cde8f25 |
|
|
![]() |
b1f19196df |
|
|
![]() |
d78dcf8822 |
|
|
![]() |
aea7cc5b31 |
|
|
![]() |
766b9fb206 |
|
|
![]() |
d548009325 |
|
|
![]() |
4bb103c014 |
|
|
![]() |
c9e3c81550 |
|
|
![]() |
3ac78785bf |
|
|
![]() |
f729d13582 |
|
|
![]() |
5efc8b08c7 |
|
|
![]() |
66e4eee2c0 |
|
|
![]() |
4edbadf077 |
|
|
![]() |
1b5b326aa6 |
|
|
![]() |
6ef956d5ef |
|
|
![]() |
b6e4df4603 |
|
|
![]() |
dbe0730fd8 |
|
|
![]() |
e93d649340 |
|
|
![]() |
1c707b40bb |
|
|
![]() |
8fd0e00046 |
|
|
![]() |
1edfdac585 |
|
|
![]() |
f33167d754 |
|
|
![]() |
79acebe7a4 |
|
|
![]() |
8fa2c831a7 |
|
|
![]() |
6432b5275c |
|
|
![]() |
6ff0dc59ed |
|
|
![]() |
48a6b0682e |
|
|
![]() |
2fc312870c |
|
|
![]() |
2f2880a9f1 |
|
|
![]() |
29d5fa95a8 |
|
|
![]() |
281aad281c |
|
|
![]() |
53b1425f4e |
|
|
![]() |
5063fa8bd9 |
|
|
![]() |
891751624a |
|
|
![]() |
6651c5e3de |
|
|
![]() |
7d92423cca |
|
|
![]() |
eaa77855b2 |
|
|
![]() |
57da1121e7 |
|
|
![]() |
cd319c5a62 |
|
|
![]() |
bd1fe91070 |
|
|
![]() |
ab634d24f9 |
|
|
![]() |
ac07c369e2 |
|
|
![]() |
e8adba821a |
|
|
![]() |
df9e023ba4 |
|
|
![]() |
bfffd06646 |
|
|
![]() |
28e8d8b808 |
|
|
![]() |
70e8b39774 |
|
|
![]() |
dfa51dd155 |
|
|
![]() |
6db5de853c |
|
|
![]() |
fb5555c1f7 |
|
|
![]() |
6bd40b2399 |
|
|
![]() |
84b3617ed5 |
|
|
![]() |
3a6ba28e78 |
|
|
![]() |
98e10011db |
|
|
![]() |
9f414cef12 |
|
|
![]() |
0b463ba8c3 |
|
|
![]() |
93a4cd74f1 |
|
|
![]() |
4b03aac9fa |
|
|
![]() |
7cbc3ded05 |
|
|
![]() |
2889ab3ac8 |
|
|
![]() |
87cccc51ab |
|
|
![]() |
3953b84329 |
|
|
![]() |
45f9205eac |
|
|
![]() |
1b29cac508 |
|
|
![]() |
a9240837be |
|
|
![]() |
4a83703608 |
|
|
![]() |
e6fbffc7b7 |
|
|
![]() |
07abc2078f |
|
|
![]() |
055b3eca39 |
|
|
![]() |
a82f0828a8 |
|
|
![]() |
a70714c6ae |
|
|
![]() |
31ffc67ca2 |
|
|
![]() |
643d0ffcb6 |
|
|
![]() |
5f8b6041ac |
|
|
![]() |
c711938f70 |
|
|
![]() |
a8d24cb084 |
|
|
![]() |
6fda9e5723 |
|
|
![]() |
71dd5078d9 |
|
|
![]() |
3ab8a85a02 |
|
|
![]() |
4e3a9e3a1b |
|
|
![]() |
50b91b9b02 |
|
|
![]() |
0566305c79 |
|
|
![]() |
e2cedd24cf |
|
|
![]() |
d6f989c4f5 |
|
|
![]() |
9c278ffe18 |
|
|
![]() |
5e839b2f6d |
|
|
![]() |
1754306625 |
|
|
![]() |
b3e144ad04 |
|
|
![]() |
6e35fbd27c |
|
|
![]() |
b2a073cc8d |
|
|
![]() |
cac23a138c |
|
|
![]() |
68a41e3b5a |
|
|
![]() |
10cf0405f7 |
|
|
![]() |
d9a4ad8c61 |
|
|
![]() |
fae8222245 |
|
|
![]() |
5fa875a495 |
|
|
![]() |
79f98c8792 |
|
|
![]() |
ff15fa45d5 |
|
|
![]() |
9b5a3a9c61 |
|
|
![]() |
d5f251f5d1 |
|
|
![]() |
bf761fc687 |
|
|
![]() |
c84a4d8194 |
|
|
![]() |
05cdb82d15 |
|
|
![]() |
3d0948c801 |
|
|
![]() |
3130ab2998 |
|
|
![]() |
e66f6adf7e |
|
|
![]() |
26429ae7bf |
|
|
![]() |
4fd36df90b |
|
|
![]() |
d427a7a9aa |
|
|
![]() |
63cef88299 |
|
|
![]() |
009e39319c |
|
|
![]() |
4d129d0c5c |
|
|
![]() |
ae877666f4 |
|
|
![]() |
3c719118a7 |
|
|
![]() |
0584fc4f30 |
|
|
![]() |
44b342f481 |
|
|
![]() |
3efcb1a27d |
|
|
![]() |
b8eaa24047 |
|
|
![]() |
034f89cec1 |
|
|
![]() |
cfc586acbc |
|
|
![]() |
c705314a96 |
|
|
![]() |
055994279b |
|
|
![]() |
4bcb4e99bb |
|
|
![]() |
a6b1fe3455 |
|
|
![]() |
f9571ab94a |
|
|
![]() |
58e568506a |
|
|
![]() |
0ab99a990c |
|
|
![]() |
d72cbb39b5 |
|
|
![]() |
fe103943f2 |
|
|
![]() |
1c3614081d |
|
|
![]() |
ad1096433e |
|
|
![]() |
d20f4c909c |
|
|
![]() |
c420f35805 |
|
|
![]() |
d8387de901 |
|
|
![]() |
b0876e1b2a |
|
|
![]() |
c3060c0315 |
|
|
![]() |
c90d0b90cd |
|
|
![]() |
6fc1e5fd9b |
|
|
![]() |
7ca54e148f |
|
|
![]() |
ec8e390718 |
|
|
![]() |
f25a850f81 |
|
|
![]() |
7380421914 |
|
|
![]() |
817498e2d9 |
|
|
![]() |
49c2bf2b25 |
|
|
![]() |
cc8d92c517 |
|
|
![]() |
f84089beec |
|
|
![]() |
a1525fdc60 |
|
|
![]() |
0f747c67c6 |
|
|
![]() |
69397edfc0 |
|
|
![]() |
55b4aa4dd6 |
|
|
![]() |
56ab746c6a |
|
|
![]() |
b7d71e960e |
|
|
![]() |
7374a44e96 |
|
|
![]() |
60f28a2a9e |
|
|
![]() |
c4304b61e6 |
|
|
![]() |
aa89677048 |
|
|
![]() |
6dcb6643b1 |
|
|
![]() |
0ab15c65f1 |
|
|
![]() |
bb1505d2c8 |
|
|
![]() |
e2f2a0afbc |
|
|
![]() |
8e5e2da2f1 |
|
|
![]() |
a25e73558c |
|
|
![]() |
5a9d817643 |
|
|
![]() |
ff5371477b |
|
|
![]() |
2dcd3b6a88 |
|
|
![]() |
29e0782ccb |
|
|
![]() |
5107781fd1 |
|
|
![]() |
4d9836d165 |
|
|
![]() |
458694315d |
|
|
![]() |
dcaca724a1 |
|
|
![]() |
9770dd133a |
|
|
![]() |
99d473948b |
|
|
![]() |
31669f5d2b |
|
|
![]() |
1183e49fd2 |
|
|
![]() |
43bea9fb0b |
|
|
![]() |
4d636614ce |
|
|
![]() |
d949120836 |
|
|
![]() |
f8677670ef |
|
|
![]() |
9359140dbe |
|
|
![]() |
ddf949f98b |
|
|
![]() |
eb8b69966d |
|
|
![]() |
0edc38fc9d |
|
|
![]() |
aff8ccb7c9 |
|
|
![]() |
bcb61bcebb |
|
|
![]() |
b0320cab5d |
|
|
![]() |
3e6abba80e |
|
|
![]() |
396afdb6c9 |
|
|
![]() |
9150036761 |
|
|
![]() |
23a98a26b4 |
|
|
![]() |
ab9e5bf3c2 |
|
|
![]() |
9bab724711 |
|
|
![]() |
a04955f468 |
|
|
![]() |
3664cd2a9b |
|
|
![]() |
fca655ecc3 |
|
|
![]() |
0011c249f0 |
|
|
![]() |
ec17b09cb5 |
|
|
![]() |
95d901d11f |
|
|
![]() |
8a8dd62b96 |
|
|
![]() |
d479a00b9a |
|
|
![]() |
05d4a2a35f |
|
|
![]() |
909af6e451 |
|
|
![]() |
d2c9340b05 |
|
|
![]() |
9fa015fb08 |
|
|
![]() |
85a7919787 |
|
|
![]() |
ad2bbaca24 |
|
|
![]() |
a1bfdfba8d |
|
|
![]() |
31f487d860 |
|
|
![]() |
c75e1c150e |
|
|
![]() |
c2dcf94775 |
|
|
@ -4,86 +4,30 @@ logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
# Runtime data
|
node_modules
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
.debug.env
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# nuxt.js build output
|
|
||||||
.nuxt
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
# ----
|
|
||||||
dist
|
dist
|
||||||
**/.tmp
|
|
||||||
release
|
|
||||||
.DS_Store
|
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
dist-electron
|
||||||
|
release
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/.debug.env
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
#lockfile
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
yarn.lock
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
import fs from 'fs'
|
import fs from 'node:fs'
|
||||||
import path from 'path'
|
import path from 'node:path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { createRequire } from 'module'
|
import { createRequire } from 'node:module'
|
||||||
|
import { spawn } from 'node:child_process'
|
||||||
|
|
||||||
const pkg = createRequire(import.meta.url)('../package.json')
|
const pkg = createRequire(import.meta.url)('../package.json')
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
// write .debug.env
|
// write .debug.env
|
||||||
const envContent = Object.entries(pkg.env).map(([key, val]) => `${key}=${val}`)
|
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
|
||||||
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
|
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
|
||||||
|
|
||||||
// bootstrap
|
// bootstrap
|
||||||
import('../scripts/watch.mjs?debug=vscode')
|
spawn(
|
||||||
|
// TODO: terminate `npm run dev` when Debug exits.
|
||||||
|
process.platform === 'win32' ? 'npm.cmd' : 'npm',
|
||||||
|
['run', 'dev'],
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"mrmlnc.vscode-json5"
|
||||||
|
]
|
||||||
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
"compounds": [
|
"compounds": [
|
||||||
{
|
{
|
||||||
"name": "Debug App",
|
"name": "Debug App",
|
||||||
"preLaunchTask": "start .debug.script.mjs",
|
"preLaunchTask": "Before Debug",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
"Debug Main Process",
|
"Debug Main Process",
|
||||||
"Debug Renderer Process"
|
"Debug Renderer Process"
|
||||||
|
@ -22,23 +22,33 @@
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Debug Main Process",
|
"name": "Debug Main Process",
|
||||||
"type": "pwa-node",
|
"type": "node",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||||
"windows": {
|
"windows": {
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||||
},
|
},
|
||||||
"runtimeArgs": [
|
"runtimeArgs": [
|
||||||
|
"--no-sandbox",
|
||||||
"--remote-debugging-port=9229",
|
"--remote-debugging-port=9229",
|
||||||
"${workspaceRoot}/dist/main/index.cjs"
|
"."
|
||||||
],
|
],
|
||||||
"envFile": "${workspaceFolder}/.vscode/.debug.env"
|
"envFile": "${workspaceFolder}/.vscode/.debug.env",
|
||||||
|
"console": "integratedTerminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Debug Renderer Process",
|
"name": "Debug Renderer Process",
|
||||||
"port": 9229,
|
"port": 9229,
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"type": "pwa-chrome"
|
"type": "chrome",
|
||||||
|
"timeout": 60000,
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**",
|
||||||
|
"${workspaceRoot}/node_modules/**",
|
||||||
|
"${workspaceRoot}/dist-electron/**",
|
||||||
|
// Skip files in host(VITE_DEV_SERVER_URL)
|
||||||
|
"http://127.0.0.1:7777/**"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"typescript.tsc.autoDetect": "off",
|
||||||
|
"json.schemas": [
|
||||||
|
{
|
||||||
|
"fileMatch": [
|
||||||
|
"/*electron-builder.json5",
|
||||||
|
"/*electron-builder.json"
|
||||||
|
],
|
||||||
|
"url": "https://json.schemastore.org/electron-builder"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -4,15 +4,28 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "start .debug.script.mjs",
|
"label": "Before Debug",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "node .vscode/.debug.script.mjs",
|
"command": "node .vscode/.debug.script.mjs",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"problemMatcher": []
|
"problemMatcher": {
|
||||||
|
"owner": "typescript",
|
||||||
|
"fileLocation": "relative",
|
||||||
|
"pattern": {
|
||||||
|
// TODO: correct "regexp"
|
||||||
|
"regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 3,
|
||||||
|
"column": 4,
|
||||||
|
"code": 5,
|
||||||
|
"message": 6
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"activeOnStart": true,
|
||||||
|
"beginsPattern": "^.*VITE v.* ready in \\d* ms.*$",
|
||||||
|
"endsPattern": "^.*\\[startup\\] Electron App.*$"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://code.visualstudio.com/docs/editor/tasks#_operating-system-specific-properties
|
|
||||||
// https://code.visualstudio.com/docs/editor/tasks#_background-watching-tasks
|
|
||||||
// https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson
|
|
||||||
|
|
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 草鞋没号
|
Copyright (c) 2023 草鞋没号
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
199
README.md
|
@ -1,156 +1,91 @@
|
||||||
# vite-react-electron
|
# electron-vite-react
|
||||||
|
|
||||||

|
[](https://github.com/vitejs/awesome-vite)
|
||||||

|

|
||||||

|

|
||||||
[](https://nodejs.org/about/releases)
|

|
||||||
|
[](https://nodejs.org/about/releases)
|
||||||
|
|
||||||
**English | [简体中文](README.zh-CN.md)**
|
English | [简体中文](README.zh-CN.md)
|
||||||
|
|
||||||
## Overview
|
## 👀 Overview
|
||||||
|
|
||||||
This is a `Vite`-integrated `Electron` template built with simplification in mind.
|
📦 Ready out of the box
|
||||||
|
🎯 Based on the official [template-react-ts](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts), project structure will be familiar to you
|
||||||
|
🌱 Easily extendable and customizable
|
||||||
|
💪 Supports Node.js API in the renderer process
|
||||||
|
🔩 Supports C/C++ native addons
|
||||||
|
🐞 Debugger configuration included
|
||||||
|
🖥 Easy to implement multiple windows
|
||||||
|
|
||||||
The repo contains only the most basic files, dependencies and functionalities to ensure flexibility for various scenarios.
|
## 🛫 Quick Setup
|
||||||
|
|
||||||
You need a basic understanding of `Electron` and `Vite` to get started. But that's not mandatory - you can learn almost all the details by reading through the source code. Trust me, this repo is not that complex. 😋
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm create electron-vite
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Debug
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<!--
|
|
||||||
```sh
|
```sh
|
||||||
# clone the project
|
# clone the project
|
||||||
git clone https://github.com/caoxiemeihao/vite-react-electron.git
|
git clone https://github.com/electron-vite/electron-vite-react.git
|
||||||
|
|
||||||
# open the project directory
|
# enter the project directory
|
||||||
cd vite-react-electron
|
cd electron-vite-react
|
||||||
|
|
||||||
# install dependencies
|
# install dependency
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# start the application
|
# develop
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
# make a production build
|
|
||||||
npm run build
|
|
||||||
```
|
```
|
||||||
-->
|
|
||||||
|
|
||||||
## Directory structure
|
## 🐞 Debug
|
||||||
|
|
||||||
Once `dev` or `build` npm-script is executed, the `dist` folder will be generated. It has the same structure as the `packages` folder, the purpose of this design is to ensure the correct path calculation.
|

|
||||||
|
|
||||||
|
## 📂 Directory structure
|
||||||
|
|
||||||
|
Familiar React application structure, just with `electron` folder on the top :wink:
|
||||||
|
*Files in this folder will be separated from your React application and built into `dist-electron`*
|
||||||
|
|
||||||
```tree
|
```tree
|
||||||
├── build Resources for the production build
|
├── electron Electron-related code
|
||||||
| ├── icon.icns Icon for the application on macOS
|
│ ├── main Main-process source code
|
||||||
| ├── icon.ico Icon for the application
|
│ └── preload Preload-scripts source code
|
||||||
| ├── installerIcon.ico Icon for the application installer
|
│
|
||||||
| └── uninstallerIcon.ico Icon for the application uninstaller
|
|
||||||
|
|
|
||||||
├── dist Generated after build according to the "packages" directory
|
|
||||||
| ├── main
|
|
||||||
| ├── preload
|
|
||||||
| └── renderer
|
|
||||||
|
|
|
||||||
├── release Generated after production build, contains executables
|
├── release Generated after production build, contains executables
|
||||||
| └──{version}
|
│ └── {version}
|
||||||
| ├── win-unpacked Contains unpacked application executable
|
│ ├── {os}-{os_arch} Contains unpacked application executable
|
||||||
| └── Setup.exe Installer for the application
|
│ └── {app_name}_{version}.{ext} Installer for the application
|
||||||
|
|
│
|
||||||
├── scripts
|
├── public Static assets
|
||||||
| ├── build.mjs Develop script -> npm run build
|
└── src Renderer source code, your React application
|
||||||
| └── watch.mjs Develop script -> npm run dev
|
|
||||||
|
|
|
||||||
├── packages
|
|
||||||
| ├── main Main-process source code
|
|
||||||
| | └── vite.config.ts
|
|
||||||
| ├── preload Preload-script source code
|
|
||||||
| | └── vite.config.ts
|
|
||||||
| └── renderer Renderer-process source code
|
|
||||||
| └── vite.config.ts
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use Electron and NodeJS API
|
|
||||||
|
|
||||||
> 🚧 By default, Electron doesn't support the use of API related to Electron and NodeJS in the Renderer process, but someone might need to use it. If so, you can see the template 👉 **[electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate)**
|
|
||||||
|
|
||||||
#### Invoke Electron and NodeJS API in `Preload-script`
|
|
||||||
|
|
||||||
- **packages/preload/index.ts**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import fs from "fs"
|
|
||||||
import { contextBridge, ipcRenderer } from "electron"
|
|
||||||
|
|
||||||
// --------- Expose some API to Renderer-process. ---------
|
|
||||||
contextBridge.exposeInMainWorld("fs", fs)
|
|
||||||
contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer)
|
|
||||||
```
|
|
||||||
|
|
||||||
- **packages/renderer/src/global.d.ts**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Defined in the window
|
|
||||||
interface Window {
|
|
||||||
fs: typeof import("fs")
|
|
||||||
ipcRenderer: import("electron").IpcRenderer
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- **packages/renderer/src/main.ts**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Use Electron and NodeJS API in the Renderer-process
|
|
||||||
console.log("fs", window.fs)
|
|
||||||
console.log("ipcRenderer", window.ipcRenderer)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use SerialPort, SQLite3, or other node-native addons in the Main-process
|
|
||||||
|
|
||||||
- First, you need to make sure that the dependencies in the `package.json` are NOT in the "devDependencies". Because the project will need them after packaged.
|
|
||||||
|
|
||||||
- Main-process, Preload-script are also built with Vite, and they're built as [build.lib](https://vitejs.dev/config/#build-lib).
|
|
||||||
So they just need to configure Rollup.
|
|
||||||
|
|
||||||
**Click to see more** 👉 [packages/main/vite.config.ts](https://github.com/caoxiemeihao/vite-react-electron/blob/main/packages/main/vite.config.ts)
|
|
||||||
|
|
||||||
```js
|
|
||||||
export default {
|
|
||||||
build: {
|
|
||||||
// built lib for Main-process, Preload-script
|
|
||||||
lib: {
|
|
||||||
entry: "index.ts",
|
|
||||||
formats: ["cjs"],
|
|
||||||
fileName: () => "[name].js",
|
|
||||||
},
|
|
||||||
rollupOptions: {
|
|
||||||
// configuration here
|
|
||||||
external: ["serialport", "sqlite3"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## `dependencies` vs `devDependencies`
|
|
||||||
|
|
||||||
- First, you need to know if your dependencies are needed after the application is packaged.
|
|
||||||
|
|
||||||
- Like [serialport](https://www.npmjs.com/package/serialport), [sqlite3](https://www.npmjs.com/package/sqlite3) they are node-native modules and should be placed in `dependencies`. In addition, Vite will not build them, but treat them as external modules.
|
|
||||||
|
|
||||||
- Dependencies like [Vue](https://www.npmjs.com/package/vue) and [React](https://www.npmjs.com/package/react), which are pure javascript modules that can be built with Vite, can be placed in `devDependencies`. This reduces the size of the application.
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
## Result
|
## 🚨 Be aware
|
||||||
|
|
||||||
<img width="400px" src="https://raw.githubusercontent.com/caoxiemeihao/blog/main/vite-react-electron/react-win.png" />
|
This template integrates Node.js API to the renderer process by default. If you want to follow **Electron Security Concerns** you might want to disable this feature. You will have to expose needed API by yourself.
|
||||||
|
|
||||||
|
To get started, remove the option as shown below. This will [modify the Vite configuration and disable this feature](https://github.com/electron-vite/vite-plugin-electron-renderer#config-presets-opinionated).
|
||||||
|
|
||||||
|
```diff
|
||||||
|
# vite.config.ts
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
...
|
||||||
|
- // Use Node.js API in the Renderer-process
|
||||||
|
- renderer({
|
||||||
|
- nodeIntegration: true,
|
||||||
|
- }),
|
||||||
|
...
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 🔧 Additional features
|
||||||
|
|
||||||
|
1. electron-updater 👉 [see docs](src/components/update/README.md)
|
||||||
|
1. playwright
|
||||||
|
|
||||||
|
## ❔ FAQ
|
||||||
|
|
||||||
|
- [C/C++ addons, Node.js modules - Pre-Bundling](https://github.com/electron-vite/vite-plugin-electron-renderer#dependency-pre-bundling)
|
||||||
|
- [dependencies vs devDependencies](https://github.com/electron-vite/vite-plugin-electron-renderer#dependencies-vs-devdependencies)
|
||||||
|
|
179
README.zh-CN.md
|
@ -1,43 +1,30 @@
|
||||||
# vite-react-electron
|
# vite-react-electron
|
||||||
|
|
||||||

|
[](https://github.com/vitejs/awesome-vite)
|
||||||

|

|
||||||

|

|
||||||
[](https://nodejs.org/about/releases)
|

|
||||||
|
[](https://nodejs.org/about/releases)
|
||||||
|
|
||||||
**[English](README.md) | 简体中文**
|
[English](README.md) | 简体中文
|
||||||
|
|
||||||
## 概述
|
## 概述
|
||||||
|
|
||||||
  这是一个追求精简的`Electron`类整合模板,只保持最基本的文件、最基本的依赖、最基本的功能;而不是大而全的、臃肿的设计。这样做的目的是能确保模板足够灵活。
|
📦 开箱即用
|
||||||
|
🎯 基于官方的 [template-react-ts](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts), 低侵入性
|
||||||
所以说如果你是对 -- 工程模板追求精简的 Coder,或者刚入世的小白想弄明白`Electron`整合类模板最基础的工作原理,亦或者你是大神只是想偷懒少干点活;那么这个模板最合适你不过了。
|
🌱 结构清晰,可塑性强
|
||||||
|
💪 支持在渲染进程中使用 Electron、Node.js API
|
||||||
尽管如此,我还是希望你对`Electron` `Vite`有一定的基础;因为除了项目结构简单外,这份`README`也显得 “精简” 。
|
🔩 支持 C/C++ 模块
|
||||||
|
🖥 很容易实现多窗口
|
||||||
模板的具体实现细节我相信你看两遍源码就能把它吃透了 😋
|
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
```sh
|
|
||||||
npm create electron-vite
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 调试
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
<!--
|
|
||||||
clone 该仓库
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# clone the project
|
# clone the project
|
||||||
git clone https://github.com/caoxiemeihao/vite-react-electron.git
|
git clone https://github.com/electron-vite/electron-vite-react.git
|
||||||
|
|
||||||
# enter the project directory
|
# enter the project directory
|
||||||
cd vite-react-electron
|
cd electron-vite-react
|
||||||
|
|
||||||
# install dependency
|
# install dependency
|
||||||
npm install
|
npm install
|
||||||
|
@ -45,104 +32,60 @@ npm install
|
||||||
# develop
|
# develop
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
-->
|
|
||||||
|
## 调试
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
一旦启动或打包脚本执行过,会在根目录产生 **`dist` 文件夹,里面的文件夹同 `packages` 一模一样**;在使用一些路径计算时,尤其是相对路径计算;`dist` 与 `packages` 里面保持相同的目录结构能避开好多问题
|
*🚨 默认情况下, `electron` 文件夹下的文件将会被构建到 `dist-electron`*
|
||||||
|
|
||||||
```tree
|
```tree
|
||||||
├── build 用于生产构建的资源
|
├── electron Electron 源码文件夹
|
||||||
| ├── icon.icns 应用图标(macOS)
|
│ ├── main Main-process 源码
|
||||||
| ├── icon.ico 应用图标
|
│ └── preload Preload-scripts 源码
|
||||||
| ├── installerIcon.ico 安装图标
|
│
|
||||||
| └── uninstallerIcon.ico 卸载图标
|
├── release 构建后生成程序目录
|
||||||
|
|
│ └── {version}
|
||||||
├── dist 构建后,根据 packages 目录生成
|
│ ├── {os}-{os_arch} 未打包的程序(绿色运行版)
|
||||||
| ├── main
|
│ └── {app_name}_{version}.{ext} 应用安装文件
|
||||||
| ├── preload
|
│
|
||||||
| └── renderer
|
├── public 同 Vite 模板的 public
|
||||||
|
|
└── src 渲染进程源码、React代码
|
||||||
├── release 在生产构建后生成,包含可执行文件
|
|
||||||
| └── {version}
|
|
||||||
| ├── win-unpacked 包含未打包的应用程序可执行文件
|
|
||||||
| └── Setup.exe 应用程序的安装程序
|
|
||||||
|
|
|
||||||
├── scripts
|
|
||||||
| ├── build.mjs 项目开发脚本 npm run build
|
|
||||||
| └── watch.mjs 项目开发脚本 npm run dev
|
|
||||||
|
|
|
||||||
├── packages
|
|
||||||
| ├── main 主进程源码
|
|
||||||
| | └── vite.config.ts
|
|
||||||
| ├── preload 预加载脚本源码
|
|
||||||
| | └── vite.config.ts
|
|
||||||
| └── renderer 渲染进程源码
|
|
||||||
| └── vite.config.ts
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 依赖放到 dependencies 还是 devDependencies
|
|
||||||
|
|
||||||
  对待 **Electron-Main、Preload-Script** 时 vite 会以 lib 形式打包 commonjs 格式代码;
|
|
||||||
如果碰 node 环境的包可以直接放到 dependencies 中,vite 会解析为 require('xxxx');
|
|
||||||
electron-builder 打包时候会将 dependencies 中的包打包到 app.asar 里面
|
|
||||||
|
|
||||||
  对待 **Electron-Renderer** 时 vite 会以 ESM 格式解析代码;
|
|
||||||
像 vue、react 这种前端用的包可以直接被 vite 构建,所以不需要 vue、react 源码;
|
|
||||||
现实情况 vue、react 放到 dependencies 或 devDependencies 中都可以被正确构建;
|
|
||||||
但是放到 dependencies 会被 electron-builder 打包到 app.asar 里面导致包体变大;
|
|
||||||
所以放到 devDependencies 既能被正确构建还可以减小 app.asar 体积,一举两得
|
|
||||||
|
|
||||||
## 渲染进程使用 NodeJs API
|
|
||||||
|
|
||||||
> 🚧 因为安全的原因 Electron 默认不支持在 渲染进程 中使用 NodeJs API,但是有些小沙雕就是想这么干,拦都拦不住;实在想那么干的话,用另一个模板更方便 👉 **[electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate)**
|
|
||||||
|
|
||||||
**推荐所有的 NodeJs、Electron API 通过 `Preload-script` 注入到 渲染进程中,例如:**
|
|
||||||
|
|
||||||
* **packages/preload/index.ts**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import fs from 'fs'
|
|
||||||
import { contextBridge, ipcRenderer } from 'electron'
|
|
||||||
|
|
||||||
// --------- Expose some API to Renderer-process. ---------
|
|
||||||
contextBridge.exposeInMainWorld('fs', fs)
|
|
||||||
contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer)
|
|
||||||
```
|
|
||||||
|
|
||||||
* **packages/renderer/src/global.d.ts**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Defined on the window
|
|
||||||
interface Window {
|
|
||||||
fs: typeof import('fs')
|
|
||||||
ipcRenderer: import('electron').IpcRenderer
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* **packages/renderer/main.ts**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Use Electron, NodeJs API in Renderer-process
|
|
||||||
console.log('fs', window.fs)
|
|
||||||
console.log('ipcRenderer', window.ipcRenderer)
|
|
||||||
```
|
|
||||||
|
|
||||||
**如果你真的在这个模板中开启了 `nodeIntegration: true` `contextIsolation: false` 我不拦着
|
|
||||||
🚧 但是要提醒你做两件事儿**
|
|
||||||
|
|
||||||
1. `preload/index.ts` 中的 `exposeInMainWorld` 删掉,已经没有用了
|
|
||||||
|
|
||||||
```diff
|
|
||||||
- contextBridge.exposeInMainWorld('fs', fs)
|
|
||||||
- contextBridge.exposeInMainWorld('ipcRenderer', ipcRenderer)
|
|
||||||
```
|
|
||||||
|
|
||||||
2. `configs/vite-renderer.config` 中有个 `resolveElectron` **最好了解下**
|
|
||||||
👉 这里有个 `issues` [请教一下vite-renderer.config中的resolveElectron函数](https://github.com/caoxiemeihao/electron-vue-vite/issues/52)
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
## 效果
|
## 🚨 这需要留神
|
||||||
|
|
||||||
<img width="400px" src="https://raw.githubusercontent.com/caoxiemeihao/blog/main/vite-react-electron/react-win.png" />
|
默认情况下,该模板在渲染进程中集成了 Node.js,如果你不需要它,你只需要删除下面的选项. [因为它会修改 Vite 默认的配置](https://github.com/electron-vite/vite-plugin-electron-renderer#config-presets-opinionated).
|
||||||
|
|
||||||
|
```diff
|
||||||
|
# vite.config.ts
|
||||||
|
|
||||||
|
export default {
|
||||||
|
plugins: [
|
||||||
|
...
|
||||||
|
- // Use Node.js API in the Renderer-process
|
||||||
|
- renderer({
|
||||||
|
- nodeIntegration: true,
|
||||||
|
- }),
|
||||||
|
...
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 🔧 额外的功能
|
||||||
|
|
||||||
|
1. Electron 自动更新 👉 [阅读文档](src/components/update/README.zh-CN.md)
|
||||||
|
2. Playwright 测试
|
||||||
|
|
||||||
|
## ❔ FAQ
|
||||||
|
|
||||||
|
- [C/C++ addons, Node.js modules - Pre-Bundling](https://github.com/electron-vite/vite-plugin-electron-renderer#dependency-pre-bundling)
|
||||||
|
- [dependencies vs devDependencies](https://github.com/electron-vite/vite-plugin-electron-renderer#dependencies-vs-devdependencies)
|
||||||
|
|
||||||
|
## 🍵 🍰 🍣 🍟
|
||||||
|
|
||||||
|
<img width="270" src="https://github.com/caoxiemeihao/blog/blob/main/assets/$qrcode/$.png?raw=true">
|
||||||
|
|
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 67 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
import { test, expect, _electron as electron } from "@playwright/test";
|
||||||
|
|
||||||
|
test("homepage has title and links to intro page", async () => {
|
||||||
|
const app = await electron.launch({ args: [".", "--no-sandbox"] });
|
||||||
|
const page = await app.firstWindow();
|
||||||
|
expect(await page.title()).toBe("Electron + Vite + React");
|
||||||
|
await page.screenshot({ path: "e2e/screenshots/example.png" });
|
||||||
|
});
|
After Width: | Height: | Size: 102 KiB |
|
@ -3,22 +3,31 @@
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
"appId": "YourAppID",
|
"appId": "YourAppID",
|
||||||
"productName": "YourAppName",
|
|
||||||
"copyright": "Copyright © 2022 ${author}",
|
|
||||||
"asar": true,
|
"asar": true,
|
||||||
"directories": {
|
"directories": {
|
||||||
"output": "release/${version}",
|
"output": "release/${version}"
|
||||||
"buildResources": "resources"
|
},
|
||||||
|
"files": [
|
||||||
|
"dist-electron",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"mac": {
|
||||||
|
"artifactName": "${productName}_${version}.${ext}",
|
||||||
|
"target": [
|
||||||
|
"dmg",
|
||||||
|
"zip"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"files": ["dist"],
|
|
||||||
"win": {
|
"win": {
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
"arch": ["x64"]
|
"arch": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"artifactName": "${productName}-${version}-Setup.${ext}"
|
"artifactName": "${productName}_${version}.${ext}"
|
||||||
},
|
},
|
||||||
"nsis": {
|
"nsis": {
|
||||||
"oneClick": false,
|
"oneClick": false,
|
||||||
|
@ -26,12 +35,9 @@
|
||||||
"allowToChangeInstallationDirectory": true,
|
"allowToChangeInstallationDirectory": true,
|
||||||
"deleteAppDataOnUninstall": false
|
"deleteAppDataOnUninstall": false
|
||||||
},
|
},
|
||||||
"mac": {
|
"publish": {
|
||||||
"target": ["dmg"],
|
"provider": "generic",
|
||||||
"artifactName": "${productName}-${version}-Installer.${ext}"
|
"channel": "latest",
|
||||||
},
|
"url": "https://github.com/electron-vite/electron-vite-react/releases/download/v0.9.9/"
|
||||||
"linux": {
|
|
||||||
"target": ["AppImage"],
|
|
||||||
"artifactName": "${productName}-${version}-Installer.${ext}"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 9.6 MiB After Width: | Height: | Size: 9.6 MiB |
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 3.4 MiB |
|
@ -0,0 +1,11 @@
|
||||||
|
/// <reference types="vite-electron-plugin/electron-env" />
|
||||||
|
|
||||||
|
declare namespace NodeJS {
|
||||||
|
interface ProcessEnv {
|
||||||
|
VSCODE_DEBUG?: 'true'
|
||||||
|
DIST_ELECTRON: string
|
||||||
|
DIST: string
|
||||||
|
/** /dist/ or /public/ */
|
||||||
|
VITE_PUBLIC: string
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
import { app, BrowserWindow, shell, ipcMain } from 'electron'
|
||||||
|
import { release } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { update } from './update'
|
||||||
|
|
||||||
|
// The built directory structure
|
||||||
|
//
|
||||||
|
// ├─┬ dist-electron
|
||||||
|
// │ ├─┬ main
|
||||||
|
// │ │ └── index.js > Electron-Main
|
||||||
|
// │ └─┬ preload
|
||||||
|
// │ └── index.js > Preload-Scripts
|
||||||
|
// ├─┬ dist
|
||||||
|
// │ └── index.html > Electron-Renderer
|
||||||
|
//
|
||||||
|
process.env.DIST_ELECTRON = join(__dirname, '../')
|
||||||
|
process.env.DIST = join(process.env.DIST_ELECTRON, '../dist')
|
||||||
|
process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL
|
||||||
|
? join(process.env.DIST_ELECTRON, '../public')
|
||||||
|
: process.env.DIST
|
||||||
|
|
||||||
|
// Disable GPU Acceleration for Windows 7
|
||||||
|
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
|
||||||
|
|
||||||
|
// Set application name for Windows 10+ notifications
|
||||||
|
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
|
||||||
|
|
||||||
|
if (!app.requestSingleInstanceLock()) {
|
||||||
|
app.quit()
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove electron security warnings
|
||||||
|
// This warning only shows in development mode
|
||||||
|
// Read more on https://www.electronjs.org/docs/latest/tutorial/security
|
||||||
|
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||||
|
|
||||||
|
let win: BrowserWindow | null = null
|
||||||
|
// Here, you can also use other preload
|
||||||
|
const preload = join(__dirname, '../preload/index.js')
|
||||||
|
const url = process.env.VITE_DEV_SERVER_URL
|
||||||
|
const indexHtml = join(process.env.DIST, 'index.html')
|
||||||
|
|
||||||
|
async function createWindow() {
|
||||||
|
win = new BrowserWindow({
|
||||||
|
title: 'Main window',
|
||||||
|
icon: join(process.env.VITE_PUBLIC, 'favicon.ico'),
|
||||||
|
webPreferences: {
|
||||||
|
preload,
|
||||||
|
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
||||||
|
// Consider using contextBridge.exposeInMainWorld
|
||||||
|
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (url) { // electron-vite-vue#298
|
||||||
|
win.loadURL(url)
|
||||||
|
// Open devTool if the app is not packaged
|
||||||
|
win.webContents.openDevTools()
|
||||||
|
} else {
|
||||||
|
win.loadFile(indexHtml)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test actively push message to the Electron-Renderer
|
||||||
|
win.webContents.on('did-finish-load', () => {
|
||||||
|
win?.webContents.send('main-process-message', new Date().toLocaleString())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Make all links open with the browser, not with the application
|
||||||
|
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
|
if (url.startsWith('https:')) shell.openExternal(url)
|
||||||
|
return { action: 'deny' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Apply electron-updater
|
||||||
|
update(win)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(createWindow)
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
win = null
|
||||||
|
if (process.platform !== 'darwin') app.quit()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('second-instance', () => {
|
||||||
|
if (win) {
|
||||||
|
// Focus on the main window if the user tried to open another
|
||||||
|
if (win.isMinimized()) win.restore()
|
||||||
|
win.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
const allWindows = BrowserWindow.getAllWindows()
|
||||||
|
if (allWindows.length) {
|
||||||
|
allWindows[0].focus()
|
||||||
|
} else {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// New window example arg: new windows url
|
||||||
|
ipcMain.handle('open-win', (_, arg) => {
|
||||||
|
const childWindow = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
preload,
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (process.env.VITE_DEV_SERVER_URL) {
|
||||||
|
childWindow.loadURL(`${url}#${arg}`)
|
||||||
|
} else {
|
||||||
|
childWindow.loadFile(indexHtml, { hash: arg })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { app, ipcMain } from 'electron'
|
||||||
|
import {
|
||||||
|
type ProgressInfo,
|
||||||
|
type UpdateDownloadedEvent,
|
||||||
|
autoUpdater
|
||||||
|
} from 'electron-updater'
|
||||||
|
|
||||||
|
export function update(win: Electron.BrowserWindow) {
|
||||||
|
|
||||||
|
// When set to false, the update download will be triggered through the API
|
||||||
|
autoUpdater.autoDownload = false
|
||||||
|
autoUpdater.disableWebInstaller = false
|
||||||
|
autoUpdater.allowDowngrade = false
|
||||||
|
|
||||||
|
// start check
|
||||||
|
autoUpdater.on('checking-for-update', function () { })
|
||||||
|
// update available
|
||||||
|
autoUpdater.on('update-available', (arg) => {
|
||||||
|
win.webContents.send('update-can-available', { update: true, version: app.getVersion(), newVersion: arg?.version })
|
||||||
|
})
|
||||||
|
// update not available
|
||||||
|
autoUpdater.on('update-not-available', (arg) => {
|
||||||
|
win.webContents.send('update-can-available', { update: false, version: app.getVersion(), newVersion: arg?.version })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Checking for updates
|
||||||
|
ipcMain.handle('check-update', async () => {
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
const error = new Error('The update feature is only available after the package.')
|
||||||
|
return { message: error.message, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await autoUpdater.checkForUpdatesAndNotify()
|
||||||
|
} catch (error) {
|
||||||
|
return { message: 'Network error', error }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start downloading and feedback on progress
|
||||||
|
ipcMain.handle('start-download', (event) => {
|
||||||
|
startDownload(
|
||||||
|
(error, progressInfo) => {
|
||||||
|
if (error) {
|
||||||
|
// feedback download error message
|
||||||
|
event.sender.send('update-error', { message: error.message, error })
|
||||||
|
} else {
|
||||||
|
// feedback update progress message
|
||||||
|
event.sender.send('download-progress', progressInfo)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// feedback update downloaded message
|
||||||
|
event.sender.send('update-downloaded')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Install now
|
||||||
|
ipcMain.handle('quit-and-install', () => {
|
||||||
|
autoUpdater.quitAndInstall(false, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function startDownload(
|
||||||
|
callback: (error: Error | null, info: ProgressInfo | null) => void,
|
||||||
|
complete: (event: UpdateDownloadedEvent) => void,
|
||||||
|
) {
|
||||||
|
autoUpdater.on('download-progress', info => callback(null, info))
|
||||||
|
autoUpdater.on('error', error => callback(error, null))
|
||||||
|
autoUpdater.on('update-downloaded', complete)
|
||||||
|
autoUpdater.downloadUpdate()
|
||||||
|
}
|
|
@ -1,10 +1,37 @@
|
||||||
|
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (condition.includes(document.readyState)) {
|
||||||
|
resolve(true)
|
||||||
|
} else {
|
||||||
|
document.addEventListener('readystatechange', () => {
|
||||||
|
if (condition.includes(document.readyState)) {
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeDOM = {
|
||||||
|
append(parent: HTMLElement, child: HTMLElement) {
|
||||||
|
if (!Array.from(parent.children).find(e => e === child)) {
|
||||||
|
return parent.appendChild(child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
remove(parent: HTMLElement, child: HTMLElement) {
|
||||||
|
if (Array.from(parent.children).find(e => e === child)) {
|
||||||
|
return parent.removeChild(child)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://tobiasahlin.com/spinkit
|
* https://tobiasahlin.com/spinkit
|
||||||
* https://connoratherton.com/loaders
|
* https://connoratherton.com/loaders
|
||||||
* https://projects.lukehaas.me/css-loaders
|
* https://projects.lukehaas.me/css-loaders
|
||||||
* https://matejkustec.github.io/SpinThatShit
|
* https://matejkustec.github.io/SpinThatShit
|
||||||
*/
|
*/
|
||||||
export function useLoading() {
|
function useLoading() {
|
||||||
const className = `loaders-css__square-spin`
|
const className = `loaders-css__square-spin`
|
||||||
const styleContent = `
|
const styleContent = `
|
||||||
@keyframes square-spin {
|
@keyframes square-spin {
|
||||||
|
@ -43,25 +70,23 @@ export function useLoading() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
appendLoading() {
|
appendLoading() {
|
||||||
safe.append(document.head, oStyle)
|
safeDOM.append(document.head, oStyle)
|
||||||
safe.append(document.body, oDiv)
|
safeDOM.append(document.body, oDiv)
|
||||||
},
|
},
|
||||||
removeLoading() {
|
removeLoading() {
|
||||||
safe.remove(document.head, oStyle)
|
safeDOM.remove(document.head, oStyle)
|
||||||
safe.remove(document.body, oDiv)
|
safeDOM.remove(document.body, oDiv)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const safe = {
|
// ----------------------------------------------------------------------
|
||||||
append(parent: HTMLElement, child: HTMLElement) {
|
|
||||||
if (!Array.from(parent.children).find(e => e === child)) {
|
const { appendLoading, removeLoading } = useLoading()
|
||||||
return parent.appendChild(child)
|
domReady().then(appendLoading)
|
||||||
}
|
|
||||||
},
|
window.onmessage = (ev) => {
|
||||||
remove(parent: HTMLElement, child: HTMLElement) {
|
ev.data.payload === 'removeLoading' && removeLoading()
|
||||||
if (Array.from(parent.children).find(e => e === child)) {
|
|
||||||
return parent.removeChild(child)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(removeLoading, 4999)
|
|
@ -2,13 +2,10 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta
|
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||||
http-equiv="Content-Security-Policy"
|
<title>Electron + Vite + React</title>
|
||||||
content="script-src 'self' 'unsafe-inline';"
|
|
||||||
/>
|
|
||||||
<title>Vite App</title>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
62
package.json
|
@ -1,40 +1,44 @@
|
||||||
{
|
{
|
||||||
"name": "vite-react-electron",
|
"name": "electron-vite-react",
|
||||||
"productName": "Electron",
|
"version": "2.2.0",
|
||||||
"private": true,
|
"main": "dist-electron/main/index.js",
|
||||||
"version": "1.0.0",
|
"description": "Electron Vite React boilerplate.",
|
||||||
"description": "Vite React Electron boilerplate.",
|
|
||||||
"author": "草鞋没号 <308487730@qq.com>",
|
"author": "草鞋没号 <308487730@qq.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "dist/main/index.cjs",
|
"private": true,
|
||||||
"scripts": {
|
"debug": {
|
||||||
"dev": "node scripts/watch.mjs",
|
"env": {
|
||||||
"build": "tsc --noEmit -p packages/renderer/tsconfig.json && node scripts/build.mjs && electron-builder"
|
"VITE_DEV_SERVER_URL": "http://127.0.0.1:7777/"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"engines": {
|
"scripts": {
|
||||||
"node": ">=14.17.0"
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build && electron-builder",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"pree2e": "vite build --mode=test",
|
||||||
|
"e2e": "playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-store": "^8.0.1"
|
"electron-updater": "^6.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.0.9",
|
"@playwright/test": "^1.37.1",
|
||||||
"@types/react-dom": "^18.0.4",
|
"@types/react": "^18.2.20",
|
||||||
"@vitejs/plugin-react": "^1.3.2",
|
"@types/react-dom": "^18.2.7",
|
||||||
"electron": "^18.2.4",
|
"@vitejs/plugin-react": "^4.0.4",
|
||||||
"electron-builder": "^23.0.3",
|
"autoprefixer": "^10.4.16",
|
||||||
"execa": "^6.1.0",
|
"electron": "^26.0.0",
|
||||||
"react": "^18.1.0",
|
"electron-builder": "^24.6.3",
|
||||||
"react-dom": "^18.1.0",
|
"postcss": "^8.4.31",
|
||||||
"sass": "^1.51.0",
|
"react": "^18.2.0",
|
||||||
"typescript": "^4.6.4",
|
"react-dom": "^18.2.0",
|
||||||
"vite": "^2.9.9",
|
"tailwindcss": "^3.3.3",
|
||||||
"vite-plugin-esmodule": "^1.2.5",
|
"typescript": "^5.1.6",
|
||||||
"vite-plugin-optimizer": "^1.3.3",
|
"vite": "^4.4.9",
|
||||||
"vite-plugin-resolve": "^2.1.1"
|
"vite-plugin-electron": "^0.13.0-beta.3",
|
||||||
|
"vite-plugin-electron-renderer": "^0.14.5"
|
||||||
},
|
},
|
||||||
"env": {
|
"engines": {
|
||||||
"VITE_DEV_SERVER_HOST": "127.0.0.1",
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
"VITE_DEV_SERVER_PORT": 7777
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { app, BrowserWindow, shell } from 'electron'
|
|
||||||
import { release } from 'os'
|
|
||||||
import { join } from 'path'
|
|
||||||
import './samples/electron-store'
|
|
||||||
import './samples/npm-esm-packages'
|
|
||||||
|
|
||||||
// Disable GPU Acceleration for Windows 7
|
|
||||||
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
|
|
||||||
|
|
||||||
// Set application name for Windows 10+ notifications
|
|
||||||
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
|
|
||||||
|
|
||||||
if (!app.requestSingleInstanceLock()) {
|
|
||||||
app.quit()
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
let win: BrowserWindow | null = null
|
|
||||||
|
|
||||||
async function createWindow() {
|
|
||||||
win = new BrowserWindow({
|
|
||||||
title: 'Main window',
|
|
||||||
webPreferences: {
|
|
||||||
preload: join(__dirname, '../preload/index.cjs')
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if (app.isPackaged) {
|
|
||||||
win.loadFile(join(__dirname, '../renderer/index.html'))
|
|
||||||
} else {
|
|
||||||
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
|
|
||||||
const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
|
|
||||||
|
|
||||||
win.loadURL(url)
|
|
||||||
// win.webContents.openDevTools()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test active push message to Renderer-process
|
|
||||||
win.webContents.on('did-finish-load', () => {
|
|
||||||
win?.webContents.send('main-process-message', (new Date).toLocaleString())
|
|
||||||
})
|
|
||||||
|
|
||||||
// Make all links open with the browser, not with the application
|
|
||||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
|
||||||
if (url.startsWith('https:')) shell.openExternal(url)
|
|
||||||
return { action: 'deny' }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
app.whenReady().then(createWindow)
|
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
win = null
|
|
||||||
if (process.platform !== 'darwin') app.quit()
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('second-instance', () => {
|
|
||||||
if (win) {
|
|
||||||
// Focus on the main window if the user tried to open another
|
|
||||||
if (win.isMinimized()) win.restore()
|
|
||||||
win.focus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('activate', () => {
|
|
||||||
const allWindows = BrowserWindow.getAllWindows()
|
|
||||||
if (allWindows.length) {
|
|
||||||
allWindows[0].focus()
|
|
||||||
} else {
|
|
||||||
createWindow()
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,19 +0,0 @@
|
||||||
/**
|
|
||||||
* Example of 'electron-store' usage.
|
|
||||||
*/
|
|
||||||
import { ipcMain } from 'electron'
|
|
||||||
import Store from 'electron-store'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expose 'electron-store' to Renderer-process through 'ipcMain.handle'
|
|
||||||
*/
|
|
||||||
const store = new Store()
|
|
||||||
ipcMain.handle(
|
|
||||||
'electron-store',
|
|
||||||
async (_event, methodSign: string, ...args: any[]) => {
|
|
||||||
if (typeof (store as any)[methodSign] === 'function') {
|
|
||||||
return (store as any)[methodSign](...args)
|
|
||||||
}
|
|
||||||
return (store as any)[methodSign]
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { execa } from 'execa'
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const { stdout } = await execa('echo', ['unicorns'])
|
|
||||||
|
|
||||||
// console.log(stdout) // unicorns
|
|
||||||
})()
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { builtinModules } from 'module'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
import esmodule from 'vite-plugin-esmodule'
|
|
||||||
import pkg from '../../package.json'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
root: __dirname,
|
|
||||||
plugins: [
|
|
||||||
esmodule([
|
|
||||||
'execa',
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
build: {
|
|
||||||
outDir: '../../dist/main',
|
|
||||||
emptyOutDir: true,
|
|
||||||
minify: process.env./* from mode option */NODE_ENV === 'production',
|
|
||||||
sourcemap: true,
|
|
||||||
lib: {
|
|
||||||
entry: 'index.ts',
|
|
||||||
formats: ['cjs'],
|
|
||||||
fileName: () => '[name].cjs',
|
|
||||||
},
|
|
||||||
rollupOptions: {
|
|
||||||
external: [
|
|
||||||
'electron',
|
|
||||||
...builtinModules,
|
|
||||||
...Object.keys(pkg.dependencies || {}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -1,36 +0,0 @@
|
||||||
import fs from 'fs'
|
|
||||||
import { contextBridge, ipcRenderer } from 'electron'
|
|
||||||
import { domReady } from './utils'
|
|
||||||
import { useLoading } from './loading'
|
|
||||||
|
|
||||||
const { appendLoading, removeLoading } = useLoading()
|
|
||||||
|
|
||||||
;(async () => {
|
|
||||||
await domReady()
|
|
||||||
|
|
||||||
appendLoading()
|
|
||||||
})()
|
|
||||||
|
|
||||||
// --------- Expose some API to the Renderer process. ---------
|
|
||||||
contextBridge.exposeInMainWorld('fs', fs)
|
|
||||||
contextBridge.exposeInMainWorld('removeLoading', removeLoading)
|
|
||||||
contextBridge.exposeInMainWorld('ipcRenderer', withPrototype(ipcRenderer))
|
|
||||||
|
|
||||||
// `exposeInMainWorld` can't detect attributes and methods of `prototype`, manually patching it.
|
|
||||||
function withPrototype(obj: Record<string, any>) {
|
|
||||||
const protos = Object.getPrototypeOf(obj)
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(protos)) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(obj, key)) continue
|
|
||||||
|
|
||||||
if (typeof value === 'function') {
|
|
||||||
// Some native APIs, like `NodeJS.EventEmitter['on']`, don't work in the Renderer process. Wrapping them into a function.
|
|
||||||
obj[key] = function (...args: any) {
|
|
||||||
return value.call(obj, ...args)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
/** Document ready */
|
|
||||||
export const domReady = (
|
|
||||||
condition: DocumentReadyState[] = ['complete', 'interactive']
|
|
||||||
) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (condition.includes(document.readyState)) {
|
|
||||||
resolve(true)
|
|
||||||
} else {
|
|
||||||
document.addEventListener('readystatechange', () => {
|
|
||||||
if (condition.includes(document.readyState)) {
|
|
||||||
resolve(true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
import { join } from 'path'
|
|
||||||
import { builtinModules } from 'module'
|
|
||||||
import { defineConfig } from 'vite'
|
|
||||||
import pkg from '../../package.json'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
root: __dirname,
|
|
||||||
build: {
|
|
||||||
outDir: '../../dist/preload',
|
|
||||||
emptyOutDir: true,
|
|
||||||
minify: process.env./* from mode option */NODE_ENV === 'production',
|
|
||||||
// https://github.com/caoxiemeihao/electron-vue-vite/issues/61
|
|
||||||
sourcemap: 'inline',
|
|
||||||
rollupOptions: {
|
|
||||||
input: {
|
|
||||||
// multiple entry
|
|
||||||
index: join(__dirname, 'index.ts'),
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
format: 'cjs',
|
|
||||||
entryFileNames: '[name].cjs',
|
|
||||||
manualChunks: {},
|
|
||||||
},
|
|
||||||
external: [
|
|
||||||
'electron',
|
|
||||||
...builtinModules,
|
|
||||||
...Object.keys(pkg.dependencies || {}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
Before Width: | Height: | Size: 16 KiB |
|
@ -1,72 +0,0 @@
|
||||||
import electron from '@/assets/electron.png'
|
|
||||||
import react from '@/assets/react.svg'
|
|
||||||
import vite from '@/assets/vite.svg'
|
|
||||||
import styles from '@/styles/app.module.scss'
|
|
||||||
import { useState } from 'react'
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [count, setCount] = useState(0)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.app}>
|
|
||||||
<header className={styles.appHeader}>
|
|
||||||
<div className={styles.logos}>
|
|
||||||
<div className={styles.imgBox}>
|
|
||||||
<img
|
|
||||||
src={electron}
|
|
||||||
style={{ height: '24vw' }}
|
|
||||||
className={styles.appLogo}
|
|
||||||
alt="electron"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.imgBox}>
|
|
||||||
<img src={vite} style={{ height: '19vw' }} alt="vite" />
|
|
||||||
</div>
|
|
||||||
<div className={styles.imgBox}>
|
|
||||||
<img
|
|
||||||
src={react}
|
|
||||||
style={{ maxWidth: '100%' }}
|
|
||||||
className={styles.appLogo}
|
|
||||||
alt="logo"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>Hello Electron + Vite + React!</p>
|
|
||||||
<p>
|
|
||||||
<button onClick={() => setCount((count) => count + 1)}>
|
|
||||||
count is: {count}
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Edit <code>App.tsx</code> and save to test HMR updates.
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
className={styles.appLink}
|
|
||||||
href="https://reactjs.org"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learn React
|
|
||||||
</a>
|
|
||||||
{' | '}
|
|
||||||
<a
|
|
||||||
className={styles.appLink}
|
|
||||||
href="https://vitejs.dev/guide/features.html"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Vite Docs
|
|
||||||
</a>
|
|
||||||
<div className={styles.staticPublic}>
|
|
||||||
Place static files into the{' '}
|
|
||||||
<code>src/renderer/public</code> folder
|
|
||||||
<img style={{ width: 90 }} src="./images/node.png" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
Before Width: | Height: | Size: 62 KiB |
|
@ -1,15 +0,0 @@
|
||||||
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
|
||||||
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#41D1FF"/>
|
|
||||||
<stop offset="1" stop-color="#BD34FE"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#FFEA83"/>
|
|
||||||
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
|
||||||
<stop offset="1" stop-color="#FFA800"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
|
@ -1,7 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
|
|
||||||
<g fill="#61DAFB">
|
|
||||||
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
|
|
||||||
<circle cx="420.9" cy="296.5" r="45.7"/>
|
|
||||||
<path d="M520.5 78.1z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1,15 +0,0 @@
|
||||||
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
|
||||||
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#41D1FF"/>
|
|
||||||
<stop offset="1" stop-color="#BD34FE"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#FFEA83"/>
|
|
||||||
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
|
||||||
<stop offset="1" stop-color="#FFA800"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 6.7 KiB |
|
@ -1,11 +0,0 @@
|
||||||
|
|
||||||
export { }
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
// Expose some Api through preload script
|
|
||||||
fs: typeof import('fs')
|
|
||||||
ipcRenderer: import('electron').IpcRenderer
|
|
||||||
removeLoading: () => void
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { StrictMode } from 'react'
|
|
||||||
import { createRoot } from 'react-dom/client'
|
|
||||||
import App from './App'
|
|
||||||
import './samples/electron-store'
|
|
||||||
import './samples/preload-module'
|
|
||||||
import './styles/index.css'
|
|
||||||
|
|
||||||
const root = createRoot(document.getElementById('root')!)
|
|
||||||
|
|
||||||
root.render(
|
|
||||||
<StrictMode>
|
|
||||||
<App />
|
|
||||||
</StrictMode>
|
|
||||||
)
|
|
||||||
|
|
||||||
window.removeLoading()
|
|
|
@ -1,31 +0,0 @@
|
||||||
// Usage of 'electron-store'
|
|
||||||
const store = {
|
|
||||||
async get(key: string) {
|
|
||||||
const { invoke } = window.ipcRenderer
|
|
||||||
let value = await invoke('electron-store', 'get', key)
|
|
||||||
try {
|
|
||||||
value = JSON.parse(value)
|
|
||||||
} finally {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async set(key: string, value: any) {
|
|
||||||
const { invoke } = window.ipcRenderer
|
|
||||||
let val = value
|
|
||||||
try {
|
|
||||||
if (value && typeof value === 'object') {
|
|
||||||
val = JSON.stringify(value)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
await invoke('electron-store', 'set', key, val)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await store.set('Date.now', Date.now())
|
|
||||||
console.log('electron-store ->', 'Date.now:', await store.get('Date.now'))
|
|
||||||
console.log('electron-store ->', 'path:', await window.ipcRenderer.invoke('electron-store', 'path'))
|
|
||||||
})();
|
|
||||||
|
|
||||||
export { }
|
|
|
@ -1,9 +0,0 @@
|
||||||
console.log('fs', window.fs)
|
|
||||||
console.log('ipcRenderer', window.ipcRenderer)
|
|
||||||
|
|
||||||
// Usage of ipcRenderer.on
|
|
||||||
window.ipcRenderer.on('main-process-message', (_event, ...args) => {
|
|
||||||
console.log('[Receive Main-process message]:', ...args)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default {}
|
|
|
@ -1,65 +0,0 @@
|
||||||
.app {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.appHeader {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
.logos {
|
|
||||||
display: flex;
|
|
||||||
box-sizing: border-box;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 5vw;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.imgBox {
|
|
||||||
width: 33.33%;
|
|
||||||
|
|
||||||
.appLogo {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.appLogo {
|
|
||||||
animation: App-logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
@keyframes App-logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
.appLink {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.staticPublic {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
code {
|
|
||||||
padding: 4px 7px;
|
|
||||||
margin: 0 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: rgb(30, 30, 30, .7);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../../paths.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNext",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": false,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx"
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
import { join } from 'path'
|
|
||||||
import { builtinModules } from 'module'
|
|
||||||
import { defineConfig, Plugin } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
import optimizer from 'vite-plugin-optimizer'
|
|
||||||
import resolve, { lib2esm } from 'vite-plugin-resolve'
|
|
||||||
import pkg from '../../package.json'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://vitejs.dev/config/
|
|
||||||
*/
|
|
||||||
export default defineConfig({
|
|
||||||
mode: process.env.NODE_ENV,
|
|
||||||
root: __dirname,
|
|
||||||
plugins: [
|
|
||||||
react(),
|
|
||||||
electron(),
|
|
||||||
resolve({
|
|
||||||
/**
|
|
||||||
* Here you can resolve some CommonJs module.
|
|
||||||
* Or some Node.js native modules they may not be built correctly by vite.
|
|
||||||
* At the same time, these modules should be put in `dependencies`,
|
|
||||||
* because they will not be built by vite, but will be packaged into `app.asar` by electron-builder
|
|
||||||
*/
|
|
||||||
|
|
||||||
// ESM format code snippets
|
|
||||||
'electron-store': 'export default require("electron-store");',
|
|
||||||
/**
|
|
||||||
* Node.js native module
|
|
||||||
* Use lib2esm() to easy to convert ESM
|
|
||||||
* Equivalent to
|
|
||||||
*
|
|
||||||
* ```js
|
|
||||||
* sqlite3: () => `
|
|
||||||
* const _M_ = require('sqlite3');
|
|
||||||
* const _D_ = _M_.default || _M_;
|
|
||||||
* export { _D_ as default }
|
|
||||||
* `
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
sqlite3: lib2esm('sqlite3', { format: 'cjs' }),
|
|
||||||
serialport: lib2esm(
|
|
||||||
// CJS lib name
|
|
||||||
'serialport',
|
|
||||||
// export memebers
|
|
||||||
[
|
|
||||||
'SerialPort',
|
|
||||||
'SerialPortMock',
|
|
||||||
],
|
|
||||||
{ format: 'cjs' },
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
base: './',
|
|
||||||
build: {
|
|
||||||
outDir: '../../dist/renderer',
|
|
||||||
emptyOutDir: true,
|
|
||||||
sourcemap: true,
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': join(__dirname, 'src'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
host: pkg.env.VITE_DEV_SERVER_HOST,
|
|
||||||
port: pkg.env.VITE_DEV_SERVER_PORT,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For usage of Electron and NodeJS APIs in the Renderer process
|
|
||||||
* @see https://github.com/caoxiemeihao/electron-vue-vite/issues/52
|
|
||||||
*/
|
|
||||||
export function electron(
|
|
||||||
entries: Parameters<typeof optimizer>[0] = {}
|
|
||||||
): Plugin {
|
|
||||||
const builtins = builtinModules.filter((t) => !t.startsWith('_'))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://github.com/caoxiemeihao/vite-plugins/tree/main/packages/resolve#readme
|
|
||||||
*/
|
|
||||||
return optimizer({
|
|
||||||
electron: electronExport(),
|
|
||||||
...builtinModulesExport(builtins),
|
|
||||||
...entries,
|
|
||||||
})
|
|
||||||
|
|
||||||
function electronExport() {
|
|
||||||
return `
|
|
||||||
/**
|
|
||||||
* For all exported modules see https://www.electronjs.org/docs/latest/api/clipboard -> Renderer Process Modules
|
|
||||||
*/
|
|
||||||
const electron = require("electron");
|
|
||||||
const {
|
|
||||||
clipboard,
|
|
||||||
nativeImage,
|
|
||||||
shell,
|
|
||||||
contextBridge,
|
|
||||||
crashReporter,
|
|
||||||
ipcRenderer,
|
|
||||||
webFrame,
|
|
||||||
desktopCapturer,
|
|
||||||
deprecate,
|
|
||||||
} = electron;
|
|
||||||
|
|
||||||
export {
|
|
||||||
electron as default,
|
|
||||||
clipboard,
|
|
||||||
nativeImage,
|
|
||||||
shell,
|
|
||||||
contextBridge,
|
|
||||||
crashReporter,
|
|
||||||
ipcRenderer,
|
|
||||||
webFrame,
|
|
||||||
desktopCapturer,
|
|
||||||
deprecate,
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
function builtinModulesExport(modules: string[]) {
|
|
||||||
return modules
|
|
||||||
.map((moduleId) => {
|
|
||||||
const nodeModule = require(moduleId)
|
|
||||||
const requireModule = `const M = require("${moduleId}");`
|
|
||||||
const exportDefault = `export default M;`
|
|
||||||
const exportMembers =
|
|
||||||
Object.keys(nodeModule)
|
|
||||||
.map((attr) => `export const ${attr} = M.${attr}`)
|
|
||||||
.join(';\n') + ';'
|
|
||||||
const nodeModuleCode = `
|
|
||||||
${requireModule}
|
|
||||||
|
|
||||||
${exportDefault}
|
|
||||||
|
|
||||||
${exportMembers}
|
|
||||||
`
|
|
||||||
|
|
||||||
return { [moduleId]: nodeModuleCode }
|
|
||||||
})
|
|
||||||
.reduce((memo, item) => Object.assign(memo, item), {})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": "./",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["packages/renderer/src/*"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// require('dotenv').config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
testDir: "./e2e",
|
||||||
|
/* Maximum time one test can run for. */
|
||||||
|
timeout: 30 * 1000,
|
||||||
|
expect: {
|
||||||
|
/**
|
||||||
|
* Maximum time expect() should wait for the condition to be met.
|
||||||
|
* For example in `await expect(locator).toHaveText();`
|
||||||
|
*/
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: "html",
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||||
|
actionTimeout: 0,
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
// baseURL: 'http://localhost:3000',
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: "on-first-retry",
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||||
|
// outputDir: 'test-results/',
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
// webServer: {
|
||||||
|
// command: 'npm run start',
|
||||||
|
// port: 3000,
|
||||||
|
// },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
'postcss-import': {},
|
||||||
|
'tailwindcss/nesting': {},
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
After Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="216" height="216" viewBox="0 0 216 216"><path fill="#80bd01" d="M104.6 180.7c-2 0-3.9-.5-5.7-1.5l-18.1-10.7c-2.7-1.5-1.4-2-.5-2.4 3.6-1.2 4.3-1.5 8.2-3.7.4-.2.9-.1 1.3.1l13.9 8.2c.5.3 1.2.3 1.7 0l54.1-31.2c.5-.3.8-.9.8-1.5V75.7c0-.6-.3-1.2-.8-1.5l-54-31.2c-.5-.3-1.2-.3-1.7 0l-54 31.2c-.5.3-.9.9-.9 1.5v62.4c0 .6.3 1.2.9 1.4l14.8 8.6c8 4 13-.7 13-5.5V81c0-.9.7-1.6 1.6-1.6h6.9c.9 0 1.6.7 1.6 1.6v61.6c0 10.7-5.8 16.9-16 16.9-3.1 0-5.6 0-12.5-3.4L44.8 148c-3.5-2-5.7-5.8-5.7-9.9V75.7c0-4.1 2.2-7.8 5.7-9.9l54.1-31.2c3.4-1.9 8-1.9 11.4 0l54.1 31.2c3.5 2 5.7 5.8 5.7 9.9v62.4c0 4.1-2.2 7.8-5.7 9.9l-54.1 31.2c-1.8 1-3.7 1.5-5.7 1.5zm43.6-61.5c0-11.7-7.9-14.8-24.5-17-16.8-2.2-18.5-3.4-18.5-7.3 0-3.2 1.4-7.6 13.9-7.6 11.1 0 15.2 2.4 16.9 9.9.1.7.8 1.2 1.5 1.2h7c.4 0 .8-.2 1.1-.5.3-.3.5-.8.4-1.2-1.1-12.9-9.7-18.9-27-18.9-15.4 0-24.6 6.5-24.6 17.4 0 11.8 9.1 15.1 23.9 16.6 17.7 1.7 19.1 4.3 19.1 7.8 0 6-4.8 8.6-16.2 8.6-14.3 0-17.5-3.6-18.5-10.7-.1-.8-.8-1.3-1.6-1.3h-7c-.9 0-1.6.7-1.6 1.6 0 9.1 5 20 28.6 20 17.3-.1 27.1-6.8 27.1-18.6zM172 55.9V57h3v8h1.2v-8h3.1v-1.1H172zm8.4 9.1h1.2v-7.6l2.6 7.6h1.2l2.6-7.6V65h1.2v-9h-1.7l-2.6 7.6-2.6-7.6h-1.8v9z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 36 KiB |
|
@ -1,5 +0,0 @@
|
||||||
import { build } from 'vite'
|
|
||||||
|
|
||||||
await build({ configFile: 'packages/main/vite.config.ts' })
|
|
||||||
await build({ configFile: 'packages/preload/vite.config.ts' })
|
|
||||||
await build({ configFile: 'packages/renderer/vite.config.ts' })
|
|
|
@ -1,70 +0,0 @@
|
||||||
import { spawn } from 'child_process'
|
|
||||||
import { createServer, build } from 'vite'
|
|
||||||
import electron from 'electron'
|
|
||||||
|
|
||||||
const query = new URLSearchParams(import.meta.url.split('?')[1])
|
|
||||||
const debug = query.has('debug')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
|
|
||||||
*/
|
|
||||||
function watchMain(server) {
|
|
||||||
/**
|
|
||||||
* @type {import('child_process').ChildProcessWithoutNullStreams | null}
|
|
||||||
*/
|
|
||||||
let electronProcess = null
|
|
||||||
const address = server.httpServer.address()
|
|
||||||
const env = Object.assign(process.env, {
|
|
||||||
VITE_DEV_SERVER_HOST: address.address,
|
|
||||||
VITE_DEV_SERVER_PORT: address.port,
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
* @type {import('vite').Plugin}
|
|
||||||
*/
|
|
||||||
const startElectron = {
|
|
||||||
name: 'electron-main-watcher',
|
|
||||||
writeBundle() {
|
|
||||||
if (electronProcess) {
|
|
||||||
electronProcess.removeAllListeners()
|
|
||||||
electronProcess.kill()
|
|
||||||
}
|
|
||||||
electronProcess = spawn(electron, ['.'], { stdio: 'inherit', env })
|
|
||||||
electronProcess.once('exit', process.exit)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return build({
|
|
||||||
configFile: 'packages/main/vite.config.ts',
|
|
||||||
mode: 'development',
|
|
||||||
plugins: [!debug && startElectron].filter(Boolean),
|
|
||||||
build: {
|
|
||||||
watch: {},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
|
|
||||||
*/
|
|
||||||
function watchPreload(server) {
|
|
||||||
return build({
|
|
||||||
configFile: 'packages/preload/vite.config.ts',
|
|
||||||
mode: 'development',
|
|
||||||
plugins: [{
|
|
||||||
name: 'electron-preload-watcher',
|
|
||||||
writeBundle() {
|
|
||||||
server.ws.send({ type: 'full-reload' })
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
build: {
|
|
||||||
watch: {},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// bootstrap
|
|
||||||
const server = await createServer({ configFile: 'packages/renderer/vite.config.ts' })
|
|
||||||
|
|
||||||
await server.listen()
|
|
||||||
await watchPreload(server)
|
|
||||||
await watchMain(server)
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
#root {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-box {
|
||||||
|
position: relative;
|
||||||
|
height: 9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(50% - 4.5em);
|
||||||
|
height: 6em;
|
||||||
|
padding: 1.5em;
|
||||||
|
will-change: filter;
|
||||||
|
transition: filter 300ms;
|
||||||
|
}
|
||||||
|
.logo:hover {
|
||||||
|
filter: drop-shadow(0 0 2em #646cffaa);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.logo.electron {
|
||||||
|
animation: logo-spin infinite 20s linear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.read-the-docs {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import UpdateElectron from '@/components/update'
|
||||||
|
import logoVite from './assets/logo-vite.svg'
|
||||||
|
import logoElectron from './assets/logo-electron.svg'
|
||||||
|
import './App.css'
|
||||||
|
|
||||||
|
console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [count, setCount] = useState(0)
|
||||||
|
return (
|
||||||
|
<div className='App'>
|
||||||
|
<div className='logo-box'>
|
||||||
|
<a href='https://github.com/electron-vite/electron-vite-react' target='_blank'>
|
||||||
|
<img src={logoVite} className='logo vite' alt='Electron + Vite logo' />
|
||||||
|
<img src={logoElectron} className='logo electron' alt='Electron + Vite logo' />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<h1>Electron + Vite + React</h1>
|
||||||
|
<div className='card'>
|
||||||
|
<button onClick={() => setCount((count) => count + 1)}>
|
||||||
|
count is {count}
|
||||||
|
</button>
|
||||||
|
<p>
|
||||||
|
Edit <code>src/App.tsx</code> and save to test HMR
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className='read-the-docs'>
|
||||||
|
Click on the Electron + Vite logo to learn more
|
||||||
|
</p>
|
||||||
|
<div className='flex-center'>
|
||||||
|
Place static files into the<code>/public</code> folder <img style={{ width: '5em' }} src='./node.svg' alt='Node logo' />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<UpdateElectron />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
|
@ -0,0 +1,8 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M51.3954 39.5028C52.3733 39.6812 53.3108 39.033 53.4892 38.055C53.6676 37.0771 53.0194 36.1396 52.0414 35.9612L51.3954 39.5028ZM28.9393 60.9358C29.4332 61.7985 30.5329 62.0976 31.3957 61.6037C32.2585 61.1098 32.5575 60.0101 32.0636 59.1473L28.9393 60.9358ZM37.6935 66.7457C37.025 66.01 35.8866 65.9554 35.1508 66.6239C34.415 67.2924 34.3605 68.4308 35.029 69.1666L37.6935 66.7457ZM96.9206 89.515C97.7416 88.9544 97.9526 87.8344 97.3919 87.0135C96.8313 86.1925 95.7113 85.9815 94.8904 86.5422L96.9206 89.515ZM52.0414 35.9612C46.4712 34.9451 41.2848 34.8966 36.9738 35.9376C32.6548 36.9806 29.0841 39.1576 27.0559 42.6762L30.1748 44.4741C31.5693 42.0549 34.1448 40.3243 37.8188 39.4371C41.5009 38.5479 46.1547 38.5468 51.3954 39.5028L52.0414 35.9612ZM27.0559 42.6762C24.043 47.9029 25.2781 54.5399 28.9393 60.9358L32.0636 59.1473C28.6579 53.1977 28.1088 48.0581 30.1748 44.4741L27.0559 42.6762ZM35.029 69.1666C39.6385 74.24 45.7158 79.1355 52.8478 83.2597L54.6499 80.1432C47.8081 76.1868 42.0298 71.5185 37.6935 66.7457L35.029 69.1666ZM52.8478 83.2597C61.344 88.1726 70.0465 91.2445 77.7351 92.3608C85.359 93.4677 92.2744 92.6881 96.9206 89.515L94.8904 86.5422C91.3255 88.9767 85.4902 89.849 78.2524 88.7982C71.0793 87.7567 62.809 84.8612 54.6499 80.1432L52.8478 83.2597ZM105.359 84.9077C105.359 81.4337 102.546 78.6127 99.071 78.6127V82.2127C100.553 82.2127 101.759 83.4166 101.759 84.9077H105.359ZM99.071 78.6127C95.5956 78.6127 92.7831 81.4337 92.7831 84.9077H96.3831C96.3831 83.4166 97.5892 82.2127 99.071 82.2127V78.6127ZM92.7831 84.9077C92.7831 88.3817 95.5956 91.2027 99.071 91.2027V87.6027C97.5892 87.6027 96.3831 86.3988 96.3831 84.9077H92.7831ZM99.071 91.2027C102.546 91.2027 105.359 88.3817 105.359 84.9077H101.759C101.759 86.3988 100.553 87.6027 99.071 87.6027V91.2027Z" fill="#A2ECFB"/>
|
||||||
|
<path d="M91.4873 65.382C90.8456 66.1412 90.9409 67.2769 91.7002 67.9186C92.4594 68.5603 93.5951 68.465 94.2368 67.7058L91.4873 65.382ZM84.507 35.2412C83.513 35.2282 82.6967 36.0236 82.6838 37.0176C82.6708 38.0116 83.4661 38.8279 84.4602 38.8409L84.507 35.2412ZM74.9407 39.8801C75.9127 39.6716 76.5315 38.7145 76.323 37.7425C76.1144 36.7706 75.1573 36.1517 74.1854 36.3603L74.9407 39.8801ZM25.5491 80.9047C25.6932 81.8883 26.6074 82.5688 27.5911 82.4247C28.5747 82.2806 29.2552 81.3664 29.1111 80.3828L25.5491 80.9047ZM94.2368 67.7058C97.8838 63.3907 100.505 58.927 101.752 54.678C103.001 50.4213 102.9 46.2472 100.876 42.7365L97.7574 44.5344C99.1494 46.9491 99.3603 50.0419 98.2974 53.6644C97.2323 57.2945 94.9184 61.3223 91.4873 65.382L94.2368 67.7058ZM100.876 42.7365C97.9119 37.5938 91.7082 35.335 84.507 35.2412L84.4602 38.8409C91.1328 38.9278 95.7262 41.0106 97.7574 44.5344L100.876 42.7365ZM74.1854 36.3603C67.4362 37.8086 60.0878 40.648 52.8826 44.8146L54.6847 47.931C61.5972 43.9338 68.5948 41.2419 74.9407 39.8801L74.1854 36.3603ZM52.8826 44.8146C44.1366 49.872 36.9669 56.0954 32.1491 62.3927C27.3774 68.63 24.7148 75.2115 25.5491 80.9047L29.1111 80.3828C28.4839 76.1026 30.4747 70.5062 35.0084 64.5802C39.496 58.7143 46.2839 52.7889 54.6847 47.931L52.8826 44.8146Z" fill="#A2ECFB"/>
|
||||||
|
<path d="M49.0825 87.2295C48.7478 86.2934 47.7176 85.8059 46.7816 86.1406C45.8455 86.4753 45.358 87.5055 45.6927 88.4416L49.0825 87.2295ZM78.5635 96.4256C79.075 95.5732 78.7988 94.4675 77.9464 93.9559C77.0941 93.4443 75.9884 93.7205 75.4768 94.5729L78.5635 96.4256ZM79.5703 85.1795C79.2738 86.1284 79.8027 87.1379 80.7516 87.4344C81.7004 87.7308 82.71 87.2019 83.0064 86.2531L79.5703 85.1795ZM69.156 22.5301C68.2477 22.1261 67.1838 22.535 66.7799 23.4433C66.3759 24.3517 66.7848 25.4155 67.6931 25.8194L69.156 22.5301ZM45.6927 88.4416C47.5994 93.7741 50.1496 98.2905 53.2032 101.505C56.2623 104.724 59.9279 106.731 63.9835 106.731V103.131C61.1984 103.131 58.4165 101.765 55.8131 99.0249C53.2042 96.279 50.8768 92.2477 49.0825 87.2295L45.6927 88.4416ZM63.9835 106.731C69.8694 106.731 74.8921 102.542 78.5635 96.4256L75.4768 94.5729C72.0781 100.235 68.0122 103.131 63.9835 103.131V106.731ZM83.0064 86.2531C85.0269 79.7864 86.1832 72.1831 86.1832 64.0673H82.5832C82.5832 71.8536 81.4723 79.0919 79.5703 85.1795L83.0064 86.2531ZM86.1832 64.0673C86.1832 54.1144 84.4439 44.922 81.4961 37.6502C78.5748 30.4436 74.3436 24.8371 69.156 22.5301L67.6931 25.8194C71.6364 27.5731 75.3846 32.1564 78.1598 39.0026C80.9086 45.7836 82.5832 54.507 82.5832 64.0673H86.1832Z" fill="#A2ECFB"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.559 84.9077C103.559 82.4252 101.55 80.4127 99.071 80.4127C96.5924 80.4127 94.5831 82.4252 94.5831 84.9077C94.5831 87.3902 96.5924 89.4027 99.071 89.4027C101.55 89.4027 103.559 87.3902 103.559 84.9077Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.8143 89.4027C31.2929 89.4027 33.3023 87.3902 33.3023 84.9077C33.3023 82.4252 31.2929 80.4127 28.8143 80.4127C26.3357 80.4127 24.3264 82.4252 24.3264 84.9077C24.3264 87.3902 26.3357 89.4027 28.8143 89.4027Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||||
|
<path d="M63.9835 27.6986C66.4621 27.6986 68.4714 25.6861 68.4714 23.2036C68.4714 20.7211 66.4621 18.7086 63.9835 18.7086C61.5049 18.7086 59.4956 20.7211 59.4956 23.2036C59.4956 25.6861 61.5049 27.6986 63.9835 27.6986Z" stroke="#A2ECFB" stroke-width="3.6" stroke-linecap="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 8.6 KiB |
|
@ -0,0 +1,20 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_103_2)">
|
||||||
|
<path d="M63.9202 127.84C99.2223 127.84 127.84 99.2223 127.84 63.9202C127.84 28.6181 99.2223 0 63.9202 0C28.6181 0 0 28.6181 0 63.9202C0 99.2223 28.6181 127.84 63.9202 127.84Z" fill="url(#paint0_linear_103_2)"/>
|
||||||
|
<path d="M70.7175 48.0096L56.3133 50.676C56.0766 50.7199 55.9013 50.9094 55.887 51.1369L55.001 65.2742C54.9801 65.6072 55.3038 65.8656 55.6478 65.7907L59.6582 64.9163C60.0334 64.8346 60.3724 65.1468 60.2953 65.5033L59.1038 71.0151C59.0237 71.386 59.3923 71.7032 59.7758 71.5932L62.2528 70.8822C62.6368 70.7721 63.0057 71.0902 62.9245 71.4615L61.031 80.1193C60.9126 80.6608 61.6751 80.9561 61.9931 80.4918L62.2055 80.1817L73.9428 58.053C74.1393 57.6825 73.8004 57.26 73.3696 57.3385L69.2417 58.0912C68.8538 58.1618 68.5237 57.8206 68.6332 57.462L71.3274 48.6385C71.437 48.2794 71.1058 47.9378 70.7175 48.0096Z" fill="url(#paint1_linear_103_2)"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_103_2" x1="1.43824" y1="7.91009" x2="56.3296" y2="82.4569" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#41D1FF"/>
|
||||||
|
<stop offset="1" stop-color="#BD34FE"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_103_2" x1="60.3173" y1="48.7336" x2="64.237" y2="77.1962" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFEA83"/>
|
||||||
|
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||||
|
<stop offset="1" stop-color="#FFA800"/>
|
||||||
|
</linearGradient>
|
||||||
|
<clipPath id="clip0_103_2">
|
||||||
|
<rect width="128" height="128" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,67 @@
|
||||||
|
import React, { ReactNode } from 'react'
|
||||||
|
import { createPortal } from 'react-dom'
|
||||||
|
import './modal.css'
|
||||||
|
|
||||||
|
const ModalTemplate: React.FC<React.PropsWithChildren<{
|
||||||
|
title?: ReactNode
|
||||||
|
footer?: ReactNode
|
||||||
|
cancelText?: string
|
||||||
|
okText?: string
|
||||||
|
onCancel?: () => void
|
||||||
|
onOk?: () => void
|
||||||
|
width?: number
|
||||||
|
}>> = props => {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
footer,
|
||||||
|
cancelText = 'Cancel',
|
||||||
|
okText = 'OK',
|
||||||
|
onCancel,
|
||||||
|
onOk,
|
||||||
|
width = 530,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='update-modal'>
|
||||||
|
<div className='update-modal__mask' />
|
||||||
|
<div className='update-modal__warp'>
|
||||||
|
<div className='update-modal__content' style={{ width }}>
|
||||||
|
<div className='content__header'>
|
||||||
|
<div className='content__header-text'>{title}</div>
|
||||||
|
<span
|
||||||
|
className='update-modal--close'
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M557.312 513.248l265.28-263.904c12.544-12.48 12.608-32.704 0.128-45.248-12.512-12.576-32.704-12.608-45.248-0.128l-265.344 263.936-263.04-263.84C236.64 191.584 216.384 191.52 203.84 204 191.328 216.48 191.296 236.736 203.776 249.28l262.976 263.776L201.6 776.8c-12.544 12.48-12.608 32.704-0.128 45.248 6.24 6.272 14.464 9.44 22.688 9.44 8.16 0 16.32-3.104 22.56-9.312l265.216-263.808 265.44 266.24c6.24 6.272 14.432 9.408 22.656 9.408 8.192 0 16.352-3.136 22.592-9.344 12.512-12.48 12.544-32.704 0.064-45.248L557.312 513.248z" p-id="2764" fill="currentColor">
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className='content__body'>{children}</div>
|
||||||
|
{typeof footer !== 'undefined' ? (
|
||||||
|
<div className='content__footer'>
|
||||||
|
<button onClick={onCancel}>{cancelText}</button>
|
||||||
|
<button onClick={onOk}>{okText}</button>
|
||||||
|
</div>
|
||||||
|
) : footer}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Modal = (props: Parameters<typeof ModalTemplate>[0] & { open: boolean }) => {
|
||||||
|
const { open, ...omit } = props
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
open ? ModalTemplate(omit) : null,
|
||||||
|
document.body,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Modal
|
|
@ -0,0 +1,87 @@
|
||||||
|
.update-modal {
|
||||||
|
--primary-color: rgb(224, 30, 90);
|
||||||
|
|
||||||
|
.update-modal__mask {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 9;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-modal__warp {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 19;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-modal__content {
|
||||||
|
box-shadow: 0 0 10px -4px rgb(130, 86, 208);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.content__header {
|
||||||
|
display: flex;
|
||||||
|
line-height: 38px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
|
||||||
|
.content__header-text {
|
||||||
|
font-weight: bold;
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-modal--close {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
margin: 4px;
|
||||||
|
line-height: 34px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__body {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content__footer {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 7px 11px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
padding: 0 15px;
|
||||||
|
width: 20px;
|
||||||
|
fill: currentColor;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react'
|
||||||
|
import './progress.css'
|
||||||
|
|
||||||
|
const Progress: React.FC<React.PropsWithChildren<{
|
||||||
|
percent?: number
|
||||||
|
}>> = props => {
|
||||||
|
const { percent = 0 } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='update-progress'>
|
||||||
|
<div className='update-progress-pr'>
|
||||||
|
<div
|
||||||
|
className='update-progress-rate'
|
||||||
|
style={{ width: `${3 * percent}px` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className='update-progress-num'>{(percent ?? 0).toString().substring(0, 4)}%</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Progress
|
|
@ -0,0 +1,21 @@
|
||||||
|
.update-progress {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.update-progress-pr {
|
||||||
|
border: 1px solid #000;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 6px;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-rate {
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-image: linear-gradient(to right, rgb(130, 86, 208) 0%, var(--primary-color) 100%)
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-num {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
# electron-updater
|
||||||
|
|
||||||
|
English | [简体中文](README.zh-CN.md)
|
||||||
|
|
||||||
|
> Use `electron-updater` to realize the update detection, download and installation of the electric program.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm i electron-updater
|
||||||
|
```
|
||||||
|
|
||||||
|
### Main logic
|
||||||
|
|
||||||
|
1. ##### Configuration of the update the service address and update information script:
|
||||||
|
|
||||||
|
Add a `publish` field to `electron-builder.json5` for configuring the update address and which strategy to use as the update service.
|
||||||
|
|
||||||
|
``` json5
|
||||||
|
{
|
||||||
|
"publish": {
|
||||||
|
"provider": "generic",
|
||||||
|
"channel": "latest",
|
||||||
|
"url": "https://foo.com/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, please refer to : [electron-builder.json5...](https://github.com/electron-vite/electron-vite-react/blob/2f2880a9f19de50ff14a0785b32a4d5427477e55/electron-builder.json5#L38)
|
||||||
|
|
||||||
|
2. ##### The update logic of Electron:
|
||||||
|
|
||||||
|
- Checking if an update is available;
|
||||||
|
- Checking the version of the software on the server;
|
||||||
|
- Checking if an update is available;
|
||||||
|
- Downloading the new version of the software from the server (when an update is available);
|
||||||
|
- Installation method;
|
||||||
|
|
||||||
|
For more information, please refer to : [update...](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts)
|
||||||
|
|
||||||
|
3. ##### Updating UI pages in Electron:
|
||||||
|
|
||||||
|
The main function is to provide a UI page for users to trigger the update logic mentioned in (2.) above. Users can click on the page to trigger different update functions in Electron.
|
||||||
|
|
||||||
|
For more information, please refer to : [components/update...](https://github.com/electron-vite/electron-vite-react/blob/main/src/components/update/index.tsx)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Here it is recommended to trigger updates through user actions (in this project, Electron update function is triggered after the user clicks on the "Check for updates" button).
|
||||||
|
|
||||||
|
For more information on using `electron-updater` for Electron updates, please refer to the documentation : [auto-update](https://www.electron.build/.html)
|
|
@ -0,0 +1,51 @@
|
||||||
|
# electron-auto-update
|
||||||
|
|
||||||
|
[English](README.md) | 简体中文
|
||||||
|
|
||||||
|
使用`electron-updater`实现electron程序的更新检测、下载和安装等功能。
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm i electron-updater
|
||||||
|
```
|
||||||
|
|
||||||
|
### 主要逻辑
|
||||||
|
|
||||||
|
1. ##### 更新地址、更新信息脚本的配置
|
||||||
|
|
||||||
|
在`electron-builder.json5`添加`publish`字段,用来配置更新地址和使用哪种策略作为更新服务
|
||||||
|
|
||||||
|
``` json5
|
||||||
|
{
|
||||||
|
"publish": {
|
||||||
|
"provider": "generic", // 提供者、提供商
|
||||||
|
"channel": "latest", // 生成yml文件的名称
|
||||||
|
"url": "https://foo.com/" //更新地址
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
更多见 : [electron-builder.json5...](xxx)
|
||||||
|
|
||||||
|
2. ##### Electron更新逻辑
|
||||||
|
|
||||||
|
- 检测更新是否可用;
|
||||||
|
|
||||||
|
- 检测服务端的软件版本;
|
||||||
|
|
||||||
|
- 检测更新是否可用;
|
||||||
|
|
||||||
|
- 下载服务端新版软件(当更新可用);
|
||||||
|
- 安装方式;
|
||||||
|
|
||||||
|
更多见 : [update...](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts)
|
||||||
|
|
||||||
|
3. ##### Electron更新UI页面
|
||||||
|
|
||||||
|
主要功能是:用户触发上述(2.)更新逻辑的UI页面。用户可以通过点击页面触发electron更新的不同功能。
|
||||||
|
更多见 : [components/update.ts...](https://github.com/electron-vite/electron-vite-react/tree/main/src/components/update/index.tsx)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
这里建议更新触发以用户操作触发(本项目的以用户点击 **更新检测** 后触发electron更新功能)
|
||||||
|
|
||||||
|
关于更多使用`electron-updater`进行electron更新,见文档:[auto-update](https://www.electron.build/.html)
|
|
@ -0,0 +1,133 @@
|
||||||
|
import { ipcRenderer } from 'electron'
|
||||||
|
import type { ProgressInfo } from 'electron-updater'
|
||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import Modal from '@/components/update/Modal'
|
||||||
|
import Progress from '@/components/update/Progress'
|
||||||
|
import './update.css'
|
||||||
|
|
||||||
|
const Update = () => {
|
||||||
|
const [checking, setChecking] = useState(false)
|
||||||
|
const [updateAvailable, setUpdateAvailable] = useState(false)
|
||||||
|
const [versionInfo, setVersionInfo] = useState<VersionInfo>()
|
||||||
|
const [updateError, setUpdateError] = useState<ErrorType>()
|
||||||
|
const [progressInfo, setProgressInfo] = useState<Partial<ProgressInfo>>()
|
||||||
|
const [modalOpen, setModalOpen] = useState<boolean>(false)
|
||||||
|
const [modalBtn, setModalBtn] = useState<{
|
||||||
|
cancelText?: string
|
||||||
|
okText?: string
|
||||||
|
onCancel?: () => void
|
||||||
|
onOk?: () => void
|
||||||
|
}>({
|
||||||
|
onCancel: () => setModalOpen(false),
|
||||||
|
onOk: () => ipcRenderer.invoke('start-download'),
|
||||||
|
})
|
||||||
|
|
||||||
|
const checkUpdate = async () => {
|
||||||
|
setChecking(true)
|
||||||
|
/**
|
||||||
|
* @type {import('electron-updater').UpdateCheckResult | null | { message: string, error: Error }}
|
||||||
|
*/
|
||||||
|
const result = await ipcRenderer.invoke('check-update')
|
||||||
|
setProgressInfo({ percent: 0 })
|
||||||
|
setChecking(false)
|
||||||
|
setModalOpen(true)
|
||||||
|
if (result?.error) {
|
||||||
|
setUpdateAvailable(false)
|
||||||
|
setUpdateError(result?.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onUpdateCanAvailable = useCallback((_event: Electron.IpcRendererEvent, arg1: VersionInfo) => {
|
||||||
|
setVersionInfo(arg1)
|
||||||
|
setUpdateError(undefined)
|
||||||
|
// Can be update
|
||||||
|
if (arg1.update) {
|
||||||
|
setModalBtn(state => ({
|
||||||
|
...state,
|
||||||
|
cancelText: 'Cancel',
|
||||||
|
okText: 'Update',
|
||||||
|
onOk: () => ipcRenderer.invoke('start-download'),
|
||||||
|
}))
|
||||||
|
setUpdateAvailable(true)
|
||||||
|
} else {
|
||||||
|
setUpdateAvailable(false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onUpdateError = useCallback((_event: Electron.IpcRendererEvent, arg1: ErrorType) => {
|
||||||
|
setUpdateAvailable(false)
|
||||||
|
setUpdateError(arg1)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onDownloadProgress = useCallback((_event: Electron.IpcRendererEvent, arg1: ProgressInfo) => {
|
||||||
|
setProgressInfo(arg1)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onUpdateDownloaded = useCallback((_event: Electron.IpcRendererEvent, ...args: any[]) => {
|
||||||
|
setProgressInfo({ percent: 100 })
|
||||||
|
setModalBtn(state => ({
|
||||||
|
...state,
|
||||||
|
cancelText: 'Later',
|
||||||
|
okText: 'Install now',
|
||||||
|
onOk: () => ipcRenderer.invoke('quit-and-install'),
|
||||||
|
}))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Get version information and whether to update
|
||||||
|
ipcRenderer.on('update-can-available', onUpdateCanAvailable)
|
||||||
|
ipcRenderer.on('update-error', onUpdateError)
|
||||||
|
ipcRenderer.on('download-progress', onDownloadProgress)
|
||||||
|
ipcRenderer.on('update-downloaded', onUpdateDownloaded)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ipcRenderer.off('update-can-available', onUpdateCanAvailable)
|
||||||
|
ipcRenderer.off('update-error', onUpdateError)
|
||||||
|
ipcRenderer.off('download-progress', onDownloadProgress)
|
||||||
|
ipcRenderer.off('update-downloaded', onUpdateDownloaded)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
open={modalOpen}
|
||||||
|
cancelText={modalBtn?.cancelText}
|
||||||
|
okText={modalBtn?.okText}
|
||||||
|
onCancel={modalBtn?.onCancel}
|
||||||
|
onOk={modalBtn?.onOk}
|
||||||
|
footer={updateAvailable ? /* hide footer */null : undefined}
|
||||||
|
>
|
||||||
|
<div className='modal-slot'>
|
||||||
|
{updateError
|
||||||
|
? (
|
||||||
|
<div>
|
||||||
|
<p>Error downloading the latest version.</p>
|
||||||
|
<p>{updateError.message}</p>
|
||||||
|
</div>
|
||||||
|
) : updateAvailable
|
||||||
|
? (
|
||||||
|
<div>
|
||||||
|
<div>The last version is: v{versionInfo?.newVersion}</div>
|
||||||
|
<div className='new-version__target'>v{versionInfo?.version} -> v{versionInfo?.newVersion}</div>
|
||||||
|
<div className='update__progress'>
|
||||||
|
<div className='progress__title'>Update progress:</div>
|
||||||
|
<div className='progress__bar'>
|
||||||
|
<Progress percent={progressInfo?.percent} ></Progress>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<div className='can-not-available'>{JSON.stringify(versionInfo ?? {}, null, 2)}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
<button disabled={checking} onClick={checkUpdate}>
|
||||||
|
{checking ? 'Checking...' : 'Check update'}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Update
|
|
@ -0,0 +1,24 @@
|
||||||
|
.modal-slot {
|
||||||
|
.update-progress {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-version__target,
|
||||||
|
.update__progress {
|
||||||
|
margin-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress__title {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress__bar {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.can-not-available {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
background-color: #242424;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin: 0 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App'
|
||||||
|
import './samples/node-api'
|
||||||
|
import './index.css'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
)
|
||||||
|
|
||||||
|
postMessage({ payload: 'removeLoading' }, '*')
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { lstat } from 'node:fs/promises'
|
||||||
|
import { cwd } from 'node:process'
|
||||||
|
import { ipcRenderer } from 'electron'
|
||||||
|
|
||||||
|
ipcRenderer.on('main-process-message', (_event, ...args) => {
|
||||||
|
console.log('[Receive Main-process message]:', ...args)
|
||||||
|
})
|
||||||
|
|
||||||
|
lstat(cwd()).then(stats => {
|
||||||
|
console.log('[fs.lstat]', stats)
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
|
@ -0,0 +1,10 @@
|
||||||
|
interface VersionInfo {
|
||||||
|
update: boolean
|
||||||
|
version: string
|
||||||
|
newVersion?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
message: string
|
||||||
|
error: Error
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
'./index.html',
|
||||||
|
'./src/**/*.{js,ts,jsx,tsx}',
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
corePlugins: {
|
||||||
|
preflight: false,
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
|
@ -1,15 +1,27 @@
|
||||||
{
|
{
|
||||||
"extends": "./paths.json",
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"useDefineForClassFields": true,
|
||||||
"allowJs": true,
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
"allowJs": false,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"skipDefaultLibCheck": true,
|
"esModuleInterop": false,
|
||||||
"esModuleInterop": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"strict": true,
|
"isolatedModules": true,
|
||||||
"jsx": "react-jsx"
|
"noEmit": true,
|
||||||
}
|
"jsx": "react-jsx",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"include": ["src", "electron"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts", "package.json"]
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
declare namespace NodeJS {
|
|
||||||
interface ProcessEnv {
|
|
||||||
NODE_ENV: 'development' | 'production'
|
|
||||||
readonly VITE_DEV_SERVER_HOST: string
|
|
||||||
readonly VITE_DEV_SERVER_PORT: string
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { rmSync } from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import electron from 'vite-plugin-electron'
|
||||||
|
import renderer from 'vite-plugin-electron-renderer'
|
||||||
|
import pkg from './package.json'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(({ command }) => {
|
||||||
|
rmSync('dist-electron', { recursive: true, force: true })
|
||||||
|
|
||||||
|
const isServe = command === 'serve'
|
||||||
|
const isBuild = command === 'build'
|
||||||
|
const sourcemap = isServe || !!process.env.VSCODE_DEBUG
|
||||||
|
|
||||||
|
return {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.join(__dirname, 'src')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
electron([
|
||||||
|
{
|
||||||
|
// Main-Process entry file of the Electron App.
|
||||||
|
entry: 'electron/main/index.ts',
|
||||||
|
onstart(options) {
|
||||||
|
if (process.env.VSCODE_DEBUG) {
|
||||||
|
console.log(/* For `.vscode/.debug.script.mjs` */'[startup] Electron App')
|
||||||
|
} else {
|
||||||
|
options.startup()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vite: {
|
||||||
|
build: {
|
||||||
|
sourcemap,
|
||||||
|
minify: isBuild,
|
||||||
|
outDir: 'dist-electron/main',
|
||||||
|
rollupOptions: {
|
||||||
|
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entry: 'electron/preload/index.ts',
|
||||||
|
onstart(options) {
|
||||||
|
// Notify the Renderer-Process to reload the page when the Preload-Scripts build is complete,
|
||||||
|
// instead of restarting the entire Electron App.
|
||||||
|
options.reload()
|
||||||
|
},
|
||||||
|
vite: {
|
||||||
|
build: {
|
||||||
|
sourcemap: sourcemap ? 'inline' : undefined, // #332
|
||||||
|
minify: isBuild,
|
||||||
|
outDir: 'dist-electron/preload',
|
||||||
|
rollupOptions: {
|
||||||
|
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
// Use Node.js API in the Renderer-process
|
||||||
|
renderer(),
|
||||||
|
],
|
||||||
|
server: process.env.VSCODE_DEBUG && (() => {
|
||||||
|
const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL)
|
||||||
|
return {
|
||||||
|
host: url.hostname,
|
||||||
|
port: +url.port,
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
clearScreen: false,
|
||||||
|
}
|
||||||
|
})
|