This commit is contained in:
yema 2023-06-04 18:09:47 +08:00
parent 3d3daf1db5
commit 49d36516c1
20 changed files with 1016 additions and 49 deletions

View File

@ -0,0 +1,41 @@
import { clog } from "../poe/login";
import { login } from "./login";
export async function getLink(options) {
const log = clog(options)
log('开始', { ident: 'gpt-link' })
const page = await login(options)
// await page.waitForTimeout(500)
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.waitForSelector('.gold-new-button', { visible: true })
log('等待升级plus按钮出现')
await page.waitForTimeout(500)
log('开始点击升级plus按钮')
await page.click('.gold-new-button')
const [response] = await Promise.all([
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
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' })
return url
}
}

View File

@ -0,0 +1,45 @@
import { ipcMain } from 'electron'
import { getLink } from './getLink'
import { validate } from './validate'
import { browser } from './login'
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 accounts = parseAccount(text)
const links = []
for(let i = 0; i < accounts.length; i++) {
const [user, pass] = accounts[i]
const link = await getLink({ user, pass, index: i, id: user })
links.push({
i,
user,
link
})
console.log('process', i, user, link)
}
browser && browser.close()
})
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()
})

100
electron/main/gpt/login.ts Normal file
View File

@ -0,0 +1,100 @@
import type { Browser, Page } from 'puppeteer'
import puppeteer from 'puppeteer-extra'
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
import { randomNum } from '../tools'
import { win } from '../index'
import { clog } from '../poe/login'
puppeteer.use(StealthPlugin())
let id
export let browser: Browser | undefined
/**
* google
*/
export async function login(options, tryCount = 1): Promise<Page> {
let resolve, reject
const log = clog(options)
const p = new Promise((res, rej) => {
resolve = res
reject = rej
})
id = options.id
const env = {
GUSER: options.user,
GPASS: options.pass
}
if (browser) await browser.close()
browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
await page.setExtraHTTPHeaders({
'accept-language': 'en-US,en;q=0.9,hy;q=0.8'
})
log('开始访问 gpt')
await page.goto('https://chat.openai.com/auth/login')
await page.waitForSelector('button')
// log('准备进入 google 登录页')
const [response] = await Promise.all([
page.waitForNavigation(() => location.href.startsWith('https://auth0.openai.com/u/login/identifier')),
page.click('button')
])
// log('已进入 google 登录页')
if (!response.ok()) return log('进入 google 登录页失败')
log('准备输入账号')
await page.type('#username', env.GUSER)
await Promise.all([
page.waitForNavigation(),
page.keyboard.press('Enter')
])
log('已输入账号,准备输入密码')
await page.waitForSelector('#password', { visible: true })
await page.type('#password', env.GPASS)
log('已输入密码,开始登录')
await Promise.all([
page.waitForNavigation(() => location.href === 'https://chat.openai.com/'),
page.keyboard.press('Enter')
])
log('登录成功')
resolve(page)
// 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('已输入账号,准备输入密码')
// 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')
// ])
// log('登录成功,准备进入 poe')
// return page
// }
// if (tryCount < 4) {
// log(`google 登录失败,准备 ${tryCount} 次重试`)
// await page.close()
// return await loginGoogle(options, tryCount + 1)
// } else {
// log('google 登录失败,重试次数已达上限')
// reject('google 登录失败')
// }
return p
// return Promise.reject('google 登录失败')
}

View File

@ -0,0 +1,27 @@
import { clog } from "../poe/login";
import { login } from "./login";
export async function validate(options) {
const log = clog(options)
log('开始', { ident: 'gpt-validate' })
const page = await login(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)
}

View File

