diff --git a/.gitignore b/.gitignore index aa4b2f5..d9cd1a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,77 +1,20 @@ -# ---> Node # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* lerna-debug.log* -.pnpm-debug.log* -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +node_modules +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 .env @@ -80,64 +23,19 @@ web_modules/ .env.production.local .env.local -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -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/ +# Editor directories and files +.vscode +.vscode/.debug.env +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +# lockfile +package-lock.json +pnpm-lock.yaml +yarn.lock +.history \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..db6dbaa --- /dev/null +++ b/CHANGELOG.md @@ -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. +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..22edc0e --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md index 90cda29..359d4a7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,71 @@ -# monitor +# electron-vite-vue +πŸ₯³ Really simple `Electron` + `Vue` + `Vite` boilerplate. + + + + + + +[![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 +``` + + + +![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) diff --git a/electron-builder.json5 b/electron-builder.json5 new file mode 100644 index 0000000..cc8c811 --- /dev/null +++ b/electron-builder.json5 @@ -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 + } +} diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts new file mode 100644 index 0000000..d02e86c --- /dev/null +++ b/electron/electron-env.d.ts @@ -0,0 +1,11 @@ +/// + +declare namespace NodeJS { + interface ProcessEnv { + VSCODE_DEBUG?: 'true' + DIST_ELECTRON: string + DIST: string + /** /dist/ or /public/ */ + PUBLIC: string + } +} diff --git a/electron/main/index.ts b/electron/main/index.ts new file mode 100644 index 0000000..8862366 --- /dev/null +++ b/electron/main/index.ts @@ -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 }) + } +}) diff --git a/electron/preload/index.ts b/electron/preload/index.ts new file mode 100644 index 0000000..ebf1276 --- /dev/null +++ b/electron/preload/index.ts @@ -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 = `
` + + 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) diff --git a/electron/simconnect/Client.ts b/electron/simconnect/Client.ts new file mode 100644 index 0000000..dd09801 --- /dev/null +++ b/electron/simconnect/Client.ts @@ -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; + 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); + } + +} \ No newline at end of file diff --git a/electron/simconnect/DataDefinition.ts b/electron/simconnect/DataDefinition.ts new file mode 100644 index 0000000..1863e02 --- /dev/null +++ b/electron/simconnect/DataDefinition.ts @@ -0,0 +1,29 @@ +import { SimConnectConstants } from 'node-simconnect'; + +export class DataDefiniion { + private definitions: Array = []; + 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): void { + this.definitions.push(definition); + } + + build(handle, requestsMap: Map) { + 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); + } + +} \ No newline at end of file diff --git a/electron/simconnect/enums.ts b/electron/simconnect/enums.ts new file mode 100644 index 0000000..35b3b1c --- /dev/null +++ b/electron/simconnect/enums.ts @@ -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, +}; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..2c2325b --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + Electron + Vite + Vue + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..355472a --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/public/electron-vite-vue.gif b/public/electron-vite-vue.gif new file mode 100644 index 0000000..66a14af Binary files /dev/null and b/public/electron-vite-vue.gif differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..843ea24 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/node.svg b/public/node.svg new file mode 100644 index 0000000..38d4eaa --- /dev/null +++ b/public/node.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..d679871 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/assets/electron.svg b/src/assets/electron.svg new file mode 100644 index 0000000..1c5cccb --- /dev/null +++ b/src/assets/electron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/vue.svg b/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue new file mode 100644 index 0000000..5230910 --- /dev/null +++ b/src/components/HelloWorld.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..e864d88 --- /dev/null +++ b/src/main.ts @@ -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' }, '*') + }) diff --git a/src/samples/node-api.ts b/src/samples/node-api.ts new file mode 100644 index 0000000..b1d9a83 --- /dev/null +++ b/src/samples/node-api.ts @@ -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) +}) diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..64aafe8 --- /dev/null +++ b/src/style.css @@ -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; + } +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..323c78a --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6d0f5ee --- /dev/null +++ b/tsconfig.json @@ -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" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..ed1b586 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts", "package.json", "electron"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..55e26c6 --- /dev/null +++ b/vite.config.ts @@ -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, + } +})