Trae Assistant commited on
Commit ·
e19997d
0
Parent(s):
feat: enhance app and configure for huggingface
Browse files- .gitignore +24 -0
- Dockerfile +21 -0
- README.md +69 -0
- dist-electron/main.js +49 -0
- dist-electron/preload.js +25 -0
- electron/main.ts +74 -0
- electron/preload.ts +28 -0
- eslint.config.js +23 -0
- index.html +13 -0
- package-lock.json +0 -0
- package.json +36 -0
- public/vite.svg +1 -0
- src/App.css +136 -0
- src/App.tsx +122 -0
- src/assets/react.svg +1 -0
- src/index.css +68 -0
- src/main.tsx +10 -0
- src/vite-env.d.ts +15 -0
- tsconfig.app.json +28 -0
- tsconfig.json +7 -0
- tsconfig.node.json +26 -0
- vite.config.ts +22 -0
.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Logs
|
| 2 |
+
logs
|
| 3 |
+
*.log
|
| 4 |
+
npm-debug.log*
|
| 5 |
+
yarn-debug.log*
|
| 6 |
+
yarn-error.log*
|
| 7 |
+
pnpm-debug.log*
|
| 8 |
+
lerna-debug.log*
|
| 9 |
+
|
| 10 |
+
node_modules
|
| 11 |
+
dist
|
| 12 |
+
dist-ssr
|
| 13 |
+
*.local
|
| 14 |
+
|
| 15 |
+
# Editor directories and files
|
| 16 |
+
.vscode/*
|
| 17 |
+
!.vscode/extensions.json
|
| 18 |
+
.idea
|
| 19 |
+
.DS_Store
|
| 20 |
+
*.suo
|
| 21 |
+
*.ntvs*
|
| 22 |
+
*.njsproj
|
| 23 |
+
*.sln
|
| 24 |
+
*.sw?
|
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:18-alpine
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY package*.json ./
|
| 6 |
+
|
| 7 |
+
RUN npm install
|
| 8 |
+
|
| 9 |
+
COPY . .
|
| 10 |
+
|
| 11 |
+
# Build the web version
|
| 12 |
+
RUN npm run build
|
| 13 |
+
|
| 14 |
+
# Install serve to run the static site
|
| 15 |
+
RUN npm install -g serve
|
| 16 |
+
|
| 17 |
+
# Hugging Face Spaces default port
|
| 18 |
+
EXPOSE 7860
|
| 19 |
+
|
| 20 |
+
# Serve the 'dist' directory (Web build output) on port 7860
|
| 21 |
+
CMD ["serve", "-s", "dist", "-l", "7860"]
|
README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Electron 跨平台演示
|
| 3 |
+
emoji: ⚡
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: cyan
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
short_description: 一个基于 Electron + Vite + React 的跨平台应用示例
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# Electron 跨平台项目
|
| 12 |
+
|
| 13 |
+
这是一个基于 Electron、Vite 和 React 的跨平台应用示例。
|
| 14 |
+
本项目旨在展示如何兼顾桌面端 (Electron) 和 Web 端开发。
|
| 15 |
+
|
| 16 |
+
## 功能特性
|
| 17 |
+
|
| 18 |
+
- ⚡ **Electron**: 桌面端跨平台支持 (Windows, macOS, Linux)
|
| 19 |
+
- ⚛️ **React**: 现代前端框架
|
| 20 |
+
- 🚀 **Vite**: 极速构建工具
|
| 21 |
+
- 🌐 **Web 兼容**: 支持在浏览器中运行
|
| 22 |
+
- 📦 **Docker**: 支持容器化部署 Web 版本
|
| 23 |
+
|
| 24 |
+
## 快速开始
|
| 25 |
+
|
| 26 |
+
### 1. 安装依赖
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
npm install
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
### 2. 开发模式
|
| 33 |
+
|
| 34 |
+
```bash
|
| 35 |
+
# 启动开发服务器 (同时启动 Electron 和 Web)
|
| 36 |
+
npm run dev
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
### 3. 构建
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
# 构建 Electron 应用和 Web 版本
|
| 43 |
+
npm run build
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
构建产物:
|
| 47 |
+
- `dist`: Web 版本静态文件
|
| 48 |
+
- `dist-electron`: Electron 主进程文件
|
| 49 |
+
- `release`: 打包后的 Electron 安装包 (需配置 electron-builder)
|
| 50 |
+
|
| 51 |
+
## Docker 部署
|
| 52 |
+
|
| 53 |
+
本项目支持 Docker 部署 Web 版本,适用于 Hugging Face Spaces 等平台。
|
| 54 |
+
|
| 55 |
+
```bash
|
| 56 |
+
docker build -t electron-web .
|
| 57 |
+
docker run -p 7860:7860 electron-web
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
## 项目结构
|
| 61 |
+
|
| 62 |
+
- `electron/`: Electron 主进程和预加载脚本
|
| 63 |
+
- `src/`: React 渲染进程源码
|
| 64 |
+
- `dist/`: Web 构建输出
|
| 65 |
+
- `dist-electron/`: Electron 构建输出
|
| 66 |
+
|
| 67 |
+
## 许可证
|
| 68 |
+
|
| 69 |
+
MIT
|
dist-electron/main.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
const electron = require("electron");
|
| 3 |
+
const path = require("node:path");
|
| 4 |
+
const os = require("node:os");
|
| 5 |
+
process.env.DIST = path.join(__dirname, "../dist");
|
| 6 |
+
process.env.VITE_PUBLIC = electron.app.isPackaged ? process.env.DIST : path.join(__dirname, "../public");
|
| 7 |
+
let win;
|
| 8 |
+
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
|
| 9 |
+
function createWindow() {
|
| 10 |
+
win = new electron.BrowserWindow({
|
| 11 |
+
icon: path.join(process.env.VITE_PUBLIC, "electron-vite.svg"),
|
| 12 |
+
webPreferences: {
|
| 13 |
+
preload: path.join(__dirname, "preload.js")
|
| 14 |
+
}
|
| 15 |
+
});
|
| 16 |
+
win.webContents.on("did-finish-load", () => {
|
| 17 |
+
win?.webContents.send("main-process-message", (/* @__PURE__ */ new Date()).toLocaleString());
|
| 18 |
+
});
|
| 19 |
+
if (VITE_DEV_SERVER_URL) {
|
| 20 |
+
win.loadURL(VITE_DEV_SERVER_URL);
|
| 21 |
+
} else {
|
| 22 |
+
win.loadFile(path.join(process.env.DIST, "index.html"));
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
electron.app.on("window-all-closed", () => {
|
| 26 |
+
if (process.platform !== "darwin") {
|
| 27 |
+
electron.app.quit();
|
| 28 |
+
win = null;
|
| 29 |
+
}
|
| 30 |
+
});
|
| 31 |
+
electron.app.on("activate", () => {
|
| 32 |
+
if (electron.BrowserWindow.getAllWindows().length === 0) {
|
| 33 |
+
createWindow();
|
| 34 |
+
}
|
| 35 |
+
});
|
| 36 |
+
electron.app.whenReady().then(() => {
|
| 37 |
+
electron.ipcMain.handle("get-system-info", () => {
|
| 38 |
+
return {
|
| 39 |
+
platform: process.platform,
|
| 40 |
+
arch: process.arch,
|
| 41 |
+
node: process.versions.node,
|
| 42 |
+
electron: process.versions.electron,
|
| 43 |
+
chrome: process.versions.chrome,
|
| 44 |
+
cpus: os.cpus().length,
|
| 45 |
+
memory: Math.round(os.totalmem() / 1024 / 1024 / 1024) + " GB"
|
| 46 |
+
};
|
| 47 |
+
});
|
| 48 |
+
createWindow();
|
| 49 |
+
});
|
dist-electron/preload.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use strict";
|
| 2 |
+
const electron = require("electron");
|
| 3 |
+
electron.contextBridge.exposeInMainWorld("ipcRenderer", {
|
| 4 |
+
on(...args) {
|
| 5 |
+
const [channel, listener] = args;
|
| 6 |
+
return electron.ipcRenderer.on(channel, (event, ...args2) => listener(event, ...args2));
|
| 7 |
+
},
|
| 8 |
+
off(...args) {
|
| 9 |
+
const [channel, ...omit] = args;
|
| 10 |
+
return electron.ipcRenderer.off(channel, ...omit);
|
| 11 |
+
},
|
| 12 |
+
send(...args) {
|
| 13 |
+
const [channel, ...omit] = args;
|
| 14 |
+
return electron.ipcRenderer.send(channel, ...omit);
|
| 15 |
+
},
|
| 16 |
+
invoke(...args) {
|
| 17 |
+
const [channel, ...omit] = args;
|
| 18 |
+
return electron.ipcRenderer.invoke(channel, ...omit);
|
| 19 |
+
}
|
| 20 |
+
// You can expose other APTs you need here.
|
| 21 |
+
// ...
|
| 22 |
+
});
|
| 23 |
+
electron.contextBridge.exposeInMainWorld("electronAPI", {
|
| 24 |
+
getSystemInfo: () => electron.ipcRenderer.invoke("get-system-info")
|
| 25 |
+
});
|
electron/main.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { app, BrowserWindow, ipcMain } from 'electron'
|
| 2 |
+
import path from 'node:path'
|
| 3 |
+
import os from 'node:os'
|
| 4 |
+
|
| 5 |
+
// The built directory structure
|
| 6 |
+
//
|
| 7 |
+
// ├─┬ dist-electron
|
| 8 |
+
// │ ├─┬ main
|
| 9 |
+
// │ │ └── index.js
|
| 10 |
+
// │ └─┬ preload
|
| 11 |
+
// │ └── index.js
|
| 12 |
+
// ├─┬ dist
|
| 13 |
+
// │ └── index.html
|
| 14 |
+
|
| 15 |
+
process.env.DIST = path.join(__dirname, '../dist')
|
| 16 |
+
process.env.VITE_PUBLIC = app.isPackaged ? process.env.DIST : path.join(__dirname, '../public')
|
| 17 |
+
|
| 18 |
+
let win: BrowserWindow | null
|
| 19 |
+
|
| 20 |
+
const VITE_DEV_SERVER_URL = process.env['VITE_DEV_SERVER_URL']
|
| 21 |
+
|
| 22 |
+
function createWindow() {
|
| 23 |
+
win = new BrowserWindow({
|
| 24 |
+
icon: path.join(process.env.VITE_PUBLIC, 'electron-vite.svg'),
|
| 25 |
+
webPreferences: {
|
| 26 |
+
preload: path.join(__dirname, 'preload.js'),
|
| 27 |
+
},
|
| 28 |
+
})
|
| 29 |
+
|
| 30 |
+
// Test active push message to Renderer-process.
|
| 31 |
+
win.webContents.on('did-finish-load', () => {
|
| 32 |
+
win?.webContents.send('main-process-message', (new Date).toLocaleString())
|
| 33 |
+
})
|
| 34 |
+
|
| 35 |
+
if (VITE_DEV_SERVER_URL) {
|
| 36 |
+
win.loadURL(VITE_DEV_SERVER_URL)
|
| 37 |
+
} else {
|
| 38 |
+
// win.loadFile('dist/index.html')
|
| 39 |
+
win.loadFile(path.join(process.env.DIST, 'index.html'))
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
// Quit when all windows are closed, except on macOS. There, it's common
|
| 44 |
+
// for applications and their menu bar to stay active until the user quits
|
| 45 |
+
// explicitly with Cmd + Q.
|
| 46 |
+
app.on('window-all-closed', () => {
|
| 47 |
+
if (process.platform !== 'darwin') {
|
| 48 |
+
app.quit()
|
| 49 |
+
win = null
|
| 50 |
+
}
|
| 51 |
+
})
|
| 52 |
+
|
| 53 |
+
app.on('activate', () => {
|
| 54 |
+
// On OS X it's common to re-create a window in the app when the
|
| 55 |
+
// dock icon is clicked and there are no other windows open.
|
| 56 |
+
if (BrowserWindow.getAllWindows().length === 0) {
|
| 57 |
+
createWindow()
|
| 58 |
+
}
|
| 59 |
+
})
|
| 60 |
+
|
| 61 |
+
app.whenReady().then(() => {
|
| 62 |
+
ipcMain.handle('get-system-info', () => {
|
| 63 |
+
return {
|
| 64 |
+
platform: process.platform,
|
| 65 |
+
arch: process.arch,
|
| 66 |
+
node: process.versions.node,
|
| 67 |
+
electron: process.versions.electron,
|
| 68 |
+
chrome: process.versions.chrome,
|
| 69 |
+
cpus: os.cpus().length,
|
| 70 |
+
memory: Math.round(os.totalmem() / 1024 / 1024 / 1024) + ' GB',
|
| 71 |
+
}
|
| 72 |
+
})
|
| 73 |
+
createWindow()
|
| 74 |
+
})
|
electron/preload.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { contextBridge, ipcRenderer } from 'electron'
|
| 2 |
+
|
| 3 |
+
// --------- Expose some API to the Renderer process ---------
|
| 4 |
+
contextBridge.exposeInMainWorld('ipcRenderer', {
|
| 5 |
+
on(...args: Parameters<typeof ipcRenderer.on>) {
|
| 6 |
+
const [channel, listener] = args
|
| 7 |
+
return ipcRenderer.on(channel, (event, ...args) => listener(event, ...args))
|
| 8 |
+
},
|
| 9 |
+
off(...args: Parameters<typeof ipcRenderer.off>) {
|
| 10 |
+
const [channel, ...omit] = args
|
| 11 |
+
return ipcRenderer.off(channel, ...omit)
|
| 12 |
+
},
|
| 13 |
+
send(...args: Parameters<typeof ipcRenderer.send>) {
|
| 14 |
+
const [channel, ...omit] = args
|
| 15 |
+
return ipcRenderer.send(channel, ...omit)
|
| 16 |
+
},
|
| 17 |
+
invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
|
| 18 |
+
const [channel, ...omit] = args
|
| 19 |
+
return ipcRenderer.invoke(channel, ...omit)
|
| 20 |
+
},
|
| 21 |
+
|
| 22 |
+
// You can expose other APTs you need here.
|
| 23 |
+
// ...
|
| 24 |
+
})
|
| 25 |
+
|
| 26 |
+
contextBridge.exposeInMainWorld('electronAPI', {
|
| 27 |
+
getSystemInfo: () => ipcRenderer.invoke('get-system-info'),
|
| 28 |
+
})
|
eslint.config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import js from '@eslint/js'
|
| 2 |
+
import globals from 'globals'
|
| 3 |
+
import reactHooks from 'eslint-plugin-react-hooks'
|
| 4 |
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
| 5 |
+
import tseslint from 'typescript-eslint'
|
| 6 |
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
| 7 |
+
|
| 8 |
+
export default defineConfig([
|
| 9 |
+
globalIgnores(['dist']),
|
| 10 |
+
{
|
| 11 |
+
files: ['**/*.{ts,tsx}'],
|
| 12 |
+
extends: [
|
| 13 |
+
js.configs.recommended,
|
| 14 |
+
tseslint.configs.recommended,
|
| 15 |
+
reactHooks.configs.flat.recommended,
|
| 16 |
+
reactRefresh.configs.vite,
|
| 17 |
+
],
|
| 18 |
+
languageOptions: {
|
| 19 |
+
ecmaVersion: 2020,
|
| 20 |
+
globals: globals.browser,
|
| 21 |
+
},
|
| 22 |
+
},
|
| 23 |
+
])
|
index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<title>electron-cross-platform</title>
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div id="root"></div>
|
| 11 |
+
<script type="module" src="/src/main.tsx"></script>
|
| 12 |
+
</body>
|
| 13 |
+
</html>
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "electron-cross-platform",
|
| 3 |
+
"private": true,
|
| 4 |
+
"version": "0.0.0",
|
| 5 |
+
"main": "dist-electron/main.js",
|
| 6 |
+
"description": "一个跨平台的 Electron 项目示例",
|
| 7 |
+
"author": "Your Name",
|
| 8 |
+
"scripts": {
|
| 9 |
+
"dev": "vite",
|
| 10 |
+
"build": "tsc -b && vite build && electron-builder",
|
| 11 |
+
"lint": "eslint .",
|
| 12 |
+
"preview": "vite preview"
|
| 13 |
+
},
|
| 14 |
+
"dependencies": {
|
| 15 |
+
"react": "^19.2.0",
|
| 16 |
+
"react-dom": "^19.2.0"
|
| 17 |
+
},
|
| 18 |
+
"devDependencies": {
|
| 19 |
+
"@eslint/js": "^9.39.1",
|
| 20 |
+
"@types/node": "^24.10.1",
|
| 21 |
+
"@types/react": "^19.2.7",
|
| 22 |
+
"@types/react-dom": "^19.2.3",
|
| 23 |
+
"@vitejs/plugin-react": "^5.1.1",
|
| 24 |
+
"electron": "^40.8.0",
|
| 25 |
+
"electron-builder": "^26.8.1",
|
| 26 |
+
"eslint": "^9.39.1",
|
| 27 |
+
"eslint-plugin-react-hooks": "^7.0.1",
|
| 28 |
+
"eslint-plugin-react-refresh": "^0.4.24",
|
| 29 |
+
"globals": "^16.5.0",
|
| 30 |
+
"typescript": "~5.9.3",
|
| 31 |
+
"typescript-eslint": "^8.48.0",
|
| 32 |
+
"vite": "^7.3.1",
|
| 33 |
+
"vite-plugin-electron": "^0.29.0",
|
| 34 |
+
"vite-plugin-electron-renderer": "^0.14.6"
|
| 35 |
+
}
|
| 36 |
+
}
|
public/vite.svg
ADDED
|
|
src/App.css
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#root {
|
| 2 |
+
max-width: 1280px;
|
| 3 |
+
margin: 0 auto;
|
| 4 |
+
padding: 2rem;
|
| 5 |
+
text-align: center;
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
.logo {
|
| 9 |
+
height: 6em;
|
| 10 |
+
padding: 1.5em;
|
| 11 |
+
will-change: filter;
|
| 12 |
+
transition: filter 300ms;
|
| 13 |
+
}
|
| 14 |
+
.logo:hover {
|
| 15 |
+
filter: drop-shadow(0 0 2em #646cffaa);
|
| 16 |
+
}
|
| 17 |
+
.logo.react:hover {
|
| 18 |
+
filter: drop-shadow(0 0 2em #61dafbaa);
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
@keyframes logo-spin {
|
| 22 |
+
from {
|
| 23 |
+
transform: rotate(0deg);
|
| 24 |
+
}
|
| 25 |
+
to {
|
| 26 |
+
transform: rotate(360deg);
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
@media (prefers-reduced-motion: no-preference) {
|
| 31 |
+
a:nth-of-type(2) .logo {
|
| 32 |
+
animation: logo-spin infinite 20s linear;
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
.card {
|
| 37 |
+
padding: 2em;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.read-the-docs {
|
| 41 |
+
color: #888;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/* Custom styles */
|
| 45 |
+
.status-badge {
|
| 46 |
+
display: inline-block;
|
| 47 |
+
padding: 8px 16px;
|
| 48 |
+
border-radius: 20px;
|
| 49 |
+
font-weight: bold;
|
| 50 |
+
margin: 10px 0;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
.status-badge.electron {
|
| 54 |
+
background-color: #2b2e3b;
|
| 55 |
+
color: #9feaf9;
|
| 56 |
+
border: 1px solid #9feaf9;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
.status-badge.web {
|
| 60 |
+
background-color: #f0f0f0;
|
| 61 |
+
color: #333;
|
| 62 |
+
border: 1px solid #ccc;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
@media (prefers-color-scheme: dark) {
|
| 66 |
+
.status-badge.web {
|
| 67 |
+
background-color: #333;
|
| 68 |
+
color: #fff;
|
| 69 |
+
border: 1px solid #555;
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
.system-info {
|
| 74 |
+
margin-top: 15px;
|
| 75 |
+
text-align: left;
|
| 76 |
+
background: rgba(0, 0, 0, 0.05);
|
| 77 |
+
padding: 15px;
|
| 78 |
+
border-radius: 8px;
|
| 79 |
+
display: inline-block;
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.system-info ul {
|
| 83 |
+
list-style: none;
|
| 84 |
+
padding: 0;
|
| 85 |
+
margin: 0;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.system-info li {
|
| 89 |
+
margin: 5px 0;
|
| 90 |
+
font-family: monospace;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
.todo-input {
|
| 94 |
+
display: flex;
|
| 95 |
+
gap: 10px;
|
| 96 |
+
justify-content: center;
|
| 97 |
+
margin-bottom: 20px;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.todo-input input {
|
| 101 |
+
padding: 8px;
|
| 102 |
+
border-radius: 4px;
|
| 103 |
+
border: 1px solid #ccc;
|
| 104 |
+
width: 200px;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
.todo-list {
|
| 108 |
+
list-style: none;
|
| 109 |
+
padding: 0;
|
| 110 |
+
max-width: 400px;
|
| 111 |
+
margin: 0 auto;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
.todo-list li {
|
| 115 |
+
display: flex;
|
| 116 |
+
justify-content: space-between;
|
| 117 |
+
align-items: center;
|
| 118 |
+
padding: 10px;
|
| 119 |
+
border-bottom: 1px solid #eee;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.todo-list li:last-child {
|
| 123 |
+
border-bottom: none;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.delete-btn {
|
| 127 |
+
background: none;
|
| 128 |
+
border: none;
|
| 129 |
+
cursor: pointer;
|
| 130 |
+
padding: 5px;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
.delete-btn:hover {
|
| 134 |
+
background: rgba(255, 0, 0, 0.1);
|
| 135 |
+
border-radius: 50%;
|
| 136 |
+
}
|
src/App.tsx
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState, useEffect } from 'react'
|
| 2 |
+
import reactLogo from './assets/react.svg'
|
| 3 |
+
import viteLogo from '/vite.svg'
|
| 4 |
+
import './App.css'
|
| 5 |
+
|
| 6 |
+
interface SystemInfo {
|
| 7 |
+
platform: string
|
| 8 |
+
arch: string
|
| 9 |
+
node: string
|
| 10 |
+
electron: string
|
| 11 |
+
chrome: string
|
| 12 |
+
cpus: number
|
| 13 |
+
memory: string
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
function App() {
|
| 17 |
+
const [count, setCount] = useState(0)
|
| 18 |
+
const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(null)
|
| 19 |
+
const [isElectron, setIsElectron] = useState(false)
|
| 20 |
+
const [todos, setTodos] = useState<string[]>(() => {
|
| 21 |
+
const saved = localStorage.getItem('todos')
|
| 22 |
+
return saved ? JSON.parse(saved) : []
|
| 23 |
+
})
|
| 24 |
+
const [inputValue, setInputValue] = useState('')
|
| 25 |
+
|
| 26 |
+
useEffect(() => {
|
| 27 |
+
// Check if running in Electron
|
| 28 |
+
const checkElectron = async () => {
|
| 29 |
+
if (window.electronAPI) {
|
| 30 |
+
setIsElectron(true)
|
| 31 |
+
const info = await window.electronAPI.getSystemInfo()
|
| 32 |
+
setSystemInfo(info)
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
checkElectron()
|
| 36 |
+
}, [])
|
| 37 |
+
|
| 38 |
+
useEffect(() => {
|
| 39 |
+
localStorage.setItem('todos', JSON.stringify(todos))
|
| 40 |
+
}, [todos])
|
| 41 |
+
|
| 42 |
+
const addTodo = () => {
|
| 43 |
+
if (inputValue.trim()) {
|
| 44 |
+
setTodos([...todos, inputValue.trim()])
|
| 45 |
+
setInputValue('')
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
const removeTodo = (index: number) => {
|
| 50 |
+
setTodos(todos.filter((_, i) => i !== index))
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
return (
|
| 54 |
+
<div className="container">
|
| 55 |
+
<div>
|
| 56 |
+
<a href="https://vite.dev" target="_blank">
|
| 57 |
+
<img src={viteLogo} className="logo" alt="Vite logo" />
|
| 58 |
+
</a>
|
| 59 |
+
<a href="https://react.dev" target="_blank">
|
| 60 |
+
<img src={reactLogo} className="logo react" alt="React logo" />
|
| 61 |
+
</a>
|
| 62 |
+
</div>
|
| 63 |
+
<h1>Vite + React + Electron</h1>
|
| 64 |
+
|
| 65 |
+
<div className="card status-card">
|
| 66 |
+
<h2>运行环境检测</h2>
|
| 67 |
+
<div className={`status-badge ${isElectron ? 'electron' : 'web'}`}>
|
| 68 |
+
{isElectron ? '🖥️ Electron 桌面端' : '🌐 Web 浏览器端'}
|
| 69 |
+
</div>
|
| 70 |
+
|
| 71 |
+
{isElectron && systemInfo && (
|
| 72 |
+
<div className="system-info">
|
| 73 |
+
<h3>系统信息</h3>
|
| 74 |
+
<ul>
|
| 75 |
+
<li><strong>平台:</strong> {systemInfo.platform} ({systemInfo.arch})</li>
|
| 76 |
+
<li><strong>CPU核心:</strong> {systemInfo.cpus} 核</li>
|
| 77 |
+
<li><strong>内存:</strong> {systemInfo.memory}</li>
|
| 78 |
+
<li><strong>Chrome:</strong> {systemInfo.chrome}</li>
|
| 79 |
+
<li><strong>Electron:</strong> {systemInfo.electron}</li>
|
| 80 |
+
<li><strong>Node.js:</strong> {systemInfo.node}</li>
|
| 81 |
+
</ul>
|
| 82 |
+
</div>
|
| 83 |
+
)}
|
| 84 |
+
</div>
|
| 85 |
+
|
| 86 |
+
<div className="card">
|
| 87 |
+
<h2>功能演示 1: 计数器</h2>
|
| 88 |
+
<button onClick={() => setCount((count) => count + 1)}>
|
| 89 |
+
计数器: {count}
|
| 90 |
+
</button>
|
| 91 |
+
</div>
|
| 92 |
+
|
| 93 |
+
<div className="card">
|
| 94 |
+
<h2>功能演示 2: 本地待办事项 (LocalStorage)</h2>
|
| 95 |
+
<div className="todo-input">
|
| 96 |
+
<input
|
| 97 |
+
value={inputValue}
|
| 98 |
+
onChange={(e) => setInputValue(e.target.value)}
|
| 99 |
+
onKeyDown={(e) => e.key === 'Enter' && addTodo()}
|
| 100 |
+
placeholder="输入待办事项..."
|
| 101 |
+
/>
|
| 102 |
+
<button onClick={addTodo}>添加</button>
|
| 103 |
+
</div>
|
| 104 |
+
<ul className="todo-list">
|
| 105 |
+
{todos.map((todo, index) => (
|
| 106 |
+
<li key={index}>
|
| 107 |
+
<span>{todo}</span>
|
| 108 |
+
<button className="delete-btn" onClick={() => removeTodo(index)}>❌</button>
|
| 109 |
+
</li>
|
| 110 |
+
))}
|
| 111 |
+
{todos.length === 0 && <li className="empty">暂无待办事项</li>}
|
| 112 |
+
</ul>
|
| 113 |
+
</div>
|
| 114 |
+
|
| 115 |
+
<p className="read-the-docs">
|
| 116 |
+
本项目支持 Docker 部署到 Hugging Face Spaces
|
| 117 |
+
</p>
|
| 118 |
+
</div>
|
| 119 |
+
)
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
export default App
|
src/assets/react.svg
ADDED
|
|
src/index.css
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
| 3 |
+
line-height: 1.5;
|
| 4 |
+
font-weight: 400;
|
| 5 |
+
|
| 6 |
+
color-scheme: light dark;
|
| 7 |
+
color: rgba(255, 255, 255, 0.87);
|
| 8 |
+
background-color: #242424;
|
| 9 |
+
|
| 10 |
+
font-synthesis: none;
|
| 11 |
+
text-rendering: optimizeLegibility;
|
| 12 |
+
-webkit-font-smoothing: antialiased;
|
| 13 |
+
-moz-osx-font-smoothing: grayscale;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
a {
|
| 17 |
+
font-weight: 500;
|
| 18 |
+
color: #646cff;
|
| 19 |
+
text-decoration: inherit;
|
| 20 |
+
}
|
| 21 |
+
a:hover {
|
| 22 |
+
color: #535bf2;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
body {
|
| 26 |
+
margin: 0;
|
| 27 |
+
display: flex;
|
| 28 |
+
place-items: center;
|
| 29 |
+
min-width: 320px;
|
| 30 |
+
min-height: 100vh;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
h1 {
|
| 34 |
+
font-size: 3.2em;
|
| 35 |
+
line-height: 1.1;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
button {
|
| 39 |
+
border-radius: 8px;
|
| 40 |
+
border: 1px solid transparent;
|
| 41 |
+
padding: 0.6em 1.2em;
|
| 42 |
+
font-size: 1em;
|
| 43 |
+
font-weight: 500;
|
| 44 |
+
font-family: inherit;
|
| 45 |
+
background-color: #1a1a1a;
|
| 46 |
+
cursor: pointer;
|
| 47 |
+
transition: border-color 0.25s;
|
| 48 |
+
}
|
| 49 |
+
button:hover {
|
| 50 |
+
border-color: #646cff;
|
| 51 |
+
}
|
| 52 |
+
button:focus,
|
| 53 |
+
button:focus-visible {
|
| 54 |
+
outline: 4px auto -webkit-focus-ring-color;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
@media (prefers-color-scheme: light) {
|
| 58 |
+
:root {
|
| 59 |
+
color: #213547;
|
| 60 |
+
background-color: #ffffff;
|
| 61 |
+
}
|
| 62 |
+
a:hover {
|
| 63 |
+
color: #747bff;
|
| 64 |
+
}
|
| 65 |
+
button {
|
| 66 |
+
background-color: #f9f9f9;
|
| 67 |
+
}
|
| 68 |
+
}
|
src/main.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StrictMode } from 'react'
|
| 2 |
+
import { createRoot } from 'react-dom/client'
|
| 3 |
+
import './index.css'
|
| 4 |
+
import App from './App.tsx'
|
| 5 |
+
|
| 6 |
+
createRoot(document.getElementById('root')!).render(
|
| 7 |
+
<StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</StrictMode>,
|
| 10 |
+
)
|
src/vite-env.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/// <reference types="vite/client" />
|
| 2 |
+
|
| 3 |
+
interface Window {
|
| 4 |
+
electronAPI?: {
|
| 5 |
+
getSystemInfo: () => Promise<{
|
| 6 |
+
platform: string
|
| 7 |
+
arch: string
|
| 8 |
+
node: string
|
| 9 |
+
electron: string
|
| 10 |
+
chrome: string
|
| 11 |
+
cpus: number
|
| 12 |
+
memory: string
|
| 13 |
+
}>
|
| 14 |
+
}
|
| 15 |
+
}
|
tsconfig.app.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
| 4 |
+
"target": "ES2022",
|
| 5 |
+
"useDefineForClassFields": true,
|
| 6 |
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
| 7 |
+
"module": "ESNext",
|
| 8 |
+
"types": ["vite/client"],
|
| 9 |
+
"skipLibCheck": true,
|
| 10 |
+
|
| 11 |
+
/* Bundler mode */
|
| 12 |
+
"moduleResolution": "bundler",
|
| 13 |
+
"allowImportingTsExtensions": true,
|
| 14 |
+
"verbatimModuleSyntax": true,
|
| 15 |
+
"moduleDetection": "force",
|
| 16 |
+
"noEmit": true,
|
| 17 |
+
"jsx": "react-jsx",
|
| 18 |
+
|
| 19 |
+
/* Linting */
|
| 20 |
+
"strict": true,
|
| 21 |
+
"noUnusedLocals": true,
|
| 22 |
+
"noUnusedParameters": true,
|
| 23 |
+
"erasableSyntaxOnly": true,
|
| 24 |
+
"noFallthroughCasesInSwitch": true,
|
| 25 |
+
"noUncheckedSideEffectImports": true
|
| 26 |
+
},
|
| 27 |
+
"include": ["src"]
|
| 28 |
+
}
|
tsconfig.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"files": [],
|
| 3 |
+
"references": [
|
| 4 |
+
{ "path": "./tsconfig.app.json" },
|
| 5 |
+
{ "path": "./tsconfig.node.json" }
|
| 6 |
+
]
|
| 7 |
+
}
|
tsconfig.node.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
| 4 |
+
"target": "ES2023",
|
| 5 |
+
"lib": ["ES2023"],
|
| 6 |
+
"module": "ESNext",
|
| 7 |
+
"types": ["node"],
|
| 8 |
+
"skipLibCheck": true,
|
| 9 |
+
|
| 10 |
+
/* Bundler mode */
|
| 11 |
+
"moduleResolution": "bundler",
|
| 12 |
+
"allowImportingTsExtensions": true,
|
| 13 |
+
"verbatimModuleSyntax": true,
|
| 14 |
+
"moduleDetection": "force",
|
| 15 |
+
"noEmit": true,
|
| 16 |
+
|
| 17 |
+
/* Linting */
|
| 18 |
+
"strict": true,
|
| 19 |
+
"noUnusedLocals": true,
|
| 20 |
+
"noUnusedParameters": true,
|
| 21 |
+
"erasableSyntaxOnly": true,
|
| 22 |
+
"noFallthroughCasesInSwitch": true,
|
| 23 |
+
"noUncheckedSideEffectImports": true
|
| 24 |
+
},
|
| 25 |
+
"include": ["vite.config.ts"]
|
| 26 |
+
}
|
vite.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from 'vite'
|
| 2 |
+
import react from '@vitejs/plugin-react'
|
| 3 |
+
import electron from 'vite-plugin-electron/simple'
|
| 4 |
+
|
| 5 |
+
// https://vite.dev/config/
|
| 6 |
+
export default defineConfig({
|
| 7 |
+
plugins: [
|
| 8 |
+
react(),
|
| 9 |
+
electron({
|
| 10 |
+
main: {
|
| 11 |
+
// Shortcut of `build.lib.entry`
|
| 12 |
+
entry: 'electron/main.ts',
|
| 13 |
+
},
|
| 14 |
+
preload: {
|
| 15 |
+
// Shortcut of `build.rollupOptions.input`
|
| 16 |
+
input: 'electron/preload.ts',
|
| 17 |
+
},
|
| 18 |
+
// Optional: Use Node.js API in the Renderer-process
|
| 19 |
+
renderer: {},
|
| 20 |
+
}),
|
| 21 |
+
],
|
| 22 |
+
})
|