@ -1,6 +1,8 @@
import { app, BrowserWindow, shell, ipcMain } from 'electron' import { app, BrowserWindow, shell, ipcMain } from 'electron'
import { release } from 'node:os' import { release } from 'node:os'
import { join } from 'node:path' import { join } from 'node:path'
import './poe/index'
import './gpt/index'
// The built directory structure // The built directory structure
// //
@ -34,7 +36,7 @@ if (!app.requestSingleInstanceLock()) {
// Read more on https://www.electronjs.org/docs/latest/tutorial/security // Read more on https://www.electronjs.org/docs/latest/tutorial/security
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' // process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
let win: BrowserWindow | null = null export let win: BrowserWindow | null = null
// Here, you can also use other preload // Here, you can also use other preload
const preload = join(__dirname, '../preload/index.js') const preload = join(__dirname, '../preload/index.js')
const url = process.env.VITE_DEV_SERVER_URL const url = process.env.VITE_DEV_SERVER_URL
@ -43,6 +45,8 @@ const indexHtml = join(process.env.DIST, 'index.html')
async function createWindow() { async function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
title: 'Main window', title: 'Main window',
width: 1000,
height: 800,
icon: join(process.env.PUBLIC, 'favicon.ico'), icon: join(process.env.PUBLIC, 'favicon.ico'),
webPreferences: { webPreferences: {
preload, preload,

View File

@ -0,0 +1,52 @@
import { clog, loginGoogle } from './login'
import { readFileSync, writeFileSync } from 'fs'
import path from 'path'
import { EOL } from 'os'
export async function getLink(options) {
const log = clog(options)
log('开始', { ident: 'poe-link' })
const page = await loginGoogle(options)
await Promise.all([
page.waitForNavigation(),
page.goto('https://poe.com/settings')
])
log('已进入设置页面, 检查中')
// page.waitForTimeout(1000)
await page.waitForSelector('[class*=SettingsSubscriptionSection_subscribeButton]')
log('1, 开始点击显示订阅套餐按钮')
await page.click('[class*=SettingsSubscriptionSection_subscribeButton]')
log('2, 开始点击显示更多套餐')
await page.waitForSelector('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
await page.click('[class*=WebSubscriptionFreeTrial_viewAllPlansButton]')
log('3, 开始点击最后一个套餐')
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' })
return url
// if (options.index === 0) {
// writeFileSync(resolve('./hao.txt'), '', 'utf8')
// }
// const fileContent = readFileSync(resolve('./hao.txt'), 'utf8')
// writeFileSync(resolve('./hao.txt'), fileContent + `${EOL}${options.index + 1} ${options.user}${EOL}${url}`, 'utf8')
}
}
function resolve(url) {
return path.resolve(__dirname, '../src/poe/', url)
}

View File

View File

@ -0,0 +1,62 @@
import { getLink } from './getLink'
import { validate } from './validate'
import { ipcMain } from 'electron'
import { browser } from './login'
// import './openBrowser'
// suzzettedeanne2393@gmail.com----zebulonlawayne3013----zebulonlawayne4749@outlook.com
const text = `
`
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)
}
browser && browser.close()
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)
}
browser && browser.close()
return links
})
ipcMain.handle('stop', async (event, arg) => {
browser && browser.close()
return true
})

View File

@ -0,0 +1,89 @@
import puppeteer from 'puppeteer-extra'
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
import { randomNum } from '../tools'
import { win } from '../index'
puppeteer.use(StealthPlugin())
let id
export let browser
/**
* 登录google
*/
export async function loginGoogle(options, tryCount = 1) {
let resolve, reject
const log = clog(options)
const p = new Promise((res, rej) => {
resolve = res
reject = rej
})
id = options.id
const env = {
GUSER: options.user,
GPASS: options.pass
}
if (browser) await browser.close()
browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
await page.setExtraHTTPHeaders({
'accept-language': 'en-US,en;q=0.9,hy;q=0.8'
})
log('开始访问 poe')
await page.goto('https://poe.com')
log('已进入 poe')
await page.waitForTimeout(randomNum(1000, 2600))
log('准备进入 google 登录页')
const [response] = await Promise.all([
page.waitForNavigation(() => location.href.startsWith('https://accounts.google.com')),
page.click('.ContinueWithGoogleButton_buttonContentWrapper__Mrp0W')
])
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('已输入账号,准备输入密码')
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')
])
log('登录成功,准备进入 poe')
return page
}
if (tryCount < 4) {
log(`google 登录失败,准备 ${tryCount} 次重试`)
await page.close()
return await loginGoogle(options, tryCount + 1)
} else {
log('google 登录失败,重试次数已达上限')
reject('google 登录失败')
}
return p
// return Promise.reject('google 登录失败')
}
export function clog(options) {
return (info, data = {}) => {
if (win) {
win.webContents.send('progress', {...options, info, ...data})
}
}
}

View File

@ -0,0 +1,8 @@
import puppeteer from 'puppeteer-extra'
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
puppeteer.use(StealthPlugin())
;(async () => {
const browser = await puppeteer.launch({ headless: false })
browser.newPage()
})()

View File

@ -0,0 +1,39 @@
import { clog, loginGoogle } from './login'
import { readFileSync, writeFileSync } from 'fs'
import path from 'path'
import { EOL } from 'os'
export async function validate (options) {
const log = clog(options)
log('开始', { ident: 'poe-validate' })
const page = await loginGoogle(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)
// page.close()
// if (options.index === 0) {
// writeFileSync(resolve('./validate.txt'), '', 'utf8')
// }
// const fileContent = readFileSync(resolve('./validate.txt'), 'utf8')
const resultTxt = length === 1 ? '失败😭' : '成功😘'
// writeFileSync(resolve('./validate.txt'), fileContent + `${options.index + 1} ${options.user}----${resultTxt}${EOL}`, 'utf8')
log(resultTxt, { type: length === 1 ? 'fail' : 'success', result: length === 1 ? '失败😭' : '成功😘' })
// // 不成功
// if (length === 1) {
// } else {
// log('充值成功😘')
// }
}
function resolve (url) {
return path.resolve(__dirname, '../src/poe/', url)
}

5
electron/main/tools.js Normal file
View File

@ -0,0 +1,5 @@
//生成从minNum到maxNum的随机数
export function randomNum (min, max) {
return parseInt(Math.random() * (max - min + 1) + min)
}

View File

@ -1,6 +1,6 @@
{ {
"name": "electron-vue-vite", "name": "poe_gpt",
"version": "2.0.0", "version": "1.0.0",
"main": "dist-electron/main/index.js", "main": "dist-electron/main/index.js",
"description": "Really simple Electron + Vue + Vite boilerplate.", "description": "Really simple Electron + Vue + Vite boilerplate.",
"author": "草鞋没号 <308487730@qq.com>", "author": "草鞋没号 <308487730@qq.com>",
@ -20,18 +20,32 @@
}, },
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc --noEmit && vite build && electron-builder", "build": "vite build && electron-builder",
"build:64": "vite build && electron-builder --win --x64",
"build:32": "vite build && electron-builder --win --x32",
"preview": "vite preview" "preview": "vite preview"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue": "^4.1.0",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"electron": "^25.0.1", "electron": "^25.0.1",
"electron-builder": "^23.6.0", "electron-builder": "^23.6.0",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"unplugin-auto-import": "^0.16.4",
"unplugin-vue-components": "^0.25.0",
"vite": "^4.1.4", "vite": "^4.1.4",
"vite-plugin-electron": "^0.11.2", "vite-plugin-electron": "^0.11.2",
"vite-plugin-electron-renderer": "^0.14.1", "vite-plugin-electron-renderer": "^0.14.1",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-tsc": "^1.2.0" "vue-tsc": "^1.2.0"
},
"dependencies": {
"@vueuse/core": "^10.1.2",
"naive-ui": "^2.34.4",
"puppeteer": "^13.3.2",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2",
"sass": "^1.62.1",
"unocss": "^0.52.7"
} }
} }

View File

