initial commit

This commit is contained in:
leejooy96 committed 2022-06-10 14:21:26 +09:00
commit 13ae6c773c
36 files changed
+24137

No files matched your search

+27
View File
@@ -0,0 +1,27 @@
{
"comments": false,
"env": {
"main": {
"presets": [
["@babel/preset-env", {
"targets": { "node": 14 }
}]
]
},
"renderer": {
"presets": [
["@babel/preset-env", {
"modules": false
}]
]
},
"web": {
"presets": [
["@babel/preset-env", {
"modules": false
}]
]
}
},
"plugins": ["@vue/babel-plugin-jsx", "@babel/plugin-transform-runtime"]
}
+4
View File
@@ -0,0 +1,4 @@
NODE_ENV=production
CSC_IDENTITY_AUTO_DISCOVERY=false
GENERATE_SOURCEMAP=false
NODE_OPTIONS="--max-old-space-size=8192"
+1
View File
@@ -0,0 +1 @@
ELECTRON_ENABLE_LOGGING=true
+1
View File
@@ -0,0 +1 @@
NODE_ENV=production
+34
View File
@@ -0,0 +1,34 @@
module.exports = {
root: true,
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@babel/eslint-parser',
sourceType: 'module',
ecmaVersion: 2018,
ecmaFeatures: {
jsx: true
}
},
env: {
browser: true,
node: true
},
extends: ['standard', 'plugin:vue/vue3-recommended'],
globals: {
__static: true
},
plugins: [
'vue'
],
rules: {
'arrow-parens': 0,
'generator-star-spacing': 0,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'no-case-declarations': 0,
'array-callback-return': 0,
'no-trailing-spaces': 1,
'no-control-regex': 0,
'no-useless-constructor': 0,
'node/no-deprecated-api': 0
}
}
+53
View File
@@ -0,0 +1,53 @@
# These are some examples of commonly ignored file patterns.
# You should customize this list as applicable to your project.
# Learn more about .gitignore:
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
# Node artifact files
node_modules/
dist/
# Compiled Java class files
*.class
# Compiled Python bytecode
*.py[cod]
# Log files
*.log
# Package files
*.jar
# Maven
target/
# JetBrains IDE
.idea/
# Unit test reports
TEST*.xml
# Generated by MacOS
.DS_Store
# Generated by Windows
Thumbs.db
# Applications
*.app
*.exe
*.war
# Large media files
*.mp4
*.tiff
*.avi
*.flv
*.mov
*.wmv
npm-debug.log
npm-debug.log.*
thumbs.db
.env
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) jooy2.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
+61
View File
@@ -0,0 +1,61 @@
# Vutron
Quick Start Templates for **[Vue.js 3](https://vuejs.org/)** + **[Electron.js](https://www.electronjs.org/)**
[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jooy2/vutron/blob/master/LICENSE)
![Programming Language Usage](https://img.shields.io/github/languages/top/jooy2/vutron)
![minified size](https://img.shields.io/bundlephobia/min/vutron)
![github repo size](https://img.shields.io/github/repo-size/jooy2/vutron)
![Stars](https://img.shields.io/github/stars/jooy2/vutron?style=social)
![Commit Count](https://img.shields.io/github/commit-activity/y/jooy2/vutron)
![Line Count](https://img.shields.io/tokei/lines/github/jooy2/vutron)
[![Followers](https://img.shields.io/github/followers/jooy2?style=social)](https://github.com/jooy2)
`Vutron` is a preconfigured template for developing `Electron` cross-platform desktop apps. It uses `Vue 3` and allows you to build a fast development environment with little effort.
## Advantages of use
- ✅ You can build immediately without any presets, so you can develop quickly.
- ✅ It is being maintained quickly to be compatible with the latest `Vue` and `Electron`, as well as many modules.
- ✅ There is no need to worry about layout and data management by using various additional templates.
## Pre-configured components
- Electron 19.x.x
- Electron Builder 23.x
- Vue 3.2.x
- Vue-i18n 9.x
- Vue-router 4.x
- Vuetify 3.x (Beta)
- Babel 7.x
- Webpack 5.x
- ESLint 8.x
## Installation
```shell
$ npm i
```
## Run in development environment
```shell
$ npm run dev
```
## Build
```shell
# For Windows (.exe, .appx)
$ npm run build:win
# For macOS (.dmg)
$ npm run build:mac
# For Linux (.rpm, .deb, .snap)
$ npm run build:linux
```
## License
Copyright © 2022 Jooy2 Released under the MIT license.
The structure and some code of this project was inspired by **[electron-vue](https://github.com/SimulatedGREG/electron-vue)**.
+100
View File
@@ -0,0 +1,100 @@
import './env.mjs'
import chalk from 'chalk'
import webpack from 'webpack'
import Listr from 'listr'
import { performance } from 'perf_hooks'
import _ from 'qsu'
import FsMan from 'fsman'
import mainConfig from '../config/webpack.main.mjs'
import rendererConfig from '../config/webpack.renderer.mjs'
const TAG_ERROR = `${chalk.bgRed.white(' ERROR ')} `
const TAG_SUCCESS = `${chalk.bgGreen.white(' SUCCESS ')} `
async function build () {
FsMan.empty('dist/electron')
let results = ''
const tasks = new Listr(
[
{
title: 'building main process',
task: async () => {
await pack(mainConfig)
.then(result => {
results += result + '\n\n'
})
.catch(err => {
console.log(`\n ${TAG_ERROR}failed to build main process`)
console.error(`\n${err}\n`)
})
}
},
{
title: 'building renderer process',
task: async () => {
await pack(rendererConfig)
.then(result => {
results += result + '\n\n'
})
.catch(err => {
console.log(`\n ${TAG_ERROR}failed to build renderer process`)
console.error(`\n${err}\n`)
})
}
}
],
{
concurrent: 2
}
)
await tasks.run()
.catch(() => {
process.exit(1)
})
process.stdout.write('\x1B[2J\x1B[0f')
console.log(`
\n\n${results}
${TAG_SUCCESS}Wait for ${chalk.greenBright('`electron-builder`')} process...
${TAG_SUCCESS}${chalk.yellowBright(`Webpack Build Time: ${_.msToTime(performance.now())}`)}\n
`)
process.exit()
}
function pack (config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (err, stats) => {
if (err) {
reject(err.stack || err)
} else if (stats.hasErrors()) {
let err = ''
stats.toString({
chunks: false,
colors: true
})
.split(/\r?\n/)
.forEach(line => {
err += ` ${line}\n`
})
reject(err)
} else {
resolve(stats.toString({
chunks: false,
colors: true
}))
}
})
})
}
(async () => {
performance.now()
await build()
})()
+2
View File
@@ -0,0 +1,2 @@
import dotenv from 'dotenv'
dotenv.config({ path: '.env.build' })
+68
View File
@@ -0,0 +1,68 @@
import path from 'path'
import packageJson from '../../package.json'
import webpack from 'webpack'
import TerserPlugin from 'terser-webpack-plugin'
import eslintFriendlyFormatter from 'eslint-friendly-formatter'
import ESLintPlugin from 'eslint-webpack-plugin'
import { fileURLToPath } from 'url'
process.env.BABEL_ENV = 'main'
const IS_DEV_ENV = process.env.NODE_ENV !== 'production'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
export default {
entry: {
main: path.join(__dirname, '../../src/main/index.js')
},
externals: [
...Object.keys(packageJson.dependencies || {})
],
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
}
]
},
node: {
__dirname: IS_DEV_ENV,
__filename: IS_DEV_ENV
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
new ESLintPlugin({
extensions: ['js'],
formatter: eslintFriendlyFormatter()
}),
new TerserPlugin(),
new webpack.DefinePlugin({
...IS_DEV_ENV
? {
__static: `"${path.join(__dirname, '../../static').replace(/\\/g, '\\\\')}"`
}
: {},
...IS_DEV_ENV
? {}
: {
'process.env.NODE_ENV': '"production"'
}
})
],
resolve: {
extensions: ['.js', '.json', '.node']
},
target: 'electron-main'
}
@@ -0,0 +1,4 @@
export default [
'vue',
'vuetify'
]
+166
View File
@@ -0,0 +1,166 @@
import path from 'path'
import packageJson from '../../package.json'
import webpack from 'webpack'
import TerserPlugin from 'terser-webpack-plugin'
import CopyWebpackPlugin from 'copy-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import { VueLoaderPlugin } from 'vue-loader'
import eslintFriendlyFormatter from 'eslint-friendly-formatter'
import ESLintPlugin from 'eslint-webpack-plugin'
import externalsWhiteList from './webpack.renderer.externals.whitelist.mjs'
import { fileURLToPath } from 'url'
const IS_DEV_ENV = process.env.NODE_ENV !== 'production'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
process.env.BABEL_ENV = 'renderer'
export default {
devtool: IS_DEV_ENV ? 'source-map' : false,
optimization: {
minimize: true,
minimizer: [new TerserPlugin()]
},
entry: {
renderer: path.join(__dirname, '../../src/renderer/main.js')
},
externals: [
...Object.keys(packageJson.dependencies || {}).filter(d => !externalsWhiteList.includes(d))
],
module: {
rules: [
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: !IS_DEV_ENV,
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader',
less: 'vue-style-loader!css-loader!less-loader'
}
}
}
},
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
type: 'asset/inline'
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
type: 'asset/inline'
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: 'asset/inline'
}
]
},
node: {
__dirname: IS_DEV_ENV,
__filename: IS_DEV_ENV
},
plugins: [
new TerserPlugin(),
new VueLoaderPlugin(),
new MiniCssExtractPlugin({ filename: 'styles.css' }),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../../src/index.ejs'),
templateParameters (compilation, assets, options) {
return {
compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options
},
process
}
},
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
isBrowser: false,
isDevelopment: IS_DEV_ENV,
nodeModules: IS_DEV_ENV
? path.resolve(__dirname, '../../node_modules')
: false
}),
new webpack.NoEmitOnErrorsPlugin(),
new ESLintPlugin({
extensions: ['js', 'vue'],
exclude: [
'/node_modules/'
],
formatter: eslintFriendlyFormatter()
}),
new webpack.DefinePlugin(IS_DEV_ENV
? {
__static: `"${path.join(__dirname, '../../static').replace(/\\/g, '\\\\')}"`,
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
}
: {
'process.env.NODE_ENV': '"production"',
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
}
),
...IS_DEV_ENV
? []
: [new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../../static'),
to: path.join(__dirname, '../../dist/electron/static')
}
]
})],
...IS_DEV_ENV
? []
: [new webpack.LoaderOptionsPlugin({
minimize: true
})]
],
cache: true,
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron')
},
resolve: {
alias: {
'@': path.join(__dirname, '../../src/renderer')
},
extensions: ['.js', '.vue', '.json', '.css', '.node']
},
target: 'electron-renderer'
}
+152
View File
@@ -0,0 +1,152 @@
import dotenv from 'dotenv'
import chalk from 'chalk'
import electron from 'electron'
import path from 'path'
import { spawn } from 'child_process'
import webpack from 'webpack'
import WebpackDevServer from 'webpack-dev-server'
import webpackHotMiddleware from 'webpack-hot-middleware'
import mainConfig from './config/webpack.main.mjs'
import rendererConfig from './config/webpack.renderer.mjs'
import { fileURLToPath } from 'url'
dotenv.config({ path: '.env.dev' })
const __dirname = path.dirname(fileURLToPath(import.meta.url))
let electronProcess = null
let manualRestart = false
function log (processName, data, color = 'yellow') {
let strLog = ''
if (typeof data === 'object') {
data.toString().split(/\r?\n/).forEach(line => {
strLog += ' ' + line + '\n'
})
} else {
strLog += ` ${data}\n`
}
if (/[\dA-z]+/.test(strLog)) {
console.log(`${chalk[color].bold(`[${processName}]---------------`)}\n`)
console.log(`${strLog}${chalk[color].bold(`[${processName}]---------------`)}\n`)
}
}
function startRenderer () {
return new Promise(resolve => {
(async () => {
rendererConfig.entry.renderer = [path.join(__dirname, 'devClient.mjs'), rendererConfig.entry.renderer]
const compiler = webpack({
mode: 'development',
...rendererConfig
})
compiler.hooks.done.tap('done', stats => {
log('Renderer', stats)
})
const server = new WebpackDevServer(
{
port: 9080,
liveReload: true,
static: {
directory: path.join(__dirname, '../'),
watch: {
usePolling: true,
ignored: /node_modules/
}
},
setupMiddlewares (middlewares, devServer) {
devServer.app.use(webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500
}))
devServer.middleware.waitUntilValid(() => {
resolve()
})
return middlewares
}
},
compiler
)
await server.start()
})()
})
}
function startMain () {
return new Promise(resolve => {
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js'), mainConfig.entry.main]
const compiler = webpack({
mode: 'development',
...mainConfig
})
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
log('Main', chalk.white.bold('Compiling...'))
done()
})
compiler.watch({}, (err, stats) => {
if (err) {
console.log(err)
return
}
log('Main', stats)
if (electronProcess?.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 3500)
}
resolve()
})
})
}
function startElectron () {
electronProcess = spawn(electron, [
...[
'--inspect=5858',
path.join(__dirname, '/../dist/electron/main.js')
],
...process.argv.slice(2)
])
electronProcess.stdout.on('data', data => {
log('Electron', data, 'blue')
})
electronProcess.stderr.on('data', data => {
log('Electron', data, 'red')
})
electronProcess.on('close', () => {
if (!manualRestart) {
process.exit()
}
})
}
(async () => {
try {
await startRenderer()
await startMain()
startElectron()
} catch (e) {
console.error(e)
}
})()
+15
View File
@@ -0,0 +1,15 @@
import * as hotClient from 'webpack-hot-middleware/client.js?noInfo=true&reload=true'
hotClient.subscribe(event => {
if (event.action === 'compiling') {
document.body.innerHTML += `
<style>
.root {
color: #000;
padding: 10px 20px;
}
</style>
<div class="root">Compiling Main Process...</div>
`
}
})
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

