diff --git a/FocusElectron/.editorconfig b/FocusElectron/.editorconfig new file mode 100644 index 0000000..4a7ea30 --- /dev/null +++ b/FocusElectron/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/FocusElectron/.gitignore b/FocusElectron/.gitignore new file mode 100644 index 0000000..cb801a2 --- /dev/null +++ b/FocusElectron/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +release +.vscode/.debug.env +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/FocusElectron/LICENSE b/FocusElectron/LICENSE new file mode 100644 index 0000000..8fd5175 --- /dev/null +++ b/FocusElectron/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 草鞋没号 + +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/FocusElectron/README.md b/FocusElectron/README.md new file mode 100644 index 0000000..28a72d0 --- /dev/null +++ b/FocusElectron/README.md @@ -0,0 +1,75 @@ +# electron-vite-react + +[![awesome-vite](https://awesome.re/mentioned-badge.svg)](https://github.com/vitejs/awesome-vite) +![GitHub stars](https://img.shields.io/github/stars/caoxiemeihao/vite-react-electron?color=fa6470) +![GitHub issues](https://img.shields.io/github/issues/caoxiemeihao/vite-react-electron?color=d8b22d) +![GitHub license](https://img.shields.io/github/license/caoxiemeihao/vite-react-electron) +[![Required Node.JS >= 14.18.0 || >=16.0.0](https://img.shields.io/static/v1?label=node&message=14.18.0%20||%20%3E=16.0.0&logo=node.js&color=3f893e)](https://nodejs.org/about/releases) + +English | [简体中文](README.zh-CN.md) + +## 👀 Overview + +📦 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 + +## 🛫 Quick start + +```sh +npm create electron-vite +``` + +![electron-vite-react.gif](https://github.com/electron-vite/electron-vite-react/blob/main/public/electron-vite-react.gif?raw=true) + +## 🐞 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 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 +├── electron Electron-related code +│ ├── main Main-process source code +│ ├── preload Preload-scripts source code +│ └── resources Resources for the production build +│ ├── icon.icns Icon for the application on macOS +│ ├── icon.ico Icon for the application +│ ├── installerIcon.ico Icon for the application installer +│ └── uninstallerIcon.ico Icon for the application uninstaller +│ +├── release Generated after production build, contains executables +│ └── {version} +│ ├── {os}-unpacked Contains unpacked application executable +│ └── Setup.{ext} Installer for the application +│ +├── public Static assets +└── src Renderer source code, your React application +``` + +## 🚨 Be aware + +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/tree/main/packages/electron-renderer#config-presets-opinionated). + +```diff +# vite.config.ts + +electron({ +- renderer: {} +}) +``` + +## ❔ FAQ + +- [dependencies vs devDependencies](https://github.com/electron-vite/vite-plugin-electron/tree/main/packages/electron-renderer#dependencies-vs-devdependencies) +- [Using C/C++ native addons in renderer](https://github.com/electron-vite/vite-plugin-electron/tree/main/packages/electron-renderer#load-nodejs-cc-native-modules) +- [Node.js ESM packages](https://github.com/electron-vite/vite-plugin-electron/tree/main/packages/electron-renderer#nodejs-esm-packages) (e.g. `execa` `node-fetch`) diff --git a/FocusElectron/electron-builder.json5 b/FocusElectron/electron-builder.json5 new file mode 100644 index 0000000..3da0db0 --- /dev/null +++ b/FocusElectron/electron-builder.json5 @@ -0,0 +1,37 @@ +/** + * @see https://www.electron.build/configuration/configuration + */ +{ + appId: "YourAppID", + productName: "YourAppName", + copyright: "Copyright © 2022 ${author}", + asar: true, + directories: { + output: "release/${version}", + buildResources: "electron/resources", + }, + files: ["dist"], + win: { + target: [ + { + target: "nsis", + arch: ["x64"], + }, + ], + artifactName: "${productName}-Windows-${version}-Setup.${ext}", + }, + nsis: { + oneClick: false, + perMachine: false, + allowToChangeInstallationDirectory: true, + deleteAppDataOnUninstall: false, + }, + mac: { + target: ["dmg"], + artifactName: "${productName}-Mac-${version}-Installer.${ext}", + }, + linux: { + target: ["AppImage"], + artifactName: "${productName}-Linux-${version}.${ext}", + }, +} diff --git a/FocusElectron/electron/electron-env.d.ts b/FocusElectron/electron/electron-env.d.ts new file mode 100644 index 0000000..b0ec4b6 --- /dev/null +++ b/FocusElectron/electron/electron-env.d.ts @@ -0,0 +1,24 @@ +/// + +declare namespace NodeJS { + interface ProcessEnv { + /** + * The built directory structure + * + * ```tree + * ├─┬ dist + * │ ├─┬ electron + * │ │ ├─┬ main + * │ │ │ └── index.js + * │ │ └─┬ preload + * │ │ └── index.js + * │ ├── index.html + * │ ├── ...other-static-files-from-public + * │ + * ``` + */ + DIST: string + /** /dist/ or /public/ */ + PUBLIC: string + } +} diff --git a/FocusElectron/electron/main/index.ts b/FocusElectron/electron/main/index.ts new file mode 100644 index 0000000..4e13919 --- /dev/null +++ b/FocusElectron/electron/main/index.ts @@ -0,0 +1,105 @@ +// The built directory structure +// +// ├─┬ dist +// │ ├─┬ electron +// │ │ ├─┬ main +// │ │ │ └── index.js +// │ │ └─┬ preload +// │ │ └── index.js +// │ ├── index.html +// │ ├── ...other-static-files-from-public +// │ +process.env.DIST = join(__dirname, '../..') +process.env.PUBLIC = app.isPackaged ? process.env.DIST : join(process.env.DIST, '../public') +process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' + +import { app, BrowserWindow, shell, ipcMain } from 'electron' +import { release } from 'os' +import { join } from 'path' + +// 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 +// 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.PUBLIC, 'favicon.svg'), + webPreferences: { + preload, + nodeIntegration: true, + contextIsolation: false, + }, + }) + + if (app.isPackaged) { + win.loadFile(indexHtml) + } else { + win.loadURL(url) + // win.webContents.openDevTools() + } + + // 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' } + }) +} + +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', (event, arg) => { + const childWindow = new BrowserWindow({ + webPreferences: { + preload, + }, + }) + + if (app.isPackaged) { + childWindow.loadFile(indexHtml, { hash: arg }) + } else { + childWindow.loadURL(`${url}/#${arg}`) + // childWindow.webContents.openDevTools({ mode: "undocked", activate: true }) + } +}) diff --git a/FocusElectron/electron/preload/index.ts b/FocusElectron/electron/preload/index.ts new file mode 100644 index 0000000..0ee2dcc --- /dev/null +++ b/FocusElectron/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/FocusElectron/electron/resources/icon.icns b/FocusElectron/electron/resources/icon.icns new file mode 100644 index 0000000..9a9c785 Binary files /dev/null and b/FocusElectron/electron/resources/icon.icns differ diff --git a/FocusElectron/electron/resources/icon.ico b/FocusElectron/electron/resources/icon.ico new file mode 100644 index 0000000..bf153e1 Binary files /dev/null and b/FocusElectron/electron/resources/icon.ico differ diff --git a/FocusElectron/electron/resources/installerIcon.ico b/FocusElectron/electron/resources/installerIcon.ico new file mode 100644 index 0000000..bf153e1 Binary files /dev/null and b/FocusElectron/electron/resources/installerIcon.ico differ diff --git a/FocusElectron/electron/resources/uninstallerIcon.ico b/FocusElectron/electron/resources/uninstallerIcon.ico new file mode 100644 index 0000000..bf153e1 Binary files /dev/null and b/FocusElectron/electron/resources/uninstallerIcon.ico differ diff --git a/FocusElectron/index.html b/FocusElectron/index.html new file mode 100644 index 0000000..4874016 --- /dev/null +++ b/FocusElectron/index.html @@ -0,0 +1,14 @@ + + + + + + + + Vite App + + +
+ + + diff --git a/FocusElectron/package.json b/FocusElectron/package.json new file mode 100644 index 0000000..8b6f66b --- /dev/null +++ b/FocusElectron/package.json @@ -0,0 +1,38 @@ +{ + "name": "electron-vite-react", + "productName": "Electron", + "private": true, + "version": "2.0.0", + "description": "Vite React Electron boilerplate.", + "author": "草鞋没号 <308487730@qq.com>", + "license": "MIT", + "main": "dist/electron/main/index.js", + "scripts": { + "dev": "vite", + "build": "tsc && vite build && electron-builder" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "dependencies": {}, + "devDependencies": { + "@types/react": "^18.0.17", + "@types/react-dom": "^18.0.6", + "@vitejs/plugin-react": "^2.0.1", + "electron": "^20.0.2", + "electron-builder": "^23.3.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.54.4", + "typescript": "^4.7.4", + "vite": "^3.0.7", + "vite-plugin-electron": "^0.9.2" + }, + "debug": { + "env": { + "VITE_DEV_SERVER_HOSTNAME": "127.0.0.1", + "VITE_DEV_SERVER_PORT": 7777, + "VITE_DEV_SERVER_URL": "http://127.0.0.1:7777" + } + } +} diff --git a/FocusElectron/public/electron-vite-react-debug.gif b/FocusElectron/public/electron-vite-react-debug.gif new file mode 100644 index 0000000..4f87992 Binary files /dev/null and b/FocusElectron/public/electron-vite-react-debug.gif differ diff --git a/FocusElectron/public/electron-vite-react.gif b/FocusElectron/public/electron-vite-react.gif new file mode 100644 index 0000000..a4b5da5 Binary files /dev/null and b/FocusElectron/public/electron-vite-react.gif differ diff --git a/FocusElectron/public/electron.png b/FocusElectron/public/electron.png new file mode 100644 index 0000000..45c8adb Binary files /dev/null and b/FocusElectron/public/electron.png differ diff --git a/FocusElectron/public/favicon.svg b/FocusElectron/public/favicon.svg new file mode 100644 index 0000000..de4aedd --- /dev/null +++ b/FocusElectron/public/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/FocusElectron/public/node.png b/FocusElectron/public/node.png new file mode 100644 index 0000000..1cd6519 Binary files /dev/null and b/FocusElectron/public/node.png differ diff --git a/FocusElectron/public/react.svg b/FocusElectron/public/react.svg new file mode 100644 index 0000000..6b60c10 --- /dev/null +++ b/FocusElectron/public/react.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/FocusElectron/public/vite.svg b/FocusElectron/public/vite.svg new file mode 100644 index 0000000..6471ae0 --- /dev/null +++ b/FocusElectron/public/vite.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FocusElectron/src/App.tsx b/FocusElectron/src/App.tsx new file mode 100644 index 0000000..bb8e260 --- /dev/null +++ b/FocusElectron/src/App.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react' +import electron from '/electron.png' +import react from '/react.svg' +import vite from '/vite.svg' +import styles from 'styles/app.module.scss' + +const App: React.FC = () => { + const [count, setCount] = useState(0) + + return ( +
+
+
+
+ electron +
+
+ vite +
+
+ logo +
+
+

Hello Electron + Vite + React!

+

+ +

+

+ Edit App.tsx and save to test HMR updates. +

+
+ + Learn React + + {' | '} + + Vite Docs + +
+ Place static files into the{' '} + /public folder + +
+
+
+
+ ) +} + +export default App diff --git a/FocusElectron/src/assets/styles/app.module.scss b/FocusElectron/src/assets/styles/app.module.scss new file mode 100644 index 0000000..7edf79a --- /dev/null +++ b/FocusElectron/src/assets/styles/app.module.scss @@ -0,0 +1,65 @@ +.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; + } + } + } +} diff --git a/FocusElectron/src/assets/styles/index.css b/FocusElectron/src/assets/styles/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/FocusElectron/src/assets/styles/index.css @@ -0,0 +1,13 @@ +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; +} diff --git a/FocusElectron/src/main.tsx b/FocusElectron/src/main.tsx new file mode 100644 index 0000000..a012db0 --- /dev/null +++ b/FocusElectron/src/main.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +// import './samples/node-api' +import 'styles/index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) + +postMessage({ payload: 'removeLoading' }, '*') diff --git a/FocusElectron/src/samples/node-api.ts b/FocusElectron/src/samples/node-api.ts new file mode 100644 index 0000000..db31e5f --- /dev/null +++ b/FocusElectron/src/samples/node-api.ts @@ -0,0 +1,13 @@ +import { lstat } from 'fs/promises' +import { cwd } from '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) +}) diff --git a/FocusElectron/src/vite-env.d.ts b/FocusElectron/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/FocusElectron/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/FocusElectron/tsconfig.json b/FocusElectron/tsconfig.json new file mode 100644 index 0000000..c81e322 --- /dev/null +++ b/FocusElectron/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "paths": { + "@/*": ["src/*"], + "styles/*": ["src/assets/styles/*"] + }, + "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"], + "references": [{ "path": "./tsconfig.node.json" }] +} + diff --git a/FocusElectron/tsconfig.node.json b/FocusElectron/tsconfig.node.json new file mode 100644 index 0000000..1eb1b76 --- /dev/null +++ b/FocusElectron/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true + }, + "include": ["package.json", "electron"] +} diff --git a/FocusElectron/vite.config.ts b/FocusElectron/vite.config.ts new file mode 100644 index 0000000..9c112e7 --- /dev/null +++ b/FocusElectron/vite.config.ts @@ -0,0 +1,55 @@ +import { rmSync } from 'fs' +import path from 'path' +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import electron, { onstart } from 'vite-plugin-electron' +import pkg from './package.json' + +rmSync(path.join(__dirname, 'dist'), { recursive: true, force: true }) // v14.14.0 + +// https://vitejs.dev/config/ +export default defineConfig({ + resolve: { + alias: { + '@': path.join(__dirname, 'src'), + 'styles': path.join(__dirname, 'src/assets/styles'), + }, + }, + plugins: [ + react(), + electron({ + main: { + entry: 'electron/main/index.ts', + vite: { + build: { + // For Debug + sourcemap: true, + outDir: 'dist/electron/main', + }, + // Will start Electron via VSCode Debug + plugins: [process.env.VSCODE_DEBUG ? onstart() : null], + }, + }, + preload: { + input: { + // You can configure multiple preload scripts here + index: path.join(__dirname, 'electron/preload/index.ts'), + }, + vite: { + build: { + // For Debug + sourcemap: 'inline', + outDir: 'dist/electron/preload', + } + }, + }, + // Enables use of Node.js API in the Electron-Renderer + // https://github.com/electron-vite/vite-plugin-electron/tree/main/packages/electron-renderer#electron-renderervite-serve + renderer: {}, + }), + ], + server: process.env.VSCODE_DEBUG ? { + host: pkg.debug.env.VITE_DEV_SERVER_HOSTNAME, + port: pkg.debug.env.VITE_DEV_SERVER_PORT, + } : undefined, +}) diff --git a/FocusLogger/FocusLogger/App.config b/FocusLogger/App.config similarity index 100% rename from FocusLogger/FocusLogger/App.config rename to FocusLogger/App.config diff --git a/FocusLogger/FocusDetection.cs b/FocusLogger/FocusDetection.cs new file mode 100644 index 0000000..1774117 --- /dev/null +++ b/FocusLogger/FocusDetection.cs @@ -0,0 +1,199 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using System.Xml; + +namespace FocusLogger +{ + class FocusDetection + { + delegate void + WinEventDelegate + ( + IntPtr hWinEventHook, + uint eventType, + IntPtr hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + ; + + [DllImport("user32.dll")] + static extern IntPtr + SetWinEventHook( + uint eventMin, + uint eventMax, + IntPtr hmodWinEventProc, + WinEventDelegate lpfnWinEventProc, + uint idProcess, + uint idThread, + uint dwFlags + ); + + [DllImport("user32.dll")] + static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + static extern Int32 + GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll")] + static extern bool UnhookWinEvent(IntPtr hWinEventHook); + + const uint EVENT_SYSTEM_FOREGROUND = 3; + + const uint WINEVENT_OUTOFCONTEXT = 0; + + IntPtr hhook; + + static WinEventDelegate + procDelegate = new WinEventDelegate(WinEventProc); + + #if DEBUG + static string pathProgramFiles = "D:/"; + #else + static string pathProgramFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + #endif + static string pathDirectory = Path.Combine( pathProgramFiles, "focus"); + static string todaysFilePath; + + static XmlDocument documentLogging = new XmlDocument(); + + + void CreateLoggingDirectory() + { + if (!Directory.Exists(pathDirectory)) + { + Directory.CreateDirectory(pathDirectory); + } + + } + + string CreateLoggingFile() + { + string dateToday = DateTime.Now.ToString("yyyy-MM-dd"); + todaysFilePath = Path.Combine(pathDirectory , dateToday + ".xml"); + if (!File.Exists(todaysFilePath)) + { + using (StreamWriter sw = File.CreateText(todaysFilePath)) + { + sw.WriteLine(""); + sw.WriteLine(""); + sw.WriteLine(""); + sw.WriteLine(""); + } + } + + documentLogging.Load(todaysFilePath); + + + XmlElement startRecord = documentLogging.CreateElement("start"); + + + XmlElement timestampEmptyElement = documentLogging.CreateElement("timestamp"); + timestampEmptyElement.InnerText = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz"); + + startRecord.AppendChild(timestampEmptyElement); + + documentLogging.DocumentElement.AppendChild(startRecord); + documentLogging.Save(todaysFilePath); + + return todaysFilePath; + } + + + public FocusDetection() + { + CreateLoggingDirectory(); + todaysFilePath = CreateLoggingFile(); + + hhook = + SetWinEventHook(EVENT_SYSTEM_FOREGROUND, + EVENT_SYSTEM_FOREGROUND, + IntPtr.Zero, + procDelegate, + 0, + 0, + WINEVENT_OUTOFCONTEXT); + + Application.Run(); + } + + ~FocusDetection() + { + UnhookWinEvent (hhook); + } + + static void WinEventProc( + IntPtr hWinEventHook, + uint eventType, + IntPtr hwnd, + int idObject, + int idChild, + uint dwEventThread, + uint dwmsEventTime + ) + { + GetForegroundProcessName(); + } + + static void GetForegroundProcessName() + { + IntPtr hwnd = GetForegroundWindow(); + + if (hwnd == null) return; + + uint pid; + GetWindowThreadProcessId(hwnd, out pid); + + documentLogging.Load(todaysFilePath); + + foreach (System.Diagnostics.Process + p + in + System.Diagnostics.Process.GetProcesses() + ) + { + if (p.Id == pid) + { + + XmlElement record = documentLogging.CreateElement("record"); + + XmlElement processNameElement = documentLogging.CreateElement("process"); + processNameElement.InnerText = p.ProcessName; + XmlElement pidElement = documentLogging.CreateElement("pid"); + pidElement.InnerText = pid.ToString(); + XmlElement timestampElement = documentLogging.CreateElement("timestamp"); + timestampElement.InnerText = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz"); + + record.AppendChild(processNameElement); + record.AppendChild(pidElement); + record.AppendChild(timestampElement); + + documentLogging.DocumentElement.AppendChild(record); + documentLogging.Save(todaysFilePath); + + return; + } + } + + + XmlElement emptyRecord = documentLogging.CreateElement("empty"); + + + XmlElement timestampEmptyElement = documentLogging.CreateElement("timestamp"); + timestampEmptyElement.InnerText = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss zzz"); + XmlElement messageEmptyElement = documentLogging.CreateElement("timestamp"); + messageEmptyElement.InnerText = "Process doesn't exist"; + + emptyRecord.AppendChild(timestampEmptyElement); + emptyRecord.AppendChild(messageEmptyElement); + + documentLogging.DocumentElement.AppendChild(emptyRecord); + documentLogging.Save(todaysFilePath); + } + } +} diff --git a/FocusLogger/FocusLogger/FocusLogger.csproj b/FocusLogger/FocusLogger.csproj similarity index 86% rename from FocusLogger/FocusLogger/FocusLogger.csproj rename to FocusLogger/FocusLogger.csproj index fabcbc1..afb07d0 100644 --- a/FocusLogger/FocusLogger/FocusLogger.csproj +++ b/FocusLogger/FocusLogger.csproj @@ -4,7 +4,7 @@ Debug AnyCPU - {DEE93A00-0F02-4365-9FBC-896AEF53CA4C} + {EBBABA64-5BFA-4037-AF72-7492D0A0CCB9} WinExe FocusLogger FocusLogger @@ -32,29 +32,31 @@ prompt 4 + + app.manifest + + + + + - - - Component - - - Service1.cs - + + \ No newline at end of file diff --git a/FocusLogger/FocusLogger.sln b/FocusLogger/FocusLogger.sln index 74e5914..285c193 100644 --- a/FocusLogger/FocusLogger.sln +++ b/FocusLogger/FocusLogger.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.32630.194 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FocusLogger", "FocusLogger\FocusLogger.csproj", "{DEE93A00-0F02-4365-9FBC-896AEF53CA4C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FocusLogger", "FocusLogger.csproj", "{EBBABA64-5BFA-4037-AF72-7492D0A0CCB9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DEE93A00-0F02-4365-9FBC-896AEF53CA4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DEE93A00-0F02-4365-9FBC-896AEF53CA4C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DEE93A00-0F02-4365-9FBC-896AEF53CA4C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DEE93A00-0F02-4365-9FBC-896AEF53CA4C}.Release|Any CPU.Build.0 = Release|Any CPU + {EBBABA64-5BFA-4037-AF72-7492D0A0CCB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBBABA64-5BFA-4037-AF72-7492D0A0CCB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBBABA64-5BFA-4037-AF72-7492D0A0CCB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBBABA64-5BFA-4037-AF72-7492D0A0CCB9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {852F7759-F51D-4BE5-9F75-313ABDD61927} + SolutionGuid = {5F1643D2-A398-496A-9D57-A5261808159F} EndGlobalSection EndGlobal diff --git a/FocusLogger/FocusLogger/Program.cs b/FocusLogger/FocusLogger/Program.cs deleted file mode 100644 index 0a524e9..0000000 --- a/FocusLogger/FocusLogger/Program.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceProcess; -using System.Text; -using System.Threading.Tasks; - -namespace FocusLogger -{ - static class Program - { - /// - /// The main entry point for the application. - /// - static void Main() - { - #if DEBUG - Service1 myService = new Service1(); - myService.onDebug(); - System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite); - #else - ServiceBase[] ServicesToRun; - ServicesToRun = new ServiceBase[] - { - new Service1() - }; - ServiceBase.Run(ServicesToRun); - #endif - } - } -} diff --git a/FocusLogger/FocusLogger/Service1.Designer.cs b/FocusLogger/FocusLogger/Service1.Designer.cs deleted file mode 100644 index 9c82d5f..0000000 --- a/FocusLogger/FocusLogger/Service1.Designer.cs +++ /dev/null @@ -1,38 +0,0 @@ - -namespace FocusLogger -{ - partial class Service1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - components = new System.ComponentModel.Container(); - this.ServiceName = "Service1"; - } - - #endregion - } -} diff --git a/FocusLogger/FocusLogger/Service1.cs b/FocusLogger/FocusLogger/Service1.cs deleted file mode 100644 index 863508a..0000000 --- a/FocusLogger/FocusLogger/Service1.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Diagnostics; -using System.Linq; -using System.ServiceProcess; -using System.Text; -using System.Threading.Tasks; - -namespace FocusLogger -{ - public partial class Service1 : ServiceBase - { - public Service1() - { - InitializeComponent(); - } - - protected override void OnStart(string[] args) - { - } - - protected override void OnStop() - { - } - - public void onDebug() - { - OnStart(null); - } - } -} diff --git a/FocusLogger/Program.cs b/FocusLogger/Program.cs new file mode 100644 index 0000000..e3b3247 --- /dev/null +++ b/FocusLogger/Program.cs @@ -0,0 +1,10 @@ +namespace FocusLogger +{ + class Program + { + static void Main(string[] _) + { + new FocusDetection(); + } + } +} diff --git a/FocusLogger/FocusLogger/Properties/AssemblyInfo.cs b/FocusLogger/Properties/AssemblyInfo.cs similarity index 82% rename from FocusLogger/FocusLogger/Properties/AssemblyInfo.cs rename to FocusLogger/Properties/AssemblyInfo.cs index 9231ebe..3e0da4b 100644 --- a/FocusLogger/FocusLogger/Properties/AssemblyInfo.cs +++ b/FocusLogger/Properties/AssemblyInfo.cs @@ -6,11 +6,11 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("FocusLogger")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyDescription("An application to keep track of activity on a device")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("HP Inc.")] +[assembly: AssemblyCompany("MUA Soft")] [assembly: AssemblyProduct("FocusLogger")] -[assembly: AssemblyCopyright("Copyright © HP Inc. 2022")] +[assembly: AssemblyCopyright("Copyright © Maifee Ul Asad 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,7 +20,7 @@ [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("dee93a00-0f02-4365-9fbc-896aef53ca4c")] +[assembly: Guid("ebbaba64-5bfa-4037-af72-7492d0a0ccb9")] // Version information for an assembly consists of the following four values: // diff --git a/FocusLogger/README.md b/FocusLogger/README.md deleted file mode 100644 index 3cda147..0000000 --- a/FocusLogger/README.md +++ /dev/null @@ -1,4 +0,0 @@ - - -# Some ref: - - How to make a service debugable: https://stackoverflow.com/a/17267161/10305444 \ No newline at end of file diff --git a/FocusLogger/app.manifest b/FocusLogger/app.manifest new file mode 100644 index 0000000..39129c0 --- /dev/null +++ b/FocusLogger/app.manifest @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +