Browse Source

style: 优化代码格式

main
npmrun 5 months ago
parent
commit
7023810b2a
  1. 2
      .vscode/extensions.json
  2. 74
      .vscode/launch.json
  3. 18
      .vscode/settings.json
  4. 2
      README.md
  5. 70
      config/index.ts
  6. 62
      electron-builder.yml
  7. 46
      electron.vite.config.ts
  8. 108
      package.json
  9. 6
      src/main/base/base.ts
  10. 106
      src/main/index.ts
  11. 56
      src/main/modules/App.ts
  12. 66
      src/main/modules/db/custom.ts
  13. 150
      src/main/modules/db/index.ts
  14. 20
      src/main/modules/index.ts
  15. 28
      src/main/modules/module.ts
  16. 459
      src/main/modules/setting/index.ts
  17. 10
      src/preload/index.d.ts
  18. 24
      src/preload/index.ts
  19. 26
      src/renderer/index.html
  20. 40
      src/renderer/src/App.vue
  21. 92
      src/renderer/src/assets/base.css
  22. 228
      src/renderer/src/assets/main.css
  23. 12
      src/renderer/src/components/Versions.vue
  24. 10
      src/renderer/src/env.d.ts
  25. 12
      src/renderer/src/main.ts
  26. 10
      src/renderer/src/shims.d.ts
  27. 16
      uno.config.ts

2
.vscode/extensions.json

@ -1,3 +1,3 @@
{ {
"recommendations": ["dbaeumer.vscode-eslint"] "recommendations": ["dbaeumer.vscode-eslint"]
} }

74
.vscode/launch.json

@ -1,39 +1,39 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Debug Main Process", "name": "Debug Main Process",
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"cwd": "${workspaceRoot}", "cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
"windows": { "windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
}, },
"runtimeArgs": ["--sourcemap"], "runtimeArgs": ["--sourcemap"],
"env": { "env": {
"REMOTE_DEBUGGING_PORT": "9222" "REMOTE_DEBUGGING_PORT": "9222"
} }
}, },
{ {
"name": "Debug Renderer Process", "name": "Debug Renderer Process",
"port": 9222, "port": 9222,
"request": "attach", "request": "attach",
"type": "chrome", "type": "chrome",
"webRoot": "${workspaceFolder}/src/renderer", "webRoot": "${workspaceFolder}/src/renderer",
"timeout": 60000, "timeout": 60000,
"presentation": { "presentation": {
"hidden": true "hidden": true
} }
} }
], ],
"compounds": [ "compounds": [
{ {
"name": "Debug All", "name": "Debug All",
"configurations": ["Debug Main Process", "Debug Renderer Process"], "configurations": ["Debug Main Process", "Debug Renderer Process"],
"presentation": { "presentation": {
"order": 1 "order": 1
} }
} }
] ]
} }

18
.vscode/settings.json

@ -1,11 +1,11 @@
{ {
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features" "editor.defaultFormatter": "vscode.typescript-language-features"
}, },
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[json]": { "[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
} }
} }

2
README.md

@ -4,7 +4,7 @@ An Electron application with Vue and TypeScript
## Recommended IDE Setup ## Recommended IDE Setup
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) - [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)
## Project Setup ## Project Setup

70
config/index.ts

@ -1,35 +1,35 @@
interface IConfig { interface IConfig {
app_title: string, app_title: string
default_config: { default_config: {
language: "zh" | "en" // i18n language: "zh" | "en" // i18n
"common.theme": "light" | "dark" | "auto" // 主题 "common.theme": "light" | "dark" | "auto" // 主题
"desktop:wallpaper": string "desktop:wallpaper": string
"update.repo"?: string // 更新地址 "update.repo"?: string // 更新地址
"update.owner"?: string // 更新通道 "update.owner"?: string // 更新通道
"update.allowDowngrade": boolean, "update.allowDowngrade": boolean
"update.allowPrerelease": boolean "update.allowPrerelease": boolean
"editor.bg": string // 更新通道 "editor.bg": string // 更新通道
"editor.logoType": "logo" | "bg" // 更新通道 "editor.logoType": "logo" | "bg" // 更新通道
"editor.fontFamily": string // 更新通道 "editor.fontFamily": string // 更新通道
// "snippet.storagePath": string // 代码片段保存位置 // "snippet.storagePath": string // 代码片段保存位置
// "bookmark.storagePath": string // 书签保存位置 // "bookmark.storagePath": string // 书签保存位置
// backup_rule: string // 备份规则 // backup_rule: string // 备份规则
storagePath: string // 存储地址 storagePath: string // 存储地址
} }
} }
export default { export default {
app_title: "ada", app_title: "ada",
default_config: { default_config: {
"storagePath": "$storagePath$", storagePath: "$storagePath$",
"language": "zh", language: "zh",
"common.theme": "auto", "common.theme": "auto",
"desktop:wallpaper": "", "desktop:wallpaper": "",
"editor.bg": "", "editor.bg": "",
"editor.logoType": "logo", "editor.logoType": "logo",
"editor.fontFamily": "Cascadia Mono, Consolas, 'Courier New', monospace", "editor.fontFamily": "Cascadia Mono, Consolas, 'Courier New', monospace",
"update.repo": "wood-desktop", "update.repo": "wood-desktop",
"update.owner": "npmrun", "update.owner": "npmrun",
"update.allowDowngrade": false, "update.allowDowngrade": false,
"update.allowPrerelease": false "update.allowPrerelease": false,
} },
} as IConfig } as IConfig

62
electron-builder.yml

