From 0904efc2771a7ff3a86a39529b7b65f0ed6a3494 Mon Sep 17 00:00:00 2001 From: Song <1667077010@qq.com> Date: Sun, 17 May 2026 07:41:22 +0000 Subject: [PATCH 01/12] refresh --- .github/workflows/build.yml | 25 ++-- .github/workflows/ci.yml | 75 ++++++------ .vscode/.debug.script.mjs | 29 ++++- .vscode/extensions.json | 7 -- .vscode/launch.json | 19 +-- .vscode/settings.json | 3 - .vscode/tasks.json | 21 ++-- electron-builder.json | 7 +- package.json | 42 +++---- pnpm-workspace.yaml | 2 + postcss.config.cjs | 8 -- src/App.css | 52 -------- src/App.tsx | 100 +++++++++++---- src/components/update/Modal/index.tsx | 67 ----------- src/components/update/Modal/modal.css | 87 -------------- src/components/update/Progress/index.tsx | 22 ---- src/components/update/Progress/progress.css | 21 ---- src/components/update/index.tsx | 35 +++--- src/components/update/modal.tsx | 67 +++++++++++ src/components/update/progress.tsx | 21 ++++ src/components/update/update.css | 24 ---- src/index.css | 127 +++++++------------- tailwind.config.js | 14 --- tsconfig.json | 12 +- tsconfig.node.json | 8 +- vite.config.ts | 24 ++-- 26 files changed, 384 insertions(+), 535 deletions(-) delete mode 100644 .vscode/extensions.json create mode 100644 pnpm-workspace.yaml delete mode 100644 postcss.config.cjs delete mode 100644 src/App.css delete mode 100644 src/components/update/Modal/index.tsx delete mode 100644 src/components/update/Modal/modal.css delete mode 100644 src/components/update/Progress/index.tsx delete mode 100644 src/components/update/Progress/progress.css create mode 100644 src/components/update/modal.tsx create mode 100644 src/components/update/progress.tsx delete mode 100644 src/components/update/update.css delete mode 100644 tailwind.config.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29a1e2d1..f0afe8db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,7 @@ name: Build on: + workflow_dispatch: push: branches: [main] paths-ignore: @@ -14,6 +15,9 @@ on: - ".github/**" - "!.github/workflows/build.yml" +permissions: + contents: read + jobs: build: runs-on: ${{ matrix.os }} @@ -24,24 +28,31 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 + cache: pnpm + - name: Install Dependencies - run: npm install + run: pnpm install --frozen-lockfile - name: Build Release Files - run: npm run build + run: pnpm build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: release_on_${{ matrix. os }} + name: release_on_${{ matrix.os }} path: release/ retention-days: 5 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d3b53e1..0d23c963 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,40 +1,47 @@ name: CI on: - pull_request_target: + pull_request: branches: - main permissions: + contents: read pull-requests: write +concurrency: + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: - job1: - name: Check Not Allowed File Changes + validate: + name: Validate Pull Request runs-on: ubuntu-latest - outputs: - markdown_change: ${{ steps.filter_markdown.outputs.change }} - markdown_files: ${{ steps.filter_markdown.outputs.change_files }} steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - - name: Check Not Allowed File Changes - uses: dorny/paths-filter@v2 - id: filter_not_allowed + - name: Detect Changed Files + uses: dorny/paths-filter@v3 + id: changes with: - list-files: json + list-files: shell filters: | - change: + lockfiles: - 'package-lock.json' - 'yarn.lock' - 'pnpm-lock.yaml' + markdown: + - added|modified: '*.md' # ref: https://github.com/github/docs/blob/main/.github/workflows/triage-unallowed-contributions.yml - name: Comment About Changes We Can't Accept - if: ${{ steps.filter_not_allowed.outputs.change == 'true' }} - uses: actions/github-script@v6 + if: ${{ steps.changes.outputs.lockfiles == 'true' }} + uses: actions/github-script@v7 with: script: | - let workflowFailMessage = "It looks like you've modified some files that we can't accept as contributions." try { const badFilesArr = [ 'package-lock.json', @@ -43,39 +50,25 @@ jobs: ] const badFiles = badFilesArr.join('\n- ') const reviewMessage = `👋 Hey there spelunker. It looks like you've modified some files that we can't accept as contributions. The complete list of files we can't accept are:\n- ${badFiles}\n\nYou'll need to revert all of the files you changed in that list using [GitHub Desktop](https://docs.github.com/en/free-pro-team@latest/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/reverting-a-commit) or \`git checkout origin/main \`. Once you get those files reverted, we can continue with the review process. :octocat:\n\nMore discussion:\n- https://github.com/electron-vite/electron-vite-vue/issues/192` - createdComment = await github.rest.issues.createComment({ + + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: context.payload.number, + issue_number: context.payload.pull_request.number, body: reviewMessage, }) - workflowFailMessage = `${workflowFailMessage} Please see ${createdComment.data.html_url} for details.` } catch(err) { - console.log("Error creating comment.", err) + core.warning(`Failed to create PR comment: ${err.message}`) } - core.setFailed(workflowFailMessage) - - - name: Check Not Linted Markdown - if: ${{ always() }} - uses: dorny/paths-filter@v2 - id: filter_markdown - with: - list-files: shell - filters: | - change: - - added|modified: '*.md' - - job2: - name: Lint Markdown - runs-on: ubuntu-latest - needs: job1 - if: ${{ always() && needs.job1.outputs.markdown_change == 'true' }} - steps: - - name: Checkout Code - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} + - name: Fail on Restricted Lockfile Changes + if: ${{ steps.changes.outputs.lockfiles == 'true' }} + run: | + echo "Lockfiles are managed by maintainers and should not be changed in PRs." + echo "Changed lockfiles:" + echo "${{ steps.changes.outputs.lockfiles_files }}" + exit 1 - name: Lint markdown - run: npx markdownlint-cli ${{ needs.job1.outputs.markdown_files }} --ignore node_modules \ No newline at end of file + if: ${{ steps.changes.outputs.markdown == 'true' }} + run: npx --yes markdownlint-cli@latest ${{ steps.changes.outputs.markdown_files }} --ignore node_modules \ No newline at end of file diff --git a/.vscode/.debug.script.mjs b/.vscode/.debug.script.mjs index 9ca93363..2109c56d 100644 --- a/.vscode/.debug.script.mjs +++ b/.vscode/.debug.script.mjs @@ -7,17 +7,38 @@ import { spawn } from 'node:child_process' const pkg = createRequire(import.meta.url)('../package.json') const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const packageManager = (pkg.packageManager || 'npm').split('@')[0] +const runCommand = process.platform === 'win32' + ? `${packageManager}.cmd` + : packageManager + // write .debug.env const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`) fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n')) // bootstrap -spawn( - // TODO: terminate `npm run dev` when Debug exits. - process.platform === 'win32' ? 'npm.cmd' : 'npm', +const child = spawn( + // Keep this command aligned with the project package manager. + runCommand, ['run', 'dev'], { + shell: process.platform === 'win32', + cwd: path.resolve(__dirname, '..'), stdio: 'inherit', env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }), }, -) \ No newline at end of file +) + +child.on('error', (error) => { + console.error('[debug] failed to start dev server:', error) + process.exit(1) +}) + +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal) + return + } + + process.exit(code ?? 0) +}) diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index fe78c45f..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "mrmlnc.vscode-json5" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json index 2177eb13..10ba04de 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -24,9 +24,10 @@ "name": "Debug Main Process", "type": "node", "request": "launch", - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", "windows": { - "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd" + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" }, "runtimeArgs": [ "--no-sandbox", @@ -34,21 +35,25 @@ "." ], "envFile": "${workspaceFolder}/.vscode/.debug.env", - "console": "integratedTerminal" + "console": "integratedTerminal", + "skipFiles": [ + "/**", + "${workspaceFolder}/node_modules/**" + ] }, { "name": "Debug Renderer Process", "port": 9229, "request": "attach", - "type": "chrome", + "type": "pwa-chrome", "timeout": 60000, "skipFiles": [ "/**", - "${workspaceRoot}/node_modules/**", - "${workspaceRoot}/dist-electron/**", + "${workspaceFolder}/node_modules/**", + "${workspaceFolder}/dist-electron/**", // Skip files in host(VITE_DEV_SERVER_URL) "http://127.0.0.1:7777/**" ] - }, + } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e3e2cde..ca8e3188 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,7 @@ { - "typescript.tsdk": "node_modules/typescript/lib", - "typescript.tsc.autoDetect": "off", "json.schemas": [ { "fileMatch": [ - "/*electron-builder.json5", "/*electron-builder.json" ], "url": "https://json.schemastore.org/electron-builder" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 85d09cde..e0851e7c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,19 +6,26 @@ { "label": "Before Debug", "type": "shell", - "command": "node .vscode/.debug.script.mjs", + "command": "node --enable-source-maps .vscode/.debug.script.mjs", "isBackground": true, + "options": { + "cwd": "${workspaceFolder}" + }, + "presentation": { + "reveal": "always", + "panel": "shared", + "clear": false + }, "problemMatcher": { "owner": "typescript", "fileLocation": "relative", "pattern": { - // TODO: correct "regexp" - "regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$", + "regexp": "^(.*):(\\d+):(\\d+): (ERROR|WARNING): (.*)$", "file": 1, - "line": 3, - "column": 4, - "code": 5, - "message": 6 + "line": 2, + "column": 3, + "code": 4, + "message": 5 }, "background": { "activeOnStart": true, diff --git a/electron-builder.json b/electron-builder.json index 75654076..5ba7c980 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -34,8 +34,9 @@ "deleteAppDataOnUninstall": false }, "publish": { - "provider": "generic", - "channel": "latest", - "url": "https://github.com/electron-vite/electron-vite-react/releases/download/v0.9.9/" + "provider": "github", + "owner": "electron-vite", + "repo": "electron-vite-react", + "releaseType": "release" } } diff --git a/package.json b/package.json index 0e091645..1984ac95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "electron-vite-react", - "version": "2.2.0", + "version": "2.3.0", "main": "dist-electron/main/index.js", "description": "Electron Vite React boilerplate.", "author": "草鞋没号 <308487730@qq.com>", @@ -12,33 +12,31 @@ } }, "type": "module", + "packageManager": "pnpm@10.32.1", "scripts": { "dev": "vite", - "build": "tsc && vite build && electron-builder", + "build": "vite build && electron-builder", "preview": "vite preview", "pretest": "vite build --mode=test", - "test": "vitest run" - }, - "dependencies": { - "electron-updater": "^6.3.9" + "test": "vitest run", + "typecheck": "tsc --noEmit" }, "devDependencies": { + "electron-updater": "^6.3.9", "@playwright/test": "^1.48.2", - "@types/react": "^18.3.12", - "@types/react-dom": "^18.3.1", - "@vitejs/plugin-react": "^4.3.3", - "autoprefixer": "^10.4.20", - "electron": "^33.2.0", - "electron-builder": "^24.13.3", - "postcss": "^8.4.49", - "postcss-import": "^16.1.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "tailwindcss": "^3.4.15", - "typescript": "^5.4.2", - "vite": "^5.4.11", - "vite-plugin-electron": "^0.29.0", - "vite-plugin-electron-renderer": "^0.14.6", - "vitest": "^2.1.5" + "@tailwindcss/vite": "^4.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "electron": "^42.1.0", + "electron-builder": "^26.8.1", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.0.13", + "vite-plugin-electron": "^1.0.0-beta.10", + "vite-plugin-electron-renderer": "^1.0.0-beta.3", + "vitest": "^4.1.6" } } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..081bece7 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + electron-winstaller: false diff --git a/postcss.config.cjs b/postcss.config.cjs deleted file mode 100644 index 825cb186..00000000 --- a/postcss.config.cjs +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - plugins: { - 'postcss-import': {}, - 'tailwindcss/nesting': {}, - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/src/App.css b/src/App.css deleted file mode 100644 index d02bb4ba..00000000 --- a/src/App.css +++ /dev/null @@ -1,52 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo-box { - position: relative; - height: 9em; -} - -.logo { - position: absolute; - left: calc(50% - 4.5em); - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - .logo.electron { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -.flex-center { - display: flex; - align-items: center; - justify-content: center; -} diff --git a/src/App.tsx b/src/App.tsx index c5462d1d..69848dad 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,35 +2,87 @@ import { useState } from 'react' import UpdateElectron from '@/components/update' import logoVite from './assets/logo-vite.svg' import logoElectron from './assets/logo-electron.svg' -import './App.css' function App() { const [count, setCount] = useState(0) return ( -
-
- - Electron + Vite logo - Electron + Vite logo - -
-