@ -1,51 +1,268 @@
<script setup lang="ts"> <script setup lang="tsx">
import HelloWorld from './components/HelloWorld.vue' import { ipcRenderer } from 'electron'
import { NButton } from 'naive-ui'
import { useClipboard } from '@vueuse/core'
const input = ref('upperside79@mail.com----Hellowpo!')
console.log("[App.vue]", `Hello world from Electron ${process.versions.electron}!`) const result = ref('')
const list = ref<any[]>([
{
"user": "jannettamoses5977@gmail.com",
"pass": "kenyatearle7610",
"auxiliary": "kenyatearle8223@outlook.com",
"index": 0,
"id": "jannettamoses5977@gmail.com",
"info": "成功😘",
"ident": "poe-link",
"type": "success",
"result": "成功😘"
},
{
"user": "tylanindea7687@gmail.com",
"pass": "adalbertokanisha4845",
"auxiliary": "adalbertokanisha8803@yandex.com",
"index": 1,
"id": "tylanindea7687@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "success",
"result": "成功😘"
},
{
"user": "bayanjae8652@gmail.com",
"pass": "lorenarosamond4075",
"auxiliary": "lorenarosamond8958@outlook.com",
"index": 2,
"id": "bayanjae8652@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "fail",
"result": "成功😘"
},
{
"user": "geneviashametra1019@gmail.com",
"pass": "sheypervis2156",
"auxiliary": "sheypervis0163@qq.com",
"index": 3,
"id": "geneviashametra1019@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "success",
"result": "成功😘"
},
{
"user": "marikamelindasue5911@gmail.com",
"pass": "ambrbreeann4103",
"auxiliary": "ambrbreeann5643@icloud.com",
"index": 4,
"id": "marikamelindasue5911@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "success",
"result": "成功😘"
},
{
"user": "caterinereba2981@gmail.com",
"pass": "arvisleo0563",
"auxiliary": "arvisleo1265@zoho.com",
"index": 5,
"id": "caterinereba2981@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "success",
"result": "成功😘"
},
{
"user": "adairaderrell9992@gmail.com",
"pass": "adairaderrell8698",
"auxiliary": "adairaderrell1859@hotmail.com",
"index": 6,
"id": "adairaderrell9992@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "success",
"result": "成功😘"
},
{
"user": "nishadarrow0444@gmail.com",
"pass": "cliftontiffani7462",
"auxiliary": "cliftontiffani2263@hotmail.com",
"index": 7,
"id": "nishadarrow0444@gmail.com",
"info": "成功😘",
"ident": "poe-validate",
"type": "success",
"result": "成功😘"
}
])
ipcRenderer.on('progress', (event, args) => {
console.log('progress', 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 getLink(val?: string) {
ipcRenderer.invoke('getLink', { text: val || input.value }).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 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.type === 'success')
})
const listFail = computed(() => {
return list.value.filter((item) => item.type !== 'success')
})
const { copy } = useClipboard()
function copyText(item: any) {
copy(item.user + '----' + (item.result || ''))
}
function copyAccount(item: any) {
copy(item.user + '----' + item.pass + '----' + item.auxiliary)
}
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.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>
}
}
]
</script> </script>
<template> <template>
<div> <div>
<a href="https://www.electronjs.org/" target="_blank"> <div class="left" flex-1>
<img src="./assets/electron.svg" class="logo electron" alt="Electron logo" /> <p>帐密</p>
</a> <textarea class="textarea w-full" v-model="input" rows="10" />
<a href="https://vitejs.dev/" target="_blank">
<img src="./assets/vite.svg" class="logo" alt="Vite logo" /> <div>
</a> <div flex gap-3 mt-3 items-center>
<a href="https://vuejs.org/" target="_blank"> <span w-15>poe</span>
<img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /> <NButton type="primary" dashed @click="getLink()">提取链接</NButton>
</a> <NButton type="primary" dashed @click="getResult()">充值结果</NButton>
</div> </div>
<HelloWorld msg="Electron + Vite + Vue" /> <div flex gap-3 mt-3 items-center>
<div class="flex-center"> <span w-15>gpt</span>
Place static files into the <code>/public</code> folder <NButton type="primary" dashed @click="gptLinkHandler">gpt提链</NButton>
<img style="width: 2.4em; margin-left: .4em;" src="/logo.svg" alt="Logo"> <NButton type="primary" dashed @click="gptResultHandler">充值结果</NButton>
</div>
<div flex gap-3 mt-3 items-center>
<span w-15>操作</span>
<NButton type="primary" dashed @click="stopHandler">停止</NButton>
</div>
</div>
</div>
<div class="right" flex-1>
<p>结果</p>
<n-space mb-3>
<n-button type="primary" dashed>复制全部成功 {{ listSuccess.length }}</n-button>
<n-button type="error" dashed>复制全部失败 {{ listFail.length }}</n-button>
</n-space>
<NDataTable :data="list" :columns="columns" :scroll-x="1000">
</NDataTable>
<div w-full h-40></div>
</div>
<pre>{{ list }}</pre>
</div> </div>
</template> </template>
<style> <style lang="scss" scoped>
.flex-center { .textarea:nth-of-type() {
display: flex; width: 100%;
align-items: center; display: block;
justify-content: center; border: 1px solid #ccc;
} border-radius: 4px;
padding: 10px;
.logo { box-sizing: border-box;
height: 6em; font-size: 14px;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo.electron:hover {
filter: drop-shadow(0 0 2em #9FEAF9);
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
} }
</style> </style>

178
src/auto-imports.d.ts vendored Normal file
View 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']>
}
}

17
src/components.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
/* 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']
NDataTable: typeof import('naive-ui')['NDataTable']
NSpace: typeof import('naive-ui')['NSpace']
}
}

View File

@ -3,6 +3,9 @@ import "./style.css"
import App from './App.vue' import App from './App.vue'
import './samples/node-api' import './samples/node-api'
import 'uno.css'
createApp(App) createApp(App)
.mount('#app') .mount('#app')
.$nextTick(() => { .$nextTick(() => {

View File

@ -1,3 +1,5 @@
button:focus{outline-color: transparent !important;}
:root { :root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5; line-height: 1.5;
@ -27,8 +29,8 @@ body {
margin: 0; margin: 0;
display: flex; display: flex;
place-items: center; place-items: center;
min-width: 320px; width: 100%;
min-height: 100vh; height: 100vh;
} }
h1 { h1 {
@ -66,11 +68,14 @@ code {
padding: 2em; padding: 2em;
} }
button,input,textarea{outline: none;}
#app { #app {
max-width: 1280px; width: 100%;
height: 100%;
box-sizing: border-box;
margin: 0 auto; margin: 0 auto;
padding: 2rem; padding: 2rem;
text-align: center;
} }
@media (prefers-color-scheme: light) { @media (prefers-color-scheme: light) {

35
unocss.config.ts Normal file
View 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(),
// ],
})

View File

@ -2,8 +2,13 @@ import { rmSync } from 'node:fs'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import electron from 'vite-plugin-electron' 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 renderer from 'vite-plugin-electron-renderer'
import pkg from './package.json' 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/ // https://vitejs.dev/config/
export default defineConfig(({ command }) => { export default defineConfig(({ command }) => {
@ -16,6 +21,27 @@ export default defineConfig(({ command }) => {
return { return {
plugins: [ plugins: [
vue(), 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([ electron([
{ {
// Main-Process entry file of the Electron App. // Main-Process entry file of the Electron App.