initial commit

This commit is contained in:
José Conde 2023-02-06 21:59:24 +01:00
parent 29069f1e37
commit e7e3b33d2f
28 changed files with 978 additions and 125 deletions

146
.gitignore vendored
View File

@ -1,77 +1,20 @@
# ---> Node
# Logs # Logs
logs logs
*.log *.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html) node_modules
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json dist
dist-ssr
dist-electron
release
*.local
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# 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/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files # dotenv environment variable files
.env .env
@ -80,64 +23,19 @@ web_modules/
.env.production.local .env.production.local
.env.local .env.local
# parcel-bundler cache (https://parceljs.org/) # Editor directories and files
.cache .vscode
.parcel-cache .vscode/.debug.env
.idea
# Next.js build output .DS_Store
.next *.suo
out *.ntvs*
*.njsproj
# Nuxt.js build / generate output *.sln
.nuxt *.sw?
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/
# lockfile
package-lock.json
pnpm-lock.yaml
yarn.lock
.history

33
CHANGELOG.md Normal file
View File

@ -0,0 +1,33 @@
## 2022-10-03
[v2.1.0](https://github.com/electron-vite/electron-vite-vue/pull/267)
- `vite-electron-plugin` is Fast, and WYSIWYG. 🌱
- last-commit: db2e830 v2.1.0: use `vite-electron-plugin` instead `vite-plugin-electron`
## 2022-06-04
[v2.0.0](https://github.com/electron-vite/electron-vite-vue/pull/156)
- 🖖 Based on the `vue-ts` template created by `npm create vite`, integrate `vite-plugin-electron`
- ⚡️ More simplify, is in line with Vite project structure
- last-commit: a15028a (HEAD -> main) feat: hoist `process.env`
## 2022-01-30
[v1.0.0](https://github.com/electron-vite/electron-vite-vue/releases/tag/v1.0.0)
- ⚡️ Main、Renderer、preload, all built with vite
## 2022-01-27
- Refactor the scripts part.
- Remove `configs` directory.
## 2021-11-11
- Refactor the project. Use vite.config.ts build `Main-process`, `Preload-script` and `Renderer-process` alternative rollup.
- Scenic `Vue>=3.2.13`, `@vue/compiler-sfc` is no longer necessary.
- If you prefer Rollup, Use rollup branch.
```bash
Error: @vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc to be present in the dependency tree.
```

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 草鞋没号
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.

View File

@ -1,2 +1,71 @@
# monitor # electron-vite-vue
🥳 Really simple `Electron` + `Vue` + `Vite` boilerplate.
<!-- [![awesome-vite](https://awesome.re/mentioned-badge.svg)](https://github.com/vitejs/awesome-vite) -->
<!-- [![Netlify Status](https://api.netlify.com/api/v1/badges/ae3863e3-1aec-4eb1-8f9f-1890af56929d/deploy-status)](https://app.netlify.com/sites/electron-vite/deploys) -->
<!-- [![GitHub license](https://img.shields.io/github/license/caoxiemeihao/electron-vite-vue)](https://github.com/electron-vite/electron-vite-vue/blob/main/LICENSE) -->
<!-- [![GitHub stars](https://img.shields.io/github/stars/caoxiemeihao/electron-vite-vue?color=fa6470)](https://github.com/electron-vite/electron-vite-vue) -->
<!-- [![GitHub forks](https://img.shields.io/github/forks/caoxiemeihao/electron-vite-vue)](https://github.com/electron-vite/electron-vite-vue) -->
[![GitHub Build](https://github.com/electron-vite/electron-vite-vue/actions/workflows/build.yml/badge.svg)](https://github.com/electron-vite/electron-vite-vue/actions/workflows/build.yml)
[![GitHub Discord](https://img.shields.io/badge/chat-discord-blue?logo=discord)](https://discord.gg/sRqjYpEAUK)
## Features
📦 Out of the box
🎯 Based on the official [template-vue-ts](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-vue-ts), less invasive
🌱 Extensible, really simple directory structure
💪 Support using Node.js API in Electron-Renderer
🔩 Support C/C++ native addons
🖥 It's easy to implement multiple windows
## Quick Start
```sh
npm create electron-vite
```
<!-- [![quick-start](https://asciinema.org/a/483731.svg)](https://asciinema.org/a/483731) -->
![electron-vite-vue.gif](/public/electron-vite-vue.gif)
## Debug
![electron-vite-react-debug.gif](https://github.com/electron-vite/electron-vite-react/blob/main/public/electron-vite-react-debug.gif?raw=true)
## Directory
```diff
+ ├─┬ electron
+ │ ├─┬ main
+ │ │ └── index.ts entry of Electron-Main
+ │ └─┬ preload
+ │ └── index.ts entry of Preload-Scripts
├─┬ src
│ └── main.ts entry of Electron-Renderer
├── index.html
├── package.json
└── vite.config.ts
```
## Be aware
🚨 By default, this template integrates Node.js in the Renderer process. If you don't need it, you just remove the option below. [Because it will modify the default config of 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,
- }),
],
}
```
## FAQ
- [dependencies vs devDependencies](https://github.com/electron-vite/vite-plugin-electron-renderer#dependencies-vs-devdependencies)
- [C/C++ addons, Node.js modules - Pre-Bundling](https://github.com/electron-vite/vite-plugin-electron-renderer#dependency-pre-bundling)

38
electron-builder.json5 Normal file
View File

@ -0,0 +1,38 @@
/**
* @see https://www.electron.build/configuration/configuration
*/
{
"appId": "YourAppID",
"asar": true,
"icon": "public/favicon.ico",
"directories": {
"output": "release/${version}"
},
"files": [
"dist-electron",
"dist"
],
"mac": {
"artifactName": "${productName}_${version}.${ext}",
"target": [
"dmg"
]
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}_${version}.${ext}"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowToChangeInstallationDirectory": true,
"deleteAppDataOnUninstall": false
}
}

11
electron/electron-env.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/// <reference types="vite-plugin-electron/electron-env" />
declare namespace NodeJS {
interface ProcessEnv {
VSCODE_DEBUG?: 'true'
DIST_ELECTRON: string
DIST: string
/** /dist/ or /public/ */
PUBLIC: string
}
}

127
electron/main/index.ts Normal file
View File

@ -0,0 +1,127 @@
import { app, BrowserWindow, shell, ipcMain } from 'electron'
import { release } from 'node:os'
import { join } from 'node:path'
import { SimconnectClient } from '../simconnect/Client'
// 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.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')
const client: SimconnectClient = new SimconnectClient();
async function createWindow() {
win = new BrowserWindow({
title: 'Main window',
icon: join(process.env.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 (process.env.VITE_DEV_SERVER_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());
try {
client.open(win);
} catch(err) {
console.log(err);
}
})
// 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()
}
})
// 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 })
}
})

92
electron/preload/index.ts Normal file
View File

@ -0,0 +1,92 @@
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://connoratherton.com/loaders
* https://projects.lukehaas.me/css-loaders
* https://matejkustec.github.io/SpinThatShit
*/
function useLoading() {
const className = `loaders-css__square-spin`
const styleContent = `
@keyframes square-spin {
25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
100% { transform: perspective(100px) rotateX(0) rotateY(0); }
}
.${className} > div {
animation-fill-mode: both;
width: 50px;
height: 50px;
background: #fff;
animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
}
.app-loading-wrap {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #282c34;
z-index: 9;
}
`
const oStyle = document.createElement('style')
const oDiv = document.createElement('div')
oStyle.id = 'app-loading-style'
oStyle.innerHTML = styleContent
oDiv.className = 'app-loading-wrap'
oDiv.innerHTML = `<div class="${className}"><div></div></div>`
return {
appendLoading() {
safeDOM.append(document.head, oStyle)
safeDOM.append(document.body, oDiv)
},
removeLoading() {
safeDOM.remove(document.head, oStyle)
safeDOM.remove(document.body, oDiv)
},
}
}
// ----------------------------------------------------------------------
const { appendLoading, removeLoading } = useLoading()
domReady().then(appendLoading)
window.onmessage = (ev) => {
ev.data.payload === 'removeLoading' && removeLoading()
}
setTimeout(removeLoading, 4999)

View File

@ -0,0 +1,141 @@
import { BrowserWindow } from 'electron';
import {
open,
Protocol,
SimConnectDataType,
SimConnectPeriod,
SimConnectConstants,
readLatLonAlt,
RecvSimObjectData
} from 'node-simconnect';
import { DataDefiniion } from './DataDefinition';
import { DefinitionID, EventID, RequestID } from './enums';
export class SimconnectClient {
private requestsMap: Map<number, Function>;
private isPaused: Boolean;
private onGround: Boolean;
private onRunway: Boolean;
private window: BrowserWindow;
constructor() {
this.requestsMap = new Map();
this.isPaused = true;
this.onGround = true;
this.onRunway = false;
}
async open(win: BrowserWindow) {
const { recvOpen, handle } = await open('My SimConnect client', Protocol.KittyHawk);
console.log('Connected to', recvOpen);
this.subscribeToSystemEvent(handle);
this.addDataDefinitions(handle);
this.window = win;
handle.on('simObjectData', this.objectDataHandler.bind(this));
}
objectDataHandler(objectData: RecvSimObjectData) {
const { requestID, data } = objectData;
if (this.requestsMap.has(requestID)) {
const fn: Function = this.requestsMap.get(requestID);
fn(data, this.window);
}
}
subscribeToSystemEvent(handle) {
handle.subscribeToSystemEvent(EventID.PAUSE, 'Pause');
handle.on('event', function(recvEvent) {
switch (recvEvent.clientEventId) {
case EventID.PAUSE:
console.log(recvEvent.data === 1 ? 'Sim paused' : 'Sim unpaused');
this.isPaused = recvEvent.data === 1;
break;
}
}.bind(this));
}
addDataDefinitions(handle) {
const onGroundDef = new DataDefiniion(
DefinitionID.ON_GROUND,
RequestID.ON_GROUND,
SimConnectPeriod.SECOND,
data => {
const onGround = data.readInt32() === 1;
console.log('On Groud :>> ', onGround);
this.onGround = onGround;
}
);
const onRunwayDef = new DataDefiniion(
DefinitionID.ON_RUNWAY,
RequestID.ON_RUNWAY,
SimConnectPeriod.SECOND,
(data, window) => {
const isOnRunway = data.readInt32() === 1;
console.log('On Runway :>> ', isOnRunway);
this.onRunway = isOnRunway;
window.send('data_on_runway', this.onRunway);
}
);
const liveDef = new DataDefiniion(
DefinitionID.LIVE_DATA,
RequestID.LIVE_DATA,
SimConnectPeriod.SECOND,
data => {
console.log('this.isPaused :>> ', this.isPaused);
if (!this.isPaused) {
const timestamp = new Date().getTime();
const { latitude, longitude, altitude } = readLatLonAlt(data);
const airspeed = data.readInt32();
const verticalSpeed = data.readInt32();
const heading = data.readInt32();
console.log("position", `${latitude} ${longitude} ${altitude}`,
"airspeed", airspeed,
"vertical speed", verticalSpeed,
"heading", heading,
// "landing lights", recvSimObjectData.data.readInt32(),
// "logo lights", recvSimObjectData.data.readInt32(),
// "taxi lights", recvSimObjectData.data.readInt32(),
// "wing lights", recvSimObjectData.data.readInt32(),
// "nav lights", recvSimObjectData.data.readInt32(),
// "beacon lights", recvSimObjectData.data.readInt32()
);
}
}
);
const aircraftDef = new DataDefiniion(
DefinitionID.AIRCRAFT_DATA,
RequestID.AIRCRAFT_DATA,
SimConnectPeriod.ONCE,
data => {
console.log('===================================');
console.log(`Type: "${data.readStringV()}"`);
console.log(`Title: "${data.readStringV()}"`);
console.log(`ATC ID: "${data.readString32()}"`);
console.log('===================================');
}
);
onGroundDef.add(['SIM ON GROUND', 'bool', SimConnectDataType.INT32, 0, SimConnectConstants.UNUSED]);
onRunwayDef.add(['ON ANY RUNWAY', 'bool', SimConnectDataType.INT32, 0, SimConnectConstants.UNUSED])
liveDef.add(['STRUCT LATLONALT', null, SimConnectDataType.LATLONALT]);
liveDef.add(['AIRSPEED INDICATED', 'knots', SimConnectDataType.INT32]);
liveDef.add(['VERTICAL SPEED', 'Feet per second', SimConnectDataType.INT32]);
liveDef.add(['PLANE HEADING DEGREES TRUE', 'Degrees', SimConnectDataType.INT32]);
liveDef.add(['LIGHT LANDING', 'bool', SimConnectDataType.INT32]);
liveDef.add(['LIGHT LOGO', 'bool', SimConnectDataType.INT32]);
liveDef.add(['LIGHT TAXI', 'bool', SimConnectDataType.INT32]);
liveDef.add(['LIGHT WING', 'bool', SimConnectDataType.INT32]);
liveDef.add(['LIGHT NAV', 'bool', SimConnectDataType.INT32]);
liveDef.add(['LIGHT BEACON', 'bool', SimConnectDataType.INT32]);
aircraftDef.add(['CATEGORY', null, SimConnectDataType.STRINGV, 0, SimConnectConstants.UNUSED]);
aircraftDef.add(['TITLE', null, SimConnectDataType.STRINGV, 0, SimConnectConstants.UNUSED]);
aircraftDef.add(['ATC ID', null, SimConnectDataType.STRING32, 0, SimConnectConstants.UNUSED]);
onGroundDef.build(handle, this.requestsMap);
onRunwayDef.build(handle, this.requestsMap);
liveDef.build(handle, this.requestsMap);
// aircraftDef.build(handle, this.requestsMap);
}
}

View File

@ -0,0 +1,29 @@
import { SimConnectConstants } from 'node-simconnect';
export class DataDefiniion {
private definitions: Array<any> = [];
private id: number;
private requestId: number;
private period: number;
private handler: Function;
constructor(id, requestId, period, handler) {
this.id = id;
this.requestId = requestId;
this.handler = handler;
this.period = period;
}
add(definition :Array<any>): void {
this.definitions.push(definition);
}
build(handle, requestsMap: Map<number, Function>) {
this.definitions.forEach(definition => {
handle.addToDataDefinition(this.id, ...definition);
});
requestsMap.set(this.requestId, this.handler);
handle.requestDataOnSimObject(this.requestId, this.id, SimConnectConstants.OBJECT_ID_USER, this.period);
}
}

View File

@ -0,0 +1,18 @@
export enum EventID {
PAUSE,
};
export enum DefinitionID {
LIVE_DATA,
AIRCRAFT_DATA,
ON_GROUND,
ON_RUNWAY,
};
export enum RequestID {
LIVE_DATA,
AIRCRAFT_DATA,
ON_GROUND,
ON_RUNWAY,
};

14
index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<title>Electron + Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

40
package.json Normal file
View File

@ -0,0 +1,40 @@
{
"name": "electron-vue-vite",
"version": "2.0.0",
"main": "dist-electron/main/index.js",
"description": "Really simple Electron + Vue + Vite boilerplate.",
"author": "草鞋没号 <308487730@qq.com>",
"license": "MIT",
"private": true,
"keywords": [
"electron",
"rollup",
"vite",
"vue3",
"vue"
],
"debug": {
"env": {
"VITE_DEV_SERVER_URL": "http://127.0.0.1:3344/"
}
},
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build && electron-builder",
"preview": "vite preview"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"electron": "^22.0.3",
"electron-builder": "^23.6.0",
"typescript": "^4.9.4",
"vite": "^4.0.4",
"vite-plugin-electron": "^0.11.1",
"vite-plugin-electron-renderer": "^0.11.4",
"vue": "^3.2.45",
"vue-tsc": "^1.0.24"
},
"dependencies": {
"node-simconnect": "^3.3.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

1
public/node.svg Normal file
View File

@ -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

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

51
src/App.vue Normal file
View File

@ -0,0 +1,51 @@
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
console.log("[App.vue]", `Hello world from Electron ${process.versions.electron}!`)
</script>
<template>
<div>
<a href="https://www.electronjs.org/" target="_blank">
<img src="./assets/electron.svg" class="logo electron" alt="Electron logo" />
</a>
<a href="https://vitejs.dev/" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
<a href="https://vuejs.org/" target="_blank">
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
</a>
</div>
<HelloWorld msg="Electron + Vite + Vue" />
<div class="flex-center">
Place static files into the <code>/public</code> folder
<img style="width:5em;" src="/node.svg" alt="Node logo">
</div>
</template>
<style>
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo.electron:hover {
filter: drop-shadow(0 0 2em #9FEAF9);
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

1
src/assets/electron.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256"><g fill="none" fill-rule="evenodd"><circle fill="#2B2E3A" cx="128" cy="128" r="128"/><g fill="#9FEAF9" fill-rule="nonzero"><path d="M100.502 71.69c-26.005-4.736-46.567.221-54.762 14.415-6.115 10.592-4.367 24.635 4.24 39.646a2.667 2.667 0 1 0 4.626-2.653c-7.752-13.522-9.261-25.641-4.247-34.326 6.808-11.791 25.148-16.213 49.187-11.835a2.667 2.667 0 0 0 .956-5.247zm-36.999 72.307c10.515 11.555 24.176 22.394 39.756 31.388 37.723 21.78 77.883 27.601 97.675 14.106a2.667 2.667 0 1 0-3.005-4.406c-17.714 12.078-55.862 6.548-92.003-14.318-15.114-8.726-28.343-19.222-38.478-30.36a2.667 2.667 0 1 0-3.945 3.59z"/><path d="M194.62 140.753c17.028-20.116 22.973-40.348 14.795-54.512-6.017-10.423-18.738-15.926-35.645-16.146a2.667 2.667 0 0 0-.069 5.333c15.205.198 26.165 4.939 31.096 13.48 6.792 11.765 1.49 29.807-14.248 48.399a2.667 2.667 0 1 0 4.071 3.446zm-43.761-68.175c-15.396 3.299-31.784 9.749-47.522 18.835-38.942 22.483-64.345 55.636-60.817 79.675a2.667 2.667 0 1 0 5.277-.775c-3.133-21.344 20.947-52.769 58.207-74.281 15.267-8.815 31.135-15.06 45.972-18.239a2.667 2.667 0 1 0-1.117-5.215z"/><path d="M87.77 187.753c8.904 24.86 23.469 40.167 39.847 40.167 11.945 0 22.996-8.143 31.614-22.478a2.667 2.667 0 1 0-4.571-2.748c-7.745 12.883-17.258 19.892-27.043 19.892-13.605 0-26.596-13.652-34.825-36.63a2.667 2.667 0 1 0-5.021 1.797zm81.322-4.863c4.61-14.728 7.085-31.718 7.085-49.423 0-44.179-15.463-82.263-37.487-92.042a2.667 2.667 0 0 0-2.164 4.874c19.643 8.723 34.317 44.866 34.317 87.168 0 17.177-2.397 33.63-6.84 47.83a2.667 2.667 0 1 0 5.09 1.593zm50.224-2.612c0-7.049-5.714-12.763-12.763-12.763-7.049 0-12.763 5.714-12.763 12.763 0 7.049 5.714 12.763 12.763 12.763 7.049 0 12.763-5.714 12.763-12.763zm-5.333 0a7.43 7.43 0 1 1-14.86 0 7.43 7.43 0 0 1 14.86 0zM48.497 193.041c7.05 0 12.764-5.714 12.764-12.763 0-7.049-5.715-12.763-12.764-12.763-7.048 0-12.763 5.714-12.763 12.763 0 7.049 5.715 12.763 12.763 12.763zm0-5.333a7.43 7.43 0 1 1 0-14.86 7.43 7.43 0 0 1 0 14.86z"/><path d="M127.617 54.444c7.049 0 12.763-5.714 12.763-12.763 0-7.049-5.714-12.763-12.763-12.763-7.049 0-12.763 5.714-12.763 12.763 0 7.049 5.714 12.763 12.763 12.763zm0-5.333a7.43 7.43 0 1 1 0-14.86 7.43 7.43 0 0 1 0 14.86zm1.949 93.382c-4.985 1.077-9.896-2.091-10.975-7.076a9.236 9.236 0 0 1 7.076-10.976c4.985-1.077 9.896 2.091 10.976 7.076 1.077 4.985-2.091 9.897-7.077 10.976z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,38 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

10
src/main.ts Normal file
View File

@ -0,0 +1,10 @@
import { createApp } from 'vue'
import "./style.css"
import App from './App.vue'
import './samples/node-api'
createApp(App)
.mount('#app')
.$nextTick(() => {
postMessage({ payload: 'removeLoading' }, '*')
})

18
src/samples/node-api.ts Normal file
View File

@ -0,0 +1,18 @@
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)
})
ipcRenderer.on('data_on_runway', (_event, ...args) => {
console.log('[Receive data_on_runway message]:', ...args)
console.log('document :>> ', document);
})
lstat(cwd()).then(stats => {
console.log('[fs.lstat]', stats)
}).catch(err => {
console.error(err)
})

90
src/style.css Normal file
View File

@ -0,0 +1,90 @@
: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;
}
}

7
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

20
tsconfig.json Normal file
View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true
},
"include": ["src"],
"references": [
{ "path": "./tsconfig.node.json" }
]
}

10
tsconfig.node.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts", "package.json", "electron"]
}

74
vite.config.ts Normal file
View File

@ -0,0 +1,74 @@
import { rmSync } from 'node:fs'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
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 {
plugins: [
vue(),
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({
nodeIntegration: true,
}),
],
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,
}
})