Electron + Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Electron + Vite logo to learn more -

-
- Place static files into the/public folder Node logo -
+
+
+
+
+
+
+
+ Electron Vite React +
+
+

+ A sharp starter with Tailwind-first styling. +

+

+ The layout, update panel, and global styles now all run through Tailwind classes instead of legacy CSS files. +

+
+
+ +
+ + + Vite logo + Electron logo + + Open project repository + +
+ Tailwind v4 via Vite plugin +
+
+
- +
+
+
Counter demo
+
{count}
+ +

+ Edit src/App.tsx and save to test HMR. +

+
+
+
+
+ +
+
+
Public assets
+

+ Place static files into the /public folder. +

+
+
+
Update panel
+

+ The built-in updater UI also moved to Tailwind class names so the codebase is consistent end to end. +

+
+
+ + +
) } diff --git a/src/components/update/Modal/index.tsx b/src/components/update/Modal/index.tsx deleted file mode 100644 index 934f1a2b..00000000 --- a/src/components/update/Modal/index.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { ReactNode } from 'react' -import { createPortal } from 'react-dom' -import './modal.css' - -const ModalTemplate: React.FC void - onOk?: () => void - width?: number -}>> = props => { - const { - title, - children, - footer, - cancelText = 'Cancel', - okText = 'OK', - onCancel, - onOk, - width = 530, - } = props - - return ( -
-
-
-
-
-
{title}
- - - - - - -
-
{children}
- {typeof footer !== 'undefined' ? ( -
- - -
- ) : footer} -
-
-
- ) -} - -const Modal = (props: Parameters[0] & { open: boolean }) => { - const { open, ...omit } = props - - return createPortal( - open ? ModalTemplate(omit) : null, - document.body, - ) -} - -export default Modal diff --git a/src/components/update/Modal/modal.css b/src/components/update/Modal/modal.css deleted file mode 100644 index d52cacba..00000000 --- a/src/components/update/Modal/modal.css +++ /dev/null @@ -1,87 +0,0 @@ -.update-modal { - --primary-color: rgb(224, 30, 90); - - .update-modal__mask { - width: 100vw; - height: 100vh; - position: fixed; - left: 0; - top: 0; - z-index: 9; - background: rgba(0, 0, 0, 0.45); - } - - .update-modal__warp { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 19; - } - - .update-modal__content { - box-shadow: 0 0 10px -4px rgb(130, 86, 208); - overflow: hidden; - border-radius: 4px; - - .content__header { - display: flex; - line-height: 38px; - background-color: var(--primary-color); - - .content__header-text { - font-weight: bold; - width: 0; - flex-grow: 1; - } - } - - .update-modal--close { - width: 30px; - height: 30px; - margin: 4px; - line-height: 34px; - text-align: center; - cursor: pointer; - - svg { - width: 17px; - height: 17px; - } - } - - .content__body { - padding: 10px; - background-color: #fff; - color: #333; - } - - .content__footer { - padding: 10px; - background-color: #fff; - display: flex; - justify-content: flex-end; - - button { - padding: 7px 11px; - background-color: var(--primary-color); - font-size: 14px; - margin-left: 10px; - - &:first-child { - margin-left: 0; - } - } - } - } - - .icon { - padding: 0 15px; - width: 20px; - fill: currentColor; - - &:hover { - color: rgba(0, 0, 0, 0.4); - } - } -} \ No newline at end of file diff --git a/src/components/update/Progress/index.tsx b/src/components/update/Progress/index.tsx deleted file mode 100644 index 1701dc57..00000000 --- a/src/components/update/Progress/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' -import './progress.css' - -const Progress: React.FC> = props => { - const { percent = 0 } = props - - return ( -
-
-
-
- {(percent ?? 0).toString().substring(0, 4)}% -
- ) -} - -export default Progress diff --git a/src/components/update/Progress/progress.css b/src/components/update/Progress/progress.css deleted file mode 100644 index 2cef7c1b..00000000 --- a/src/components/update/Progress/progress.css +++ /dev/null @@ -1,21 +0,0 @@ -.update-progress { - display: flex; - align-items: center; - - .update-progress-pr { - border: 1px solid #000; - border-radius: 3px; - height: 6px; - width: 300px; - } - - .update-progress-rate { - height: 6px; - border-radius: 3px; - background-image: linear-gradient(to right, rgb(130, 86, 208) 0%, var(--primary-color) 100%) - } - - .update-progress-num { - margin: 0 10px; - } -} \ No newline at end of file diff --git a/src/components/update/index.tsx b/src/components/update/index.tsx index 2f73940e..70fdc13a 100644 --- a/src/components/update/index.tsx +++ b/src/components/update/index.tsx @@ -1,8 +1,7 @@ import type { ProgressInfo } from 'electron-updater' import { useCallback, useEffect, useState } from 'react' -import Modal from '@/components/update/Modal' -import Progress from '@/components/update/Progress' -import './update.css' +import Modal from '@/components/update/modal' +import Progress from '@/components/update/progress' const Update = () => { const [checking, setChecking] = useState(false) @@ -97,32 +96,38 @@ const Update = () => { onOk={modalBtn?.onOk} footer={updateAvailable ? /* hide footer */null : undefined} > -
+
{updateError ? ( -
-

Error downloading the latest version.

-

{updateError.message}

+
+

Error downloading the latest version.

+

{updateError.message}

) : updateAvailable ? ( -
-
The last version is: v{versionInfo?.newVersion}
-
v{versionInfo?.version} -> v{versionInfo?.newVersion}
-
-
Update progress:
-
+
+
The latest version is v{versionInfo?.newVersion}
+
+ v{versionInfo?.version} -> v{versionInfo?.newVersion} +
+
+
Update progress:
+
) : ( -
{JSON.stringify(versionInfo ?? {}, null, 2)}
+
{JSON.stringify(versionInfo ?? {}, null, 2)}
)}
- diff --git a/src/components/update/modal.tsx b/src/components/update/modal.tsx new file mode 100644 index 00000000..ad4210c8 --- /dev/null +++ b/src/components/update/modal.tsx @@ -0,0 +1,67 @@ +import React, { type ReactNode } from 'react' +import { createPortal } from 'react-dom' + +const ModalTemplate: React.FC void + onOk?: () => void + width?: number +}>> = props => { + const { + title, + children, + footer, + cancelText = 'Cancel', + okText = 'OK', + onCancel, + onOk, + width = 530, + } = props + + return ( +
+
+
+
+
+
{title}
+ + + + + + +
+
{children}
+ {footer !== undefined ? ( +
+ + +
+ ) : footer} +
+
+
+ ) +} + +const Modal = (props: Parameters[0] & { open: boolean }) => { + const { open, ...omit } = props + + return createPortal( + open ? : null, + document.body, + ) +} + +export default Modal diff --git a/src/components/update/progress.tsx b/src/components/update/progress.tsx new file mode 100644 index 00000000..17c62649 --- /dev/null +++ b/src/components/update/progress.tsx @@ -0,0 +1,21 @@ +import React from 'react' + +const Progress: React.FC> = props => { + const { percent = 0 } = props + + return ( +
+
+
+
+ {(percent ?? 0).toString().substring(0, 4)}% +
+ ) +} + +export default Progress diff --git a/src/components/update/update.css b/src/components/update/update.css deleted file mode 100644 index e7769684..00000000 --- a/src/components/update/update.css +++ /dev/null @@ -1,24 +0,0 @@ -.modal-slot { - .update-progress { - display: flex; - } - - .new-version__target, - .update__progress { - margin-left: 40px; - } - - .progress__title { - margin-right: 10px; - } - - .progress__bar { - width: 0; - flex-grow: 1; - } - - .can-not-available { - padding: 20px; - text-align: center; - } -} \ No newline at end of file diff --git a/src/index.css b/src/index.css index f6ec8bae..4cabd5a8 100644 --- a/src/index.css +++ b/src/index.css @@ -1,94 +1,55 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -code { - background-color: #1a1a1a; - padding: 2px 4px; - margin: 0 4px; - border-radius: 4px; +@theme { + --font-sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } -.card { - padding: 2em; -} +@layer base { + :root { + color-scheme: dark; + font-family: var(--font-sans); + line-height: 1.5; + font-weight: 400; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + } -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} + html { + min-width: 320px; + min-height: 100%; + } -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; + body { + min-width: 320px; + min-height: 100vh; + margin: 0; + background: + radial-gradient(circle at top, rgba(56, 189, 248, 0.22), transparent 30%), + radial-gradient(circle at bottom right, rgba(244, 114, 182, 0.2), transparent 24%), + #020617; + color: #e2e8f0; } - a:hover { - color: #747bff; + + a { + color: inherit; + text-decoration: none; } - button { - background-color: #f9f9f9; + + button, + input, + textarea, + select { + font: inherit; } + code { - background-color: #f9f9f9; + border-radius: 0.375rem; + border: 1px solid rgba(148, 163, 184, 0.25); + background: rgba(15, 23, 42, 0.75); + padding: 0.125rem 0.375rem; + color: #e2e8f0; } } diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index 42872c4f..00000000 --- a/tailwind.config.js +++ /dev/null @@ -1,14 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: [ - './index.html', - './src/**/*.{js,ts,jsx,tsx}', - ], - theme: { - extend: {}, - }, - corePlugins: { - preflight: false, - }, - plugins: [], -} diff --git a/tsconfig.json b/tsconfig.json index c9c47659..856da789 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,24 +3,26 @@ "target": "ESNext", "useDefineForClassFields": true, "lib": ["DOM", "DOM.Iterable", "ESNext"], + "types": ["vite/client"], "allowJs": false, "skipLibCheck": true, - "esModuleInterop": false, + "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", "forceConsistentCasingInFileNames": true, "module": "ESNext", - "moduleResolution": "Node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "baseUrl": "./", "paths": { "@/*": [ - "src/*" + "./src/*" ] - }, + } }, "include": ["src", "electron"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/tsconfig.node.json b/tsconfig.node.json index 1e7e7d60..2d75e0cc 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -2,9 +2,13 @@ "compilerOptions": { "composite": true, "module": "ESNext", - "moduleResolution": "Node", + "moduleResolution": "bundler", + "moduleDetection": "force", + "verbatimModuleSyntax": true, + "types": ["node"], + "skipLibCheck": true, "resolveJsonModule": true, "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "package.json"] + "include": ["vite.config.ts", "vitest.config.ts", "package.json"] } diff --git a/vite.config.ts b/vite.config.ts index b3f25021..4a61f95e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,9 +2,13 @@ import { rmSync } from 'node:fs' import path from 'node:path' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' import electron from 'vite-plugin-electron/simple' import pkg from './package.json' +const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL) +const external = Object.keys('dependencies' in pkg ? pkg.dependencies as Record : {}) + // https://vitejs.dev/config/ export default defineConfig(({ command }) => { rmSync('dist-electron', { recursive: true, force: true }) @@ -21,6 +25,7 @@ export default defineConfig(({ command }) => { }, plugins: [ react(), + tailwindcss(), electron({ main: { // Shortcut of `build.lib.entry` @@ -37,14 +42,14 @@ export default defineConfig(({ command }) => { sourcemap, minify: isBuild, outDir: 'dist-electron/main', - rollupOptions: { - external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}), + rolldownOptions: { + external, }, }, }, }, preload: { - // Shortcut of `build.rollupOptions.input`. + // Shortcut of `build.rolldownOptions.input`. // Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`. input: 'electron/preload/index.ts', vite: { @@ -52,25 +57,24 @@ export default defineConfig(({ command }) => { sourcemap: sourcemap ? 'inline' : undefined, // #332 minify: isBuild, outDir: 'dist-electron/preload', - rollupOptions: { - external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}), + rolldownOptions: { + external, }, }, }, }, - // Ployfill the Electron and Node.js API for Renderer process. + // Polyfill the Electron and Node.js API for Renderer process. // If you want use Node.js in Renderer process, the `nodeIntegration` needs to be enabled in the Main process. // See 👉 https://github.com/electron-vite/vite-plugin-electron-renderer renderer: {}, }), ], - server: process.env.VSCODE_DEBUG && (() => { - const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL) - return { + server: process.env.VSCODE_DEBUG + ? { host: url.hostname, port: +url.port, } - })(), + : undefined, clearScreen: false, } }) From 74bb52619dba0dbb7cc552c21f29d243bf916a4d Mon Sep 17 00:00:00 2001 From: Song <1667077010@qq.com> Date: Sun, 17 May 2026 11:03:54 +0000 Subject: [PATCH 02/12] e2e test --- .gitignore | 2 + electron/main/update.ts | 10 ++-- package.json | 1 + playwright.config.ts | 11 ++++ test/e2e.spec.ts | 113 +++++++++++++++++++++++++--------------- 5 files changed, 91 insertions(+), 46 deletions(-) create mode 100644 playwright.config.ts diff --git a/.gitignore b/.gitignore index c2c37f0a..3cad0a49 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ yarn.lock /test-results/ /playwright-report/ /playwright/.cache/ + +test/screenshots diff --git a/electron/main/update.ts b/electron/main/update.ts index 6a365d6a..4d2d3de5 100644 --- a/electron/main/update.ts +++ b/electron/main/update.ts @@ -1,10 +1,10 @@ import { app, ipcMain } from 'electron' import { createRequire } from 'node:module' -import type { - ProgressInfo, - UpdateDownloadedEvent, - UpdateInfo, - CancellationToken +import { + CancellationToken, + type ProgressInfo, + type UpdateDownloadedEvent, + type UpdateInfo, } from 'electron-updater' const { autoUpdater } = createRequire(import.meta.url)('electron-updater'); diff --git a/package.json b/package.json index 1984ac95..a0b9f0f7 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "preview": "vite preview", "pretest": "vite build --mode=test", "test": "vitest run", + "test:e2e": "pnpm pretest && playwright test", "typecheck": "tsc --noEmit" }, "devDependencies": { diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..9cb376ea --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from '@playwright/test' + +export default defineConfig({ + testDir: './test', + fullyParallel: false, + workers: 1, + reporter: 'list', + use: { + trace: 'on-first-retry', + }, +}) diff --git a/test/e2e.spec.ts b/test/e2e.spec.ts index b8200596..794311a1 100644 --- a/test/e2e.spec.ts +++ b/test/e2e.spec.ts @@ -1,64 +1,95 @@ import path from 'node:path' +import { spawn, type ChildProcess } from 'node:child_process' import { type ElectronApplication, type Page, type JSHandle, - _electron as electron, -} from 'playwright' -import type { BrowserWindow } from 'electron' -import { - beforeAll, - afterAll, - describe, expect, test, -} from 'vitest' + _electron as electron, +} from '@playwright/test' +import type { BrowserWindow } from 'electron' -const root = path.join(__dirname, '..') +const root = path.join(import.meta.dirname, '..') let electronApp: ElectronApplication let page: Page +let xvfbProcess: ChildProcess | undefined -if (process.platform === 'linux') { - // pass ubuntu - test(() => expect(true).true) -} else { - beforeAll(async () => { - electronApp = await electron.launch({ - args: ['.', '--no-sandbox'], - cwd: root, - env: { ...process.env, NODE_ENV: 'development' }, - }) - page = await electronApp.firstWindow() +function startXvfbOnLinux(): Promise { + if (process.platform !== 'linux' || process.env.DISPLAY) { + return Promise.resolve() + } - const mainWin: JSHandle = await electronApp.browserWindow(page) - await mainWin.evaluate(async (win) => { - win.webContents.executeJavaScript('console.log("Execute JavaScript with e2e testing.")') + return new Promise((resolve, reject) => { + xvfbProcess = spawn('Xvfb', [':99', '-screen', '0', '1280x720x24', '-ac'], { + stdio: 'ignore', + detached: true, }) + + xvfbProcess.once('error', reject) + + setTimeout(() => { + process.env.DISPLAY = ':99' + resolve() + }, 500) + }) +} + +test.beforeAll(async () => { + test.setTimeout(30000) + await startXvfbOnLinux() + + electronApp = await electron.launch({ + args: ['.', '--no-sandbox'], + cwd: root, + env: { ...process.env, NODE_ENV: 'development' }, }) + page = await electronApp.firstWindow() - afterAll(async () => { + const mainWin: JSHandle = await electronApp.browserWindow(page) + await mainWin.evaluate(async (win) => { + win.webContents.executeJavaScript('console.log("Execute JavaScript with e2e testing.")') + }) +}) + +test.afterAll(async () => { + if (page) { await page.screenshot({ path: 'test/screenshots/e2e.png' }) await page.close() + } + + if (electronApp) { await electronApp.close() + } + + if (xvfbProcess?.pid) { + process.kill(-xvfbProcess.pid) + xvfbProcess = undefined + } +}) + +test.describe('[electron-vite-react] e2e tests', () => { + test('startup', async () => { + const title = await page.title() + expect(title).toBe('Electron + Vite + React') }) - describe('[electron-vite-react] e2e tests', async () => { - test('startup', async () => { - const title = await page.title() - expect(title).eq('Electron + Vite + React') - }) + test('should be home page is load correctly', async () => { + const h1 = await page.$('h1') + const title = await h1?.textContent() + expect(title).toBe('A sharp starter with Tailwind-first styling.') + }) - test('should be home page is load correctly', async () => { - const h1 = await page.$('h1') - const title = await h1?.textContent() - expect(title).eq('Electron + Vite + React') - }) + test('should be count button can click', async () => { + const countButton = await page.$('button:has-text("Increment counter")') + const countValue = await page.$('div.text-5xl') - test('should be count button can click', async () => { - const countButton = await page.$('button') - await countButton?.click() - const countValue = await countButton?.textContent() - expect(countValue).eq('count is 1') - }) + const valueBeforeClick = await countValue?.textContent() + expect(valueBeforeClick).toBe('0') + + await countButton?.click() + + const valueAfterClick = await countValue?.textContent() + expect(valueAfterClick).toBe('1') }) -} +}) From ab0ae0bff9f57287930fb95520292e0811205e4c Mon Sep 17 00:00:00 2001 From: Song <1667077010@qq.com> Date: Sun, 17 May 2026 21:47:21 +0800 Subject: [PATCH 03/12] Change debugger type from 'pwa-chrome' to 'chrome' --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 10ba04de..afcefd9c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -45,7 +45,7 @@ "name": "Debug Renderer Process", "port": 9229, "request": "attach", - "type": "pwa-chrome", + "type": "chrome", "timeout": 60000, "skipFiles": [ "/**", @@ -56,4 +56,4 @@ ] } ] -} \ No newline at end of file +} From 1ec780b87bed7757c0112695a84f28efc621e1f6 Mon Sep 17 00:00:00 2001 From: Song <1667077010@qq.com> Date: Sun, 24 May 2026 03:22:02 +0000 Subject: [PATCH 04/12] ci: align workflows with electron-vite-vue#540 --- .github/workflows/build.yml | 3 +- .github/workflows/ci.yml | 59 ++++-------------------------- .github/workflows/pr-guard.yml | 65 ++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/pr-guard.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0afe8db..b1560fc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -41,7 +43,6 @@ jobs: node-version: 22 cache: pnpm - - name: Install Dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d23c963..1e023353 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,71 +4,26 @@ on: pull_request: branches: - main + paths: + - '*.md' + - '**/*.md' permissions: contents: read - pull-requests: write concurrency: group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: - validate: - name: Validate Pull Request + lint-markdown: + name: Lint Markdown runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Detect Changed Files - uses: dorny/paths-filter@v3 - id: changes - with: - list-files: shell - filters: | - lockfiles: - - 'package-lock.json' - - 'yarn.lock' - - 'pnpm-lock.yaml' - markdown: - - added|modified: '*.md' - - # ref: https://github.com/github/docs/blob/main/.github/workflows/triage-unallowed-contributions.yml - - name: Comment About Changes We Can't Accept - if: ${{ steps.changes.outputs.lockfiles == 'true' }} - uses: actions/github-script@v7 - with: - script: | - try { - const badFilesArr = [ - 'package-lock.json', - 'yarn.lock', - 'pnpm-lock.yaml', - ] - const badFiles = badFilesArr.join('\n- ') - const reviewMessage = `👋 Hey there spelunker. It looks like you've modified some files that we can't accept as contributions. The complete list of files we can't accept are:\n- ${badFiles}\n\nYou'll need to revert all of the files you changed in that list using [GitHub Desktop](https://docs.github.com/en/free-pro-team@latest/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/reverting-a-commit) or \`git checkout origin/main \`. Once you get those files reverted, we can continue with the review process. :octocat:\n\nMore discussion:\n- https://github.com/electron-vite/electron-vite-vue/issues/192` - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: reviewMessage, - }) - } catch(err) { - core.warning(`Failed to create PR comment: ${err.message}`) - } - - - name: Fail on Restricted Lockfile Changes - if: ${{ steps.changes.outputs.lockfiles == 'true' }} - run: | - echo "Lockfiles are managed by maintainers and should not be changed in PRs." - echo "Changed lockfiles:" - echo "${{ steps.changes.outputs.lockfiles_files }}" - exit 1 + persist-credentials: false - name: Lint markdown - if: ${{ steps.changes.outputs.markdown == 'true' }} - run: npx --yes markdownlint-cli@latest ${{ steps.changes.outputs.markdown_files }} --ignore node_modules \ No newline at end of file + run: npx --yes markdownlint-cli@0.48.0 '**/*.md' --ignore node_modules \ No newline at end of file diff --git a/.github/workflows/pr-guard.yml b/.github/workflows/pr-guard.yml new file mode 100644 index 00000000..607f7cf6 --- /dev/null +++ b/.github/workflows/pr-guard.yml @@ -0,0 +1,65 @@ +name: PR Guard + +on: + pull_request_target: + branches: + - main + +permissions: + contents: read + pull-requests: write + +concurrency: + group: pr-guard-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + validate: + name: Validate Pull Request + runs-on: ubuntu-latest + steps: + - name: Detect Changed Files + uses: dorny/paths-filter@v3 + id: changes + with: + list-files: shell + filters: | + lockfiles: + - 'package-lock.json' + - 'yarn.lock' + - 'pnpm-lock.yaml' + - 'bun.lock' + + # ref: https://github.com/github/docs/blob/main/.github/workflows/triage-unallowed-contributions.yml + - name: Comment About Changes We Can't Accept + if: ${{ steps.changes.outputs.lockfiles == 'true' }} + uses: actions/github-script@v7 + with: + script: | + try { + const badFilesArr = [ + 'package-lock.json', + 'yarn.lock', + 'pnpm-lock.yaml', + 'bun.lock', + ] + const badFiles = badFilesArr.join('\n- ') + const reviewMessage = `👋 Hey there spelunker. It looks like you've modified some files that we can't accept as contributions. The complete list of files we can't accept are:\n- ${badFiles}\n\nYou'll need to revert all of the files you changed in that list using [GitHub Desktop](https://docs.github.com/en/free-pro-team@latest/desktop/contributing-and-collaborating-using-github-desktop/managing-commits/reverting-a-commit) or \`git checkout origin/main \`. Once you get those files reverted, we can continue with the review process. :octocat:\n\nMore discussion:\n- https://github.com/electron-vite/electron-vite-vue/issues/192` + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: reviewMessage, + }) + } catch(err) { + core.warning(`Failed to create PR comment: ${err.message}`) + } + + - name: Fail on Restricted Lockfile Changes + if: ${{ steps.changes.outputs.lockfiles == 'true' }} + run: | + echo "Lockfiles are managed by maintainers and should not be changed in PRs." + echo "Changed lockfiles:" + echo "${{ steps.changes.outputs.lockfiles_files }}" + exit 1 From e2b392951536f94412182ad239149136a8e3a7d3 Mon Sep 17 00:00:00 2001 From: Song <1667077010@qq.com> Date: Sun, 24 May 2026 03:23:28 +0000 Subject: [PATCH 05/12] chore: update test, tsconfig and deps --- package.json | 14 +++++++------- playwright.config.ts | 2 +- pnpm-workspace.yaml | 2 -- test/{ => e2e}/e2e.spec.ts | 2 +- test/index.test.ts | 11 +++++++++++ tsconfig.json | 2 +- tsconfig.node.json | 2 +- vitest.config.ts | 2 ++ 8 files changed, 24 insertions(+), 13 deletions(-) delete mode 100644 pnpm-workspace.yaml rename test/{ => e2e}/e2e.spec.ts (97%) create mode 100644 test/index.test.ts diff --git a/package.json b/package.json index a0b9f0f7..ab1bb44e 100644 --- a/package.json +++ b/package.json @@ -23,21 +23,21 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "electron-updater": "^6.3.9", - "@playwright/test": "^1.48.2", + "@playwright/test": "^1.60.0", "@tailwindcss/vite": "^4.3.0", - "@types/react": "^19.2.14", + "@types/react": "^19.2.15", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", "electron": "^42.1.0", "electron-builder": "^26.8.1", + "electron-updater": "^6.8.3", "react": "^19.2.6", "react-dom": "^19.2.6", "tailwindcss": "^4.3.0", "typescript": "^6.0.3", - "vite": "^8.0.13", - "vite-plugin-electron": "^1.0.0-beta.10", - "vite-plugin-electron-renderer": "^1.0.0-beta.3", - "vitest": "^4.1.6" + "vite": "^8.0.14", + "vite-plugin-electron": "^1.0.0-beta.12", + "vite-plugin-electron-renderer": "^1.0.0-beta.6", + "vitest": "^4.1.7" } } diff --git a/playwright.config.ts b/playwright.config.ts index 9cb376ea..40263e40 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from '@playwright/test' export default defineConfig({ - testDir: './test', + testDir: './test/e2e', fullyParallel: false, workers: 1, reporter: 'list', diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index 081bece7..00000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -allowBuilds: - electron-winstaller: false diff --git a/test/e2e.spec.ts b/test/e2e/e2e.spec.ts similarity index 97% rename from test/e2e.spec.ts rename to test/e2e/e2e.spec.ts index 794311a1..fa951803 100644 --- a/test/e2e.spec.ts +++ b/test/e2e/e2e.spec.ts @@ -10,7 +10,7 @@ import { } from '@playwright/test' import type { BrowserWindow } from 'electron' -const root = path.join(import.meta.dirname, '..') +const root = path.resolve(import.meta.dirname, '..', '..') let electronApp: ElectronApplication let page: Page let xvfbProcess: ChildProcess | undefined diff --git a/test/index.test.ts b/test/index.test.ts new file mode 100644 index 00000000..bc7601db --- /dev/null +++ b/test/index.test.ts @@ -0,0 +1,11 @@ +import { describe, it, expect } from 'vitest' + +describe('vitest smoke', () => { + it('runs a normal unit test', () => { + expect(1 + 1).toBe(2) + }) + + it('has test mode enabled while running tests', () => { + expect(process.env.NODE_ENV).toBe('test') + }) +}) diff --git a/tsconfig.json b/tsconfig.json index 856da789..6211d12f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,6 +24,6 @@ ] } }, - "include": ["src", "electron"], + "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/tsconfig.node.json b/tsconfig.node.json index 2d75e0cc..01f1ac80 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -10,5 +10,5 @@ "resolveJsonModule": true, "allowSyntheticDefaultImports": true }, - "include": ["vite.config.ts", "vitest.config.ts", "package.json"] + "include": ["electron", "vite.config.ts", "vitest.config.ts", "package.json"] } diff --git a/vitest.config.ts b/vitest.config.ts index 068f6fd0..6439980c 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,8 @@ export default defineConfig({ test: { root: __dirname, include: ['test/**/*.{test,spec}.?(c|m)[jt]s?(x)'], + exclude: ['test/e2e.spec.ts'], + passWithNoTests: true, testTimeout: 1000 * 29, }, }) From f09977198f86bed14b0eb2750c363ef04fb695d9 Mon Sep 17 00:00:00 2001 From: subframe7536 <1667077010@qq.com> Date: Tue, 26 May 2026 21:08:18 +0800 Subject: [PATCH 06/12] chore: update deps and debug config --- .vscode/.debug.script.mjs | 44 --------------------------- .vscode/launch.json | 62 +++++++++++++-------------------------- package.json | 4 +-- vite.config.ts | 13 -------- 4 files changed, 23 insertions(+), 100 deletions(-) delete mode 100644 .vscode/.debug.script.mjs diff --git a/.vscode/.debug.script.mjs b/.vscode/.debug.script.mjs deleted file mode 100644 index 2109c56d..00000000 --- a/.vscode/.debug.script.mjs +++ /dev/null @@ -1,44 +0,0 @@ -import fs from 'node:fs' -import path from 'node:path' -import { fileURLToPath } from 'node:url' -import { createRequire } from 'node:module' -import { spawn } from 'node:child_process' - -const pkg = createRequire(import.meta.url)('../package.json') -const __dirname = path.dirname(fileURLToPath(import.meta.url)) - -const packageManager = (pkg.packageManager || 'npm').split('@')[0] -const runCommand = process.platform === 'win32' - ? `${packageManager}.cmd` - : packageManager - -// write .debug.env -const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`) -fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n')) - -// bootstrap -const child = spawn( - // Keep this command aligned with the project package manager. - runCommand, - ['run', 'dev'], - { - shell: process.platform === 'win32', - cwd: path.resolve(__dirname, '..'), - stdio: 'inherit', - env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }), - }, -) - -child.on('error', (error) => { - console.error('[debug] failed to start dev server:', error) - process.exit(1) -}) - -child.on('exit', (code, signal) => { - if (signal) { - process.kill(process.pid, signal) - return - } - - process.exit(code ?? 0) -}) diff --git a/.vscode/launch.json b/.vscode/launch.json index afcefd9c..80edc4fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,59 +1,39 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", - "compounds": [ - { - "name": "Debug App", - "preLaunchTask": "Before Debug", - "configurations": [ - "Debug Main Process", - "Debug Renderer Process" - ], - "presentation": { - "hidden": false, - "group": "", - "order": 1 - }, - "stopAll": true - } - ], "configurations": [ { "name": "Debug Main Process", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", - "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/vite", "windows": { - "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/vite.cmd" }, - "runtimeArgs": [ - "--no-sandbox", - "--remote-debugging-port=9229", - "." - ], - "envFile": "${workspaceFolder}/.vscode/.debug.env", - "console": "integratedTerminal", - "skipFiles": [ - "/**", - "${workspaceFolder}/node_modules/**" - ] + "runtimeArgs": ["dev"], + "env": { + "REMOTE_DEBUGGING_PORT": "9222" + } }, { "name": "Debug Renderer Process", - "port": 9229, + "port": 9222, "request": "attach", "type": "chrome", + "webRoot": "${workspaceFolder}/src", "timeout": 60000, - "skipFiles": [ - "/**", - "${workspaceFolder}/node_modules/**", - "${workspaceFolder}/dist-electron/**", - // Skip files in host(VITE_DEV_SERVER_URL) - "http://127.0.0.1:7777/**" - ] + "presentation": { + "hidden": true + } + } + ], + "compounds": [ + { + "name": "Debug All", + "configurations": ["Debug Main Process", "Debug Renderer Process"], + "presentation": { + "order": 1 + } } ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index ab1bb44e..04801df7 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,8 @@ "tailwindcss": "^4.3.0", "typescript": "^6.0.3", "vite": "^8.0.14", - "vite-plugin-electron": "^1.0.0-beta.12", - "vite-plugin-electron-renderer": "^1.0.0-beta.6", + "vite-plugin-electron": "1.0.0-beta.13", + "vite-plugin-electron-renderer": "1.0.0-beta.9", "vitest": "^4.1.7" } } diff --git a/vite.config.ts b/vite.config.ts index 4a61f95e..9e642402 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -30,13 +30,6 @@ export default defineConfig(({ command }) => { main: { // Shortcut of `build.lib.entry` entry: 'electron/main/index.ts', - onstart(args) { - if (process.env.VSCODE_DEBUG) { - console.log(/* For `.vscode/.debug.script.mjs` */'[startup] Electron App') - } else { - args.startup() - } - }, vite: { build: { sourcemap, @@ -69,12 +62,6 @@ export default defineConfig(({ command }) => { renderer: {}, }), ], - server: process.env.VSCODE_DEBUG - ? { - host: url.hostname, - port: +url.port, - } - : undefined, clearScreen: false, } }) From 2e58b6110f8acd59e49f72383f2177d86d62c942 Mon Sep 17 00:00:00 2001 From: subframe7536 <1667077010@qq.com> Date: Wed, 27 May 2026 11:26:08 +0800 Subject: [PATCH 07/12] chore: refresh style --- .github/workflows/ci.yml | 2 +- README.md | 9 +-- README.zh-CN.md | 5 +- electron/main/update.ts | 33 +++++++-- package.json | 15 ++-- src/App.tsx | 73 ++++++++++++-------- src/assets/logo-tailwindcss.svg | 1 + src/assets/tailwlindcss.svg | 1 + src/components/update/README.md | 50 +++++++------- src/components/update/README.zh-CN.md | 53 +++++++-------- src/components/update/index.tsx | 98 +++++++++++++++------------ src/components/update/modal.tsx | 83 ++++++++++++++--------- src/components/update/progress.tsx | 21 +++--- src/index.css | 45 +++++++++--- vite.config.ts | 7 +- 15 files changed, 299 insertions(+), 197 deletions(-) create mode 100644 src/assets/logo-tailwindcss.svg create mode 100644 src/assets/tailwlindcss.svg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e023353..24d5eb47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,4 +26,4 @@ jobs: persist-credentials: false - name: Lint markdown - run: npx --yes markdownlint-cli@0.48.0 '**/*.md' --ignore node_modules \ No newline at end of file + run: npx --yes markdownlint-cli@0.48.0 '**/*.md' --ignore node_modules -disable MD013 diff --git a/README.md b/README.md index 0a643cfa..81aab915 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ English | [简体中文](README.zh-CN.md) 💪 Supports Node.js API in the renderer process 🔩 Supports C/C++ native addons 🐞 Debugger configuration included -🖥 Easy to implement multiple windows +🖥 Easy to implement multiple windows ## 🛫 Quick Setup @@ -41,7 +41,7 @@ npm run dev ## 📂 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`* +_Files in this folder will be separated from your React application and built into `dist-electron`_ ```tree ├── electron Electron-related code @@ -60,7 +60,7 @@ Familiar React application structure, just with `electron` folder on the top :wi +The `renderer: {}` preset in `vite.config.ts` is only a Vite adapter that polyfills Electron, Node.js APIs and native modules for the renderer process. It is not the same as enabling Node integration. If you want direct Node.js access in the renderer, enable `nodeIntegration` in the `BrowserWindow` webPreferences in the main process and review the security impact carefully. -## 🔧 Additional features +## Features -1. electron-updater 👉 [see docs](src/components/update/README.md) -2. Playwright -3. TailwindCSS +1. Electron auto update with docs in [src/components/update/README.md](src/components/update/README.md). +2. Vitest unit tests and Playwright end-to-end tests. +3. TailwindCSS v4. -## ❔ FAQ +## Resources +- Auto-update docs: [English](src/components/update/README.md) | [简体中文](src/components/update/README.zh-CN.md) - [C/C++ addons, Node.js modules - Pre-Bundling](https://github.com/electron-vite/vite-plugin-electron-renderer#dependency-pre-bundling) - [dependencies vs devDependencies](https://github.com/electron-vite/vite-plugin-electron-renderer#dependencies-vs-devdependencies) diff --git a/README.zh-CN.md b/README.zh-CN.md index 5084ea51..23e5b5e0 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,92 +1,79 @@ -# vite-react-electron +# 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) +![GitHub stars](https://img.shields.io/github/stars/electron-vite/electron-vite-react?color=fa6470) +![GitHub issues](https://img.shields.io/github/issues/electron-vite/electron-vite-react?color=d8b22d) +![GitHub license](https://img.shields.io/github/license/electron-vite/electron-vite-react) +[![Required Node.js >= 20.19.0 || >= 22.12.0](https://img.shields.io/static/v1?label=node&message=%3E=20.19.0%20||%20%3E=22.12.0&logo=node.js&color=3f893e)](https://nodejs.org/about/releases) [English](README.md) | 简体中文 -## 概述 +## 概览 -📦 开箱即用 -🎯 基于官方的 [template-react-ts](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts), 低侵入性 -🌱 结构清晰,可塑性强 -💪 支持在渲染进程中使用 Electron、Node.js API -🔩 支持 C/C++ 模块 -🖥 很容易实现多窗口 +- 开箱即用。 +- 基于官方的 [template-react-ts](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts)。 +- 支持在渲染进程中使用 Electron 和 Node.js API。 +- 支持 C/C++ 原生模块。 +- 包含调试配置。 +- 易于扩展为多窗口应用。 ## 快速开始 ```sh -# clone the project +# 克隆项目 git clone https://github.com/electron-vite/electron-vite-react.git -# enter the project directory +# 进入项目目录 cd electron-vite-react -# install dependency -npm install +# 安装依赖 +pnpm install -# develop -npm run dev +# 启动开发环境 +pnpm dev ``` -## 调试 +## 可用脚本 -![electron-vite-react-debug.gif](/electron-vite-react-debug.gif) +- `pnpm dev`:启动 Vite 开发服务器。 +- `pnpm build`:构建渲染进程并使用 electron-builder 打包应用。 +- `pnpm preview`:本地预览生产构建结果。 +- `pnpm test`:运行 Vitest 单元测试。 +- `pnpm test:e2e`:构建测试模式产物并运行 Playwright 测试。 +- `pnpm typecheck`:运行 TypeScript 类型检查。 -## 目录 - -_🚨 默认情况下, `electron` 文件夹下的文件将会被构建到 `dist-electron`_ +## 项目结构 ```tree -├── electron Electron 源码文件夹 -│ ├── main Main-process 源码 -│ └── preload Preload-scripts 源码 -│ -├── release 构建后生成程序目录 -│ └── {version} -│ ├── {os}-{os_arch} 未打包的程序(绿色运行版) -│ └── {app_name}_{version}.{ext} 应用安装文件 -│ -├── public 同 Vite 模板的 public -└── src 渲染进程源码、React代码 +├── build/ 打包资源 +├── dist-electron/ 编译后的 Electron 输出 +├── electron/ 主进程和 preload 源码 +│ ├── main/ +│ └── preload/ +├── public/ 静态资源 +├── src/ 渲染进程源码 +│ ├── components/ +│ │ └── update/ +│ ├── demos/ +│ └── type/ +└── test/ 单元测试和端到端测试 + └── e2e/ ``` - +`vite.config.ts` 里的 `renderer: {}` 只是给 Vite 用的适配器,用来在渲染进程中修复 Electron、Node.js API 和原生模块的使用,它本身并不等同于开启 Node integration。若确实需要在渲染进程中直接使用 Node.js,请在主进程创建 `BrowserWindow` 时开启 `nodeIntegration`,并谨慎评估安全影响。 -## 🔧 额外的功能 +## 功能 -1. Electron 自动更新 👉 [阅读文档](src/components/update/README.zh-CN.md) -2. Playwright 测试 -3. TailwindCSS +1. Electron 自动更新,文档见 [src/components/update/README.md](src/components/update/README.md)。 +2. Vitest 单元测试和 Playwright 端到端测试。 +3. TailwindCSS v4。 -## ❔ FAQ +## 资源 +- 自动更新文档:[English](src/components/update/README.md) | [简体中文](src/components/update/README.zh-CN.md) - [C/C++ addons, Node.js modules - Pre-Bundling](https://github.com/electron-vite/vite-plugin-electron-renderer#dependency-pre-bundling) - [dependencies vs devDependencies](https://github.com/electron-vite/vite-plugin-electron-renderer#dependencies-vs-devdependencies) - -## 🍵 🍰 🍣 🍟 - - diff --git a/src/components/update/README.md b/src/components/update/README.md index e4d12197..31eafaa6 100644 --- a/src/components/update/README.md +++ b/src/components/update/README.md @@ -2,17 +2,17 @@ English | [简体中文](README.zh-CN.md) -> Use `electron-updater` to realize the update detection, download and installation of the electric program. +> Use `electron-updater` to detect, download, and install Electron app updates. ```sh -npm i electron-updater +pnpm add electron-updater ``` -### Main logic +### Main Flow -#### 1. Configuration of the update the service address and update information script: +#### 1. Configure the update server -Add a `publish` field to `electron-builder.json` for configuring the update address and which strategy to use as the update service. +Add a `publish` field to `electron-builder.json` to define the update source and provider. ```json5 { @@ -24,26 +24,32 @@ Add a `publish` field to `electron-builder.json` for configuring the update addr } ``` -For more information, please refer to : [electron-builder.json](https://github.com/electron-vite/electron-vite-react/blob/main/electron-builder.json) +For more information, please refer to [electron-builder.json](https://github.com/electron-vite/electron-vite-react/blob/main/electron-builder.json) -#### 2. The update logic of Electron: +#### 2. Main-process update logic -- Checking if an update is available; -- Checking the version of the software on the server; -- Checking if an update is available; -- Downloading the new version of the software from the server (when an update is available); -- Installation method; +This project keeps `autoDownload` disabled, so users start downloads manually. -For more information, please refer to : [update.ts](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) +- `check-update` calls `autoUpdater.checkForUpdates()` after packaging. +- The main process emits `update-can-available` when it receives `update-available` or `update-not-available`. +- `start-download` begins downloading the update and forwards download progress to the renderer. +- `cancel-download` stops the current download and resets the cancellation token. +- `quit-and-install` installs the downloaded update and restarts the app. -#### 3. Updating UI pages in Electron: +For more information, please refer to [update.ts](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) -The main function is to provide a UI page for users to trigger the update logic mentioned in (2.) above. Users can click on the page to trigger different update functions in Electron. +#### 3. Renderer UI -For more information, please refer to : [components/update/index.tsx](https://github.com/electron-vite/electron-vite-react/blob/main/src/components/update/index.tsx) +The update page is the user-facing entry point for the flow above. Users click the page button to trigger update actions in Electron. + +- Click **Check update** to trigger `check-update`. +- If an update is available, the page lets the user start or cancel the download. +- After the download completes, the page switches to **Install now**. + +For more information, please refer to [components/update/index.tsx](https://github.com/electron-vite/electron-vite-react/blob/main/src/components/update/index.tsx) --- -Here it is recommended to trigger updates through user actions (in this project, Electron update function is triggered after the user clicks on the "Check for updates" button). +Updates should be triggered by user action. In this project, the flow starts after the user clicks **Check update**. For more information on using `electron-updater` for Electron updates, please refer to the [documentation](https://www.electron.build) diff --git a/src/components/update/README.zh-CN.md b/src/components/update/README.zh-CN.md index 20c0fe88..b7e87741 100644 --- a/src/components/update/README.zh-CN.md +++ b/src/components/update/README.zh-CN.md @@ -1,48 +1,55 @@ -# electron-auto-update +# electron-updater [English](README.md) | 简体中文 -使用`electron-updater`实现electron程序的更新检测、下载和安装等功能。 +使用 `electron-updater` 检测、下载并安装 Electron 应用更新。 ```sh -npm i electron-updater +pnpm add electron-updater ``` -### 主要逻辑 +### 主要流程 -#### 1. 更新地址、更新信息脚本的配置 +#### 1. 配置更新服务 -在`electron-builder.json5`添加`publish`字段,用来配置更新地址和使用哪种策略作为更新服务 +在 `electron-builder.json` 中添加 `publish` 字段,用来配置更新地址和 provider。 ```json5 { publish: { - provider: 'generic', // 提供者、提供商 - channel: 'latest', // 生成yml文件的名称 - url: 'https://foo.com/', //更新地址 + provider: 'generic', + channel: 'latest', + url: 'https://foo.com/', }, } ``` -更多见 : [electron-builder.json5](https://github.com/electron-vite/electron-vite-react/blob/main/electron-builder.json) +更多详情请见:[electron-builder.json](https://github.com/electron-vite/electron-vite-react/blob/main/electron-builder.json) -#### 2. Electron更新逻辑 +#### 2. 主进程更新逻辑 -- 检测更新是否可用; -- 检测服务端的软件版本; -- 检测更新是否可用; -- 下载服务端新版软件(当更新可用); -- 安装方式; +本项目保持 `autoDownload` 关闭,因此下载需要由用户手动触发。 -更多见 : [update.ts](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) +- `check-update` 会在打包后调用 `autoUpdater.checkForUpdates()`。 +- 主进程在收到 `update-available` 或 `update-not-available` 时,会向渲染进程发送 `update-can-available`。 +- `start-download` 会开始下载更新,并把下载进度转发到渲染进程。 +- `cancel-download` 会取消当前下载,并重置取消令牌。 +- `quit-and-install` 会安装已下载的更新并重启应用。 -#### 3. Electron更新UI页面 +更多详情请见:[update.ts](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) -主要功能是:用户触发上述(2.)更新逻辑的UI页面。用户可以通过点击页面触发electron更新的不同功能。 -更多见 : [components/update/index.tsx](https://github.com/electron-vite/electron-vite-react/tree/main/src/components/update/index.tsx) +#### 3. 渲染进程 UI + +更新页面是用户触发上述流程的入口,用户通过页面按钮触发 Electron 的更新动作。 + +- 点击 **Check update** 触发 `check-update`。 +- 如果发现新版本,页面会允许用户开始或取消下载。 +- 下载完成后,页面会切换为 **Install now**。 + +更多详情请见:[components/update/index.tsx](https://github.com/electron-vite/electron-vite-react/blob/main/src/components/update/index.tsx) --- -这里建议更新触发以用户操作触发(本项目的以用户点击 **更新检测** 后触发electron更新功能) +建议通过用户操作触发更新。在本项目中,流程从用户点击 **Check update** 开始。 -关于更多使用`electron-updater`进行electron更新,见[文档](https://www.electron.build) +更多使用 `electron-updater` 的信息请参考 [官方文档](https://www.electron.build) From b5e9f69a3af52af08b1f26edd446b8441822da7e Mon Sep 17 00:00:00 2001 From: subframe7536 <1667077010@qq.com> Date: Thu, 28 May 2026 09:51:40 +0800 Subject: [PATCH 09/12] docs: make linter happy --- src/components/update/README.md | 8 ++++---- src/components/update/README.zh-CN.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/update/README.md b/src/components/update/README.md index 31eafaa6..abd9dd2d 100644 --- a/src/components/update/README.md +++ b/src/components/update/README.md @@ -8,9 +8,9 @@ English | [简体中文](README.zh-CN.md) pnpm add electron-updater ``` -### Main Flow +## Main Flow -#### 1. Configure the update server +### 1. Configure the update server Add a `publish` field to `electron-builder.json` to define the update source and provider. @@ -26,7 +26,7 @@ Add a `publish` field to `electron-builder.json` to define the update source and For more information, please refer to [electron-builder.json](https://github.com/electron-vite/electron-vite-react/blob/main/electron-builder.json) -#### 2. Main-process update logic +### 2. Main-process update logic This project keeps `autoDownload` disabled, so users start downloads manually. @@ -38,7 +38,7 @@ This project keeps `autoDownload` disabled, so users start downloads manually. For more information, please refer to [update.ts](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) -#### 3. Renderer UI +### 3. Renderer UI The update page is the user-facing entry point for the flow above. Users click the page button to trigger update actions in Electron. diff --git a/src/components/update/README.zh-CN.md b/src/components/update/README.zh-CN.md index b7e87741..7a8b89e9 100644 --- a/src/components/update/README.zh-CN.md +++ b/src/components/update/README.zh-CN.md @@ -8,9 +8,9 @@ pnpm add electron-updater ``` -### 主要流程 +## 主要流程 -#### 1. 配置更新服务 +### 1. 配置更新服务 在 `electron-builder.json` 中添加 `publish` 字段,用来配置更新地址和 provider。 @@ -26,7 +26,7 @@ pnpm add electron-updater 更多详情请见:[electron-builder.json](https://github.com/electron-vite/electron-vite-react/blob/main/electron-builder.json) -#### 2. 主进程更新逻辑 +### 2. 主进程更新逻辑 本项目保持 `autoDownload` 关闭,因此下载需要由用户手动触发。 @@ -38,7 +38,7 @@ pnpm add electron-updater 更多详情请见:[update.ts](https://github.com/electron-vite/electron-vite-react/blob/main/electron/main/update.ts) -#### 3. 渲染进程 UI +### 3. 渲染进程 UI 更新页面是用户触发上述流程的入口,用户通过页面按钮触发 Electron 的更新动作。 From 248d1c7831203463c9597d8ec4486a9eba758f2e Mon Sep 17 00:00:00 2001 From: subframe7536 <1667077010@qq.com> Date: Thu, 28 May 2026 10:01:28 +0800 Subject: [PATCH 10/12] ci: bump versions --- .github/workflows/build.yml | 8 ++++---- .github/workflows/ci.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1560fc6..2ea0fbd8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,17 +28,17 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: persist-credentials: false - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 with: version: 10 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 22 cache: pnpm @@ -52,7 +52,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: release_on_${{ matrix.os }} path: release/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6ea3703..f2f8e5c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: persist-credentials: false From c996180f252b4caec3a746bda1163d1f7fba88b6 Mon Sep 17 00:00:00 2001 From: subframe7536 <1667077010@qq.com> Date: Fri, 29 May 2026 09:56:44 +0800 Subject: [PATCH 11/12] chore: refactor update handling and improve UI elements --- electron/main/update.ts | 3 +-- src/App.tsx | 36 ++++++++++++++++----------------- src/components/update/index.tsx | 2 +- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/electron/main/update.ts b/electron/main/update.ts index f98e390a..64216895 100644 --- a/electron/main/update.ts +++ b/electron/main/update.ts @@ -1,13 +1,12 @@ import { app, ipcMain } from 'electron' -import { createRequire } from 'node:module' import { CancellationToken, + autoUpdater, type ProgressInfo, type UpdateDownloadedEvent, type UpdateInfo, } from 'electron-updater' -const { autoUpdater } = createRequire(import.meta.url)('electron-updater'); let cancellationToken = new CancellationToken() let isDownloading = false diff --git a/src/App.tsx b/src/App.tsx index 56f65d85..0dab2c88 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,24 +32,24 @@ function App() {
- + + + Vite logo + Electron logo + + + Open project repository + +
diff --git a/src/components/update/index.tsx b/src/components/update/index.tsx index e8f2d5dc..8b8f1ec3 100644 --- a/src/components/update/index.tsx +++ b/src/components/update/index.tsx @@ -107,7 +107,7 @@ const Update = () => { {updateError ? (

Error downloading the latest version.

-

{updateError.message}

+

{updateError.message}

) : updateAvailable ? (
From 8756ae660adc4dd80d2cb35332bc227abd464734 Mon Sep 17 00:00:00 2001 From: subframe7536 <1667077010@qq.com> Date: Sun, 31 May 2026 22:38:48 +0800 Subject: [PATCH 12/12] 1.0.0 --- electron/main/update.ts | 17 +++++++++-------- package.json | 8 +++++--- vite.config.ts | 18 +++++++++--------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/electron/main/update.ts b/electron/main/update.ts index 64216895..e9536171 100644 --- a/electron/main/update.ts +++ b/electron/main/update.ts @@ -1,13 +1,14 @@ import { app, ipcMain } from 'electron' -import { - CancellationToken, - autoUpdater, - type ProgressInfo, - type UpdateDownloadedEvent, - type UpdateInfo, +import type { + ProgressInfo, + UpdateDownloadedEvent, + UpdateInfo, } from 'electron-updater' +// Pure cjs module does not support named exports, so we need to import the default export and access the autoUpdater property +import updater from 'electron-updater' -let cancellationToken = new CancellationToken() +const autoUpdater = updater.autoUpdater +let cancellationToken = new updater.CancellationToken() let isDownloading = false export function update(win: Electron.BrowserWindow) { @@ -70,7 +71,7 @@ export function update(win: Electron.BrowserWindow) { // Cancel downloading ipcMain.handle('cancel-download', () => { cancellationToken.cancel() - cancellationToken = new CancellationToken(); + cancellationToken = new updater.CancellationToken(); }) // Install now diff --git a/package.json b/package.json index 7db950be..e01072a2 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,9 @@ "test:e2e": "pnpm pretest && playwright test", "typecheck": "tsc --noEmit" }, + "dependencies": { + "electron-updater": "^6.8.3" + }, "devDependencies": { "@playwright/test": "^1.60.0", "@tailwindcss/vite": "^4.3.0", @@ -24,14 +27,13 @@ "@vitejs/plugin-react": "^6.0.2", "electron": "^42.1.0", "electron-builder": "^26.8.1", - "electron-updater": "^6.8.3", "react": "^19.2.6", "react-dom": "^19.2.6", "tailwindcss": "^4.3.0", "typescript": "^6.0.3", "vite": "^8.0.14", - "vite-plugin-electron": "1.0.0-beta.13", - "vite-plugin-electron-renderer": "1.0.0-beta.9", + "vite-plugin-electron": "^1.0.0", + "vite-plugin-electron-renderer": "^1.0.0", "vitest": "^4.1.7" }, "packageManager": "pnpm@10.32.1" diff --git a/vite.config.ts b/vite.config.ts index 06d79be3..977969f6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,7 +3,8 @@ import path from 'node:path' import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' -import electron from 'vite-plugin-electron/simple' +import { electronSimple } from 'vite-plugin-electron/multi-env' +import { notBundle } from 'vite-plugin-electron/plugin' import pkg from './package.json' const external = Object.keys( @@ -27,11 +28,11 @@ export default defineConfig(({ command }) => { plugins: [ react(), tailwindcss(), - electron({ + electronSimple({ main: { - // Shortcut of `build.lib.entry` - entry: 'electron/main/index.ts', - vite: { + input: 'electron/main/index.ts', + plugins: [notBundle()], + options: { build: { sourcemap, minify: isBuild, @@ -43,10 +44,9 @@ export default defineConfig(({ command }) => { }, }, preload: { - // Shortcut of `build.rolldownOptions.input`. - // Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`. input: 'electron/preload/index.ts', - vite: { + plugins: [notBundle()], + options: { build: { sourcemap: sourcemap ? 'inline' : undefined, // #332 minify: isBuild, @@ -60,7 +60,7 @@ export default defineConfig(({ command }) => { // Polyfill the Electron and Node.js API for Renderer process. // If you want use Node.js in Renderer process, the `nodeIntegration` needs to be enabled in the Main process. // See 👉 https://github.com/electron-vite/vite-plugin-electron-renderer - renderer: {}, + // renderer: {}, }), ], clearScreen: false,