Compare commits
272 Commits
v1.0.0
...
02d4f9dff6
Author | SHA1 | Date | |
---|---|---|---|
|
02d4f9dff6 | ||
|
b4cf3b4c5f | ||
|
69e11a78ac | ||
|
77da3cf339 | ||
|
9dbe2184dd | ||
|
836bf7393a | ||
|
87880184b5 | ||
|
7a5dfc0439 | ||
|
8d5beec179 | ||
|
74507b17e0 | ||
|
828e987119 | ||
|
49d36516c1 | ||
|
3d3daf1db5 | ||
|
516234dce5 | ||
|
a551ff2f3a | ||
|
c74834b35e | ||
|
1017a297e8 | ||
|
cfd0fff1ed | ||
|
546eeb29d9 | ||
|
3b766bd0b7 | ||
|
b1b5ee4d24 | ||
|
c96459cf7a | ||
|
6fbe501537 | ||
|
8171344a13 | ||
|
8720ba1f8a | ||
|
efaa80cd11 | ||
|
85bcc91b66 | ||
|
0bb17a61a8 | ||
|
b2f8887fab | ||
|
40b8c08cf9 | ||
|
025addbceb | ||
|
3544247016 | ||
|
82e54afe81 | ||
|
a7b905980f | ||
|
efbe4d9645 | ||
|
d9c4d3502a | ||
|
bafa8ae8ae | ||
|
67bbe33498 | ||
|
919be49609 | ||
|
7f003bd95e | ||
|
afc83731d5 | ||
|
0776ebea38 | ||
|
f9fee2e62f | ||
|
722298fd6e | ||
|
89d8730352 | ||
|
e522b5bbc6 | ||
|
5a3c6c7d23 | ||
|
5e7c54a90d | ||
|
c43f6484b9 | ||
|
f55466cdad | ||
|
77cda603be | ||
|
cd8fea0c28 | ||
|
b361d2af2c | ||
|
1fd0096b81 | ||
|
f652719e90 | ||
|
a55e7c79e5 | ||
|
df13b52039 | ||
|
ffe6fc651f | ||
|
7c87cd6f53 | ||
|
cd22f31c1c | ||
|
6a84e66974 | ||
|
72ee89d06d | ||
|
2512ad1652 | ||
|
b2ef751ada | ||
|
e89fa00a58 | ||
|
ed99285f83 | ||
|
d2e4d619d1 | ||
|
0e04ff4569 | ||
|
f2eff45e91 | ||
|
fa0911df3d | ||
|
1991c8651c | ||
|
5c841f5e5f | ||
|
dfa7f855f9 | ||
|
4273144935 | ||
|
a6d8b6febd | ||
|
17142a9ec8 | ||
|
d08921d6f6 | ||
|
fe52c1f937 | ||
|
02e38c52b9 | ||
|
2f0f044772 | ||
|
9bd69b156b | ||
|
95a83b3918 | ||
|
7c3b8929de | ||
|
db2e830dfd | ||
|
f163e0ca1b | ||
|
587ba97c86 | ||
|
a15028ad68 | ||
|
8a67ac6954 | ||
|
4b46601879 | ||
|
de234a1fd5 | ||
|
37a8a1cf1c | ||
|
5123ad6597 | ||
|
2d1987c3a2 | ||
|
45abf13de3 | ||
|
1bb2daf798 | ||
|
00c288bbe7 | ||
|
e9ba2ecace | ||
|
8b6aedbaf1 | ||
|
ebb226e917 | ||
|
5b21e8f189 | ||
|
642403f478 | ||
|
f53dba80a3 | ||
|
f50255be34 | ||
|
2d895140f2 | ||
|
437c325752 | ||
|
09181510d2 | ||
|
1550a01ae9 | ||
|
55106f98fc | ||
|
b4678fe50c | ||
|
838c0ebb35 | ||
|
7b27d3546b | ||
|
74ce72367e | ||
|
e27c6c371d | ||
|
6aaa47eac3 | ||
|
05a9c08770 | ||
|
6d2c8494b6 | ||
|
cbf0f835d2 | ||
|
2437966661 | ||
|
926faffd3e | ||
|
a01107e35c | ||
|
2b439adbe4 | ||
|
daa441e773 | ||
|
5e2c5d0a74 | ||
|
039f66f2cb | ||
|
b26af020db | ||
|
7c41560050 | ||
|
153d974f13 | ||
|
940033deff | ||
|
b74293ac2d | ||
|
c1e32b347a | ||
|
641e061d90 | ||
|
ec630d408a | ||
|
fbbe3ec649 | ||
|
ee24811d35 | ||
|
0fadab5c23 | ||
|
aa62a680a3 | ||
|
644a9f6b3e | ||
|
039c272fb2 | ||
|
0d1fe635d3 | ||
|
aec9a41f66 | ||
|
858ea14cfa | ||
|
c632ab866b | ||
|
bfe73fea55 | ||
|
9ef141fc66 | ||
|
6005d6f7ea | ||
|
3041cc6d85 | ||
|
f877b5b1c8 | ||
|
792b87a3a5 | ||
|
8b740153f2 | ||
|
6de4c5cce7 | ||
|
20674739ef | ||
|
a2d023acf9 | ||
|
4d70a51267 | ||
|
7e1fd6e1d7 | ||
|
a7456a1963 | ||
|
b7dfc8fec4 | ||
|
d22104ed12 | ||
|
0406b1bf67 | ||
|
04f7bb5f01 | ||
|
0c052ef9ef | ||
|
e255257d4a | ||
|
2cbc705f89 | ||
|
e657454cd7 | ||
|
e8aea13b34 | ||
|
facdbbab57 | ||
|
d79f8fc16c | ||
|
66c380af90 | ||
|
7ff8d82600 | ||
|
697279df45 | ||
|
bbf38e3a16 | ||
|
27d342aa4a | ||
|
37985c568c | ||
|
9a5f829156 | ||
|
e5e341b041 | ||
|
8751bf1d20 | ||
|
dd8419140d | ||
|
38b5092832 | ||
|
fe9ad9dd2a | ||
|
07a342951a | ||
|
a4eb6a4fc9 | ||
|
14c018c09d | ||
|
2abbae8dc7 | ||
|
2dae540bce | ||
|
c8eabac9cf | ||
|
09eb5e3104 | ||
|
045a7ec91b | ||
|
825df2bbb9 | ||
|
bc6438e4c8 | ||
|
4a2f86a43e | ||
|
befd9c4717 | ||
|
77acb0a5b0 | ||
|
0bcaa99bda | ||
|
760c69ecef | ||
|
5241ef8e2d | ||
|
0a17a2a1b0 | ||
|
68fe4bf3b7 | ||
|
3c8c4147c8 | ||
|
815a97250b | ||
|
c4dec0b031 | ||
|
e51eccb41b | ||
|
f65ed8f486 | ||
|
8c846a34c0 | ||
|
899d20c70c | ||
|
a24223d22d | ||
|
0827c0cdeb | ||
|
2319e339ec | ||
|
67cf1e7f31 | ||
|
ef7022696b | ||
|
e0b35bc4e8 | ||
|
c861aaeb33 | ||
|
f129465d28 | ||
|
4672552a8e | ||
|
f094cbac34 | ||
|
54b44eb4b6 | ||
|
6527ee03dc | ||
|
042f7af691 | ||
|
d4800fa089 | ||
|
1294005455 | ||
|
e1300d5ae5 | ||
|
1065ca9c1f | ||
|
dae6924546 | ||
|
6728af59d5 | ||
|
2dffca3b32 | ||
|
08fcc99b38 | ||
|
1576ccfb05 | ||
|
d10b5630ac | ||
|
85f823bd6a | ||
|
95c247d07e | ||
|
e0b33df336 | ||
|
7ba9a5a368 | ||
|
b6ffbf7fce | ||
|
de93a6526a | ||
|
5416e33b85 | ||
|
82551cf63b | ||
|
c4094b59bb | ||
|
16f915075d | ||
|
aa0e9b62fe | ||
|
ea32555e20 | ||
|
ec42dafe03 | ||
|
ecbed8a8cd | ||
|
c1675af905 | ||
|
5387510adc | ||
|
d6f1bdc9ab | ||
|
59a53b3ab8 | ||
|
711599f332 | ||
|
1a281c3511 | ||
|
22c8dcf8e6 | ||
|
ee96bdd2c5 | ||
|
fcce0884fa | ||
|
6c7e947871 | ||
|
f41ddead18 | ||
|
e2798e025d | ||
|
768c2b9385 | ||
|
6f18c6fbfb | ||
|
b6a24b71eb | ||
|
c9188fd2f9 | ||
|
6fe969ffcb | ||
|
f2881a3137 | ||
|
b2b3a5d63c | ||
|
d9b453e194 | ||
|
2cec8d810c | ||
|
58c4898929 | ||
|
7d2e44611f | ||
|
50256e53c3 | ||
|
02e36c856a | ||
|
0d8e24ca61 | ||
|
6bb8a182e5 | ||
|
b5c358def9 | ||
|
a244c3dea6 | ||
|
7a7fc20c23 | ||
|
a99985edde | ||
|
14b30b0e32 |
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,5 +1,6 @@
|
||||
---
|
||||
name: Bug_Report
|
||||
|
||||
name: 🐞 Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[Bug] the title of bug report"
|
||||
labels: bug
|
||||
@@ -7,23 +8,4 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### What did I do
|
||||
|
||||
Something you have done.
|
||||
|
||||
#### What happened
|
||||
|
||||
Get an error.
|
||||
|
||||
#### Expected
|
||||
|
||||
What is the expected result.
|
||||
|
||||
#### Environment
|
||||
|
||||
- OS: OS version, e.g. macOS Big Sur 11.1
|
||||
- electron-vue-vite version (or commit hash), e.g. v1.0.0
|
||||
|
||||
#### More detail
|
||||
|
||||
More detail like screenshot
|
||||
#### Describe the bug
|
||||
|
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: Feature_Want
|
||||
about: Suggest an idea for electron-vue-vite
|
||||
title: "[Feature] the title of Feature_Want report"
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Motivation
|
||||
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
#### Detailed design
|
||||
|
||||
Describe the solution you'd like, a clear and concise description of what you want to happen.
|
||||
|
||||
#### Alternatives
|
||||
|
||||
Describe alternatives you've considered, what other designs have been considered? What is the impact of not doing this?
|
||||
|
||||
#### More detail (optional)
|
||||
|
||||
Add any other context or screenshots about the feature request here.
|
12
.github/ISSUE_TEMPLATE/help_wanted.md
vendored
@@ -1,16 +1,10 @@
|
||||
---
|
||||
name: Help_Wanted
|
||||
name: 🥺 Help wanted
|
||||
about: Confuse about the use of electron-vue-vite
|
||||
title: "[Help] the title of Help_Want report"
|
||||
title: "[Help] the title of help wanted report"
|
||||
labels: help wanted
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
#### Describe the problem you Confuse
|
||||
|
||||
A clear and concise description of what you are confusing about.
|
||||
|
||||
#### More detail (optional)
|
||||
|
||||
Add any other context or screenshots.
|
||||
#### Describe the problem you confuse
|
||||
|
29
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,25 +1,12 @@
|
||||
### PR Checklist
|
||||
<!-- Thank you for contributing! -->
|
||||
|
||||
#### What is the current behavior?
|
||||
### Description
|
||||
|
||||
Please describe the current behavior and link to a relevant issue.
|
||||
<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->
|
||||
|
||||
#### Issue Number
|
||||
### What is the purpose of this pull request? <!-- (put an "X" next to an item) -->
|
||||
|
||||
Example: \#123
|
||||
|
||||
#### What is the new behavior?
|
||||
|
||||
Please describe the new behavior or provide screenshots.
|
||||
|
||||
#### Does this PR introduce a breaking change?
|
||||
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
#### Specific Instructions
|
||||
|
||||
Are there any specific instructions or things that should be known prior to review?
|
||||
|
||||
#### Other information
|
||||
- [ ] Bug fix
|
||||
- [ ] New Feature
|
||||
- [ ] Documentation update
|
||||
- [ ] Other
|
||||
|
47
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**.spec.js"
|
||||
- ".idea"
|
||||
- ".vscode"
|
||||
- ".dockerignore"
|
||||
- "Dockerfile"
|
||||
- ".gitignore"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/build.yml"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build Release Files
|
||||
run: npm run build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release_on_${{ matrix. os }}
|
||||
path: release/
|
||||
retention-days: 5
|
81
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
job1:
|
||||
name: Check Not Allowed File Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
markdown_change: ${{ steps.filter_markdown.outputs.change }}
|
||||
markdown_files: ${{ steps.filter_markdown.outputs.change_files }}
|
||||
steps:
|
||||
|
||||
- name: Check Not Allowed File Changes
|
||||
uses: dorny/paths-filter@v2
|
||||
id: filter_not_allowed
|
||||
with:
|
||||
list-files: json
|
||||
filters: |
|
||||
change:
|
||||
- 'package-lock.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
|
||||
# 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
|
||||
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',
|
||||
'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 <file name>\`. 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({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.number,
|
||||
body: reviewMessage,
|
||||
})
|
||||
workflowFailMessage = `${workflowFailMessage} Please see ${createdComment.data.html_url} for details.`
|
||||
} catch(err) {
|
||||
console.log("Error creating comment.", err)
|
||||
}
|
||||
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: Lint markdown
|
||||
run: npx markdownlint-cli ${{ needs.job1.outputs.markdown_files }} --ignore node_modules
|
77
.github/workflows/release.yml
vendored
@@ -1,77 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
release:
|
||||
branches: [main]
|
||||
types:
|
||||
- published
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**.spec.js"
|
||||
- ".idea"
|
||||
- ".vscode"
|
||||
- ".dockerignore"
|
||||
- "Dockerfile"
|
||||
- ".gitignore"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/release.yml"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: "bash"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# see more environment https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on
|
||||
runs-on: [ubuntu-20.04]
|
||||
# https://www.electron.build/multi-platform-build#provided-docker-images
|
||||
container: electronuserland/builder:wine
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ELECTRON_BUILDER_CACHE: "/root/.cache/electron-builder"
|
||||
ELECTRON_CACHE: "/root/.cache/electron"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["14"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
# - name: Run tests
|
||||
# run: npm run test
|
||||
|
||||
# - name: Build dependencies
|
||||
# run: npm run build
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: upload-artifact
|
||||
# path: |
|
||||
# release/electron-vue-vite*.exe
|
||||
# release/electron-vue-vite*.AppImage
|
||||
# release/electron-vue-vite*.snap
|
||||
|
||||
# https://github.com/marketplace/actions/electron-builder-action
|
||||
- name: Compile & Release Electron app
|
||||
uses: samuelmeuli/action-electron-builder@v1
|
||||
with:
|
||||
build_script_name: prebuild
|
||||
args: --config electron-builder.json5
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release: ${{ startsWith(github.ref, 'refs/tags/v') && github.event_name == 'push' && needs.get_metadata.outputs.branch == 'main'}}
|
||||
max_attempts: 3
|
32
.gitignore
vendored
@@ -1,10 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
.debug.env
|
||||
|
||||
tmp
|
||||
**/.tmp
|
||||
dist-electron
|
||||
release
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/.debug.env
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# lockfile
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
// "pre-commit": "npx nano-staged",
|
||||
};
|
21
.vscode/.debug.script.mjs
vendored
@@ -1,14 +1,23 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { createRequire } from 'module'
|
||||
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))
|
||||
|
||||
// write .debug.env
|
||||
const envContent = Object.entries(pkg.env).map(([key, val]) => `${key}=${val}`)
|
||||
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
|
||||
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
|
||||
|
||||
// bootstrap
|
||||
import('../scripts/watch.mjs?debug=vscode')
|
||||
spawn(
|
||||
// TODO: terminate `npm run dev` when Debug exits.
|
||||
process.platform === 'win32' ? 'npm.cmd' : 'npm',
|
||||
['run', 'dev'],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
|
||||
},
|
||||
)
|
||||
|
19
.vscode/launch.json
vendored
@@ -6,7 +6,7 @@
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Debug App",
|
||||
"preLaunchTask": "start .debug.script.mjs",
|
||||
"preLaunchTask": "Before Debug",
|
||||
"configurations": [
|
||||
"Debug Main Process",
|
||||
"Debug Renderer Process"
|
||||
@@ -22,7 +22,7 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Main Process",
|
||||
"type": "pwa-node",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
@@ -30,15 +30,24 @@
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"--remote-debugging-port=9229",
|
||||
"${workspaceRoot}/dist/main/index.cjs"
|
||||
"."
|
||||
],
|
||||
"envFile": "${workspaceFolder}/.vscode/.debug.env"
|
||||
"envFile": "${workspaceFolder}/.vscode/.debug.env",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Debug Renderer Process",
|
||||
"port": 9229,
|
||||
"request": "attach",
|
||||
"type": "pwa-chrome"
|
||||
"type": "chrome",
|
||||
"timeout": 60000,
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"${workspaceRoot}/node_modules/**",
|
||||
"${workspaceRoot}/dist-electron/**",
|
||||
// Skip files in host(VITE_DEV_SERVER_URL)
|
||||
"http://127.0.0.1:3344/**"
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
13
.vscode/settings.json
vendored
@@ -1,4 +1,13 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
}
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"/*electron-builder.json5",
|
||||
"/*electron-builder.json"
|
||||
],
|
||||
"url": "https://json.schemastore.org/electron-builder"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
21
.vscode/tasks.json
vendored
@@ -4,11 +4,28 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "start .debug.script.mjs",
|
||||
"label": "Before Debug",
|
||||
"type": "shell",
|
||||
"command": "node .vscode/.debug.script.mjs",
|
||||
"isBackground": true,
|
||||
"problemMatcher": []
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"fileLocation": "relative",
|
||||
"pattern": {
|
||||
// TODO: correct "regexp"
|
||||
"regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$",
|
||||
"file": 1,
|
||||
"line": 3,
|
||||
"column": 4,
|
||||
"code": 5,
|
||||
"message": 6
|
||||
},
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": "^.*VITE v.* ready in \\d* ms.*$",
|
||||
"endsPattern": "^.*\\[startup\\] Electron App.*$"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
24
CHANGELOG.md
@@ -1,9 +1,29 @@
|
||||
## 2022-10-03
|
||||
|
||||
## 22-01-27
|
||||
[v2.1.0](https://github.com/electron-vite/electron-vite-vue/pull/267)
|
||||
|
||||
- `vite-electron-plugin` is Fast, and WYSIWYG. 🌱
|
||||
- last-commit: db2e830 v2.1.0: use `vite-electron-plugin` instead `vite-plugin-electron`
|
||||
|
||||
## 2022-06-04
|
||||
|
||||
[v2.0.0](https://github.com/electron-vite/electron-vite-vue/pull/156)
|
||||
|
||||
- 🖖 Based on the `vue-ts` template created by `npm create vite`, integrate `vite-plugin-electron`
|
||||
- ⚡️ More simplify, is in line with Vite project structure
|
||||
- last-commit: a15028a (HEAD -> main) feat: hoist `process.env`
|
||||
|
||||
## 2022-01-30
|
||||
|
||||
[v1.0.0](https://github.com/electron-vite/electron-vite-vue/releases/tag/v1.0.0)
|
||||
|
||||
- ⚡️ Main、Renderer、preload, all built with vite
|
||||
|
||||
## 2022-01-27
|
||||
- Refactor the scripts part.
|
||||
- Remove `configs` directory.
|
||||
|
||||
## 21-11-11
|
||||
## 2021-11-11
|
||||
- Refactor the project. Use vite.config.ts build `Main-process`, `Preload-script` and `Renderer-process` alternative rollup.
|
||||
- Scenic `Vue>=3.2.13`, `@vue/compiler-sfc` is no longer necessary.
|
||||
- If you prefer Rollup, Use rollup branch.
|
||||
|
24
Dockerfile
@@ -1,24 +0,0 @@
|
||||
# use the version that corresponds to your electron version
|
||||
FROM node:14.16
|
||||
|
||||
LABEL NAME="electron-wrapper"
|
||||
LABEL RUN="docker run --rm -it electron-wrapper bash"
|
||||
|
||||
# install electron dependencies or more if your library has other dependencies
|
||||
RUN apt-get update && apt-get install \
|
||||
git libx11-xcb1 libxcb-dri3-0 libxtst6 libnss3 libatk-bridge2.0-0 libgtk-3-0 libxss1 libasound2 \
|
||||
-yq --no-install-suggests --no-install-recommends \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# copy the source into /app
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN chown -R node /app
|
||||
|
||||
# install node modules and perform an electron rebuild
|
||||
USER node
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
USER node
|
||||
CMD bash
|
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 草鞋没号
|
||||
Copyright (c) 2023 草鞋没号
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
89
README.md
@@ -1,22 +1,22 @@
|
||||
# electron-vite-vue
|
||||
|
||||
[](https://github.com/vitejs/awesome-vite)
|
||||
[](https://app.netlify.com/sites/electron-vite/deploys)
|
||||

|
||||

|
||||

|
||||
🥳 Really simple `Electron` + `Vue` + `Vite` boilerplate.
|
||||
|
||||
<!-- [](https://github.com/vitejs/awesome-vite) -->
|
||||
<!-- [](https://app.netlify.com/sites/electron-vite/deploys) -->
|
||||
<!-- [](https://github.com/electron-vite/electron-vite-vue/blob/main/LICENSE) -->
|
||||
<!-- [](https://github.com/electron-vite/electron-vite-vue) -->
|
||||
<!-- [](https://github.com/electron-vite/electron-vite-vue) -->
|
||||
[](https://github.com/electron-vite/electron-vite-vue/actions/workflows/build.yml)
|
||||
[](https://discord.gg/sRqjYpEAUK)
|
||||
|
||||
**English | [简体中文](README.zh-CN.md)**
|
||||
|
||||
🥳 Real simple `Electron` + `Vue` + `Vite` boilerplate.
|
||||
|
||||
## Overview
|
||||
## Features
|
||||
|
||||
📦 Out of the box
|
||||
💪 Support C/C++ addons
|
||||
🔩 Support Use Electron、Node.js API in Renderer-process
|
||||
🌱 Simple directory structure,real flexible
|
||||
🎯 Based on the official [template-vue-ts](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-vue-ts), less invasive
|
||||
🌱 Extensible, really simple directory structure
|
||||
💪 Support using Node.js API in Electron-Renderer
|
||||
🔩 Support C/C++ native addons
|
||||
🖥 It's easy to implement multiple windows
|
||||
|
||||
## Quick Start
|
||||
@@ -27,48 +27,47 @@ npm create electron-vite
|
||||
|
||||
<!-- [](https://asciinema.org/a/483731) -->
|
||||
|
||||

|
||||

|
||||
|
||||
## Debug
|
||||
|
||||

|
||||

|
||||
|
||||
## Directory
|
||||
|
||||
A `dist` folder will be generated everytime when `dev` or `build` command is executed. File structure of `dist` is identical to the `packages` directory to avoid any potential path calculation errors.
|
||||
|
||||
```tree
|
||||
├── dist Will be generated following the structure of "packages" directory
|
||||
| ├── main
|
||||
| ├── preload
|
||||
| └── renderer
|
||||
|
|
||||
├── scripts
|
||||
| ├── build.mjs Build script -> npm run build
|
||||
| └── watch.mjs Develop script -> npm run dev
|
||||
|
|
||||
├── packages
|
||||
| ├── main Main-process source code
|
||||
| | └── vite.config.ts
|
||||
| ├── preload Preload-script source code
|
||||
| | └── vite.config.ts
|
||||
| └── renderer Renderer-process source code
|
||||
| └── vite.config.ts
|
||||
```diff
|
||||
+ ├─┬ electron
|
||||
+ │ ├─┬ main
|
||||
+ │ │ └── index.ts entry of Electron-Main
|
||||
+ │ └─┬ preload
|
||||
+ │ └── index.ts entry of Preload-Scripts
|
||||
├─┬ src
|
||||
│ └── main.ts entry of Electron-Renderer
|
||||
├── index.html
|
||||
├── package.json
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
## List the modules you may use as far as possible
|
||||
<!--
|
||||
## Be aware
|
||||
|
||||
Used in `Main-process` 👉 [electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate)
|
||||
🚨 By default, this template integrates Node.js in the Renderer process. If you don't need it, you just remove the option below. [Because it will modify the default config of Vite](https://github.com/electron-vite/vite-plugin-electron-renderer#config-presets-opinionated).
|
||||
|
||||
Used in `Renderer-process` 👉 [electron-vite-boilerplate/tree/nodeIntegration](https://github.com/caoxiemeihao/electron-vite-boilerplate/tree/nodeIntegration)
|
||||
```diff
|
||||
# vite.config.ts
|
||||
|
||||
**ES Modules**
|
||||
export default {
|
||||
plugins: [
|
||||
- // Use Node.js API in the Renderer-process
|
||||
- renderer({
|
||||
- nodeIntegration: true,
|
||||
- }),
|
||||
],
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
- [execa](https://www.npmjs.com/package/execa)
|
||||
- [node-fetch](https://www.npmjs.com/package/node-fetch)
|
||||
- [file-type](https://www.npmjs.com/package/file-type)
|
||||
## FAQ
|
||||
|
||||
**Native Addons(C/C++)**
|
||||
|
||||
- [sqlite3](https://www.npmjs.com/package/sqlite3)
|
||||
- [serialport](https://www.npmjs.com/package/serialport)
|
||||
- [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)
|
||||
|
@@ -1,71 +0,0 @@
|
||||
# electron-vite-vue
|
||||
|
||||
[](https://github.com/vitejs/awesome-vite)
|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
**[English](README.md) | 简体中文**
|
||||
|
||||
🥳 Electron + Vite + Vue 整合模板 -- **结构简单,容易上手!**
|
||||
|
||||
## 概述
|
||||
|
||||
📦 开箱即用
|
||||
💪 支持 C/C++ 模块
|
||||
🔩 支持在渲染进程中使用 Electron、Node.js API
|
||||
🌱 结构清晰,可塑性强
|
||||
🖥 很容易实现多窗口
|
||||
|
||||
## 快速开始
|
||||
|
||||
```sh
|
||||
npm create electron-vite
|
||||
```
|
||||
|
||||
<!-- [](https://asciinema.org/a/483731) -->
|
||||
|
||||

|
||||
|
||||
## 调试
|
||||
|
||||

|
||||
|
||||
## 目录结构
|
||||
|
||||
```tree
|
||||
├── dist 构建后,根据 packages 目录生成
|
||||
| ├── main
|
||||
| ├── preload
|
||||
| └── renderer
|
||||
|
|
||||
├── scripts
|
||||
| ├── build.mjs 项目开发脚本 npm run build
|
||||
| └── watch.mjs 项目开发脚本 npm run dev
|
||||
|
|
||||
├── packages
|
||||
| ├── main 主进程源码
|
||||
| | └── vite.config.ts
|
||||
| ├── preload 预加载脚本源码
|
||||
| | └── vite.config.ts
|
||||
| └── renderer 渲染进程源码
|
||||
| └── vite.config.ts
|
||||
```
|
||||
|
||||
## 一些常见的案例
|
||||
|
||||
在 Main-process 中使用 👉 [electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate)
|
||||
|
||||
在 Renderer-process 中使用 👉 [electron-vite-boilerplate/tree/nodeIntegration](https://github.com/caoxiemeihao/electron-vite-boilerplate/tree/nodeIntegration)
|
||||
|
||||
**ES Modules**
|
||||
|
||||
- [execa](https://www.npmjs.com/package/execa)
|
||||
- [node-fetch](https://www.npmjs.com/package/node-fetch)
|
||||
- [file-type](https://www.npmjs.com/package/file-type)
|
||||
|
||||
**Native Addons(C/C++)**
|
||||
|
||||
- [sqlite3](https://www.npmjs.com/package/sqlite3)
|
||||
- [serialport](https://www.npmjs.com/package/serialport)
|
@@ -2,12 +2,14 @@
|
||||
* @see https://www.electron.build/configuration/configuration
|
||||
*/
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
|
||||
"appId": "YourAppID",
|
||||
"asar": true,
|
||||
"asar": false,
|
||||
"directories": {
|
||||
"output": "release/${version}"
|
||||
},
|
||||
"files": [
|
||||
"dist-electron",
|
||||
"dist"
|
||||
],
|
||||
"mac": {
|
||||
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 3.3 MiB |
11
electron/electron-env.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/// <reference types="vite-plugin-electron/electron-env" />
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
VSCODE_DEBUG?: 'true'
|
||||
DIST_ELECTRON: string
|
||||
DIST: string
|
||||
/** /dist/ or /public/ */
|
||||
PUBLIC: string
|
||||
}
|
||||
}
|
152
electron/main/gpt/batchApplication.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import type { Browser, Page } from 'puppeteer'
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
import { clog } from "../tools";
|
||||
import { awaitWrap, randomNum } from '../tools';
|
||||
import login from '../login'
|
||||
import Mock from 'mockjs'
|
||||
import axios from 'axios'
|
||||
import { SocksProxyAgent } from 'socks-proxy-agent'
|
||||
import proxyChain from 'proxy-chain'
|
||||
// const proxyChain = require('proxy-chain');
|
||||
|
||||
puppeteer.use(StealthPlugin())
|
||||
|
||||
|
||||
function getProxy(options = {} as any) {
|
||||
const log = clog(options)
|
||||
log('开始获取代理ip')
|
||||
return axios.get('http://api.proxy.ipidea.io/getBalanceProxyIp?num=1&return_type=json&lb=1&sb=0&flow=1®ions=&protocol=socks5', {
|
||||
// return axios.get('https://www.miyaip.com/api/ProxyGenerage/PublicGenerateProxy?country=us&city=jaffrey®ion=nh&num=1&apiSwitch=0&mealType=2&genType=2&username=nmfk549724@163.com&secret=E3BCBmiyaipC23358C250F5', {
|
||||
// return axios.get('http://api.tianqiip.com/getip?secret=loy0r7fpmnlshm8l&num=1&type=json&port=3&time=3&mr=1&sign=5f73ab58ad7ab40346311014bef59b79', {
|
||||
timeout: 10 * 1000,
|
||||
}).then(res => {
|
||||
|
||||
// // const [user, pass] = res.data.split(':')
|
||||
// // return { user, pass, ip, port }
|
||||
// // // return axios.get('https://www.miyaip.com/api/ProxyGenerage/PublicGenerateProxy?country=us&city=cairo®ion=ny&num=1&apiSwitch=0&mealType=2&genType=2&username=nmfk549724@163.com&secret=E3BCBmiyaipC23358C250F5').then(res => {
|
||||
// const [ip, port] = res.data.replace(/(\n|\r\s)/g, '').split(':')
|
||||
// console.log({ ip, port, })
|
||||
// // const [ip, pory, user, pass] = res.data.split(':')
|
||||
return res.data.data[0]
|
||||
})
|
||||
return Promise.resolve({
|
||||
ip: '43.130.10.70',
|
||||
port: '22993',
|
||||
// user: '1E783B07miyaip489C251B3FA7',
|
||||
// pass: '1g7E3M4U5w03vO'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取组织id
|
||||
async function getOrgId(page: Page, options: any) {
|
||||
const log = clog(options)
|
||||
log('准备进入组织页面')
|
||||
await page.goto('https://platform.openai.com/account/org-settings')
|
||||
await page.waitForSelector('input')
|
||||
log('开始获取组织id')
|
||||
const orgId = await page.$$eval('input', (inputs: HTMLInputElement[]) => inputs?.[1].value || '')
|
||||
log('获取组织id成功', { orgId })
|
||||
return orgId
|
||||
}
|
||||
|
||||
// 申请
|
||||
export async function application(page: Page, options = {} as any) {
|
||||
const log = clog(options)
|
||||
|
||||
log('准备进入申请页面')
|
||||
await page.goto('https://openai.com/waitlist/gpt-4-api')
|
||||
|
||||
log('等待出现输入框')
|
||||
await page.waitForSelector('input')
|
||||
|
||||
log('开始申请')
|
||||
|
||||
const f = Mock.mock('@first')
|
||||
const l = Mock.mock('@last')
|
||||
console.log(f, l);
|
||||
|
||||
await page.type('#firstname', f)
|
||||
await page.type('#lastname', l)
|
||||
await page.type('#email', options.user)
|
||||
await page.type('#organizationId', options.orgId)
|
||||
|
||||
const i = randomNum(1, 4)
|
||||
|
||||
log('选择申请类型', { i })
|
||||
page.evaluate((i) => {
|
||||
document.querySelector('#primaryUse').nextSibling.childNodes[i].childNodes[1].click()
|
||||
}, i)
|
||||
|
||||
log('等待 gpt 生成描述')
|
||||
const desc = await generateDescription(i)
|
||||
await page.type('#ideas', desc)
|
||||
|
||||
log('点击提交申请')
|
||||
const isSuccess = await page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
|
||||
if (target) target.click()
|
||||
return !!target
|
||||
}, 'button', 'Join waitlist')
|
||||
|
||||
if (isSuccess) {
|
||||
log('提交成功,等待 gpt 返回结果')
|
||||
console.log('isSuccess', isSuccess)
|
||||
await page.waitForSelector('button[type="submit"] + .ui-richtext')
|
||||
const text = await page.evaluate(() => document.querySelector('button[type="submit"] + .ui-richtext').textContent)
|
||||
log('gpt 返回已结果', { result: text })
|
||||
}
|
||||
|
||||
|
||||
// await page.click('form[data-gtm-form-interact-id] button[type="submit"]')
|
||||
}
|
||||
|
||||
|
||||
// 生成描述
|
||||
export async function generateDescription(desIndex = 0): Promise<string> {
|
||||
const types = {
|
||||
1: 'Build a new product',
|
||||
2: 'Integrate into an existing product',
|
||||
3: 'General exploration of capabilities',
|
||||
4: 'Academic research',
|
||||
}
|
||||
|
||||
return axios({
|
||||
url: 'https://api.openai-proxy.com/v1/chat/completions',
|
||||
method: 'post',
|
||||
headers: {
|
||||
Authorization: `Bearer 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`
|
||||
},
|
||||
data: {
|
||||
max_tokens: 1024,
|
||||
model: 'gpt-3.5-turbo',
|
||||
messages: [
|
||||
{ "role": "user", "content": `我在申请gpt4.0,我的类型是 ${types[desIndex]},不要解释,给我一段申请的理由,以我的身份,以英语形式发给我,结尾不要带任何名字或位置,三四句话就行` },
|
||||
]
|
||||
},
|
||||
timeout: 1000 * 30,
|
||||
}).then(res => {
|
||||
console.log('gpt 生成描述', res.data.choices[0].message.content)
|
||||
return res.data.choices[0].message.content
|
||||
})
|
||||
}
|
||||
|
||||
export async function batchApplication(options) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'gpt-batch-4.0' })
|
||||
|
||||
const [error, [page, browser]] = await awaitWrap(login.openai(options))
|
||||
if (error) return log('登录失败', { error, ident: 'gpt-batch-4.0' })
|
||||
|
||||
await page.waitForSelector('.ovr-section')
|
||||
|
||||
const orgId = await getOrgId(page, options)
|
||||
options.orgId = orgId
|
||||
console.log('orgId', orgId)
|
||||
|
||||
await application(page, options)
|
||||
|
||||
browser.close()
|
||||
}
|
48
electron/main/gpt/getLink.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { clog } from "../tools";
|
||||
import login from "../login";
|
||||
|
||||
export async function getLink(options) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'gpt-link', ...options })
|
||||
const [page, browser] = await login.chatgpt({ ...options, changeUS: false })
|
||||
|
||||
// await page.waitForTimeout(500)
|
||||
|
||||
await page.waitForSelector('body > div.absolute.inset-0', { timeout: 0 })
|
||||
await page.evaluate(() => {
|
||||
const $mark = document.querySelector("body > div.absolute.inset-0")
|
||||
if ($mark) $mark.hidden = true
|
||||
document.body.style.pointerEvents = 'all'
|
||||
})
|
||||
|
||||
log('隐藏欢迎页面')
|
||||
|
||||
await page.waitForSelector('.gold-new-button', { visible: true })
|
||||
log('等待升级plus按钮出现')
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.evaluate(() => {
|
||||
const $mark = document.querySelector("body > div.absolute.inset-0")
|
||||
if ($mark) $mark.remove()
|
||||
})
|
||||
|
||||
log('开始点击升级plus按钮')
|
||||
await page.click('.gold-new-button')
|
||||
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 0 }),
|
||||
page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
target && target.click()
|
||||
}, 'button', 'Upgrade plan')
|
||||
])
|
||||
|
||||
if (response.ok()) {
|
||||
const url = response._request._frame._url
|
||||
log('获取链接成功', { result: url, type: 'success' })
|
||||
browser.close()
|
||||
return url
|
||||
}
|
||||
browser.close()
|
||||
}
|
93
electron/main/gpt/index.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { getLink } from './getLink'
|
||||
import { validate } from './validate'
|
||||
import { batchApplication } from './batchApplication'
|
||||
import { chunk } from 'lodash'
|
||||
import { sleep } from '../tools'
|
||||
|
||||
const parseAccount = text => text.split('\n').filter(Boolean).map(v => {
|
||||
v = v.split(/(——|-)+/).filter(v => !['-', '——'].includes(v))
|
||||
return v
|
||||
})
|
||||
|
||||
ipcMain.handle('gpt-link', async (event, arg) => {
|
||||
const { text } = arg
|
||||
// 进程数
|
||||
const processNum = 2
|
||||
const totalArr = parseAccount(text)
|
||||
const accounts = chunk(totalArr, Math.ceil(totalArr.length / processNum))
|
||||
|
||||
async function run (accounts) {
|
||||
for(let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass] = accounts[i]
|
||||
const link = await getLink({ user, pass, index: i, id: user, ...arg })
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
}
|
||||
const links = []
|
||||
for(let i = 0; i < accounts.length; i++) {
|
||||
if (i !== 0) await sleep(2000)
|
||||
run(accounts[i])
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('gpt-result', async (event, arg) => {
|
||||
const { text } = arg
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for(let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass] = accounts[i]
|
||||
const link = await validate({ user, pass, index: i, id: user })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
// browser && browser.close()
|
||||
})
|
||||
|
||||
|
||||
ipcMain.handle('gpt-batch-4.0', async (event, arg) => {
|
||||
const { text } = arg
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for(let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass] = accounts[i]
|
||||
const link = await batchApplication({ user, pass, index: i, id: user })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
// browsers.forEach(browser => browser.close())
|
||||
})
|
||||
|
||||
|
||||
const actions = {
|
||||
'gpt-link': getLink,
|
||||
'gpt-result': validate,
|
||||
'gpt-batch-4.0': batchApplication
|
||||
}
|
||||
|
||||
export async function runActions(action: keyof typeof actions, options: any) {
|
||||
const { text } = options
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for(let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass] = accounts[i]
|
||||
const link = await batchApplication({ user, pass, index: i, id: user })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
}
|
28
electron/main/gpt/validate.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { clog } from "../tools";
|
||||
import login from "../login";
|
||||
|
||||
export async function validate(options) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'gpt-validate' })
|
||||
const [page, browser] = await login.chatgpt(options)
|
||||
|
||||
await page.waitForSelector('body > div.absolute.inset-0')
|
||||
await page.evaluate(() => {
|
||||
const $mark = document.querySelector("body > div.absolute.inset-0")
|
||||
if ($mark) $mark.hidden = true
|
||||
document.body.style.pointerEvents = 'all'
|
||||
})
|
||||
log('隐藏欢迎页面,开始检测')
|
||||
|
||||
await page.waitForTimeout(2000)
|
||||
|
||||
const isSuccess = await page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
return !!target
|
||||
}, 'button', 'GPT-4')
|
||||
log('获取链接成功', { result: isSuccess ? '充值成功😘' : '充值失败😭', type: isSuccess ? 'success' : 'fail' })
|
||||
|
||||
console.log(isSuccess)
|
||||
browser.close()
|
||||
}
|
144
electron/main/index.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import { app, BrowserWindow, shell, ipcMain } from 'electron'
|
||||
import { release } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import './poe/index'
|
||||
import './gpt/index'
|
||||
import { browsers, test1 } from './tools'
|
||||
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
puppeteer.use(StealthPlugin())
|
||||
|
||||
|
||||
// test1()
|
||||
// puppeteer.launch({
|
||||
// headless: false,
|
||||
// args: [
|
||||
// '--no-sandbox',
|
||||
// '--disable-setuid-sandbox',
|
||||
// '--proxy-server=socks5://127.0.0.1:40000'
|
||||
// ]
|
||||
// })
|
||||
|
||||
// The built directory structure
|
||||
//
|
||||
// ├─┬ dist-electron
|
||||
// │ ├─┬ main
|
||||
// │ │ └── index.js > Electron-Main
|
||||
// │ └─┬ preload
|
||||
// │ └── index.js > Preload-Scripts
|
||||
// ├─┬ dist
|
||||
// │ └── index.html > Electron-Renderer
|
||||
//
|
||||
process.env.DIST_ELECTRON = join(__dirname, '..')
|
||||
process.env.DIST = join(process.env.DIST_ELECTRON, '../dist')
|
||||
process.env.PUBLIC = process.env.VITE_DEV_SERVER_URL
|
||||
? join(process.env.DIST_ELECTRON, '../public')
|
||||
: process.env.DIST
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
|
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Remove electron security warnings
|
||||
// This warning only shows in development mode
|
||||
// Read more on https://www.electronjs.org/docs/latest/tutorial/security
|
||||
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||
|
||||
export let win: BrowserWindow | null = null
|
||||
// Here, you can also use other preload
|
||||
const preload = join(__dirname, '../preload/index.js')
|
||||
const url = process.env.VITE_DEV_SERVER_URL
|
||||
const indexHtml = join(process.env.DIST, 'index.html')
|
||||
|
||||
async function createWindow() {
|
||||
win = new BrowserWindow({
|
||||
title: 'Main window',
|
||||
width: 1000,
|
||||
height: 800,
|
||||
icon: join(process.env.PUBLIC, 'favicon.ico'),
|
||||
webPreferences: {
|
||||
preload,
|
||||
// Warning: Enable nodeIntegration and disable contextIsolation is not secure in production
|
||||
// Consider using contextBridge.exposeInMainWorld
|
||||
// Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
})
|
||||
|
||||
if (process.env.VITE_DEV_SERVER_URL) { // electron-vite-vue#298
|
||||
win.loadURL(url)
|
||||
console.log('process.env.VITE_DEV_SERVER_URL', process.env.VITE_DEV_SERVER_URL);
|
||||
|
||||
// Open devTool if the app is not packaged
|
||||
// win.webContents.openDevTools()
|
||||
} else {
|
||||
win.loadFile(indexHtml)
|
||||
}
|
||||
|
||||
// Test actively push message to the Electron-Renderer
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
win?.webContents.send('main-process-message', new Date().toLocaleString())
|
||||
})
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https:')) shell.openExternal(url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
// win.webContents.on('will-navigate', (event, url) => { }) #344
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
win = null
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
app.on('second-instance', () => {
|
||||
if (win) {
|
||||
// Focus on the main window if the user tried to open another
|
||||
if (win.isMinimized()) win.restore()
|
||||
win.focus()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length) {
|
||||
allWindows[0].focus()
|
||||
} else {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// New window example arg: new windows url
|
||||
ipcMain.handle('open-win', (_, arg) => {
|
||||
const childWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload,
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
})
|
||||
|
||||
if (process.env.VITE_DEV_SERVER_URL) {
|
||||
childWindow.loadURL(`${url}#${arg}`)
|
||||
} else {
|
||||
childWindow.loadFile(indexHtml, { hash: arg })
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('stop', async (event, arg) => {
|
||||
browsers.forEach(browser => browser.close())
|
||||
return true
|
||||
})
|
373
electron/main/login.ts
Normal file
@@ -0,0 +1,373 @@
|
||||
import type { Page, Browser } from 'puppeteer'
|
||||
import { browsers, clog, statusCheck } from './tools'
|
||||
import { awaitWrap, browserAndPage, randomNum } from './tools'
|
||||
|
||||
const login = {
|
||||
// poe 邮箱登录
|
||||
async poe_email(options, getCodeFn: Function, tryCount = 1): Promise<[Page, Browser]> {
|
||||
const log = clog(options)
|
||||
|
||||
if (tryCount > 2) {
|
||||
log('重试次数已达上限')
|
||||
throw '重试次数已达上限'
|
||||
return
|
||||
}
|
||||
|
||||
log('启动浏览器')
|
||||
|
||||
const { browser, page } = await browserAndPage({ ...options, proxy: true })
|
||||
|
||||
log('正在进入登录页面')
|
||||
await page.goto('https://poe.com/login')
|
||||
|
||||
log('设置登录方式')
|
||||
await page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
|
||||
if (!target) return false
|
||||
|
||||
const text = target.nextElementSibling?.textContent || ''
|
||||
if (text.includes('email')) target.nextElementSibling.click()
|
||||
}, 'button', 'Go')
|
||||
|
||||
log('开始输入邮箱')
|
||||
await page.waitForSelector('input[type="email"]')
|
||||
await page.type('input[type="email"]', options.user)
|
||||
await page.waitForTimeout(1000)
|
||||
await statusCheck(
|
||||
async () => await page.keyboard.press('Enter'),
|
||||
async () => await page.$('.LoadingDots_wrapper__lXyQd'), // LoadingDots_wrapper__lXyQd
|
||||
)
|
||||
|
||||
// 检查是否有错误
|
||||
const isOk = await Promise.race([
|
||||
page.waitForSelector('.InfoText_error__OQwmg').then(() => false),
|
||||
page.waitForSelector('input[class^="VerificationCodeInput_verificationCodeInput"]').then(() => true)
|
||||
])
|
||||
|
||||
console.log('isOk', isOk)
|
||||
if (!isOk) {
|
||||
const reason = await page.$eval('.InfoText_error__OQwmg', (el: HTMLElement) => el.textContent.trim())
|
||||
if (reason.startsWith('Something')) {
|
||||
await page.waitForTimeout(3482)
|
||||
await statusCheck(
|
||||
async () => await page.keyboard.press('Enter'),
|
||||
async () => await page.$('.LoadingDots_wrapper__lXyQd'), // LoadingDots_wrapper__lXyQd
|
||||
)
|
||||
} else {
|
||||
log('', { result: `登录失败: ${reason}` })
|
||||
browser.close()
|
||||
throw '登录失败'
|
||||
}
|
||||
// return login.poe_email(options, getCodeFn, tryCount + 1)
|
||||
}
|
||||
|
||||
// await page.keyboard.press('Enter')
|
||||
log('开始输入密码')
|
||||
|
||||
const code = await getCodeFn(options, { page, browser })
|
||||
|
||||
await page.type('input[class^=VerificationCodeInput_verificationCodeInput]', code)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
await page.waitForNavigation()
|
||||
|
||||
return [page, browser]
|
||||
},
|
||||
|
||||
async poe_google(options, tryCount = 1): Promise<[Page, Browser]> {
|
||||
let resolve: (value: [Page, Browser] | PromiseLike<[Page, Browser]>) => void,
|
||||
reject
|
||||
const log = clog(options)
|
||||
const p = new Promise<[Page, Browser]>((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
|
||||
const env = {
|
||||
GUSER: options.user,
|
||||
GPASS: options.pass
|
||||
}
|
||||
const { browser, page } = await browserAndPage(options)
|
||||
|
||||
log('开始访问 poe')
|
||||
await page.goto('https://poe.com')
|
||||
log('已进入 poe')
|
||||
await page.waitForTimeout(randomNum(1000, 2600))
|
||||
|
||||
log('准备进入 google 登录页')
|
||||
|
||||
page.click('.ContinueWithGoogleButton_buttonContentWrapper__Mrp0W')
|
||||
const [err, response] = await awaitWrap(page.waitForNavigation({ timeout: 10000 }))
|
||||
if (err) {
|
||||
reject({ text: '登录报错', try: true })
|
||||
return p
|
||||
}
|
||||
log('已进入 google 登录页')
|
||||
|
||||
if (response.ok()) {
|
||||
await page.waitForSelector('input[type="email"]')
|
||||
log('准备输入账号')
|
||||
await page.type('input[type="email"]', env.GUSER)
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
await page.keyboard.press('Enter')
|
||||
])
|
||||
log('已输入账号,准备输入密码')
|
||||
if (await isError(page)) {
|
||||
reject({ text: '登录报错', try: true })
|
||||
return p
|
||||
}
|
||||
|
||||
await page.waitForSelector('input[type="password"]', { visible: true })
|
||||
await page.type('input[type="password"]', env.GPASS)
|
||||
log('已输入密码,开始登录')
|
||||
await Promise.all([
|
||||
page.waitForFunction(() => location.href === 'https://poe.com/'),
|
||||
await page.keyboard.press('Enter')
|
||||
])
|
||||
if (await isError(page)) {
|
||||
reject({ text: '登录报错', try: true })
|
||||
return p
|
||||
}
|
||||
|
||||
log('登录成功,准备进入 poe')
|
||||
resolve([page, browser])
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
if (tryCount < 4) {
|
||||
log(`google 登录失败,准备 ${tryCount} 次重试`)
|
||||
await page.close()
|
||||
return await login.poe_google(options, tryCount + 1)
|
||||
} else {
|
||||
log('google 登录失败,重试次数已达上限')
|
||||
reject('google 登录失败')
|
||||
}
|
||||
|
||||
return p
|
||||
},
|
||||
|
||||
async openai(options): Promise<[Page, Browser]> {
|
||||
const { user, pass } = options
|
||||
const log = clog(options)
|
||||
|
||||
log('启动浏览器')
|
||||
|
||||
const { browser, page } = await browserAndPage({ ...options, changeUS: false })
|
||||
|
||||
log('准备进入 gpt 登录')
|
||||
await page.goto('https://platform.openai.com')
|
||||
|
||||
|
||||
log('等待出现输入框')
|
||||
await page.waitForSelector('#username', { visible: true, timeout: 10000 })
|
||||
|
||||
// 输入账号
|
||||
log('输入账号')
|
||||
await page.type('#username', user)
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.keyboard.press('Enter')
|
||||
])
|
||||
|
||||
log('等待出现密码输入框')
|
||||
await page.waitForSelector('#password', { visible: true })
|
||||
log('输入密码')
|
||||
await page.type('#password', pass)
|
||||
|
||||
|
||||
log('准备登录')
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ timeout: 10000 }),
|
||||
page.keyboard.press('Enter')
|
||||
])
|
||||
|
||||
log('登录成功')
|
||||
|
||||
return [page, browser]
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录邮箱并获取验证码
|
||||
*/
|
||||
async mail_get_code(options): Promise<[Page, Browser, {
|
||||
code?: string,
|
||||
validateCode: () => Promise<string>
|
||||
}]> {
|
||||
const { emailText = 'Poe' } = options
|
||||
const log = clog(options)
|
||||
const { browser, page } = await browserAndPage({ ...options })
|
||||
|
||||
log('准备进入邮箱登录页')
|
||||
const [error] = await awaitWrap(page.goto('https://www.mail.com/', { waitUntil: 'domcontentloaded' }))
|
||||
|
||||
await page.waitForSelector('.header-bar .button-login')
|
||||
await page.click('.header-bar .button-login')
|
||||
|
||||
log('输入帐密')
|
||||
await page.waitForTimeout(500)
|
||||
await page.waitForSelector('input#login-email')
|
||||
await page.waitForTimeout(500)
|
||||
await page.type('input#login-email', options.user)
|
||||
await page.type('input#login-password', options.pass)
|
||||
await page.keyboard.press('Enter')
|
||||
log('开始登录')
|
||||
|
||||
/**
|
||||
*
|
||||
* @param reload 是否点击刷新验证码按钮
|
||||
*/
|
||||
async function validateCode() {
|
||||
log('开始获取code')
|
||||
let $iframe = await page.$('#thirdPartyFrame_home')
|
||||
let frame = await $iframe?.contentFrame()
|
||||
|
||||
if (!frame) {
|
||||
log('未找到页面,刷新重试中...')
|
||||
await page.reload()
|
||||
return await validateCode()
|
||||
}
|
||||
|
||||
const [err] = await awaitWrap(frame.waitForSelector('ul.inbox-container li', { timeout: 4000 }))
|
||||
if (err) {
|
||||
console.log(err)
|
||||
console.log('有报错')
|
||||
await frame.waitForSelector('.ico.sync')
|
||||
const sync = await page.$('.ico.sync')
|
||||
if (!sync) {
|
||||
console.log('未找到刷新按钮')
|
||||
log('未找到刷新按钮')
|
||||
return ''
|
||||
}
|
||||
await frame.click('.ico.sync')
|
||||
await frame.waitForTimeout(4000)
|
||||
return await validate()
|
||||
}
|
||||
|
||||
return await validate()
|
||||
|
||||
|
||||
// console.log('type', type)
|
||||
// if (type === 'list') {
|
||||
// return await validate()
|
||||
// }
|
||||
|
||||
|
||||
await frame.waitForSelector('.ico.sync')
|
||||
await frame.click('.ico.sync')
|
||||
await frame.waitForTimeout(1000)
|
||||
|
||||
await frame.waitForSelector('ul.inbox-container li')
|
||||
log('查找邮箱')
|
||||
|
||||
async function validate() {
|
||||
const $li = await frame.evaluate((emailText) => {
|
||||
const $li: any = Array.from(document.querySelectorAll('ul.inbox-container li')).find($li => {
|
||||
let sender = $li.querySelector('.sender')?.textContent || ''
|
||||
return sender.includes(emailText)
|
||||
})
|
||||
|
||||
if ($li) $li.click()
|
||||
return $li
|
||||
}, emailText)
|
||||
|
||||
if (!$li) {
|
||||
log('未找到邮箱')
|
||||
return ''
|
||||
} else {
|
||||
log('等待出现验证码 iframe')
|
||||
await page.waitForSelector('#thirdPartyFrame_mail')
|
||||
log('出现了验证码 iframe,寻找他的 iframe')
|
||||
$iframe = await page.$('#thirdPartyFrame_mail')
|
||||
frame = await $iframe?.contentFrame()
|
||||
|
||||
await frame.waitForSelector('iframe#mail-detail')
|
||||
log('出现验证码 iframe')
|
||||
|
||||
$iframe = await frame.$('iframe#mail-detail')
|
||||
frame = await $iframe?.contentFrame()
|
||||
|
||||
log('等待验证码')
|
||||
await frame.waitForSelector('table table table ')
|
||||
const code = (await frame.$eval('table table table tr:nth-of-type(5)', el => el.textContent)) || ''
|
||||
return code.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await awaitWrap(page.waitForNavigation({ waitUntil: 'load' }))
|
||||
const code = await validateCode()
|
||||
|
||||
log(`邮箱验证码 ${code}`)
|
||||
return [page, browser, { code, validateCode }]
|
||||
},
|
||||
|
||||
async chatgpt(options, tryCount = 1): Promise<[Page, Browser]> {
|
||||
let resolve: (value: [Page, Browser] | PromiseLike<[Page, Browser]>) => void,
|
||||
reject
|
||||
const log = clog(options)
|
||||
const p = new Promise<[Page, Browser]>((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
|
||||
const env = {
|
||||
GUSER: options.user,
|
||||
GPASS: options.pass
|
||||
}
|
||||
|
||||
const { browser, page } = await browserAndPage({ ...options, changeUS: false })
|
||||
log('开始访问 gpt')
|
||||
await page.goto('https://chat.openai.com/auth/login')
|
||||
await page.waitForSelector('button')
|
||||
|
||||
// log('准备进入 google 登录页')
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation({ timeout: 0 }),
|
||||
page.click('button')
|
||||
])
|
||||
// log('已进入 google 登录页')
|
||||
|
||||
if (!response.ok()) {
|
||||
log('进入 google 登录页失败')
|
||||
reject('进入 google 登录页失败')
|
||||
return p
|
||||
}
|
||||
|
||||
log('准备输入账号')
|
||||
await page.type('#username', env.GUSER)
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ timeout: 0 }),
|
||||
page.keyboard.press('Enter')
|
||||
])
|
||||
|
||||
log('已输入账号,准备输入密码')
|
||||
await page.waitForSelector('#password', { visible: true })
|
||||
await page.type('#password', env.GPASS)
|
||||
log('已输入密码,开始登录')
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation({ timeout: 10000, waitUntil: 'domcontentloaded' }),
|
||||
page.keyboard.press('Enter')
|
||||
])
|
||||
log('登录成功')
|
||||
resolve([page, browser])
|
||||
|
||||
return p
|
||||
},
|
||||
|
||||
closeAll() {
|
||||
browsers.map(b => b.close())
|
||||
}
|
||||
}
|
||||
|
||||
export default login
|
||||
|
||||
async function isError(page: Page) {
|
||||
const text = await page.evaluate(() => (document.querySelector('p')?.textContent || ''));
|
||||
// const text = await page.evaluate('p', element => element.textContent);
|
||||
return text && text.includes('error')
|
||||
}
|
193
electron/main/poe/cookie.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import type { Browser, Page } from 'puppeteer'
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
import { browserAndPage, browsers, clog, createPromise, statusCheck } from "../tools"
|
||||
import login from '../login'
|
||||
puppeteer.use(StealthPlugin())
|
||||
|
||||
export async function getCookie(options) {
|
||||
console.log('options', options);
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'link_7day' })
|
||||
|
||||
let { p, resolve, reject } = createPromise<string>()
|
||||
|
||||
login.mail_get_code({ ...options, changeUS: false }).then(async ([page, browser, options]) => {
|
||||
const isOk = await statusCheck(
|
||||
() => options.code,
|
||||
async () => {
|
||||
log('未获取验证码,重试一次', { ident: 'link_7day' })
|
||||
options.code = await options.validateCode()
|
||||
return options.code
|
||||
},
|
||||
{ interval: 4000 }
|
||||
)
|
||||
resolve(options.code)
|
||||
browser.close()
|
||||
})
|
||||
|
||||
const [page, browser] = await login.poe_email(options, async () => {
|
||||
const code = await p
|
||||
console.log('获取验证码结果', code)
|
||||
if (!code) {
|
||||
log('获取验证码失败', { ident: 'link_7day' })
|
||||
reject('获取验证码失败')
|
||||
throw '获取验证码失败'
|
||||
}
|
||||
|
||||
return code
|
||||
}).catch(() => '' as any)
|
||||
|
||||
// 登录失败了
|
||||
if (!page) {
|
||||
console.log('登录失败了')
|
||||
browsers.map(b => b.close())
|
||||
return
|
||||
}
|
||||
|
||||
const cookies = await page.cookies()
|
||||
const token = cookies.find(v => v.name === 'p-b')?.value
|
||||
|
||||
log('获取token', { result: token })
|
||||
console.log('cookies', cookies)
|
||||
// browser.close()
|
||||
// await page.waitForTimeout(2000)
|
||||
browser.close()
|
||||
// const { page } = await login.poe_email(options)
|
||||
}
|
||||
|
||||
async function recharge(options: {
|
||||
browser: Browser,
|
||||
page: Page,
|
||||
user: string,
|
||||
liao: any,
|
||||
}) {
|
||||
const { browser, page, liao, ...args } = options
|
||||
const log = clog(args)
|
||||
log('开始充值')
|
||||
await page.waitForSelector('#cardNumber')
|
||||
|
||||
await page.evaluate(() => {
|
||||
const btn: HTMLElement = document.querySelector('.AddressAutocomplete-manual-entry .Link')
|
||||
if (btn) btn.click()
|
||||
})
|
||||
|
||||
await page.evaluate((email: string) => {
|
||||
const el: HTMLInputElement = document.querySelector('#email')
|
||||
if (el) el.value = email
|
||||
}, options.user)
|
||||
|
||||
await page.type('#cardNumber', liao.bank)
|
||||
await page.type('#cardExpiry', liao.date)
|
||||
await page.type('#cardCvc', liao.cvc)
|
||||
await page.type('#billingName', liao.name)
|
||||
await page.select('#billingCountry', liao.nation)
|
||||
await page.type('#billingAddressLine1', liao.address)
|
||||
await page.type('#billingLocality', liao.city)
|
||||
await page.type('#billingPostalCode', liao.postalCode)
|
||||
await page.waitForTimeout(1000)
|
||||
await page.click('.SubmitButton-IconContainer')
|
||||
|
||||
// await page.waitForTimeout(9000)
|
||||
|
||||
// await page.waitForSelector('iframe')
|
||||
// let $frame = await page.$('iframe')
|
||||
// let frame = await $frame.contentFrame()
|
||||
|
||||
// console.log('$frame', $frame)
|
||||
// // console.log('frame', frame)
|
||||
// await $frame.waitForSelector('iframe')
|
||||
// $frame = await frame.$('iframe')
|
||||
// frame = await $frame.contentFrame()
|
||||
// await frame.waitForSelector('iframe')
|
||||
// $frame = await frame.$('iframe')
|
||||
// frame = await $frame.contentFrame()
|
||||
|
||||
// await frame.waitForSelector('#checkbox', { timeout: 0 })
|
||||
// await frame.click('#checkbox')
|
||||
// await page.waitForNavigation()
|
||||
console.log('充值成功')
|
||||
log('充值成功')
|
||||
}
|
||||
|
||||
function existDialog(page: Page) {
|
||||
return page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
target && target.click()
|
||||
|
||||
return !!target
|
||||
}, 'button', 'Start free trial')
|
||||
}
|
||||
|
||||
export async function getLink(options, [page, browser]: [Page, Browser]) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'poe-link' })
|
||||
await page.waitForTimeout(1000)
|
||||
const isExistDialog = await existDialog(page)
|
||||
|
||||
isExistDialog && log('检测到充值弹窗,无需前往设置页')
|
||||
|
||||
if (!isExistDialog) {
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.goto('https://poe.com/settings')
|
||||
])
|
||||
log('已进入设置页面, 检查中')
|
||||
|
||||
const existMange = await page.evaluate(() => {
|
||||
const mange = document.querySelector('[class*="SettingsSubscriptionSection_manageSubscription"]')
|
||||
if (mange) return true
|
||||
return false
|
||||
})
|
||||
|
||||
if (existMange) {
|
||||
log('已经订阅')
|
||||
return
|
||||
}
|
||||
await page.waitForSelector('[class*=SettingsSubscriptionSection_subscribeButton]', { timeout: 0 })
|
||||
|
||||
page.waitForTimeout(500)
|
||||
log('点击显示订阅套餐按钮')
|
||||
// await page.click('[class*=SettingsSubscriptionSection_subscribeButton]')
|
||||
|
||||
const disabled = await page.$eval('[class*=SettingsSubscriptionSection_subscribeButton]', (el: HTMLButtonElement) => el.disabled)
|
||||
if (disabled) {
|
||||
log('订阅按钮不可用,地区不可用')
|
||||
return
|
||||
}
|
||||
|
||||
await statusCheck(
|
||||
async () => {
|
||||
return page.evaluate(() => {
|
||||
const $el = document.querySelector("[class*=SettingsSubscriptionSection_subscribeButton]")
|
||||
console.log('$el', $el)
|
||||
$el.click();
|
||||
})
|
||||
},
|
||||
// async () => page.click('[class*=SettingsSubscriptionSection_subscribeButton]'),
|
||||
async () => page.$('.Modal_modal__SxITf'),
|
||||
)
|
||||
}
|
||||
|
||||
// log('显示更多套餐')
|
||||
// await page.waitForSelector('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
|
||||
// await page.click('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
|
||||
// log('点击最后一个套餐')
|
||||
// await page.waitForSelector('[class*=WebSubscriptionPaywall_plans]')
|
||||
// await page.click('[class*=WebSubscriptionPaywall_plans] > button:last-child')
|
||||
|
||||
// 点击订阅
|
||||
log('4, 开始点击订阅')
|
||||
await page.waitForSelector('[class*=WebSubscriptionPaywall_button]', { timeout: 0 })
|
||||
page.click('[class*=WebSubscriptionPaywall_button]')
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
])
|
||||
|
||||
if (response.ok()) {
|
||||
const url = response._request._frame._url
|
||||
log('获取链接成功', { result: url, type: 'success' })
|
||||
return url
|
||||
}
|
||||
}
|
69
electron/main/poe/getLink.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { clog } from '../tools'
|
||||
import type { Page } from 'puppeteer'
|
||||
import path from 'path'
|
||||
import login from '../login'
|
||||
|
||||
function existDialog(page: Page) {
|
||||
return page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
target && target.click()
|
||||
|
||||
return !!target
|
||||
}, 'button', 'Start free trial')
|
||||
}
|
||||
|
||||
export async function getLink(options) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'poe-link' })
|
||||
return login.poe_google(options).then(async ([page, browser]) => {
|
||||
await page.waitForTimeout(1000)
|
||||
const isExistDialog = await existDialog(page)
|
||||
|
||||
isExistDialog && log('检测到充值弹窗,无需前往设置页')
|
||||
|
||||
if (!isExistDialog) {
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.goto('https://poe.com/settings')
|
||||
])
|
||||
log('已进入设置页面, 检查中')
|
||||
await page.waitForSelector('[class*=SettingsSubscriptionSection_subscribeButton]')
|
||||
log('点击显示订阅套餐按钮')
|
||||
await page.click('[class*=SettingsSubscriptionSection_subscribeButton]')
|
||||
}
|
||||
|
||||
log('显示更多套餐')
|
||||
await page.waitForSelector('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
|
||||
await page.click('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
|
||||
log('点击最后一个套餐')
|
||||
await page.waitForSelector('[class*=WebSubscriptionPaywall_plans]')
|
||||
await page.click('[class*=WebSubscriptionPaywall_plans] > button:last-child')
|
||||
|
||||
// 点击订阅
|
||||
log('4, 开始点击订阅')
|
||||
page.click('[class*=WebSubscriptionPaywall_button]')
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
])
|
||||
|
||||
if (response.ok()) {
|
||||
const url = response._request._frame._url
|
||||
log('获取链接成功', { result: url, type: 'success' })
|
||||
browser.close()
|
||||
return url
|
||||
}
|
||||
browser.close()
|
||||
}).catch(error => {
|
||||
console.log('error ->', error.try, error.text, error)
|
||||
if (error?.try) {
|
||||
return getLink(options)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
function resolve(url) {
|
||||
return path.resolve(__dirname, '../src/poe/', url)
|
||||
}
|
102
electron/main/poe/index.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import { getLink } from './getLink'
|
||||
import { validate } from './validate'
|
||||
import { ipcMain } from 'electron'
|
||||
import { link_7day } from './link_7day'
|
||||
import { getCookie } from './cookie'
|
||||
import { browserAndPage } from '../tools'
|
||||
|
||||
export const parseAccount = text => text.split('\n').filter(Boolean).map(v => {
|
||||
v = v.split(/(——|-)+/).filter(v => !['-', '——'].includes(v))
|
||||
return v
|
||||
})
|
||||
|
||||
ipcMain.handle('getLink', async (event, arg) => {
|
||||
const { text } = arg
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass, auxiliary] = accounts[i]
|
||||
const link = await getLink({ user, pass, auxiliary, index: i, id: user })
|
||||
// .catch(err => {
|
||||
// console.log('error ->', err)
|
||||
// })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
return links
|
||||
})
|
||||
|
||||
ipcMain.handle('get-poe-link-7day', async (event, arg) => {
|
||||
const { text, liao } = arg
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass, auxiliary] = accounts[i]
|
||||
const link = await link_7day({ user, pass, auxiliary, index: i, id: user, liao })
|
||||
// .catch(err => {
|
||||
// console.log('error ->', err)
|
||||
// })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
return links
|
||||
})
|
||||
|
||||
|
||||
|
||||
ipcMain.handle('get-poe-cookie', async (event, arg) => {
|
||||
const { text, liao } = arg
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass, auxiliary] = accounts[i]
|
||||
const link = await getCookie({ user, pass, auxiliary, index: i, id: user, liao })
|
||||
// .catch(err => {
|
||||
// console.log('error ->', err)
|
||||
// })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
return links
|
||||
})
|
||||
|
||||
|
||||
ipcMain.handle('poe-result', async (event, arg) => {
|
||||
const { text } = arg
|
||||
const accounts = parseAccount(text)
|
||||
|
||||
const links = []
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const [user, pass, auxiliary] = accounts[i]
|
||||
const link = await validate({ user, pass, auxiliary, index: i, id: user })
|
||||
// .catch(err => {
|
||||
// console.log('error ->', err)
|
||||
// })
|
||||
links.push({
|
||||
i,
|
||||
user,
|
||||
link
|
||||
})
|
||||
console.log('process', i, user, link)
|
||||
}
|
||||
return links
|
||||
})
|
||||
|
||||
ipcMain.handle('start-one-chrom', async (event, arg) => {
|
||||
browserAndPage({ proxy: true })
|
||||
})
|
217
electron/main/poe/link_7day.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import type { Browser, Page } from 'puppeteer'
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
import { awaitWrap, browserAndPage, browsers, clog, createPromise, statusCheck } from "../tools"
|
||||
import login from '../login'
|
||||
puppeteer.use(StealthPlugin())
|
||||
|
||||
export async function link_7day(options) {
|
||||
console.log('options', options);
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'link_7day' })
|
||||
|
||||
let { p, resolve, reject } = createPromise<string>()
|
||||
|
||||
login.mail_get_code({ ...options, changeUS: false }).then(async ([page, browser, options]) => {
|
||||
const isOk = await statusCheck(
|
||||
() => options.code,
|
||||
async () => {
|
||||
log('未获取验证码,重试一次', { ident: 'link_7day' })
|
||||
options.code = await options.validateCode()
|
||||
return options.code
|
||||
},
|
||||
{ interval: 4000 }
|
||||
)
|
||||
resolve(options.code)
|
||||
browser.close()
|
||||
})
|
||||
|
||||
const [page, browser] = await login.poe_email(options, async () => {
|
||||
const code = await p
|
||||
console.log('获取验证码结果', code)
|
||||
if (!code) {
|
||||
log('获取验证码失败', { ident: 'link_7day' })
|
||||
reject('获取验证码失败')
|
||||
throw '获取验证码失败'
|
||||
}
|
||||
|
||||
return code
|
||||
}).catch(() => '' as any)
|
||||
|
||||
// 登录失败了
|
||||
if (!page) {
|
||||
console.log('登录失败了')
|
||||
browsers.map(b => b.close())
|
||||
return
|
||||
}
|
||||
|
||||
const url = await getLink(options, [page, browser])
|
||||
|
||||
return url
|
||||
if (url && options.liao) {
|
||||
await recharge({ ...options, page, browser })
|
||||
}
|
||||
|
||||
// browser.close()
|
||||
// await page.waitForTimeout(2000)
|
||||
// browser.close()
|
||||
// const { page } = await login.poe_email(options)
|
||||
}
|
||||
|
||||
async function test() {
|
||||
const url = 'https://checkout.stripe.com/c/pay/cs_live_a1MXhrEYuI3qJEjw85zmuIxjFsgswafv0xlcxUGAOIeyiGJBGLA56mvRto#fidkdWxOYHwnPyd1blppbHNgWjxITDJsdEROY3Y1NjZpNTc8Q1RMU3ZTNicpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl'
|
||||
const liao = {"bank":"4833160230060672","cvc":"727","date":"04/26","name":"Zackary Wais","address":"1200LakeshoreaveApt8G","city":"Oakland","postalCode":"94606","nation":"US"}
|
||||
const user = 'neletegcongder@mail.com'
|
||||
|
||||
const { page, browser } = await browserAndPage()
|
||||
await page.goto(url, { waitUntil: 'domcontentloaded' })
|
||||
await recharge({ browser, page, user, liao })
|
||||
}
|
||||
|
||||
// test()
|
||||
|
||||
async function recharge(options: {
|
||||
browser: Browser,
|
||||
page: Page,
|
||||
user: string,
|
||||
liao: any,
|
||||
}) {
|
||||
const { browser, page, liao, ...args } = options
|
||||
const log = clog(args)
|
||||
log('开始充值')
|
||||
await page.waitForSelector('#cardNumber')
|
||||
|
||||
await page.evaluate(() => {
|
||||
const btn: HTMLElement = document.querySelector('.AddressAutocomplete-manual-entry .Link')
|
||||
if (btn) btn.click()
|
||||
})
|
||||
|
||||
const isEmail = await page.evaluate((email: string) => {
|
||||
const el: HTMLInputElement = document.querySelector('#email')
|
||||
return !!el
|
||||
}, options.user)
|
||||
if (isEmail) await page.type('#email', options.user)
|
||||
|
||||
|
||||
await page.type('#cardNumber', liao.bank)
|
||||
await page.type('#cardExpiry', liao.date)
|
||||
await page.type('#cardCvc', liao.cvc)
|
||||
await page.type('#billingName', liao.name)
|
||||
await page.select('#billingCountry', liao.nation)
|
||||
await page.type('#billingAddressLine1', liao.address)
|
||||
await page.type('#billingLocality', liao.city)
|
||||
await page.type('#billingPostalCode', liao.postalCode)
|
||||
await page.waitForTimeout(1000)
|
||||
await page.click('.SubmitButton-IconContainer')
|
||||
|
||||
// await page.waitForTimeout(8000)
|
||||
|
||||
// const [error, config] = await awaitWrap(page.solveRecaptchas())
|
||||
// if (error) {
|
||||
// log('充值失败', { result: '充值失败', })
|
||||
// } else {
|
||||
// console.log(config)
|
||||
// log('充值成功', { result: '充值成功', })
|
||||
// }
|
||||
|
||||
// await page.waitForSelector('iframe')
|
||||
// let $frame = await page.$('iframe')
|
||||
// let frame = await $frame.contentFrame()
|
||||
|
||||
// console.log('$frame', $frame)
|
||||
// // console.log('frame', frame)
|
||||
// await frame.waitForSelector('iframe')
|
||||
// $frame = await frame.$('iframe')
|
||||
// frame = await $frame.contentFrame()
|
||||
// await frame.waitForSelector('iframe')
|
||||
// $frame = await frame.$('iframe')
|
||||
// frame = await $frame.contentFrame()
|
||||
|
||||
// await frame.waitForSelector('#checkbox', { timeout: 0 })
|
||||
// await frame.click('#checkbox')
|
||||
await page.waitForNavigation()
|
||||
console.log('充值成功')
|
||||
log('充值成功')
|
||||
}
|
||||
|
||||
function existDialog(page: Page) {
|
||||
return page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
target && target.click()
|
||||
|
||||
return !!target
|
||||
}, 'button', 'Start free trial')
|
||||
}
|
||||
|
||||
export async function getLink(options, [page, browser]: [Page, Browser]) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'poe-link' })
|
||||
await page.waitForTimeout(1000)
|
||||
const isExistDialog = await existDialog(page)
|
||||
|
||||
isExistDialog && log('检测到充值弹窗,无需前往设置页')
|
||||
|
||||
if (!isExistDialog) {
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.goto('https://poe.com/settings')
|
||||
])
|
||||
log('已进入设置页面, 检查中')
|
||||
|
||||
const existMange = await page.evaluate(() => {
|
||||
const mange = document.querySelector('[class*="SettingsSubscriptionSection_manageSubscription"]')
|
||||
if (mange) return true
|
||||
return false
|
||||
})
|
||||
|
||||
if (existMange) {
|
||||
log('已经订阅')
|
||||
return
|
||||
}
|
||||
await page.waitForSelector('[class*=SettingsSubscriptionSection_subscribeButton]', { timeout: 0 })
|
||||
|
||||
page.waitForTimeout(500)
|
||||
log('点击显示订阅套餐按钮')
|
||||
// await page.click('[class*=SettingsSubscriptionSection_subscribeButton]')
|
||||
|
||||
const disabled = await page.$eval('[class*=SettingsSubscriptionSection_subscribeButton]', (el: HTMLButtonElement) => el.disabled)
|
||||
if (disabled) {
|
||||
log('订阅按钮不可用,地区不可用')
|
||||
return
|
||||
}
|
||||
|
||||
await statusCheck(
|
||||
async () => {
|
||||
return page.evaluate(() => {
|
||||
const $el = document.querySelector("[class*=SettingsSubscriptionSection_subscribeButton]")
|
||||
console.log('$el', $el)
|
||||
$el.click();
|
||||
})
|
||||
},
|
||||
// async () => page.click('[class*=SettingsSubscriptionSection_subscribeButton]'),
|
||||
async () => page.$('.Modal_modal__SxITf'),
|
||||
)
|
||||
}
|
||||
|
||||
// log('显示更多套餐')
|
||||
// await page.waitForSelector('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
|
||||
// await page.click('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
|
||||
// log('点击最后一个套餐')
|
||||
// await page.waitForSelector('[class*=WebSubscriptionPaywall_plans]')
|
||||
// await page.click('[class*=WebSubscriptionPaywall_plans] > button:last-child')
|
||||
|
||||
// 点击订阅
|
||||
log('4, 开始点击订阅')
|
||||
await page.waitForSelector('[class*=WebSubscriptionPaywall_button]', { timeout: 0 })
|
||||
page.click('[class*=WebSubscriptionPaywall_button]')
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
|
||||
])
|
||||
|
||||
if (response.ok()) {
|
||||
const url = response._request._frame._url
|
||||
log('获取链接成功', { result: url, type: 'success' })
|
||||
return url
|
||||
}
|
||||
}
|
26
electron/main/poe/validate.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import login from '../login'
|
||||
import path from 'path'
|
||||
import { clog } from '../tools'
|
||||
|
||||
export async function validate (options) {
|
||||
const log = clog(options)
|
||||
log('开始', { ident: 'poe-validate' })
|
||||
const [page, browser] = await login.poe_google(options)
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.goto('https://poe.com/settings')
|
||||
])
|
||||
|
||||
log('已进入设置页面, 检查中', { ident: 'poe-validate' })
|
||||
|
||||
const length = await page.$$eval('.SettingsSubscriptionSection_botLimitSection__j4mSO > div:first-child > div', (doms) => doms.length)
|
||||
const resultTxt = length === 1 ? '失败😭' : '成功😘'
|
||||
log(resultTxt, { type: length === 1 ? 'fail' : 'success', result: length === 1 ? '失败😭' : '成功😘' })
|
||||
|
||||
browser.close()
|
||||
}
|
||||
|
||||
function resolve (url) {
|
||||
return path.resolve(__dirname, '../src/poe/', url)
|
||||
}
|
236
electron/main/tools.ts
Normal file
@@ -0,0 +1,236 @@
|
||||
import type { Page } from "puppeteer"
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
import RecaptchaPlugin from 'puppeteer-extra-plugin-recaptcha'
|
||||
import chromeLauncher from 'chrome-launcher'
|
||||
import { win } from './index'
|
||||
import fakeUa from 'fake-useragent'
|
||||
import axios from "axios"
|
||||
puppeteer.use(StealthPlugin())
|
||||
puppeteer.use(RecaptchaPlugin({
|
||||
provider: {
|
||||
id: '2captcha',
|
||||
token: '25bd4a3638f3836b0d119007cbc21954'
|
||||
},
|
||||
visualFeedback: true
|
||||
}))
|
||||
|
||||
|
||||
export function test1() {
|
||||
// puppeteer-extra is a drop-in replacement for puppeteer,
|
||||
// it augments the installed puppeteer with plugin functionality
|
||||
const puppeteer = require('puppeteer-extra')
|
||||
|
||||
// add recaptcha plugin and provide it your 2captcha token (= their apiKey)
|
||||
// 2captcha is the builtin solution provider but others would work as well.
|
||||
// Please note: You need to add funds to your 2captcha account for this to work
|
||||
const RecaptchaPlugin = require('puppeteer-extra-plugin-recaptcha')
|
||||
puppeteer.use(
|
||||
RecaptchaPlugin({
|
||||
provider: {
|
||||
id: '2captcha',
|
||||
token: '25bd4a3638f3836b0d119007cbc21954' // REPLACE THIS WITH YOUR OWN 2CAPTCHA API KEY ⚡
|
||||
},
|
||||
visualFeedback: true // colorize reCAPTCHAs (violet = detected, green = solved)
|
||||
})
|
||||
)
|
||||
|
||||
// puppeteer usage as normal
|
||||
puppeteer.launch({ headless: false }).then(async browser => {
|
||||
const page = await browser.newPage()
|
||||
await page.goto('https://www.google.com/recaptcha/api2/demo')
|
||||
|
||||
// That's it, a single line of code to solve reCAPTCHAs 🎉
|
||||
const {
|
||||
captchas,
|
||||
filtered,
|
||||
solutions,
|
||||
solved,
|
||||
error
|
||||
} = await page.solveRecaptchas()
|
||||
|
||||
console.log({ captchas, filtered, solutions, solved, error })
|
||||
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
page.click(`#recaptcha-demo-submit`)
|
||||
])
|
||||
await page.screenshot({ path: 'response.png', fullPage: true })
|
||||
await browser.close()
|
||||
})
|
||||
}
|
||||
|
||||
//生成从minNum到maxNum的随机数
|
||||
export function randomNum (min, max) {
|
||||
return parseInt(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
// promise 错误处理
|
||||
export function awaitWrap<T, U = any>(promise: Promise<T>): Promise<[U | null, T | null]> {
|
||||
return promise
|
||||
.then<[null, T]>((data: T) => [null, data])
|
||||
.catch<[U, null]>(err => [err, null])
|
||||
}
|
||||
|
||||
// 设置页面请求
|
||||
export function pageRequest (page: Page) {
|
||||
page.setRequestInterception(true)
|
||||
page.on('request', (request) => {
|
||||
if (['stylesheet', 'font'].indexOf(request.resourceType()) !== -1) {
|
||||
request.abort()
|
||||
} else {
|
||||
request.continue()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const browsers = []
|
||||
|
||||
export async function browserAndPage (options: any = {}) {
|
||||
const { request, changeUS = true, proxy = false } = options
|
||||
|
||||
async function proxyCommand () {
|
||||
// const proxy = (await axios.get('http://127.0.0.1:10101/api/get_ip_list?num=1&country=US&state=all&city=all&zip=all&isp=all&t=1&port=40000&ip_time=1')).data
|
||||
console.log('proxy', proxy);
|
||||
|
||||
// return proxy ?
|
||||
return `--proxy-server=socks5://127.0.0.1:40000`
|
||||
}
|
||||
|
||||
const args = [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-web-security',
|
||||
'--disable-features=IsolateOrigins,site-per-process,SitePerProcess',
|
||||
'--flag-switches-begin --disable-site-isolation-trials --flag-switches-end'
|
||||
]
|
||||
|
||||
proxy ? args.push(await proxyCommand()) : null
|
||||
|
||||
// proxy ? args.push(await proxyCommand()) : ''
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: false,
|
||||
args
|
||||
})
|
||||
|
||||
browsers.push(browser)
|
||||
const close = browser.close
|
||||
browser.close = async function () {
|
||||
let i = browsers.find(b => b === browser)
|
||||
await close.call(this)
|
||||
if (i !== -1) browsers.slice(i, 1)
|
||||
}
|
||||
|
||||
const page = await browser.newPage()
|
||||
console.log('fakeUa', fakeUa());
|
||||
|
||||
changeUS && page.setUserAgent(fakeUa())
|
||||
await page.setExtraHTTPHeaders({
|
||||
'accept-language': 'en-US,en;q=0.9,hy;q=0.8'
|
||||
})
|
||||
|
||||
if (request) pageRequest(page)
|
||||
|
||||
return { browser, page }
|
||||
}
|
||||
|
||||
function getProxy () {
|
||||
return axios({
|
||||
url: 'http://api.tianqiip.com/getip?secret=loy0r7fpmnlshm8l&num=1&type=json&port=3&time=5&mr=1&sign=5f73ab58ad7ab40346311014bef59b79',
|
||||
method: 'get',
|
||||
}).then(res => {
|
||||
console.log('proxy', res.data.data[0])
|
||||
return res.data.data[0]
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 记录日志
|
||||
*/
|
||||
export function clog(options) {
|
||||
return (info, data = {}) => {
|
||||
if (win) {
|
||||
win?.webContents?.send?.('progress', { ...options, info, ...data })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个 promise
|
||||
*/
|
||||
export function createPromise<T> () {
|
||||
let resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject
|
||||
const p = new Promise<T>((res, rej) => {
|
||||
resolve = res
|
||||
reject = rej
|
||||
})
|
||||
return { resolve, reject, p }
|
||||
}
|
||||
|
||||
async function findElement (page: Page, selector: string, searchText: string) {
|
||||
return page.evaluate((selector, searchText) => {
|
||||
const elements = Array.from(document.querySelectorAll(selector));
|
||||
const target = elements.find(el => el.textContent.trim() === searchText);
|
||||
return target
|
||||
}, selector, searchText)
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素检查,被点击或按下按键后,检查是否成功,不成功测重试或其他操作
|
||||
*/
|
||||
export async function statusCheck(
|
||||
operationsFn: Function,
|
||||
checkFn: Function,
|
||||
options: {interval?: number, tryCount?: number} = {}
|
||||
) {
|
||||
options.interval = options.interval ?? 1000
|
||||
options.tryCount = options.tryCount ?? 1
|
||||
let { interval, tryCount } = options
|
||||
const { p, resolve, reject } = createPromise<boolean>()
|
||||
await operationsFn()
|
||||
const pass = await checkFn()
|
||||
console.log('检查状态', !!pass)
|
||||
if (pass) {
|
||||
resolve(true)
|
||||
} else {
|
||||
if (tryCount <= 5) {
|
||||
await sleep(interval)
|
||||
options.tryCount++
|
||||
return await statusCheck(operationsFn, checkFn, options)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
export function sleep (time = 1000) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, time)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 将方法重试n次
|
||||
*/
|
||||
export function retry(fn: Function, tryCount = 1) {
|
||||
return async function (...args) {
|
||||
try {
|
||||
return await fn(...args)
|
||||
} catch (error) {
|
||||
if (tryCount <= 5) {
|
||||
await sleep(1000)
|
||||
tryCount++
|
||||
return await retry(fn, tryCount)(...args)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,37 @@
|
||||
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
|
||||
return new Promise((resolve) => {
|
||||
if (condition.includes(document.readyState)) {
|
||||
resolve(true)
|
||||
} else {
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (condition.includes(document.readyState)) {
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const safeDOM = {
|
||||
append(parent: HTMLElement, child: HTMLElement) {
|
||||
if (!Array.from(parent.children).find(e => e === child)) {
|
||||
return parent.appendChild(child)
|
||||
}
|
||||
},
|
||||
remove(parent: HTMLElement, child: HTMLElement) {
|
||||
if (Array.from(parent.children).find(e => e === child)) {
|
||||
return parent.removeChild(child)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* https://tobiasahlin.com/spinkit
|
||||
* https://connoratherton.com/loaders
|
||||
* https://projects.lukehaas.me/css-loaders
|
||||
* https://matejkustec.github.io/SpinThatShit
|
||||
*/
|
||||
export function useLoading() {
|
||||
function useLoading() {
|
||||
const className = `loaders-css__square-spin`
|
||||
const styleContent = `
|
||||
@keyframes square-spin {
|
||||
@@ -43,25 +70,23 @@ export function useLoading() {
|
||||
|
||||
return {
|
||||
appendLoading() {
|
||||
safe.append(document.head, oStyle)
|
||||
safe.append(document.body, oDiv)
|
||||
safeDOM.append(document.head, oStyle)
|
||||
safeDOM.append(document.body, oDiv)
|
||||
},
|
||||
removeLoading() {
|
||||
safe.remove(document.head, oStyle)
|
||||
safe.remove(document.body, oDiv)
|
||||
safeDOM.remove(document.head, oStyle)
|
||||
safeDOM.remove(document.body, oDiv)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const safe = {
|
||||
append(parent: HTMLElement, child: HTMLElement) {
|
||||
if (!Array.from(parent.children).find(e => e === child)) {
|
||||
return parent.appendChild(child)
|
||||
}
|
||||
},
|
||||
remove(parent: HTMLElement, child: HTMLElement) {
|
||||
if (Array.from(parent.children).find(e => e === child)) {
|
||||
return parent.removeChild(child)
|
||||
}
|
||||
},
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const { appendLoading, removeLoading } = useLoading()
|
||||
domReady().then(appendLoading)
|
||||
|
||||
window.onmessage = (ev) => {
|
||||
ev.data.payload === 'removeLoading' && removeLoading()
|
||||
}
|
||||
|
||||
setTimeout(removeLoading, 4999)
|
@@ -2,10 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
<title>Vite App</title>
|
||||
<title>Electron + Vite + Vue</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
@@ -1,7 +0,0 @@
|
||||
export default {
|
||||
// eslint
|
||||
'*.{js,ts,tsx,vue}': 'eslint --cache --fix',
|
||||
// typecheck
|
||||
'packages/renderer/**/{*.ts,*.tsx,*.vue,tsconfig.json}': ({ filenames }) =>
|
||||
'npm run typecheck',
|
||||
}
|
7975
package-lock.json
generated
80
package.json
@@ -1,43 +1,59 @@
|
||||
{
|
||||
"name": "electron-vue-vite",
|
||||
"name": "poe_gpt",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/main/index.cjs",
|
||||
"main": "dist-electron/main/index.js",
|
||||
"description": "Really simple Electron + Vue + Vite boilerplate.",
|
||||
"author": "草鞋没号 <308487730@qq.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "node scripts/watch.mjs",
|
||||
"prebuild": "vue-tsc --noEmit --p packages/renderer/tsconfig.json && node scripts/build.mjs",
|
||||
"build": "electron-builder",
|
||||
"init": "git config core.hooksPath .git/hooks/ && rm -rf .git/hooks && npx simple-git-hooks",
|
||||
"test:e2e": "npx playwright test",
|
||||
"test:e2e:headless": "npx playwright test --headed"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.22.2",
|
||||
"@vitejs/plugin-vue": "^2.3.2",
|
||||
"electron": "19.0.1",
|
||||
"electron-builder": "^23.0.3",
|
||||
"nano-staged": "^0.8.0",
|
||||
"simple-git-hooks": "^2.8.0",
|
||||
"typescript": "^4.7.2",
|
||||
"vite": "^2.9.8",
|
||||
"vite-plugin-electron": "^0.4.5",
|
||||
"vite-plugin-resolve": "^2.1.2",
|
||||
"vue": "^3.2.36",
|
||||
"vue-tsc": "^0.35.2"
|
||||
},
|
||||
"env": {
|
||||
"VITE_DEV_SERVER_HOST": "127.0.0.1",
|
||||
"VITE_DEV_SERVER_PORT": 3344
|
||||
},
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"electron",
|
||||
"rollup",
|
||||
"vite",
|
||||
"vue3",
|
||||
"vue"
|
||||
]
|
||||
],
|
||||
"debug": {
|
||||
"env": {
|
||||
"VITE_DEV_SERVER_URL": "http://127.0.0.1:3200/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build && electron-builder",
|
||||
"build:64": "vite build && electron-builder --win --x64",
|
||||
"build:32": "vite build && electron-builder --win --x32",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"electron": "^25.0.1",
|
||||
"electron-builder": "^24.4.0",
|
||||
"typescript": "^5.0.2",
|
||||
"unplugin-auto-import": "^0.16.4",
|
||||
"unplugin-vue-components": "^0.25.0",
|
||||
"vite": "^4.1.4",
|
||||
"vite-plugin-electron": "^0.12.0",
|
||||
"vite-plugin-electron-renderer": "^0.14.1",
|
||||
"vue": "^3.2.47",
|
||||
"vue-tsc": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"axios": "^1.4.0",
|
||||
"chrome-launcher": "^0.15.2",
|
||||
"fake-useragent": "^1.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"naive-ui": "^2.34.4",
|
||||
"proxy-chain": "^2.3.0",
|
||||
"puppeteer": "^13.3.2",
|
||||
"puppeteer-extra": "^3.3.6",
|
||||
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||
"sass": "^1.62.1",
|
||||
"socks-proxy-agent": "^8.0.1",
|
||||
"unocss": "^0.52.7"
|
||||
}
|
||||
}
|
||||
|
@@ -1,93 +0,0 @@
|
||||
import { app, BrowserWindow, shell,ipcMain } from 'electron'
|
||||
import { release } from 'os'
|
||||
import { join } from 'path'
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
|
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||
|
||||
let win: BrowserWindow | null = null
|
||||
|
||||
async function createWindow() {
|
||||
win = new BrowserWindow({
|
||||
title: 'Main window',
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.cjs'),
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
},
|
||||
})
|
||||
|
||||
if (app.isPackaged) {
|
||||
win.loadFile(join(__dirname, '../renderer/index.html'))
|
||||
} else {
|
||||
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
|
||||
const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
|
||||
|
||||
win.loadURL(url)
|
||||
// win.webContents.openDevTools()
|
||||
}
|
||||
|
||||
// Test active push message to Renderer-process
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
win?.webContents.send('main-process-message', new Date().toLocaleString())
|
||||
})
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https:')) shell.openExternal(url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(createWindow)
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
win = null
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
app.on('second-instance', () => {
|
||||
if (win) {
|
||||
// Focus on the main window if the user tried to open another
|
||||
if (win.isMinimized()) win.restore()
|
||||
win.focus()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length) {
|
||||
allWindows[0].focus()
|
||||
} else {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
// new window example arg: new windows url
|
||||
ipcMain.handle("open-win", (event, arg) => {
|
||||
const childWindow = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: join(__dirname, "../preload/index.cjs"),
|
||||
},
|
||||
});
|
||||
|
||||
if (app.isPackaged) {
|
||||
childWindow.loadFile(join(__dirname, `../renderer/index.html`), {
|
||||
hash: `${arg}`,
|
||||
})
|
||||
} else {
|
||||
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
|
||||
const url = `http://${process.env["VITE_DEV_SERVER_HOST"]}:${process.env["VITE_DEV_SERVER_PORT"]}/#${arg}`
|
||||
childWindow.loadURL(url);
|
||||
// childWindow.webContents.openDevTools({ mode: "undocked", activate: true })
|
||||
}
|
||||
});
|
@@ -1,26 +0,0 @@
|
||||
import { builtinModules } from 'module'
|
||||
import { defineConfig } from 'vite'
|
||||
import pkg from '../../package.json'
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
build: {
|
||||
outDir: '../../dist/main',
|
||||
emptyOutDir: true,
|
||||
minify: process.env./* from mode option */NODE_ENV === 'production',
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry: 'index.ts',
|
||||
formats: ['cjs'],
|
||||
fileName: () => '[name].cjs',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
'electron',
|
||||
...builtinModules,
|
||||
// @ts-ignore
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
@@ -1,7 +0,0 @@
|
||||
import { domReady } from './utils'
|
||||
import { useLoading } from './loading'
|
||||
|
||||
const { appendLoading, removeLoading } = useLoading()
|
||||
window.removeLoading = removeLoading
|
||||
|
||||
domReady().then(appendLoading)
|
@@ -1,15 +0,0 @@
|
||||
|
||||
/** docoment ready */
|
||||
export function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
|
||||
return new Promise(resolve => {
|
||||
if (condition.includes(document.readyState)) {
|
||||
resolve(true)
|
||||
} else {
|
||||
document.addEventListener('readystatechange', () => {
|
||||
if (condition.includes(document.readyState)) {
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
import { join } from 'path'
|
||||
import { builtinModules } from 'module'
|
||||
import { defineConfig } from 'vite'
|
||||
import pkg from '../../package.json'
|
||||
|
||||
export default defineConfig({
|
||||
root: __dirname,
|
||||
build: {
|
||||
outDir: '../../dist/preload',
|
||||
emptyOutDir: true,
|
||||
minify: process.env./* from mode option */NODE_ENV === 'production',
|
||||
// https://github.com/caoxiemeihao/electron-vue-vite/issues/61
|
||||
sourcemap: 'inline',
|
||||
rollupOptions: {
|
||||
input: {
|
||||
// multiple entry
|
||||
index: join(__dirname, 'index.ts'),
|
||||
},
|
||||
output: {
|
||||
format: 'cjs',
|
||||
entryFileNames: '[name].cjs',
|
||||
manualChunks: {},
|
||||
},
|
||||
external: [
|
||||
'electron',
|
||||
...builtinModules,
|
||||
// @ts-ignore
|
||||
...Object.keys(pkg.dependencies || {}),
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 5.9 MiB |
@@ -1,55 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
// This starter template is using Vue 3 <script setup> SFCs
|
||||
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="logo-box">
|
||||
<img style="height:140px;" src="./assets/electron.png" >
|
||||
<span/>
|
||||
<img style="height:140px;" src="./assets/vite.svg" >
|
||||
<span/>
|
||||
<img style="height:140px;" src="./assets/vue.png" >
|
||||
</div>
|
||||
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
|
||||
<div class="static-public">
|
||||
Place static files into the <code>src/renderer/public</code> folder
|
||||
<img style="width:90px;" :src="'./images/node.png'" >
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.logo-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo-box span {
|
||||
width: 74px;
|
||||
}
|
||||
|
||||
.static-public {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.static-public code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
margin: 0 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
Before Width: | Height: | Size: 62 KiB |
@@ -1,15 +0,0 @@
|
||||
<svg width="410" height="404" viewBox="0 0 410 404" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M399.641 59.5246L215.643 388.545C211.844 395.338 202.084 395.378 198.228 388.618L10.5817 59.5563C6.38087 52.1896 12.6802 43.2665 21.0281 44.7586L205.223 77.6824C206.398 77.8924 207.601 77.8904 208.776 77.6763L389.119 44.8058C397.439 43.2894 403.768 52.1434 399.641 59.5246Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M292.965 1.5744L156.801 28.2552C154.563 28.6937 152.906 30.5903 152.771 32.8664L144.395 174.33C144.198 177.662 147.258 180.248 150.51 179.498L188.42 170.749C191.967 169.931 195.172 173.055 194.443 176.622L183.18 231.775C182.422 235.487 185.907 238.661 189.532 237.56L212.947 230.446C216.577 229.344 220.065 232.527 219.297 236.242L201.398 322.875C200.278 328.294 207.486 331.249 210.492 326.603L212.5 323.5L323.454 102.072C325.312 98.3645 322.108 94.137 318.036 94.9228L279.014 102.454C275.347 103.161 272.227 99.746 273.262 96.1583L298.731 7.86689C299.767 4.27314 296.636 0.855181 292.965 1.5744Z" fill="url(#paint1_linear)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#41D1FF"/>
|
||||
<stop offset="1" stop-color="#BD34FE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFEA83"/>
|
||||
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||
<stop offset="1" stop-color="#FFA800"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 6.7 KiB |
@@ -1,52 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<p>
|
||||
Recommended IDE setup:
|
||||
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
|
||||
+
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
</p>
|
||||
|
||||
<p>See <code>README.md</code> for more information.</p>
|
||||
|
||||
<p>
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
||||
Vite Docs
|
||||
</a>
|
||||
|
|
||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
||||
</p>
|
||||
|
||||
<button type="button" @click="count++">count is: {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 0 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #eee;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
color: #304455;
|
||||
}
|
||||
</style>
|
8
packages/renderer/src/global.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
|
||||
export { }
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
removeLoading: () => void
|
||||
}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["esnext", "dom"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import resolve, { lib2esm } from 'vite-plugin-resolve'
|
||||
import electron from 'vite-plugin-electron/renderer'
|
||||
import pkg from '../../package.json'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
mode: process.env.NODE_ENV,
|
||||
root: __dirname,
|
||||
plugins: [
|
||||
vue(),
|
||||
electron(),
|
||||
resolve(
|
||||
/**
|
||||
* Here you can specify other modules
|
||||
* 🚧 You have to make sure that your module is in `dependencies` and not in the` devDependencies`,
|
||||
* which will ensure that the electron-builder can package it correctly
|
||||
*/
|
||||
{
|
||||
// If you use the following modules, the following configuration will work
|
||||
// What they have in common is that they will return - ESM format code snippets
|
||||
|
||||
// ESM format string
|
||||
'electron-store': 'export default require("electron-store");',
|
||||
// Use lib2esm() to easy to convert ESM
|
||||
// Equivalent to
|
||||
/**
|
||||
* sqlite3: () => `
|
||||
* const _M_ = require('sqlite3');
|
||||
* const _D_ = _M_.default || _M_;
|
||||
* export { _D_ as default }
|
||||
* `
|
||||
*/
|
||||
sqlite3: lib2esm('sqlite3', { format: 'cjs' }),
|
||||
serialport: lib2esm(
|
||||
// CJS lib name
|
||||
'serialport',
|
||||
// export memebers
|
||||
[
|
||||
'SerialPort',
|
||||
'SerialPortMock',
|
||||
],
|
||||
{ format: 'cjs' },
|
||||
),
|
||||
}
|
||||
),
|
||||
],
|
||||
base: './',
|
||||
build: {
|
||||
outDir: '../../dist/renderer',
|
||||
emptyOutDir: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
server: {
|
||||
host: pkg.env.VITE_DEV_SERVER_HOST,
|
||||
port: pkg.env.VITE_DEV_SERVER_PORT,
|
||||
},
|
||||
})
|
@@ -1,16 +0,0 @@
|
||||
import { PlaywrightTestConfig, devices } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
};
|
||||
export default config;
|
20
public/logo.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<svg width="534" height="534" viewBox="0 0 534 534" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M266.667 533.333C413.943 533.333 533.333 413.943 533.333 266.667C533.333 119.391 413.943 0 266.667 0C119.391 0 0 119.391 0 266.667C0 413.943 119.391 533.333 266.667 533.333Z" fill="url(#paint0_linear)"/>
|
||||
<path d="M408.012 291.99C443.487 250.081 455.873 207.931 438.835 178.423C426.3 156.708 399.798 145.244 364.575 144.785C363.115 144.788 361.715 145.364 360.677 146.39C359.639 147.417 359.046 148.81 359.027 150.27C359.008 151.729 359.565 153.138 360.576 154.191C361.587 155.243 362.972 155.856 364.431 155.896C396.108 156.308 418.942 166.185 429.215 183.979C443.365 208.49 432.319 246.077 399.531 284.81C399.05 285.366 398.684 286.011 398.454 286.709C398.224 287.408 398.135 288.144 398.192 288.877C398.249 289.61 398.45 290.324 398.785 290.978C399.12 291.632 399.581 292.214 400.142 292.689C400.703 293.163 401.352 293.522 402.053 293.744C402.753 293.966 403.491 294.047 404.223 293.982C404.955 293.917 405.667 293.708 406.317 293.366C406.968 293.023 407.544 292.556 408.012 291.99ZM316.844 149.958C284.769 156.831 250.627 170.269 217.84 189.198C136.71 236.037 83.7874 305.106 91.1374 355.187C91.2354 355.916 91.4771 356.618 91.8485 357.253C92.2198 357.888 92.7135 358.442 93.3008 358.885C93.8881 359.327 94.5574 359.649 95.2699 359.831C95.9824 360.013 96.7239 360.051 97.4515 359.944C98.179 359.837 98.8781 359.587 99.5082 359.208C100.138 358.829 100.687 358.329 101.122 357.736C101.558 357.144 101.871 356.47 102.044 355.756C102.217 355.041 102.247 354.299 102.131 353.573C95.6041 309.106 145.771 243.637 223.396 198.821C255.202 180.456 288.26 167.446 319.171 160.823C319.894 160.68 320.582 160.395 321.194 159.984C321.806 159.573 322.33 159.044 322.736 158.429C323.142 157.813 323.421 157.123 323.557 156.398C323.693 155.673 323.684 154.929 323.529 154.208C323.375 153.487 323.078 152.804 322.657 152.199C322.236 151.593 321.699 151.078 321.077 150.682C320.455 150.287 319.76 150.019 319.033 149.895C318.306 149.771 317.562 149.792 316.844 149.958V149.958Z" fill="#9feaf9"/>
|
||||
<path d="M211.933 148.108C157.756 138.242 114.919 148.569 97.8458 178.14C85.1062 200.206 88.7478 229.462 106.679 260.735C107.033 261.385 107.513 261.957 108.092 262.419C108.67 262.88 109.334 263.221 110.046 263.423C110.758 263.624 111.503 263.681 112.237 263.59C112.971 263.5 113.68 263.264 114.322 262.896C114.963 262.528 115.525 262.035 115.974 261.447C116.423 260.859 116.75 260.188 116.936 259.472C117.121 258.755 117.162 258.01 117.056 257.277C116.95 256.545 116.698 255.842 116.317 255.208C100.167 227.037 97.0228 201.79 107.469 183.696C121.652 159.131 159.86 149.919 209.942 159.04C211.381 159.279 212.856 158.943 214.05 158.104C215.243 157.265 216.059 155.99 216.32 154.555C216.582 153.119 216.268 151.639 215.448 150.433C214.627 149.227 213.364 148.392 211.933 148.108Z" fill="#9feaf9"/>
|
||||
<path d="M298.488 204.045L234.806 216.594C233.76 216.8 232.985 217.692 232.922 218.763L229.004 285.297C228.912 286.864 230.343 288.081 231.864 287.728L249.594 283.613C251.253 283.228 252.752 284.698 252.411 286.375L247.143 312.315C246.789 314.061 248.419 315.554 250.114 315.036L261.065 311.69C262.762 311.172 264.394 312.669 264.035 314.416L255.664 355.162C255.14 357.711 258.511 359.101 259.917 356.916L260.856 355.456L312.747 251.312C313.616 249.569 312.117 247.58 310.213 247.95L291.963 251.492C290.248 251.824 288.789 250.218 289.273 248.531L301.184 207.005C301.669 205.315 300.205 203.707 298.488 204.045Z" fill="url(#paint1_linear)"/>
|
||||
<path d="M217.677 364.14C185.219 345.402 156.758 322.821 134.852 298.748C134.358 298.209 133.975 297.577 133.725 296.89C133.475 296.202 133.363 295.472 133.396 294.742C133.429 294.011 133.606 293.294 133.917 292.632C134.228 291.97 134.666 291.375 135.207 290.883C135.748 290.391 136.381 290.01 137.07 289.763C137.758 289.516 138.489 289.407 139.219 289.443C139.95 289.479 140.666 289.658 141.327 289.972C141.988 290.285 142.581 290.726 143.071 291.269C164.185 314.473 191.746 336.34 223.233 354.519C298.527 397.99 378.002 409.51 414.906 384.348C415.508 383.922 416.189 383.619 416.91 383.459C417.63 383.299 418.375 383.284 419.101 383.414C419.827 383.545 420.52 383.819 421.139 384.22C421.758 384.622 422.291 385.142 422.707 385.752C423.123 386.362 423.413 387.048 423.56 387.771C423.708 388.494 423.71 389.239 423.567 389.963C423.424 390.686 423.137 391.374 422.725 391.986C422.313 392.598 421.783 393.122 421.167 393.527C379.933 421.642 296.267 409.515 217.677 364.14Z" fill="#9feaf9"/>
|
||||
<path d="M185.408 389.906C203.958 441.698 234.302 473.587 268.423 473.587C293.308 473.587 316.331 456.623 334.285 426.758C334.674 426.133 334.934 425.436 335.051 424.709C335.168 423.982 335.139 423.239 334.967 422.523C334.794 421.807 334.481 421.133 334.045 420.539C333.609 419.945 333.06 419.444 332.428 419.065C331.797 418.685 331.097 418.435 330.368 418.329C329.639 418.222 328.897 418.262 328.184 418.445C327.47 418.628 326.8 418.951 326.213 419.396C325.626 419.84 325.133 420.397 324.762 421.033C308.627 447.873 288.808 462.475 268.423 462.475C240.079 462.475 213.015 434.033 195.871 386.162C195.636 385.461 195.264 384.814 194.776 384.259C194.288 383.704 193.694 383.252 193.029 382.929C192.364 382.606 191.641 382.419 190.903 382.379C190.165 382.339 189.426 382.447 188.73 382.696C188.034 382.945 187.395 383.331 186.85 383.83C186.304 384.329 185.864 384.932 185.555 385.604C185.246 386.275 185.074 387.001 185.049 387.74C185.024 388.479 185.147 389.215 185.41 389.906H185.408ZM354.829 379.775C364.433 349.092 369.59 313.696 369.59 276.81C369.59 184.771 337.375 105.429 291.492 85.0562C290.151 84.4948 288.644 84.4809 287.294 85.0174C285.943 85.554 284.857 86.5983 284.267 87.9265C283.677 89.2546 283.631 90.761 284.139 92.1226C284.647 93.4842 285.668 94.5927 286.983 95.2104C327.906 113.383 358.477 188.681 358.477 276.81C358.477 312.596 353.483 346.873 344.227 376.456C343.997 377.156 343.908 377.894 343.966 378.628C344.024 379.362 344.227 380.077 344.564 380.732C344.901 381.387 345.364 381.968 345.928 382.442C346.491 382.917 347.143 383.274 347.846 383.494C348.549 383.714 349.288 383.792 350.021 383.724C350.754 383.655 351.467 383.442 352.117 383.096C352.767 382.75 353.341 382.278 353.807 381.708C354.273 381.138 354.621 380.481 354.831 379.775H354.829ZM459.462 374.333C459.462 359.648 447.558 347.744 432.873 347.744C418.187 347.744 406.283 359.648 406.283 374.333C406.283 389.019 418.187 400.923 432.873 400.923C447.558 400.923 459.462 389.019 459.462 374.333ZM448.352 374.333C448.352 376.366 447.952 378.379 447.174 380.257C446.396 382.135 445.256 383.841 443.818 385.279C442.381 386.716 440.674 387.856 438.796 388.634C436.918 389.412 434.906 389.812 432.873 389.812C430.84 389.812 428.827 389.412 426.949 388.634C425.071 387.856 423.365 386.716 421.927 385.279C420.49 383.841 419.35 382.135 418.572 380.257C417.794 378.379 417.394 376.366 417.394 374.333C417.394 370.228 419.025 366.291 421.927 363.388C424.83 360.485 428.768 358.854 432.873 358.854C436.978 358.854 440.915 360.485 443.818 363.388C446.721 366.291 448.352 370.228 448.352 374.333ZM103.59 400.923C118.277 400.923 130.181 389.019 130.181 374.333C130.181 359.648 118.275 347.744 103.59 347.744C88.9062 347.744 77 359.648 77 374.333C77 389.019 88.9062 400.923 103.59 400.923ZM103.59 389.812C99.4843 389.812 95.5471 388.182 92.6442 385.279C89.7413 382.376 88.1104 378.439 88.1104 374.333C88.1104 370.228 89.7413 366.291 92.6442 363.388C95.5471 360.485 99.4843 358.854 103.59 358.854C107.695 358.854 111.632 360.485 114.535 363.388C117.438 366.291 119.069 370.228 119.069 374.333C119.069 378.439 117.438 382.376 114.535 385.279C111.632 388.182 107.695 389.812 103.59 389.812Z" fill="#9feaf9"/>
|
||||
<path d="M268.423 112.179C283.108 112.179 295.013 100.275 295.013 85.5896C295.013 70.9042 283.108 59 268.423 59C253.738 59 241.833 70.9042 241.833 85.5896C241.833 100.275 253.738 112.179 268.423 112.179ZM268.423 101.069C264.318 101.069 260.38 99.4379 257.478 96.535C254.575 93.6321 252.944 89.6949 252.944 85.5896C252.944 81.4843 254.575 77.5471 257.478 74.6442C260.38 71.7413 264.318 70.1104 268.423 70.1104C272.528 70.1104 276.465 71.7413 279.368 74.6442C282.271 77.5471 283.902 81.4843 283.902 85.5896C283.902 89.6949 282.271 93.6321 279.368 96.535C276.465 99.4379 272.528 101.069 268.423 101.069Z" fill="#9feaf9"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="6.00017" y1="32.9999" x2="235" y2="344" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#41D1FF"/>
|
||||
<stop offset="1" stop-color="#BD34FE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="194.651" y1="8.81818" x2="236.076" y2="292.989" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFEA83"/>
|
||||
<stop offset="0.0833333" stop-color="#FFDD35"/>
|
||||
<stop offset="1" stop-color="#FFA800"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 8.6 KiB |
BIN
response.png
Normal file
After Width: | Height: | Size: 28 KiB |
@@ -1,5 +0,0 @@
|
||||
import { build } from 'vite'
|
||||
|
||||
await build({ configFile: 'packages/main/vite.config.ts' })
|
||||
await build({ configFile: 'packages/preload/vite.config.ts' })
|
||||
await build({ configFile: 'packages/renderer/vite.config.ts' })
|
@@ -1,98 +0,0 @@
|
||||
import { spawn } from 'child_process'
|
||||
import { createServer, build } from 'vite'
|
||||
import electron from 'electron'
|
||||
import readline from 'readline'
|
||||
|
||||
const query = new URLSearchParams(import.meta.url.split('?')[1])
|
||||
const debug = query.has('debug')
|
||||
|
||||
/** The log will display on the next screen */
|
||||
function clearConsole() {
|
||||
const blank = '\n'.repeat(process.stdout.rows)
|
||||
console.log(blank)
|
||||
readline.cursorTo(process.stdout, 0, 0)
|
||||
readline.clearScreenDown(process.stdout)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
|
||||
*/
|
||||
function watchMain(server) {
|
||||
/**
|
||||
* @type {import('child_process').ChildProcessWithoutNullStreams | null}
|
||||
*/
|
||||
let electronProcess = null
|
||||
const address = server.httpServer.address()
|
||||
const env = Object.assign(process.env, {
|
||||
VITE_DEV_SERVER_HOST: address.address,
|
||||
VITE_DEV_SERVER_PORT: address.port,
|
||||
})
|
||||
|
||||
/**
|
||||
* @type {import('vite').Plugin}
|
||||
*/
|
||||
const startElectron = {
|
||||
name: 'electron-main-watcher',
|
||||
writeBundle() {
|
||||
clearConsole()
|
||||
|
||||
if (electronProcess) {
|
||||
electronProcess.removeAllListeners()
|
||||
electronProcess.kill()
|
||||
}
|
||||
|
||||
electronProcess = spawn(electron, ['.'], { env })
|
||||
electronProcess.once('exit', process.exit)
|
||||
// https://github.com/electron-vite/electron-vite-vue/pull/129
|
||||
electronProcess.stdout.on('data', (data) => {
|
||||
const str = data.toString().trim()
|
||||
str && console.log(str)
|
||||
})
|
||||
electronProcess.stderr.on('data', (data) => {
|
||||
const str = data.toString().trim()
|
||||
str && console.error(str)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
return build({
|
||||
configFile: 'packages/main/vite.config.ts',
|
||||
mode: 'development',
|
||||
plugins: [!debug && startElectron].filter(Boolean),
|
||||
build: {
|
||||
watch: {},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
|
||||
*/
|
||||
function watchPreload(server) {
|
||||
return build({
|
||||
configFile: 'packages/preload/vite.config.ts',
|
||||
mode: 'development',
|
||||
plugins: [{
|
||||
name: 'electron-preload-watcher',
|
||||
writeBundle() {
|
||||
clearConsole()
|
||||
server.ws.send({ type: 'full-reload' })
|
||||
},
|
||||
}],
|
||||
build: {
|
||||
watch: {},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Block the CTRL + C shortcut on a Windows terminal and exit the application without displaying a query
|
||||
if (process.platform === 'win32') {
|
||||
readline.createInterface({ input: process.stdin, output: process.stdout }).on('SIGINT', process.exit)
|
||||
}
|
||||
|
||||
// bootstrap
|
||||
const server = await createServer({ configFile: 'packages/renderer/vite.config.ts' })
|
||||
|
||||
await server.listen()
|
||||
await watchPreload(server)
|
||||
await watchMain(server)
|
390
src/App.vue
Normal file
@@ -0,0 +1,390 @@
|
||||
<script setup lang="tsx">
|
||||
import { ipcRenderer } from 'electron'
|
||||
import { NButton } from 'naive-ui'
|
||||
import { useClipboard, useLocalStorage } from '@vueuse/core'
|
||||
import Business from './Business.vue'
|
||||
|
||||
const input = useLocalStorage('accountInput', 'pollcribracacom@mail.com-----XAxeEgy34j')
|
||||
// const input = ref('126vdsjmgyanpgqrvb@ddmvp.icu----EOJ2NgPfS')
|
||||
// const input = ref('traceetakashi6274@gmail.com----kedaraditi0214----kedaraditi4760@hotmail.com')
|
||||
|
||||
const accountArr = computed(() => {
|
||||
const text = input.value.split('\n').filter(Boolean).map(v => {
|
||||
return v.split(/(——|-)+/).filter(v => !['-', '——'].includes(v))
|
||||
})
|
||||
return text
|
||||
})
|
||||
|
||||
const result = ref('')
|
||||
const liao = useLocalStorage('result', '')
|
||||
|
||||
const list = useLocalStorage('list', [] as any[])
|
||||
const listInstock = ref([] as any[])
|
||||
listInstock.value = list.value.filter(v => v.instock === true)
|
||||
|
||||
const filterInput = ref('')
|
||||
|
||||
const renderList = computed(() => {
|
||||
// 成功的排在前面
|
||||
let data = list.value.sort((a, b) => {
|
||||
if (a.type === 'success' && b.type !== 'success') {
|
||||
return -1
|
||||
} else if (a.type !== 'success' && b.type === 'success') {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}).filter(v => v.instock !== true)
|
||||
|
||||
if (filterInput.value) {
|
||||
data = data.filter(v => {
|
||||
return v.user.startsWith(filterInput.value)
|
||||
})
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
|
||||
|
||||
const remainingData = computed(() => {
|
||||
return accountArr.value.filter((item, i) => {
|
||||
const user = item[0]
|
||||
const target = list.value.find((v) => v.user === user)
|
||||
if (target && target.type === 'success') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
function copyRemaining() {
|
||||
const text = remainingData.value.map(v => `${v[0]}----${v[1]}`).join('\n')
|
||||
copyText(text)
|
||||
}
|
||||
|
||||
const remainingColumns = [{
|
||||
title: '序列',
|
||||
render: (row, index) => h('span', undefined, index + 1),
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
key: '0',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: '密码',
|
||||
key: '1',
|
||||
width: 220
|
||||
},
|
||||
]
|
||||
|
||||
ipcRenderer.on('progress', (event, args) => {
|
||||
const { user } = args
|
||||
const target = list.value.find((item) => item.user === user)
|
||||
|
||||
if (target) {
|
||||
Object.assign(target, args)
|
||||
} else {
|
||||
list.value.push(args)
|
||||
}
|
||||
})
|
||||
|
||||
// function handler () {
|
||||
// const data = {
|
||||
// 'getLink'
|
||||
// }
|
||||
// }
|
||||
|
||||
function getLink(val?: string) {
|
||||
ipcRenderer.invoke('getLink', { text: val || input.value }).then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
}
|
||||
|
||||
function getLink_7day(val?: string) {
|
||||
let liaoObj
|
||||
if (liao.value) {
|
||||
const [_1, bank, cvc, cardExpiry, name, address, city, _2, postalCode, nation] = liao.value.split('|').map(v => v.trim())
|
||||
liaoObj = { bank, cvc, date: cardExpiry, name, address, city, postalCode, nation }
|
||||
}
|
||||
|
||||
console.log(liaoObj)
|
||||
ipcRenderer.invoke('get-poe-link-7day', { text: val || input.value, liao: liaoObj }).then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
}
|
||||
|
||||
function getResult(val?: string) {
|
||||
ipcRenderer.invoke('poe-result', { text: val || input.value }).then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
}
|
||||
|
||||
function stopHandler() {
|
||||
ipcRenderer.invoke('stop')
|
||||
}
|
||||
function startOneChromHandler() {
|
||||
ipcRenderer.invoke('start-one-chrom')
|
||||
}
|
||||
|
||||
function gptLinkHandler() {
|
||||
ipcRenderer.invoke('gpt-link', { text: input.value })
|
||||
}
|
||||
|
||||
function gptResultHandler() {
|
||||
ipcRenderer.invoke('gpt-result', { text: input.value })
|
||||
}
|
||||
|
||||
const listSuccess = computed(() => {
|
||||
return list.value.filter((item) => item.instock !== true && item.type === 'success')
|
||||
})
|
||||
|
||||
function copyAllSuccess() {
|
||||
const text = listSuccess.value.map((item, i) => `${i + 1}. ${item.user}----${item.result}`).join('\n')
|
||||
copyText(text)
|
||||
}
|
||||
|
||||
const listFail = computed(() => {
|
||||
return list.value.filter((item) => item.type !== 'success')
|
||||
})
|
||||
|
||||
function copyFailHandler() {
|
||||
const text = listFail.value.map((item) => `${item.user}----${item.pass}`).join('\n\n')
|
||||
copyText(text)
|
||||
}
|
||||
|
||||
function clearLocalHandler() {
|
||||
list.value = list.value.filter(v => v.instock === true)
|
||||
}
|
||||
|
||||
const { copy } = useClipboard()
|
||||
function copyText(text: any) {
|
||||
copy(text)
|
||||
}
|
||||
function copyAccount(item: any) {
|
||||
copy(item.user + '----' + item.pass + '----' + item.auxiliary)
|
||||
}
|
||||
|
||||
function application() {
|
||||
ipcRenderer.invoke('gpt-batch-4.0', { text: input.value })
|
||||
}
|
||||
|
||||
// 申请结果
|
||||
function applicationResult() {
|
||||
ipcRenderer.invoke('gpt-batch-4.0-result', { text: input.value })
|
||||
}
|
||||
|
||||
const showBusiness = ref(false)
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序列',
|
||||
render: (row, index) => h('span', undefined, index + 1),
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
key: 'user',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: '进度',
|
||||
key: 'info',
|
||||
width: 220
|
||||
},
|
||||
{
|
||||
title: '结果',
|
||||
key: 'result',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 230,
|
||||
align: 'left',
|
||||
fixed: 'right',
|
||||
render: (row: any) => {
|
||||
/**
|
||||
* poe-link
|
||||
* 再次提链: [type: fail, loading: false]
|
||||
* 复制链接: [type: success, loading: false]
|
||||
* 充值结果: [type: success, loading: false]
|
||||
*/
|
||||
|
||||
/**
|
||||
* poe-validate
|
||||
* 充值结果: [type: fail, loading: false]
|
||||
*/
|
||||
const rechargeResult = () => {
|
||||
let link = row.ident === 'poe-link' && !row.loading
|
||||
let validate = row.ident === 'poe-validate' && row.type !== 'success' && !row.loading
|
||||
return link || validate
|
||||
}
|
||||
|
||||
return <n-space>
|
||||
{row.ident === 'poe-link' && row.type === 'success' && !row.loading
|
||||
? <NButton text type="primary" onClick={() => copyText(`${row.user}\n${row.result}`)}>复制链接</NButton>
|
||||
: null
|
||||
}
|
||||
|
||||
{row.type === 'success'
|
||||
? <NButton text type="primary" onClick={() => row.instock = true}>标记入库</NButton>
|
||||
: null
|
||||
}
|
||||
|
||||
{row.ident === 'poe-link' && row.type !== 'success' && !row.loading
|
||||
? <NButton text type="primary" onClick={() => getLink(`${row.user}----${row.pass}----${row.auxiliary}`)}>再次提链</NButton>
|
||||
: null
|
||||
}
|
||||
|
||||
<NButton text type="primary" onClick={() => copyAccount(row)}>复制帐密</NButton>
|
||||
|
||||
{rechargeResult() //.ident === 'poe-link' && row.type === 'success' && !row.loading
|
||||
? <NButton text type="info" onClick={() => getResult(`${row.user}----${row.pass}----${row.auxiliary}`)}>充值结果</NButton>
|
||||
: null
|
||||
}
|
||||
|
||||
</n-space>
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const liaoColumns = [
|
||||
{
|
||||
key: 'bank',
|
||||
title: '银行'
|
||||
},
|
||||
{
|
||||
key: 'cvc',
|
||||
title: 'cvc'
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
title: '日期'
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: '名字'
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
title: '地址'
|
||||
},
|
||||
{
|
||||
key: 'city',
|
||||
title: '城市'
|
||||
},
|
||||
{
|
||||
key: 'postalCode',
|
||||
title: '邮政编码'
|
||||
},
|
||||
{
|
||||
key: 'nation',
|
||||
title: '国家'
|
||||
}
|
||||
]
|
||||
|
||||
const liaoData = computed(() => {
|
||||
if (liao.value) {
|
||||
const [_1, bank, cvc, cardExpiry, name, address, city, _2, postalCode, nation] = liao.value.split('|').map(v => v.trim())
|
||||
return { bank, cvc, date: cardExpiry, name, address, city, postalCode, nation }
|
||||
}
|
||||
return {}
|
||||
})
|
||||
|
||||
function getLiao() {
|
||||
let liaoObj = liaoData.value
|
||||
copy(JSON.stringify(liaoObj))
|
||||
return liaoObj
|
||||
}
|
||||
|
||||
function handler(proxyType, name) {
|
||||
const isProxy = proxyType === 'proxy'
|
||||
console.log(proxyType, name, isProxy)
|
||||
let liaoObj
|
||||
// if (liao.value) {
|
||||
// const [_1, bank, cvc, cardExpiry, name, address, city, _2, postalCode, nation] = liao.value.split('|').map(v => v.trim())
|
||||
// liaoObj = { bank, cvc, date: cardExpiry, name, address, city, postalCode, nation }
|
||||
// }
|
||||
|
||||
ipcRenderer.invoke(name, { text: input.value, liao: liaoObj, proxy: isProxy })
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="left" flex-1>
|
||||
<p>帐密: {{ accountArr.length }}</p>
|
||||
<textarea class="textarea w-full min-w-100 max-w-94vw" v-model="input" rows="20" />
|
||||
|
||||
<div flex items-center>
|
||||
<p>liao:</p>
|
||||
<NInput class="textarea w-full min-w-100 max-w-94vw flex-1 mr-3" v-model:value="liao" />
|
||||
<NButton @click="getLiao">复制</NButton>
|
||||
</div>
|
||||
|
||||
<NDataTable v-if="liaoData.bank" :data="[liaoData]" :scroll-x="1000" :columns="liaoColumns" />
|
||||
|
||||
<div>
|
||||
<div flex gap-3 mt-3 items-center>
|
||||
<span w-15>poe:</span>
|
||||
<TheButton type="primary" dashed @select="key => handler(key, 'getLink')">提取链接</TheButton>
|
||||
<TheButton type="primary" dashed @select="key => handler(key, 'poe-result')">充值结果</TheButton>
|
||||
<TheButton type="warning" dashed @select="key => handler(key, 'get-poe-link-7day')">提取链接-7天</TheButton>
|
||||
<TheButton type="warning" dashed @select="key => handler(key, 'get-poe-cookie')">获取cookie</TheButton>
|
||||
</div>
|
||||
<div flex gap-3 mt-3 items-center>
|
||||
<span w-15>gpt4.0:</span>
|
||||
<TheButton type="primary" dashed @select="key => handler(key, 'gpt-batch-4.0')">申请4.0</TheButton>
|
||||
<TheButton type="primary" dashed @select="key => handler(key, 'gpt-batch-4.0-result')">检查申请结果(mail邮箱)
|
||||
</TheButton>
|
||||
</div>
|
||||
<div flex gap-3 mt-3 items-center>
|
||||
<span w-15 class="whitespace-nowrap">gpt plus:</span>
|
||||
<TheButton type="primary" dashed @select="key => handler(key, 'gpt-link')">gpt提链</TheButton>
|
||||
<TheButton type="primary" dashed @select="key => handler(key, 'gpt-result')">充值结果</TheButton>
|
||||
</div>
|
||||
<div flex gap-3 mt-3 items-center>
|
||||
<span w-15>操作:</span>
|
||||
<NButton type="error" dashed @click="stopHandler">关闭所有浏览器</NButton>
|
||||
<NButton type="primary" dashed @click="startOneChromHandler">开一个浏览器测试</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right" flex-1>
|
||||
<p>结果</p>
|
||||
|
||||
<n-space mb-3>
|
||||
<n-button type="primary" dashed @click="copyAllSuccess">复制全部成功 {{ listSuccess.length }}个</n-button>
|
||||
<n-button type="error" dashed @click="copyFailHandler">复制全部失败 {{ listFail.length }}个</n-button>
|
||||
<n-button type="error" dashed @dblclick="clearLocalHandler">清除记录(双击)</n-button>
|
||||
<n-button type="info" dashed @click="showBusiness = true">库存</n-button>
|
||||
|
||||
<n-input v-model:value="filterInput" clearable />
|
||||
</n-space>
|
||||
<NDataTable :data="renderList" :columns="columns" :scroll-x="1000" />
|
||||
</div>
|
||||
|
||||
<div flex items-center gap-4>
|
||||
<p>剩余:{{ remainingData.length }}</p>
|
||||
<NButton type="primary" @click="copyRemaining">复制</NButton>
|
||||
</div>
|
||||
<NDataTable :data="remainingData" :columns="remainingColumns"></NDataTable>
|
||||
|
||||
<Business v-if="showBusiness" @close="showBusiness = false" :data="list"/>
|
||||
<div h-10></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.textarea:nth-of-type() {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
108
src/Business.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<script lang="tsx" setup>
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
|
||||
const emits = defineEmits(['close'])
|
||||
const { data } = defineProps<{
|
||||
data: any[]
|
||||
}>()
|
||||
|
||||
const show = ref(true)
|
||||
|
||||
watch(show, () => {
|
||||
if (!show.value) emits('close')
|
||||
})
|
||||
|
||||
const key = useLocalStorage('modal_ident', '')
|
||||
const keys = computed(() => {
|
||||
return data.map((item) => item.ident)
|
||||
})
|
||||
const input = ref('')
|
||||
|
||||
const list = computed(() => {
|
||||
// 未卖
|
||||
const unsold = data.filter((item) => {
|
||||
return item.sold !== true
|
||||
}).sort((a, b) => b.soldTime - a.soldTime)
|
||||
// 已卖
|
||||
const sold = data.filter((item) => {
|
||||
return item.sold === true
|
||||
}).sort((a, b) => b.soldTime - a.soldTime)
|
||||
const newData = [...unsold, ...sold]
|
||||
|
||||
return newData.filter((item) => {
|
||||
return item.instock === true && item.ident === key.value && (input.value ? item.user.startsWith(input.value) : true)
|
||||
})
|
||||
})
|
||||
|
||||
// 标记已卖
|
||||
function handleMarkSold(row) {
|
||||
row.sold = true
|
||||
row.soldTime = Date.now()
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '序列',
|
||||
render: (row, index) => h('span', undefined, index + 1),
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
title: '邮箱',
|
||||
key: 'user',
|
||||
width: 220,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'info',
|
||||
width: 80,
|
||||
render: (row) => {
|
||||
return <n-space>
|
||||
{row.sold === true
|
||||
? <n-tag type="info">已卖</n-tag>
|
||||
: <n-tag type="success">未卖</n-tag>
|
||||
}
|
||||
</n-space>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '结果',
|
||||
key: 'result',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 100,
|
||||
align: 'left',
|
||||
fixed: 'right',
|
||||
render: (row: any) => {
|
||||
return <n-space>
|
||||
{row.sold !== true
|
||||
? <n-button text type="primary" onClick={() => handleMarkSold(row)}>标记已卖</n-button>
|
||||
: null
|
||||
}
|
||||
</n-space>
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NModal v-model:show="show">
|
||||
<n-card class="w-90%! max-w-1000px!" title="库存" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<n-input placeholder="搜索邮箱" v-model:value="input" />
|
||||
<n-tabs type="line" animated v-model:value="key">
|
||||
<n-tab-pane v-for="(item, index) in keys" :key="index" :name="item" />
|
||||
</n-tabs>
|
||||
|
||||
<NDataTable :data="list" :columns="columns" :scroll-x="1000"></NDataTable>
|
||||
<template #header-extra>
|
||||
<span px-3 class="cursor-pointer" @click="emits('close')">X</span>
|
||||
</template>
|
||||
<!-- <pre>{{ data }}</pre> -->
|
||||
</n-card>
|
||||
</NModal>
|
||||
</template>
|
1
src/assets/electron.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256"><g fill="none" fill-rule="evenodd"><circle fill="#2B2E3A" cx="128" cy="128" r="128"/><g fill="#9FEAF9" fill-rule="nonzero"><path d="M100.502 71.69c-26.005-4.736-46.567.221-54.762 14.415-6.115 10.592-4.367 24.635 4.24 39.646a2.667 2.667 0 1 0 4.626-2.653c-7.752-13.522-9.261-25.641-4.247-34.326 6.808-11.791 25.148-16.213 49.187-11.835a2.667 2.667 0 0 0 .956-5.247zm-36.999 72.307c10.515 11.555 24.176 22.394 39.756 31.388 37.723 21.78 77.883 27.601 97.675 14.106a2.667 2.667 0 1 0-3.005-4.406c-17.714 12.078-55.862 6.548-92.003-14.318-15.114-8.726-28.343-19.222-38.478-30.36a2.667 2.667 0 1 0-3.945 3.59z"/><path d="M194.62 140.753c17.028-20.116 22.973-40.348 14.795-54.512-6.017-10.423-18.738-15.926-35.645-16.146a2.667 2.667 0 0 0-.069 5.333c15.205.198 26.165 4.939 31.096 13.48 6.792 11.765 1.49 29.807-14.248 48.399a2.667 2.667 0 1 0 4.071 3.446zm-43.761-68.175c-15.396 3.299-31.784 9.749-47.522 18.835-38.942 22.483-64.345 55.636-60.817 79.675a2.667 2.667 0 1 0 5.277-.775c-3.133-21.344 20.947-52.769 58.207-74.281 15.267-8.815 31.135-15.06 45.972-18.239a2.667 2.667 0 1 0-1.117-5.215z"/><path d="M87.77 187.753c8.904 24.86 23.469 40.167 39.847 40.167 11.945 0 22.996-8.143 31.614-22.478a2.667 2.667 0 1 0-4.571-2.748c-7.745 12.883-17.258 19.892-27.043 19.892-13.605 0-26.596-13.652-34.825-36.63a2.667 2.667 0 1 0-5.021 1.797zm81.322-4.863c4.61-14.728 7.085-31.718 7.085-49.423 0-44.179-15.463-82.263-37.487-92.042a2.667 2.667 0 0 0-2.164 4.874c19.643 8.723 34.317 44.866 34.317 87.168 0 17.177-2.397 33.63-6.84 47.83a2.667 2.667 0 1 0 5.09 1.593zm50.224-2.612c0-7.049-5.714-12.763-12.763-12.763-7.049 0-12.763 5.714-12.763 12.763 0 7.049 5.714 12.763 12.763 12.763 7.049 0 12.763-5.714 12.763-12.763zm-5.333 0a7.43 7.43 0 1 1-14.86 0 7.43 7.43 0 0 1 14.86 0zM48.497 193.041c7.05 0 12.764-5.714 12.764-12.763 0-7.049-5.715-12.763-12.764-12.763-7.048 0-12.763 5.714-12.763 12.763 0 7.049 5.715 12.763 12.763 12.763zm0-5.333a7.43 7.43 0 1 1 0-14.86 7.43 7.43 0 0 1 0 14.86z"/><path d="M127.617 54.444c7.049 0 12.763-5.714 12.763-12.763 0-7.049-5.714-12.763-12.763-12.763-7.049 0-12.763 5.714-12.763 12.763 0 7.049 5.714 12.763 12.763 12.763zm0-5.333a7.43 7.43 0 1 1 0-14.86 7.43 7.43 0 0 1 0 14.86zm1.949 93.382c-4.985 1.077-9.896-2.091-10.975-7.076a9.236 9.236 0 0 1 7.076-10.976c4.985-1.077 9.896 2.091 10.976 7.076 1.077 4.985-2.091 9.897-7.077 10.976z"/></g></g></svg>
|
After Width: | Height: | Size: 2.4 KiB |
1
src/assets/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
178
src/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
|
||||
}
|
||||
// for vue template auto import
|
||||
import { UnwrapRef } from 'vue'
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||
}
|
||||
}
|
25
src/components.d.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
TheButton: typeof import('./components/TheButton.vue')['default']
|
||||
}
|
||||
}
|
38
src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps<{ msg: string }>()
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||
in your IDE for a better DX
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
30
src/components/TheButton.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
const options = [
|
||||
{
|
||||
label: '不加代理',
|
||||
key: 'notProxy',
|
||||
},
|
||||
{
|
||||
label: '加代理(port: 40000)',
|
||||
key: "proxy"
|
||||
},
|
||||
]
|
||||
|
||||
const emits = defineEmits(['select'])
|
||||
|
||||
const handler = (key: string) => {
|
||||
|
||||
emits('select', key)
|
||||
}
|
||||
|
||||
const config = useAttrs()
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<n-dropdown trigger="hover" :options="options" @select="handler">
|
||||
<n-button v-bind="config">
|
||||
<slot></slot>
|
||||
</n-button>
|
||||
</n-dropdown>
|
||||
</template>
|
@@ -1,7 +1,13 @@
|
||||
import { createApp } from 'vue'
|
||||
import "./style.css"
|
||||
import App from './App.vue'
|
||||
import './samples/node-api'
|
||||
|
||||
import 'uno.css'
|
||||
|
||||
|
||||
createApp(App)
|
||||
.mount('#app')
|
||||
.$nextTick(window.removeLoading)
|
||||
.$nextTick(() => {
|
||||
postMessage({ payload: 'removeLoading' }, '*')
|
||||
})
|
@@ -1,8 +1,7 @@
|
||||
import { lstat } from 'fs/promises'
|
||||
import { cwd } from 'process'
|
||||
import { lstat } from 'node:fs/promises'
|
||||
import { cwd } from 'node:process'
|
||||
import { ipcRenderer } from 'electron'
|
||||
|
||||
// Usage of ipcRenderer.on
|
||||
ipcRenderer.on('main-process-message', (_event, ...args) => {
|
||||
console.log('[Receive Main-process message]:', ...args)
|
||||
})
|
95
src/style.css
Normal file
@@ -0,0 +1,95 @@
|
||||
button:focus{outline-color: transparent !important;}
|
||||
|
||||
: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;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #1a1a1a;
|
||||
padding: 2px 4px;
|
||||
margin: 0 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
button,input,textarea{outline: none;}
|
||||
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
code {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
// example.spec.ts
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { env } from '../package.json'
|
||||
const VITE_SERVER_ADDRESS = `http://127.0.0.1:${env.PORT || 3344}`
|
||||
|
||||
test('example test case', async ({ page }) => {
|
||||
await page.goto(VITE_SERVER_ADDRESS)
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Vite App/)
|
||||
|
||||
// Expect an attribute "Hello Vue 3 + TypeScript + Vite" to be visible on the page.
|
||||
await expect(
|
||||
page.locator('text=Hello Vue 3 + TypeScript + Vite').first(),
|
||||
).toBeVisible()
|
||||
})
|
@@ -1,17 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"jsx": "preserve",
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"paths": {},
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
"noImplicitAny": false,
|
||||
"jsx": "preserve",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
|
10
tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts", "package.json", "electron"]
|
||||
}
|
8
types.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
NODE_ENV: 'development' | 'production'
|
||||
readonly VITE_DEV_SERVER_HOST: string
|
||||
readonly VITE_DEV_SERVER_PORT: string
|
||||
}
|
||||
}
|
35
unocss.config.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetUno,
|
||||
presetWebFonts,
|
||||
// transformerDirectives,
|
||||
// transformerVariantGroup,
|
||||
} from 'unocss'
|
||||
|
||||
export default defineConfig({
|
||||
// shortcuts: [
|
||||
// ['btn', 'px-4 py-1 rounded inline-block bg-teal-600 text-white cursor-pointer hover:bg-teal-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
|
||||
// ['icon-btn', 'text-[0.9em] inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600 !outline-none'],
|
||||
// ],
|
||||
presets: [
|
||||
presetUno(),
|
||||
presetAttributify(),
|
||||
presetIcons({
|
||||
scale: 1.2,
|
||||
warn: true,
|
||||
}),
|
||||
presetWebFonts({
|
||||
fonts: {
|
||||
sans: 'DM Sans',
|
||||
serif: 'DM Serif Display',
|
||||
mono: 'DM Mono',
|
||||
},
|
||||
}),
|
||||
],
|
||||
// transformers: [
|
||||
// transformerDirectives(),
|
||||
// transformerVariantGroup(),
|
||||
// ],
|
||||
})
|
98
vite.config.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { rmSync } from 'node:fs'
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import electron from 'vite-plugin-electron'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||
import renderer from 'vite-plugin-electron-renderer'
|
||||
import pkg from './package.json'
|
||||
import Unocss from 'unocss/vite'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ command }) => {
|
||||
rmSync('dist-electron', { recursive: true, force: true })
|
||||
|
||||
const isServe = command === 'serve'
|
||||
const isBuild = command === 'build'
|
||||
const sourcemap = isServe || !!process.env.VSCODE_DEBUG
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
Unocss(),
|
||||
AutoImport({
|
||||
imports: [
|
||||
'vue',
|
||||
],
|
||||
dts: 'src/auto-imports.d.ts',
|
||||
dirs: [],
|
||||
vueTemplate: true,
|
||||
}),
|
||||
// https://github.com/antfu/unplugin-vue-components
|
||||
Components({
|
||||
resolvers: [NaiveUiResolver()],
|
||||
// allow auto load markdown components under `./src/components/`
|
||||
extensions: ['vue', 'md'],
|
||||
// allow auto import and register components used in markdown
|
||||
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
||||
dts: 'src/components.d.ts',
|
||||
}),
|
||||
|
||||
|
||||
electron([
|
||||
{
|
||||
// Main-Process entry file of the Electron App.
|
||||
entry: 'electron/main/index.ts',
|
||||
onstart(options) {
|
||||
if (process.env.VSCODE_DEBUG) {
|
||||
console.log(/* For `.vscode/.debug.script.mjs` */'[startup] Electron App')
|
||||
} else {
|
||||
options.startup()
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
sourcemap,
|
||||
minify: isBuild,
|
||||
outDir: 'dist-electron/main',
|
||||
rollupOptions: {
|
||||
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
entry: 'electron/preload/index.ts',
|
||||
onstart(options) {
|
||||
// Notify the Renderer-Process to reload the page when the Preload-Scripts build is complete,
|
||||
// instead of restarting the entire Electron App.
|
||||
options.reload()
|
||||
},
|
||||
vite: {
|
||||
build: {
|
||||
sourcemap: sourcemap ? 'inline' : undefined, // #332
|
||||
minify: isBuild,
|
||||
outDir: 'dist-electron/preload',
|
||||
rollupOptions: {
|
||||
external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
]),
|
||||
// Use Node.js API in the Renderer-process
|
||||
renderer(),
|
||||
],
|
||||
server: process.env.VSCODE_DEBUG && (() => {
|
||||
const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL)
|
||||
return {
|
||||
host: url.hostname,
|
||||
port: +url.port,
|
||||
}
|
||||
})(),
|
||||
clearScreen: false,
|
||||
}
|
||||
})
|