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
+
+[](https://github.com/vitejs/awesome-vite)
+
+
+
+[](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
+```
+
+
+
+## 🐞 Debug
+
+
+
+## 📂 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 (
+
+
+
+ Hello Electron + Vite + React!
+
+
+
+
+ Edit App.tsx and save to test HMR updates.
+
+
+
+
+ )
+}
+
+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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+