+23025
View File
File diff suppressed because it is too large. Load diff
+127
View File
@@ -0,0 +1,127 @@
{
"name": "vutron",
"version": "1.0.0",
"description": "Quick Start Templates for Vue 3 + Electron",
"main": "./dist/electron/main.js",
"scripts": {
"build:dir": "node --experimental-json-modules _runner/build/build.mjs && electron-builder --dir",
"build": "node --experimental-json-modules _runner/build/build.mjs && electron-builder",
"build:mac": "node --experimental-json-modules _runner/build/build.mjs && electron-builder --mac",
"build:linux": "node --experimental-json-modules _runner/build/build.mjs && electron-builder --linux",
"build:win": "node --experimental-json-modules _runner/build/build.mjs && electron-builder --windows",
"build:win:appx": "node --experimental-json-modules _runner/build/build.mjs && electron-builder --windows appx",
"build:win-portable": "node --experimental-json-modules _runner/build/build.mjs && electron-builder --win nsis:ia32 portable",
"dev": "node --experimental-json-modules _runner/dev.mjs --ignore-gpu-blacklist",
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "node -r dotenv/config webpack --progress --colors --config _runner/webpack.main.mjs dotenv_config_path=.env.pack",
"pack:renderer": "node -r dotenv/config webpack --progress --colors --config _runner/webpack.renderer.mjs dotenv_config_path=.env.pack",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src",
"postinstall": "npm run lint:fix && electron-builder install-app-deps"
},
"author": "jooy2 <jootc.help@gmail.com>",
"license": "MIT",
"dependencies": {
"@electron/remote": "^2.0.8",
"vue": "^3.2.37",
"vue-i18n": "^9.1.10",
"vue-router": "^4.0.15",
"vuetify": "^3.0.0-beta.3"
},
"devDependencies": {
"@babel/core": "^7.18.2",
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-transform-runtime": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@vue/babel-plugin-jsx": "^1.1.1",
"@vue/runtime-dom": "^3.2.37",
"babel-loader": "^8.2.5",
"chalk": "^5.0.1",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1",
"dotenv": "^16.0.1",
"electron": "^19.0.4",
"electron-builder": "^23.0.3",
"electron-devtools-installer": "^3.2.0",
"eslint": "^8.17.0",
"eslint-config-standard": "^17.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-plugin-html": "^6.2.0",
"eslint-plugin-vue": "^9.1.0",
"eslint-webpack-plugin": "^3.1.1",
"fsman": "^1.2.0",
"html-webpack-plugin": "^5.5.0",
"listr": "^0.14.3",
"mini-css-extract-plugin": "^2.6.0",
"node-loader": "^2.0.0",
"qsu": "^1.0.3",
"terser-webpack-plugin": "^5.3.3",
"vue-eslint-parser": "^9.0.2",
"vue-html-loader": "^1.2.4",
"vue-loader": "^17.0.0",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.6.14",
"webpack": "^5.73.0",
"webpack-dev-server": "^4.9.2",
"webpack-hot-middleware": "^2.25.1"
},
"build": {
"productName": "Vutron",
"appId": "com.vutron.vutron",
"compression": "maximum",
"directories": {
"output": "dist",
"buildResources": "buildRes"
},
"files": [
"dist/electron/**/*"
],
"mac": {
"bundleVersion": "1.0",
"hardenedRuntime": true,
"gatekeeperAssess": false,
"icon": "buildRes/icon.icns",
"type": "distribution",
"target": {
"target": "dmg",
"arch": [
"x64"
]
},
"artifactName": "${productName} ${version}.${ext}"
},
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
],
"sign": false
},
"win": {
"artifactName": "${productName} ${version}.${ext}",
"icon": "buildRes/icons/icon.ico",
"target": [
"appx",
"nsis"
]
},
"linux": {
"executableName": "vutron",
"icon": "buildRes/icons",
"target": [
"snap",
"deb",
"rpm"
]
}
}
}
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vutron</title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
<script>
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
</script>
<% } %>
</head>
<body>
<div id="app"></div>
<!-- Set `__static` path to static files in production -->
<% if (!htmlWebpackPlugin.options.isBrowser && !htmlWebpackPlugin.options.isDevelopment) { %>
<script>
window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<% } %>
<!-- webpack builds are automatically injected -->
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
{
"app": {
"title": "Vutron"
}
}
+81
View File
@@ -0,0 +1,81 @@
import { app, BrowserWindow } from 'electron'
import path from 'path'
import Constants from './utils/Constants'
export default class MainRunner {
static async createMainWindow (mainWindow) {
mainWindow = new BrowserWindow({
show: false,
width: Constants.IS_DEV_ENV ? 1500 : 1200,
height: 650,
icon: path.join(__static, Constants.APP_ICON),
title: Constants.APP_NAME,
useContentSize: true,
webPreferences: Constants.DEFAULT_WEB_PREFERENCES
})
mainWindow.setMenu(null)
mainWindow.on('close', (event) => {
event.preventDefault()
MainRunner.exitApp(mainWindow)
})
mainWindow.on('show', () => {
mainWindow.focus()
})
mainWindow.webContents.on('did-frame-finish-load', () => {
if (Constants.IS_DEV_ENV) {
mainWindow.webContents.openDevTools()
}
})
mainWindow.once('ready-to-show', () => {
mainWindow.setAlwaysOnTop(true)
mainWindow.show()
mainWindow.setAlwaysOnTop(false)
})
await mainWindow.loadURL(`${Constants.APP_INDEX_URL}`)
return mainWindow
}
static createCrashWindow (crashWindow, mainWindow, details) {
if (!Constants.IS_DEV_ENV) {
mainWindow?.hide()
}
crashWindow = new BrowserWindow({
show: false,
title: Constants.APP_NAME,
icon: path.join(__static, Constants.APP_ICON),
resizable: Constants.IS_DEV_ENV,
webPreferences: Constants.DEFAULT_WEB_PREFERENCES
})
crashWindow.setMenu(null)
crashWindow.loadURL(`${Constants.APP_INDEX_URL}#/crash`).then(() => true)
crashWindow.on('ready-to-show', () => {
if (!Constants.IS_DEV_ENV && mainWindow && !mainWindow.isDestroyed()) {
mainWindow.destroy()
}
crashWindow.show()
crashWindow.focus()
})
crashWindow.webContents.on('did-frame-finish-load', () => {
if (Constants.IS_DEV_ENV) {
crashWindow.webContents.openDevTools()
}
})
return crashWindow
}
static exitApp (mainWindow) {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.hide()
}
mainWindow = null
app.exit()
}
}
+10
View File
@@ -0,0 +1,10 @@
import { app } from 'electron'
import './index'
import * as electronDevtoolsInstaller from 'electron-devtools-installer'
app.on('ready', async () => {
await electronDevtoolsInstaller.default(electronDevtoolsInstaller.VUEJS_DEVTOOLS)
.catch(e => {
console.log(`Failed to install Vue Devtools \n(${e})`)
})
})
+38
View File
@@ -0,0 +1,38 @@
import { app } from 'electron'
import path from 'path'
import MainRunner from './MainRunner'
import { fileURLToPath } from 'url'
import Constants from './utils/Constants'
import electronRemote from '@electron/remote/main'
let mainWindow
let crashWindow
if (!Constants.IS_DEV_ENV) {
global.__static = path.join(path.dirname(fileURLToPath(import.meta.url)), '/static')
.replace(/\\/g, '\\\\')
}
app.on('ready', () => {
electronRemote.initialize()
mainWindow = MainRunner.createMainWindow(mainWindow)
})
app.on('activate', () => {
if (!mainWindow) {
mainWindow = MainRunner.createMainWindow(mainWindow)
}
})
app.on('window-all-closed', () => {
mainWindow = null
if (!Constants.IS_MAC) {
app.quit()
}
})
app.on('render-process-gone', (ev, webContents, details) => {
crashWindow = MainRunner.createCrashWindow(crashWindow, mainWindow, details)
})
+23
View File
@@ -0,0 +1,23 @@
import path from 'path'
import os from 'os'
import { app } from 'electron'
export default class Constants {
static APP_NAME = 'Vutron'
static APP_ICON = 'app-icon.png'
static IS_DEV_ENV = process.env.NODE_ENV === 'development'
static IS_MAC = os.platform() === 'darwin'
static DEFAULT_WEB_PREFERENCES = {
nodeIntegration: true,
contextIsolation: false,
nodeIntegrationInWorker: true,
enableRemoteModule: true
}
static APP_INDEX_URL = process.env.NODE_ENV === 'development'
? 'http://localhost:9080/index.html'
: path.join('file://', app.getAppPath(), '/dist/electron/index.html')
}
+11
View File
@@ -0,0 +1,11 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
View File
Whitespace-only changes.
@@ -0,0 +1,11 @@
<template>
<div>
Unknown error occurred
</div>
</template>
<script>
export default {
name: 'ErrorScreen'
}
</script>
@@ -0,0 +1,11 @@
<template>
<div>
Main Screen
</div>
</template>
<script>
export default {
name: 'MainScreen'
}
</script>
+7
View File
@@ -0,0 +1,7 @@
import ErrorScreen from './ErrorScreen.vue'
import MainScreen from './MainScreen.vue'
export {
ErrorScreen,
MainScreen
}
+14
View File
@@ -0,0 +1,14 @@
import { createApp } from 'vue'
import App from './App'
import router from './router'
import vuetify from './plugins/vuetify'
import 'vuetify/dist/vuetify.min.css'
const app = createApp(App)
app
.use(vuetify)
.use(router)
app.mount('#app')
+5
View File
@@ -0,0 +1,5 @@
import en from '../../locales/en.json'
export default {
en
}
+15
View File
@@ -0,0 +1,15 @@
import { createVuetify } from 'vuetify/dist/vuetify'
import messages from './i18n'
const vuetify = createVuetify({
icons: {
iconfont: 'mdi'
},
locale: {
defaultLocale: 'en',
fallbackLocale: 'en',
messages
}
})
export default vuetify
+22
View File
@@ -0,0 +1,22 @@
import { createRouter, createWebHashHistory } from 'vue-router/dist/vue-router.esm-bundler'
import { MainScreen, ErrorScreen } from '../components/screen'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
component: MainScreen
},
{
path: '/crash',
component: ErrorScreen
},
{
path: '/:pathMatch(.*)*',
redirect: '/'
}
]
})
export default router
Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B