@ -1,45 +1,45 @@
appId: com.electron.app appId: com.electron.app
productName: my-app productName: my-app
directories: directories:
buildResources: build buildResources: build
files: files:
- '!**/.vscode/*' - "!**/.vscode/*"
- '!src/*' - "!src/*"
- '!electron.vite.config.{js,ts,mjs,cjs}' - "!electron.vite.config.{js,ts,mjs,cjs}"
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' - "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}"
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' - "!{.env,.env.*,.npmrc,pnpm-lock.yaml}"
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' - "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}"
asarUnpack: asarUnpack:
- resources/** - resources/**
win: win:
executableName: my-app executableName: my-app
nsis: nsis:
artifactName: ${name}-${version}-setup.${ext} artifactName: ${name}-${version}-setup.${ext}
shortcutName: ${productName} shortcutName: ${productName}
uninstallDisplayName: ${productName} uninstallDisplayName: ${productName}
createDesktopShortcut: always createDesktopShortcut: always
mac: mac:
entitlementsInherit: build/entitlements.mac.plist entitlementsInherit: build/entitlements.mac.plist
extendInfo: extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera. - NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false notarize: false
dmg: dmg:
artifactName: ${name}-${version}.${ext} artifactName: ${name}-${version}.${ext}
linux: linux:
target: target:
- AppImage - AppImage
- snap - snap
- deb - deb
maintainer: electronjs.org maintainer: electronjs.org
category: Utility category: Utility
appImage: appImage:
artifactName: ${name}-${version}.${ext} artifactName: ${name}-${version}.${ext}
npmRebuild: false npmRebuild: false
publish: publish:
provider: generic provider: generic
url: https://example.com/auto-updates url: https://example.com/auto-updates
electronDownload: electronDownload:
mirror: https://npmmirror.com/mirrors/electron/ mirror: https://npmmirror.com/mirrors/electron/

46
electron.vite.config.ts

@ -1,28 +1,28 @@
import { resolve } from 'path' import { resolve } from "path"
import { defineConfig, externalizeDepsPlugin } from 'electron-vite' import { defineConfig, externalizeDepsPlugin } from "electron-vite"
import vue from '@vitejs/plugin-vue' import vue from "@vitejs/plugin-vue"
import UnoCSS from 'unocss/vite' import UnoCSS from "unocss/vite"
export default defineConfig({ export default defineConfig({
main: { main: {
resolve: { resolve: {
alias: { alias: {
config: resolve('config'), config: resolve("config"),
vc: resolve('src/main'), vc: resolve("src/main"),
res: resolve('resources') res: resolve("resources"),
} },
},
plugins: [externalizeDepsPlugin()],
}, },
plugins: [externalizeDepsPlugin()] preload: {
}, plugins: [externalizeDepsPlugin()],
preload: { },
plugins: [externalizeDepsPlugin()] renderer: {
}, resolve: {
renderer: { alias: {
resolve: { "@renderer": resolve("src/renderer/src"),
alias: { },
'@renderer': resolve('src/renderer/src') },
} plugins: [UnoCSS(), vue()],
}, },
plugins: [UnoCSS(), vue()]
}
}) })

108
package.json

@ -1,56 +1,56 @@
{ {
"name": "my-app", "name": "my-app",
"type": "module", "type": "module",
"version": "1.0.0", "version": "1.0.0",
"description": "An Electron application with Vue and TypeScript", "description": "An Electron application with Vue and TypeScript",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "example.com", "author": "example.com",
"homepage": "https://electron-vite.org", "homepage": "https://electron-vite.org",
"scripts": { "scripts": {
"format": "prettier --write .", "format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", "typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false",
"typecheck": "npm run typecheck:node && npm run typecheck:web", "typecheck": "npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview", "start": "electron-vite preview",
"dev": "electron-vite dev", "dev": "electron-vite dev",
"dev:watch": "electron-vite dev --watch", "dev:watch": "electron-vite dev --watch",
"build": "npm run typecheck && electron-vite build", "build": "npm run typecheck && electron-vite build",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir", "build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win", "build:win": "npm run build && electron-builder --win",
"build:mac": "npm run build && electron-builder --mac", "build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux" "build:linux": "npm run build && electron-builder --linux"
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@unocss/reset": "^0.64.1", "@unocss/reset": "^0.64.1",
"electron-updater": "^6.1.7", "electron-updater": "^6.1.7",
"inversify": "^6.1.4", "inversify": "^6.1.4",
"lowdb": "^7.0.1", "lowdb": "^7.0.1",
"reflect-metadata": "^0.2.2" "reflect-metadata": "^0.2.2"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.2", "@electron-toolkit/eslint-config": "^1.0.2",
"@electron-toolkit/eslint-config-ts": "^2.0.0", "@electron-toolkit/eslint-config-ts": "^2.0.0",
"@electron-toolkit/tsconfig": "^1.0.1", "@electron-toolkit/tsconfig": "^1.0.1",
"@rushstack/eslint-patch": "^1.10.3", "@rushstack/eslint-patch": "^1.10.3",
"@types/node": "^20.14.8", "@types/node": "^20.14.8",
"@unocss/preset-rem-to-px": "^0.64.1", "@unocss/preset-rem-to-px": "^0.64.1",
"@vitejs/plugin-vue": "^5.0.5", "@vitejs/plugin-vue": "^5.0.5",
"@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0", "@vue/eslint-config-typescript": "^13.0.0",
"electron": "^31.0.2", "electron": "^31.0.2",
"electron-builder": "^24.13.3", "electron-builder": "^24.13.3",
"electron-vite": "^2.3.0", "electron-vite": "^2.3.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-vue": "^9.26.0", "eslint-plugin-vue": "^9.26.0",
"prettier": "^3.3.2", "prettier": "^3.3.2",
"typescript": "^5.5.2", "typescript": "^5.5.2",
"unocss": "^0.64.1", "unocss": "^0.64.1",
"vite": "^5.3.1", "vite": "^5.3.1",
"vue": "^3.4.30", "vue": "^3.4.30",
"vue-tsc": "^2.0.22" "vue-tsc": "^2.0.22"
} }
} }

6
src/main/base/base.ts

@ -1,3 +1,3 @@
abstract class Base{ abstract class Base {
constructor(){} constructor() {}
} }

106
src/main/index.ts

@ -1,78 +1,78 @@
import "reflect-metadata"; import "reflect-metadata"
import { app, shell, BrowserWindow, ipcMain } from 'electron' import { app, shell, BrowserWindow, ipcMain } from "electron"
import { join } from 'path' import { join } from "path"
import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { electronApp, optimizer, is } from "@electron-toolkit/utils"
import icon from 'res/icon.png?asset' import icon from "res/icon.png?asset"
import { container } from 'vc/modules' import { container } from "vc/modules"
import { App } from 'vc/modules/App' import { App } from "vc/modules/App"
container.get(App).init() container.get(App).init()
function createWindow(): void { function createWindow(): void {
// Create the browser window. // Create the browser window.
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: 900, width: 900,
height: 670, height: 670,
show: false, show: false,
autoHideMenuBar: true, autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}), ...(process.platform === "linux" ? { icon } : {}),
webPreferences: { webPreferences: {
preload: join(__dirname, '../preload/index.mjs'), preload: join(__dirname, "../preload/index.mjs"),
sandbox: false sandbox: false,
} },
}) })
mainWindow.on('ready-to-show', () => { mainWindow.on("ready-to-show", () => {
mainWindow.show() mainWindow.show()
}) })
mainWindow.webContents.setWindowOpenHandler((details) => { mainWindow.webContents.setWindowOpenHandler(details => {
shell.openExternal(details.url) shell.openExternal(details.url)
return { action: 'deny' } return { action: "deny" }
}) })
// HMR for renderer base on electron-vite cli. // HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production. // Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"])
} else { } else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html')) mainWindow.loadFile(join(__dirname, "../renderer/index.html"))
} }
} }
// This method will be called when Electron has finished // This method will be called when Electron has finished
// initialization and is ready to create browser windows. // initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs. // Some APIs can only be used after this event occurs.
app.whenReady().then(() => { app.whenReady().then(() => {
// Set app user model id for windows // Set app user model id for windows
electronApp.setAppUserModelId('com.electron') electronApp.setAppUserModelId("com.electron")
// Default open or close DevTools by F12 in development // Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production. // and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => { app.on("browser-window-created", (_, window) => {
optimizer.watchWindowShortcuts(window) optimizer.watchWindowShortcuts(window)
}) })
// IPC test // IPC test
ipcMain.on('ping', () => console.log(icon)) ipcMain.on("ping", () => console.log(icon))
createWindow() createWindow()
app.on('activate', function () { app.on("activate", function () {
// On macOS it's common to re-create a window in the app when the // On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open. // dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow() if (BrowserWindow.getAllWindows().length === 0) createWindow()
}) })
}) })
// Quit when all windows are closed, except on macOS. There, it's common // Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits // for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q. // explicitly with Cmd + Q.
app.on('window-all-closed', () => { app.on("window-all-closed", () => {
if (process.platform !== 'darwin') { if (process.platform !== "darwin") {
app.quit() app.quit()
} }
}) })
// In this file you can include the rest of your app"s specific main process // In this file you can include the rest of your app"s specific main process

56
src/main/modules/App.ts

@ -1,31 +1,25 @@
import { injectable, inject } from "inversify" import { injectable, inject } from "inversify"
import Setting from "./setting" import Setting from "./setting"
import DB from "./db" import DB from "./db"
@injectable()
@injectable() class App {
class App { private _setting: Setting
private _setting: Setting private _db: DB
private _db: DB
constructor(@inject(Setting) setting: Setting, @inject(DB) db: DB) {
constructor( console.log(`App inited`)
@inject(Setting) setting: Setting,
@inject(DB) db: DB, this._setting = setting
) { this._db = db
console.log(`App inited`); }
this._setting = setting async init() {
this._db = db console.log(this._setting.config())
} this._db.saveData("aaa", { a: 123123 })
console.log(await this._db.getData("aaa"))
async init() { }
console.log(this._setting.config()); }
this._db.saveData("aaa", { a: 123123 })
console.log(await this._db.getData("aaa")); export default App
} export { App }
}
export default App
export {
App
}

66
src/main/modules/db/custom.ts

@ -1,33 +1,33 @@
import { JSONFile } from "lowdb/node" import { JSONFile } from "lowdb/node"
import { Low } from 'lowdb' import { Low } from "lowdb"
import fs from 'fs-extra' import fs from "fs-extra"
export class CustomAdapter<T> extends JSONFile<T> { export class CustomAdapter<T> extends JSONFile<T> {
constructor(filepath: string) { constructor(filepath: string) {
super(filepath) super(filepath)
this.filepath = filepath this.filepath = filepath
} }
filepath: string = '' filepath: string = ""
async read() { async read() {
if (!fs.existsSync(this.filepath)) { if (!fs.existsSync(this.filepath)) {
return null return null
} }
let data = fs.readJSONSync(this.filepath, { throws: false }) const data = fs.readJSONSync(this.filepath, { throws: false })
if (!data) { if (!data) {
return null return null
} }
return data return data
} }
async write(data: T) { async write(data: T) {
fs.ensureFileSync(this.filepath) fs.ensureFileSync(this.filepath)
await super.write(data) await super.write(data)
} }
} }
export class CustomLow<T> extends Low<T> { export class CustomLow<T> extends Low<T> {
constructor(adapter: CustomAdapter<T>, defaultData: T) { constructor(adapter: CustomAdapter<T>, defaultData: T) {
super(adapter, defaultData) super(adapter, defaultData)
this.filepath = adapter.filepath this.filepath = adapter.filepath
} }
filepath: string = '' filepath: string = ""
} }

150
src/main/modules/db/index.ts

@ -1,76 +1,74 @@
import { inject, injectable } from "inversify"; import { inject, injectable } from "inversify"
import Setting from "../setting"; import Setting from "../setting"
import { CustomAdapter, CustomLow } from "./custom"; import { CustomAdapter, CustomLow } from "./custom"
import path from "node:path"; import path from "node:path"
@injectable() @injectable()
class DB { class DB {
private _setting: Setting private _setting: Setting
Modules: Record<string, CustomLow<any>> = {} Modules: Record<string, CustomLow<any>> = {}
constructor(@inject(Setting) setting: Setting) { constructor(@inject(Setting) setting: Setting) {
console.log(`DB inited`); console.log(`DB inited`)
this._setting = setting this._setting = setting
} }
create(filepath) { create(filepath) {
let adapter = new CustomAdapter<any>(filepath) const adapter = new CustomAdapter<any>(filepath)
const db = new CustomLow<{}>(adapter, {}) const db = new CustomLow<object>(adapter, {})
db.filepath = filepath db.filepath = filepath
return db return db
} }
getDB(dbName: string) { getDB(dbName: string) {
if (this.Modules[dbName] === undefined) { if (this.Modules[dbName] === undefined) {
let filepath = path.resolve(this._setting.values("storagePath"), './db/' + dbName + '.json') const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json")
this.Modules[dbName] = this.create(filepath) this.Modules[dbName] = this.create(filepath)
return this.Modules[dbName] return this.Modules[dbName]
} else { } else {
let cur = this.Modules[dbName] const cur = this.Modules[dbName]
let filepath = path.resolve(this._setting.values("storagePath"), './db/' + dbName + '.json') const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json")
if (cur.filepath != filepath) { if (cur.filepath != filepath) {
this.Modules[dbName] = this.create(filepath) this.Modules[dbName] = this.create(filepath)
} }
return this.Modules[dbName] return this.Modules[dbName]
} }
} }
async saveData(data: any): Promise<any> async saveData(data: any): Promise<any>
async saveData(dbName: string, data: any): Promise<any> async saveData(dbName: string, data: any): Promise<any>
async saveData(dbName: string, data?: any): Promise<any> { async saveData(dbName: string, data?: any): Promise<any> {
let db, rData let db, rData
if (arguments.length === 2) { if (arguments.length === 2) {
db = this.getDB(dbName) db = this.getDB(dbName)
rData = data rData = data
} else { } else {
db = this.getDB('db') db = this.getDB("db")
rData = dbName rData = dbName
} }
if (db) { if (db) {
db.data = rData db.data = rData
await db.write() await db.write()
return db.data return db.data
} }
return null return null
} }
async getData(dbName?: string) { async getData(dbName?: string) {
let db let db
if (dbName) { if (dbName) {
db = this.getDB(dbName) db = this.getDB(dbName)
} else { } else {
db = this.getDB('db') db = this.getDB("db")
} }
if (db) { if (db) {
await db.read() await db.read()
return db.data return db.data
} }
return null return null
} }
} }
export default DB export default DB
export { export { DB }
DB
}

20
src/main/modules/index.ts

@ -1,11 +1,9 @@
import { Container } from "inversify" import { Container } from "inversify"
import module from "./module" import module from "./module"
const container = new Container() const container = new Container()
container.load(module) container.load(module)
export default container export default container
export { export { container }
container
}

28
src/main/modules/module.ts

@ -1,15 +1,13 @@
import { ContainerModule } from "inversify" import { ContainerModule } from "inversify"
import { Setting } from "./setting" import { Setting } from "./setting"
import { DB } from "./db" import { DB } from "./db"
import App from "./App" import App from "./App"
const module = new ContainerModule((bind) => { const module = new ContainerModule(bind => {
bind(Setting).toConstantValue(new Setting()) bind(Setting).toConstantValue(new Setting())
bind(DB).toSelf().inSingletonScope() bind(DB).toSelf().inSingletonScope()
bind(App).toSelf().inSingletonScope() bind(App).toSelf().inSingletonScope()
}) })
export default module export default module
export { export { module }
module
}

459
src/main/modules/setting/index.ts

@ -1,233 +1,226 @@
import fs from "fs-extra" import fs from "fs-extra"
import { app } from "electron" import { app } from "electron"
import path from "path" import path from "path"
import { cloneDeep } from "lodash" import { cloneDeep } from "lodash"
import { injectable } from "inversify" import { injectable } from "inversify"
import Config from "config" import Config from "config"
type IConfig = typeof Config.default_config type IConfig = typeof Config.default_config
type IOnFunc = (n: IConfig, c: IConfig, keys?: (keyof IConfig)[]) => void type IOnFunc = (n: IConfig, c: IConfig, keys?: (keyof IConfig)[]) => void
type IT = (keyof IConfig)[] | keyof IConfig | "_" type IT = (keyof IConfig)[] | keyof IConfig | "_"
let storagePath = path.join(app.getPath("documents"), Config.app_title) let storagePath = path.join(app.getPath("documents"), Config.app_title)
let storagePathDev = path.join(app.getPath("documents"), Config.app_title + "-dev") const storagePathDev = path.join(app.getPath("documents"), Config.app_title + "-dev")
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
storagePath = storagePathDev storagePath = storagePathDev
} }
let _tempConfig = cloneDeep(Config.default_config as IConfig) const _tempConfig = cloneDeep(Config.default_config as IConfig)
Object.keys(_tempConfig).forEach(key => { Object.keys(_tempConfig).forEach(key => {
if (typeof _tempConfig[key] === "string" && _tempConfig[key].includes("$storagePath$")) { if (typeof _tempConfig[key] === "string" && _tempConfig[key].includes("$storagePath$")) {
_tempConfig[key] = _tempConfig[key].replace(/\$storagePath\$/g, storagePath) _tempConfig[key] = _tempConfig[key].replace(/\$storagePath\$/g, storagePath)
if (_tempConfig[key] && path.isAbsolute(_tempConfig[key])) { if (_tempConfig[key] && path.isAbsolute(_tempConfig[key])) {
_tempConfig[key] = path.normalize(_tempConfig[key]) _tempConfig[key] = path.normalize(_tempConfig[key])
} }
} }
}) })
function isPath(str) { function isPath(str) {
// 使用正则表达式检查字符串是否以斜杠或盘符开头 // 使用正则表达式检查字符串是否以斜杠或盘符开头
return /^(?:\/|[a-zA-Z]:\\)/.test(str); return /^(?:\/|[a-zA-Z]:\\)/.test(str)
} }
function init(config: IConfig) { function init(config: IConfig) {
// 在配置初始化后执行 // 在配置初始化后执行
Object.keys(config).forEach(key => { Object.keys(config).forEach(key => {
if (config[key] && isPath(config[key]) && path.isAbsolute(config[key])) { if (config[key] && isPath(config[key]) && path.isAbsolute(config[key])) {
fs.ensureDirSync(config[key]) fs.ensureDirSync(config[key])
} }
}) })
// 在配置初始化后执行 // 在配置初始化后执行
// fs.ensureDirSync(config["snippet.storagePath"]) // fs.ensureDirSync(config["snippet.storagePath"])
// fs.ensureDirSync(config["bookmark.storagePath"]) // fs.ensureDirSync(config["bookmark.storagePath"])
} }
// 判断是否是空文件夹 // 判断是否是空文件夹
function isEmptyDir(fPath: string) { function isEmptyDir(fPath: string) {
var pa = fs.readdirSync(fPath) const pa = fs.readdirSync(fPath)
if (pa.length === 0) { if (pa.length === 0) {
return true return true
} else { } else {
return false return false
} }
} }
@injectable() @injectable()
class Setting { class Setting {
constructor() { constructor() {
console.log(`Setting inited`); console.log(`Setting inited`)
this.#init() this.#init()
} }
#cb: [IT, IOnFunc][] = [] #cb: [IT, IOnFunc][] = []
onChange(fn: IOnFunc, that?: any) onChange(fn: IOnFunc, that?: any)
onChange(key: IT, fn: IOnFunc, that?: any) onChange(key: IT, fn: IOnFunc, that?: any)
onChange(fnOrType: IT | IOnFunc, fnOrThat: IOnFunc | any = null, that: any = null) { onChange(fnOrType: IT | IOnFunc, fnOrThat: IOnFunc | any = null, that: any = null) {
if (typeof fnOrType === "function") { if (typeof fnOrType === "function") {
this.#cb.push(["_", fnOrType.bind(fnOrThat)]) this.#cb.push(["_", fnOrType.bind(fnOrThat)])
} else { } else {
this.#cb.push([fnOrType, fnOrThat.bind(that)]) this.#cb.push([fnOrType, fnOrThat.bind(that)])
} }
} }
#runCB(n: IConfig, c: IConfig, keys: (keyof IConfig)[]) { #runCB(n: IConfig, c: IConfig, keys: (keyof IConfig)[]) {
for (let i = 0; i < this.#cb.length; i++) { for (let i = 0; i < this.#cb.length; i++) {
const temp = this.#cb[i] const temp = this.#cb[i]
const k = temp[0] const k = temp[0]
const fn = temp[1] const fn = temp[1]
if (k === "_") { if (k === "_") {
fn(n, c, keys) fn(n, c, keys)
} }
if (typeof k === "string" && keys.includes(k as keyof IConfig)) { if (typeof k === "string" && keys.includes(k as keyof IConfig)) {
fn(n, c) fn(n, c)
} }
if (Array.isArray(k) && k.filter(v => keys.indexOf(v) !== -1).length) { if (Array.isArray(k) && k.filter(v => keys.indexOf(v) !== -1).length) {
fn(n, c) fn(n, c)
} }
} }
} }
#pathFile: string = process.env.NODE_ENV === "development" ? path.resolve(app.getPath("userData"), "./config_path-dev") : path.resolve(app.getPath("userData"), "./config_path") #pathFile: string =
#config: IConfig = cloneDeep(_tempConfig) process.env.NODE_ENV === "development"
#configPath(storagePath?: string): string { ? path.resolve(app.getPath("userData"), "./config_path-dev")
return path.join(storagePath || this.#config.storagePath, "./config.json") : path.resolve(app.getPath("userData"), "./config_path")
} #config: IConfig = cloneDeep(_tempConfig)
/** #configPath(storagePath?: string): string {
* return path.join(storagePath || this.#config.storagePath, "./config.json")
* @param confingPath }
*/ /**
#syncVar(confingPath?: string) { *
const configFile = this.#configPath(confingPath) * @param confingPath
if (!fs.pathExistsSync(configFile)) { */
fs.ensureFileSync(configFile) #syncVar(confingPath?: string) {
fs.writeJSONSync(configFile, {}) const configFile = this.#configPath(confingPath)
} if (!fs.pathExistsSync(configFile)) {
const config = fs.readJSONSync(configFile) as IConfig fs.ensureFileSync(configFile)
confingPath && (config.storagePath = confingPath) fs.writeJSONSync(configFile, {})
// 优先取本地的值 }
for (const key in config) { const config = fs.readJSONSync(configFile) as IConfig
// if (Object.prototype.hasOwnProperty.call(this.#config, key)) { confingPath && (config.storagePath = confingPath)
// this.#config[key] = config[key] || this.#config[key] // 优先取本地的值
// } for (const key in config) {
// 删除配置时本地的配置不会改变,想一下哪种方式更好 // if (Object.prototype.hasOwnProperty.call(this.#config, key)) {
this.#config[key] = config[key] || this.#config[key] // this.#config[key] = config[key] || this.#config[key]
} // }
} // 删除配置时本地的配置不会改变,想一下哪种方式更好
#init() { this.#config[key] = config[key] || this.#config[key]
console.log(`位置:${this.#pathFile}`) }
if (fs.pathExistsSync(this.#pathFile)) { }
const confingPath = fs.readFileSync(this.#pathFile, { encoding: "utf8" }) #init() {
if (confingPath && fs.pathExistsSync(this.#configPath(confingPath))) { console.log(`位置:${this.#pathFile}`)
this.#syncVar(confingPath) if (fs.pathExistsSync(this.#pathFile)) {
// 防止增加了配置本地却没变的情况 const confingPath = fs.readFileSync(this.#pathFile, { encoding: "utf8" })
this.#sync(confingPath) if (confingPath && fs.pathExistsSync(this.#configPath(confingPath))) {
} else { this.#syncVar(confingPath)
this.#syncVar(confingPath) // 防止增加了配置本地却没变的情况
this.#sync(confingPath) this.#sync(confingPath)
} } else {
} else { this.#syncVar(confingPath)
this.#syncVar() this.#sync(confingPath)
this.#sync() }
} } else {
init.call(this, this.#config) this.#syncVar()
} this.#sync()
config() { }
return this.#config init.call(this, this.#config)
} }
#sync(c?: string) { config() {
const config = cloneDeep(this.#config) return this.#config
delete config.storagePath }
const p = this.#configPath(c) #sync(c?: string) {
fs.ensureFileSync(p) const config = cloneDeep(this.#config)
fs.writeJSONSync(this.#configPath(c), config) delete config.storagePath
} const p = this.#configPath(c)
#change(p: string) { fs.ensureFileSync(p)
const storagePath = this.#config.storagePath fs.writeJSONSync(this.#configPath(c), config)
if (fs.existsSync(storagePath) && !fs.existsSync(p)) { }
fs.moveSync(storagePath, p) #change(p: string) {
} const storagePath = this.#config.storagePath
if (fs.existsSync(p) && fs.existsSync(storagePath) && isEmptyDir(p)) { if (fs.existsSync(storagePath) && !fs.existsSync(p)) {
console.log("文件夹为空,直接覆盖") fs.moveSync(storagePath, p)
fs.moveSync(storagePath, p, { overwrite: true }) }
} if (fs.existsSync(p) && fs.existsSync(storagePath) && isEmptyDir(p)) {
fs.writeFileSync(this.#pathFile, p, { encoding: "utf8" }) console.log("文件夹为空,直接覆盖")
} fs.moveSync(storagePath, p, { overwrite: true })
reset(key: keyof IConfig) { }
this.set(key, cloneDeep(_tempConfig[key])) fs.writeFileSync(this.#pathFile, p, { encoding: "utf8" })
} }
set(key: keyof IConfig | Partial<IConfig>, value?: any) { reset(key: keyof IConfig) {
let oldMainConfig = Object.assign({}, this.#config) this.set(key, cloneDeep(_tempConfig[key]))
let isChange = false }
let changeKeys: (keyof IConfig)[] = [] set(key: keyof IConfig | Partial<IConfig>, value?: any) {
let canChangeStorage = (targetPath: string) => { const oldMainConfig = Object.assign({}, this.#config)
if (fs.existsSync(oldMainConfig.storagePath) && fs.existsSync(targetPath) && !isEmptyDir(targetPath)) { let isChange = false
if (fs.existsSync(path.join(targetPath, "./config.json"))) { const changeKeys: (keyof IConfig)[] = []
return true const canChangeStorage = (targetPath: string) => {
} if (fs.existsSync(oldMainConfig.storagePath) && fs.existsSync(targetPath) && !isEmptyDir(targetPath)) {
return false if (fs.existsSync(path.join(targetPath, "./config.json"))) {
} return true
return true }
} return false
if (typeof key === "string") { }
if (value != undefined && value !== this.#config[key]) { return true
if (key === "storagePath") { }
if (!canChangeStorage(value)) { if (typeof key === "string") {
throw "无法改变存储地址" if (value != undefined && value !== this.#config[key]) {
return if (key === "storagePath") {
} if (!canChangeStorage(value)) {
try { throw "无法改变存储地址"
this.#change(value) return
} catch (error) { }
throw error this.#change(value)
} changeKeys.push("storagePath")
changeKeys.push("storagePath") this.#config["storagePath"] = value
this.#config["storagePath"] = value } else {
} else { changeKeys.push(key)
changeKeys.push(key) this.#config[key as string] = value
this.#config[key as string] = value }
} isChange = true
isChange = true }
} } else {
} else { if (key["storagePath"] !== undefined && key["storagePath"] !== this.#config["storagePath"]) {
if (key['storagePath'] !== undefined && key['storagePath'] !== this.#config['storagePath']) { if (!canChangeStorage(key["storagePath"])) {
if (!canChangeStorage(key['storagePath'])) { throw "无法改变存储地址"
throw "无法改变存储地址" return
return }
} this.#change(key["storagePath"])
try { this.#config["storagePath"] = key["storagePath"]
this.#change(key['storagePath']) changeKeys.push("storagePath")
} catch (error) { isChange = true
throw error }
} for (const _ in key) {
this.#config['storagePath'] = key['storagePath'] if (Object.prototype.hasOwnProperty.call(key, _)) {
changeKeys.push('storagePath') const v = key[_]
isChange = true if (v != undefined && _ !== "storagePath" && v !== this.#config[_]) {
} this.#config[_] = v
for (const _ in key as any) { changeKeys.push(_ as keyof IConfig)
if (Object.prototype.hasOwnProperty.call(key, _)) { isChange = true
const v = key[_] }
if (v != undefined && _ !== "storagePath" && v !== this.#config[_]) { }
this.#config[_] = v }
changeKeys.push(_ as keyof IConfig) }
isChange = true if (isChange) {
} this.#sync()
} this.#runCB(this.#config, oldMainConfig, changeKeys)
} }
} }
if (isChange) { values<T extends keyof IConfig>(key: T): IConfig[T] {
this.#sync() return this.#config[key]
this.#runCB(this.#config, oldMainConfig, changeKeys) }
} }
}
values<T extends keyof IConfig>(key: T): IConfig[T] { export default Setting
return this.#config[key] export { Setting }
}
}
export default Setting
export {
Setting
}

10
src/preload/index.d.ts

@ -1,8 +1,8 @@
import { ElectronAPI } from '@electron-toolkit/preload' import { ElectronAPI } from "@electron-toolkit/preload"
declare global { declare global {
interface Window { interface Window {
electron: ElectronAPI electron: ElectronAPI
api: unknown api: unknown
} }
} }

24
src/preload/index.ts

@ -1,5 +1,5 @@
import { contextBridge } from 'electron' import { contextBridge } from "electron"
import { electronAPI } from '@electron-toolkit/preload' import { electronAPI } from "@electron-toolkit/preload"
// Custom APIs for renderer // Custom APIs for renderer
const api = {} const api = {}
@ -8,15 +8,15 @@ const api = {}
// renderer only if context isolation is enabled, otherwise // renderer only if context isolation is enabled, otherwise
// just add to the DOM global. // just add to the DOM global.
if (process.contextIsolated) { if (process.contextIsolated) {
try { try {
contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld("electron", electronAPI)
contextBridge.exposeInMainWorld('api', api) contextBridge.exposeInMainWorld("api", api)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
} else { } else {
// @ts-ignore (define in dts) // @ts-ignore (define in dts)
window.electron = electronAPI window.electron = electronAPI
// @ts-ignore (define in dts) // @ts-ignore (define in dts)
window.api = api window.api = api
} }

26
src/renderer/index.html

@ -1,17 +1,17 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Electron</title> <title>Electron</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta <meta
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/> />
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

40
src/renderer/src/App.vue

@ -1,26 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import Versions from './components/Versions.vue' import Versions from "./components/Versions.vue"
const ipcHandle = () => window.electron.ipcRenderer.send('ping') const ipcHandle = () => window.electron.ipcRenderer.send("ping")
</script> </script>
<template> <template>
<img alt="logo" class="logo" src="./assets/electron.svg" /> <img alt="logo" class="logo" src="./assets/electron.svg" />
<div class="creator">Powered by electron-vite</div> <div class="creator">Powered by electron-vite</div>
<div class="text"> <div class="text">
Build an Electron app with Build an Electron app with
<span class="vue">Vue</span> <span class="vue">Vue</span>
and and
<span class="ts">TypeScript</span> <span class="ts">TypeScript</span>
</div>
<p class="tip">Please try pressing <code>F12</code> to open the devTool</p>
<div class="actions">
<div class="action">
<a href="https://electron-vite.org/" target="_blank" rel="noreferrer">Documentation</a>
</div> </div>
<div class="action"> <p class="tip">
<a target="_blank" rel="noreferrer" @click="ipcHandle">Send IPC</a> Please try pressing
<code>F12</code>
to open the devTool
</p>
<div class="actions">
<div class="action">
<a href="https://electron-vite.org/" target="_blank" rel="noreferrer">Documentation</a>
</div>
<div class="action">
<a target="_blank" rel="noreferrer" @click="ipcHandle">Send IPC</a>
</div>
</div> </div>
</div> <Versions />
<Versions />
</template> </template>

92
src/renderer/src/assets/base.css

@ -1,67 +1,67 @@
:root { :root {
--ev-c-white: #ffffff; --ev-c-white: #ffffff;
--ev-c-white-soft: #f8f8f8; --ev-c-white-soft: #f8f8f8;
--ev-c-white-mute: #f2f2f2; --ev-c-white-mute: #f2f2f2;
--ev-c-black: #1b1b1f; --ev-c-black: #1b1b1f;
--ev-c-black-soft: #222222; --ev-c-black-soft: #222222;
--ev-c-black-mute: #282828; --ev-c-black-mute: #282828;
--ev-c-gray-1: #515c67; --ev-c-gray-1: #515c67;
--ev-c-gray-2: #414853; --ev-c-gray-2: #414853;
--ev-c-gray-3: #32363f; --ev-c-gray-3: #32363f;
--ev-c-text-1: rgba(255, 255, 245, 0.86); --ev-c-text-1: rgba(255, 255, 245, 0.86);
--ev-c-text-2: rgba(235, 235, 245, 0.6); --ev-c-text-2: rgba(235, 235, 245, 0.6);
--ev-c-text-3: rgba(235, 235, 245, 0.38); --ev-c-text-3: rgba(235, 235, 245, 0.38);
--ev-button-alt-border: transparent; --ev-button-alt-border: transparent;
--ev-button-alt-text: var(--ev-c-text-1); --ev-button-alt-text: var(--ev-c-text-1);
--ev-button-alt-bg: var(--ev-c-gray-3); --ev-button-alt-bg: var(--ev-c-gray-3);
--ev-button-alt-hover-border: transparent; --ev-button-alt-hover-border: transparent;
--ev-button-alt-hover-text: var(--ev-c-text-1); --ev-button-alt-hover-text: var(--ev-c-text-1);
--ev-button-alt-hover-bg: var(--ev-c-gray-2); --ev-button-alt-hover-bg: var(--ev-c-gray-2);
} }
:root { :root {
--color-background: var(--ev-c-black); --color-background: var(--ev-c-black);
--color-background-soft: var(--ev-c-black-soft); --color-background-soft: var(--ev-c-black-soft);
--color-background-mute: var(--ev-c-black-mute); --color-background-mute: var(--ev-c-black-mute);
--color-text: var(--ev-c-text-1); --color-text: var(--ev-c-text-1);
} }
*, *,
*::before, *::before,
*::after { *::after {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;
} }
ul { ul {
list-style: none; list-style: none;
} }
body { body {
min-height: 100vh; min-height: 100vh;
color: var(--color-text); color: var(--color-text);
background: var(--color-background); background: var(--color-background);
line-height: 1.6; line-height: 1.6;
font-family: font-family:
Inter, Inter,
-apple-system, -apple-system,
BlinkMacSystemFont, BlinkMacSystemFont,
'Segoe UI', "Segoe UI",
Roboto, Roboto,
Oxygen, Oxygen,
Ubuntu, Ubuntu,
Cantarell, Cantarell,
'Fira Sans', "Fira Sans",
'Droid Sans', "Droid Sans",
'Helvetica Neue', "Helvetica Neue",
sans-serif; sans-serif;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }

228
src/renderer/src/assets/main.css

@ -1,171 +1,171 @@
@import './base.css'; @import "./base.css";
body { body {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
background-image: url('./wavy-lines.svg'); background-image: url("./wavy-lines.svg");
background-size: cover; background-size: cover;
user-select: none; user-select: none;
} }
code { code {
font-weight: 600; font-weight: 600;
padding: 3px 5px; padding: 3px 5px;
border-radius: 2px; border-radius: 2px;
background-color: var(--color-background-mute); background-color: var(--color-background-mute);
font-family: font-family:
ui-monospace, ui-monospace,
SFMono-Regular, SFMono-Regular,
SF Mono, SF Mono,
Menlo, Menlo,
Consolas, Consolas,
Liberation Mono, Liberation Mono,
monospace; monospace;
font-size: 85%; font-size: 85%;
} }
#app { #app {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
margin-bottom: 80px; margin-bottom: 80px;
} }
.logo { .logo {
margin-bottom: 20px; margin-bottom: 20px;
-webkit-user-drag: none; -webkit-user-drag: none;
height: 128px; height: 128px;
width: 128px; width: 128px;
will-change: filter; will-change: filter;
transition: filter 300ms; transition: filter 300ms;
} }
.logo:hover { .logo:hover {
filter: drop-shadow(0 0 1.2em #6988e6aa); filter: drop-shadow(0 0 1.2em #6988e6aa);
} }
.creator { .creator {
font-size: 14px; font-size: 14px;
line-height: 16px; line-height: 16px;
color: var(--ev-c-text-2); color: var(--ev-c-text-2);
font-weight: 600; font-weight: 600;
margin-bottom: 10px; margin-bottom: 10px;
} }
.text { .text {
font-size: 28px; font-size: 28px;
color: var(--ev-c-text-1); color: var(--ev-c-text-1);
font-weight: 700; font-weight: 700;
line-height: 32px; line-height: 32px;
text-align: center; text-align: center;
margin: 0 10px; margin: 0 10px;
padding: 16px 0; padding: 16px 0;
} }
.tip { .tip {
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
color: var(--ev-c-text-2); color: var(--ev-c-text-2);
font-weight: 600; font-weight: 600;
} }
.vue { .vue {
background: -webkit-linear-gradient(315deg, #42d392 25%, #647eff); background: -webkit-linear-gradient(315deg, #42d392 25%, #647eff);
background-clip: text; background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
font-weight: 700; font-weight: 700;
} }
.ts { .ts {
background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e); background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e);
background-clip: text; background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
font-weight: 700; font-weight: 700;
} }
.actions { .actions {
display: flex; display: flex;
padding-top: 32px; padding-top: 32px;
margin: -6px; margin: -6px;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-start; justify-content: flex-start;
} }
.action { .action {
flex-shrink: 0; flex-shrink: 0;
padding: 6px; padding: 6px;
} }
.action a { .action a {
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
border: 1px solid transparent; border: 1px solid transparent;
text-align: center; text-align: center;
font-weight: 600; font-weight: 600;
white-space: nowrap; white-space: nowrap;
border-radius: 20px; border-radius: 20px;
padding: 0 20px; padding: 0 20px;
line-height: 38px; line-height: 38px;
font-size: 14px; font-size: 14px;
border-color: var(--ev-button-alt-border); border-color: var(--ev-button-alt-border);
color: var(--ev-button-alt-text); color: var(--ev-button-alt-text);
background-color: var(--ev-button-alt-bg); background-color: var(--ev-button-alt-bg);
} }
.action a:hover { .action a:hover {
border-color: var(--ev-button-alt-hover-border); border-color: var(--ev-button-alt-hover-border);
color: var(--ev-button-alt-hover-text); color: var(--ev-button-alt-hover-text);
background-color: var(--ev-button-alt-hover-bg); background-color: var(--ev-button-alt-hover-bg);
} }
.versions { .versions {
position: absolute; position: absolute;
bottom: 30px; bottom: 30px;
margin: 0 auto; margin: 0 auto;
padding: 15px 0; padding: 15px 0;
font-family: 'Menlo', 'Lucida Console', monospace; font-family: "Menlo", "Lucida Console", monospace;
display: inline-flex; display: inline-flex;
overflow: hidden; overflow: hidden;
align-items: center; align-items: center;
border-radius: 22px; border-radius: 22px;
background-color: #202127; background-color: #202127;
backdrop-filter: blur(24px); backdrop-filter: blur(24px);
} }
.versions li { .versions li {
display: block; display: block;
float: left; float: left;
border-right: 1px solid var(--ev-c-gray-1); border-right: 1px solid var(--ev-c-gray-1);
padding: 0 20px; padding: 0 20px;
font-size: 14px; font-size: 14px;
line-height: 14px; line-height: 14px;
opacity: 0.8; opacity: 0.8;
&:last-child { &:last-child {
border: none; border: none;
} }
} }
@media (max-width: 720px) { @media (max-width: 720px) {
.text { .text {
font-size: 20px; font-size: 20px;
} }
} }
@media (max-width: 620px) { @media (max-width: 620px) {
.versions { .versions {
display: none; display: none;
} }
} }
@media (max-width: 350px) { @media (max-width: 350px) {
.tip, .tip,
.actions { .actions {
display: none; display: none;
} }
} }

12
src/renderer/src/components/Versions.vue

@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive } from 'vue' import { reactive } from "vue"
const versions = reactive({ ...window.electron.process.versions }) const versions = reactive({ ...window.electron.process.versions })
</script> </script>
<template> <template>
<ul class="versions"> <ul class="versions">
<li class="electron-version">Electron v{{ versions.electron }}</li> <li class="electron-version">Electron v{{ versions.electron }}</li>
<li class="chrome-version">Chromium v{{ versions.chrome }}</li> <li class="chrome-version">Chromium v{{ versions.chrome }}</li>
<li class="node-version">Node v{{ versions.node }}</li> <li class="node-version">Node v{{ versions.node }}</li>
</ul> </ul>
</template> </template>

10
src/renderer/src/env.d.ts

@ -1,8 +1,8 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
declare module '*.vue' { declare module "*.vue" {
import type { DefineComponent } from 'vue' import type { DefineComponent } from "vue"
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any> const component: DefineComponent<{}, {}, any>
export default component export default component
} }

12
src/renderer/src/main.ts

@ -1,8 +1,8 @@
import 'virtual:uno.css' import "virtual:uno.css"
import '@unocss/reset/normalize.css' import "@unocss/reset/normalize.css"
import './assets/main.css' import "./assets/main.css"
import { createApp } from 'vue' import { createApp } from "vue"
import App from './App.vue' import App from "./App.vue"
createApp(App).mount('#app') createApp(App).mount("#app")

10
src/renderer/src/shims.d.ts

@ -1,5 +1,5 @@
import type { AttributifyAttributes } from '@unocss/preset-attributify' import type { AttributifyAttributes } from "@unocss/preset-attributify"
declare module '@vue/runtime-dom' { declare module "@vue/runtime-dom" {
interface HTMLAttributes extends AttributifyAttributes { } interface HTMLAttributes extends AttributifyAttributes {}
} }

16
uno.config.ts

@ -1,10 +1,6 @@
import { defineConfig, presetAttributify, presetUno } from 'unocss' import { defineConfig, presetAttributify, presetUno } from "unocss"
import presetRemToPx from '@unocss/preset-rem-to-px' import presetRemToPx from "@unocss/preset-rem-to-px"
export default defineConfig({ export default defineConfig({
presets: [ presets: [presetAttributify(), presetUno(), presetRemToPx()],
presetAttributify(), })
presetUno(),
presetRemToPx(),
],
})

Loading…
Cancel
Save