27 changed files with 867 additions and 886 deletions
@ -1,3 +1,3 @@ |
|||
{ |
|||
"recommendations": ["dbaeumer.vscode-eslint"] |
|||
"recommendations": ["dbaeumer.vscode-eslint"] |
|||
} |
|||
|
@ -1,39 +1,39 @@ |
|||
{ |
|||
"version": "0.2.0", |
|||
"configurations": [ |
|||
{ |
|||
"name": "Debug Main Process", |
|||
"type": "node", |
|||
"request": "launch", |
|||
"cwd": "${workspaceRoot}", |
|||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", |
|||
"windows": { |
|||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" |
|||
}, |
|||
"runtimeArgs": ["--sourcemap"], |
|||
"env": { |
|||
"REMOTE_DEBUGGING_PORT": "9222" |
|||
} |
|||
}, |
|||
{ |
|||
"name": "Debug Renderer Process", |
|||
"port": 9222, |
|||
"request": "attach", |
|||
"type": "chrome", |
|||
"webRoot": "${workspaceFolder}/src/renderer", |
|||
"timeout": 60000, |
|||
"presentation": { |
|||
"hidden": true |
|||
} |
|||
} |
|||
], |
|||
"compounds": [ |
|||
{ |
|||
"name": "Debug All", |
|||
"configurations": ["Debug Main Process", "Debug Renderer Process"], |
|||
"presentation": { |
|||
"order": 1 |
|||
} |
|||
} |
|||
] |
|||
"version": "0.2.0", |
|||
"configurations": [ |
|||
{ |
|||
"name": "Debug Main Process", |
|||
"type": "node", |
|||
"request": "launch", |
|||
"cwd": "${workspaceRoot}", |
|||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", |
|||
"windows": { |
|||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" |
|||
}, |
|||
"runtimeArgs": ["--sourcemap"], |
|||
"env": { |
|||
"REMOTE_DEBUGGING_PORT": "9222" |
|||
} |
|||
}, |
|||
{ |
|||
"name": "Debug Renderer Process", |
|||
"port": 9222, |
|||
"request": "attach", |
|||
"type": "chrome", |
|||
"webRoot": "${workspaceFolder}/src/renderer", |
|||
"timeout": 60000, |
|||
"presentation": { |
|||
"hidden": true |
|||
} |
|||
} |
|||
], |
|||
"compounds": [ |
|||
{ |
|||
"name": "Debug All", |
|||
"configurations": ["Debug Main Process", "Debug Renderer Process"], |
|||
"presentation": { |
|||
"order": 1 |
|||
} |
|||
} |
|||
] |
|||
} |
|||
|
@ -1,11 +1,11 @@ |
|||
{ |
|||
"[typescript]": { |
|||
"editor.defaultFormatter": "vscode.typescript-language-features" |
|||
}, |
|||
"[javascript]": { |
|||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|||
}, |
|||
"[json]": { |
|||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|||
} |
|||
"[typescript]": { |
|||
"editor.defaultFormatter": "vscode.typescript-language-features" |
|||
}, |
|||
"[javascript]": { |
|||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|||
}, |
|||
"[json]": { |
|||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
|||
} |
|||
} |
|||
|
@ -1,35 +1,35 @@ |
|||
interface IConfig { |
|||
app_title: string, |
|||
default_config: { |
|||
language: "zh" | "en" // i18n
|
|||
"common.theme": "light" | "dark" | "auto" // 主题
|
|||
"desktop:wallpaper": string |
|||
"update.repo"?: string // 更新地址
|
|||
"update.owner"?: string // 更新通道
|
|||
"update.allowDowngrade": boolean, |
|||
"update.allowPrerelease": boolean |
|||
"editor.bg": string // 更新通道
|
|||
"editor.logoType": "logo" | "bg" // 更新通道
|
|||
"editor.fontFamily": string // 更新通道
|
|||
// "snippet.storagePath": string // 代码片段保存位置
|
|||
// "bookmark.storagePath": string // 书签保存位置
|
|||
// backup_rule: string // 备份规则
|
|||
storagePath: string // 存储地址
|
|||
} |
|||
} |
|||
export default { |
|||
app_title: "ada", |
|||
default_config: { |
|||
"storagePath": "$storagePath$", |
|||
"language": "zh", |
|||
"common.theme": "auto", |
|||
"desktop:wallpaper": "", |
|||
"editor.bg": "", |
|||
"editor.logoType": "logo", |
|||
"editor.fontFamily": "Cascadia Mono, Consolas, 'Courier New', monospace", |
|||
"update.repo": "wood-desktop", |
|||
"update.owner": "npmrun", |
|||
"update.allowDowngrade": false, |
|||
"update.allowPrerelease": false |
|||
} |
|||
} as IConfig |
|||
interface IConfig { |
|||
app_title: string |
|||
default_config: { |
|||
language: "zh" | "en" // i18n
|
|||
"common.theme": "light" | "dark" | "auto" // 主题
|
|||
"desktop:wallpaper": string |
|||
"update.repo"?: string // 更新地址
|
|||
"update.owner"?: string // 更新通道
|
|||
"update.allowDowngrade": boolean |
|||
"update.allowPrerelease": boolean |
|||
"editor.bg": string // 更新通道
|
|||
"editor.logoType": "logo" | "bg" // 更新通道
|
|||
"editor.fontFamily": string // 更新通道
|
|||
// "snippet.storagePath": string // 代码片段保存位置
|
|||
// "bookmark.storagePath": string // 书签保存位置
|
|||
// backup_rule: string // 备份规则
|
|||
storagePath: string // 存储地址
|
|||
} |
|||
} |
|||
export default { |
|||
app_title: "ada", |
|||
default_config: { |
|||
storagePath: "$storagePath$", |
|||
language: "zh", |
|||
"common.theme": "auto", |
|||
"desktop:wallpaper": "", |
|||
"editor.bg": "", |
|||
"editor.logoType": "logo", |
|||
"editor.fontFamily": "Cascadia Mono, Consolas, 'Courier New', monospace", |
|||
"update.repo": "wood-desktop", |
|||
"update.owner": "npmrun", |
|||
"update.allowDowngrade": false, |
|||
"update.allowPrerelease": false, |
|||
}, |
|||
} as IConfig |
|||
|
@ -1,45 +1,45 @@ |
|||
appId: com.electron.app |
|||
productName: my-app |
|||
directories: |
|||
buildResources: build |
|||
buildResources: build |
|||
files: |
|||
- '!**/.vscode/*' |
|||
- '!src/*' |
|||
- '!electron.vite.config.{js,ts,mjs,cjs}' |
|||
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' |
|||
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}' |
|||
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}' |
|||
- "!**/.vscode/*" |
|||
- "!src/*" |
|||
- "!electron.vite.config.{js,ts,mjs,cjs}" |
|||
- "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}" |
|||
- "!{.env,.env.*,.npmrc,pnpm-lock.yaml}" |
|||
- "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}" |
|||
asarUnpack: |
|||
- resources/** |
|||
- resources/** |
|||
win: |
|||
executableName: my-app |
|||
executableName: my-app |
|||
nsis: |
|||
artifactName: ${name}-${version}-setup.${ext} |
|||
shortcutName: ${productName} |
|||
uninstallDisplayName: ${productName} |
|||
createDesktopShortcut: always |
|||
artifactName: ${name}-${version}-setup.${ext} |
|||
shortcutName: ${productName} |
|||
uninstallDisplayName: ${productName} |
|||
createDesktopShortcut: always |
|||
mac: |
|||
entitlementsInherit: build/entitlements.mac.plist |
|||
extendInfo: |
|||
- NSCameraUsageDescription: Application requests access to the device's camera. |
|||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. |
|||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. |
|||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. |
|||
notarize: false |
|||
entitlementsInherit: build/entitlements.mac.plist |
|||
extendInfo: |
|||
- NSCameraUsageDescription: Application requests access to the device's camera. |
|||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. |
|||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. |
|||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. |
|||
notarize: false |
|||
dmg: |
|||
artifactName: ${name}-${version}.${ext} |
|||
artifactName: ${name}-${version}.${ext} |
|||
linux: |
|||
target: |
|||
- AppImage |
|||
- snap |
|||
- deb |
|||
maintainer: electronjs.org |
|||
category: Utility |
|||
target: |
|||
- AppImage |
|||
- snap |
|||
- deb |
|||
maintainer: electronjs.org |
|||
category: Utility |
|||
appImage: |
|||
artifactName: ${name}-${version}.${ext} |
|||
artifactName: ${name}-${version}.${ext} |
|||
npmRebuild: false |
|||
publish: |
|||
provider: generic |
|||
url: https://example.com/auto-updates |
|||
provider: generic |
|||
url: https://example.com/auto-updates |
|||
electronDownload: |
|||
mirror: https://npmmirror.com/mirrors/electron/ |
|||
mirror: https://npmmirror.com/mirrors/electron/ |
|||
|
@ -1,28 +1,28 @@ |
|||
import { resolve } from 'path' |
|||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite' |
|||
import vue from '@vitejs/plugin-vue' |
|||
import UnoCSS from 'unocss/vite' |
|||
import { resolve } from "path" |
|||
import { defineConfig, externalizeDepsPlugin } from "electron-vite" |
|||
import vue from "@vitejs/plugin-vue" |
|||
import UnoCSS from "unocss/vite" |
|||
|
|||
export default defineConfig({ |
|||
main: { |
|||
resolve: { |
|||
alias: { |
|||
config: resolve('config'), |
|||
vc: resolve('src/main'), |
|||
res: resolve('resources') |
|||
} |
|||
main: { |
|||
resolve: { |
|||
alias: { |
|||
config: resolve("config"), |
|||
vc: resolve("src/main"), |
|||
res: resolve("resources"), |
|||
}, |
|||
}, |
|||
plugins: [externalizeDepsPlugin()], |
|||
}, |
|||
plugins: [externalizeDepsPlugin()] |
|||
}, |
|||
preload: { |
|||
plugins: [externalizeDepsPlugin()] |
|||
}, |
|||
renderer: { |
|||
resolve: { |
|||
alias: { |
|||
'@renderer': resolve('src/renderer/src') |
|||
} |
|||
preload: { |
|||
plugins: [externalizeDepsPlugin()], |
|||
}, |
|||
renderer: { |
|||
resolve: { |
|||
alias: { |
|||
"@renderer": resolve("src/renderer/src"), |
|||
}, |
|||
}, |
|||
plugins: [UnoCSS(), vue()], |
|||
}, |
|||
plugins: [UnoCSS(), vue()] |
|||
} |
|||
}) |
|||
|
@ -1,56 +1,56 @@ |
|||
{ |
|||
"name": "my-app", |
|||
"type": "module", |
|||
"version": "1.0.0", |
|||
"description": "An Electron application with Vue and TypeScript", |
|||
"main": "./out/main/index.js", |
|||
"author": "example.com", |
|||
"homepage": "https://electron-vite.org", |
|||
"scripts": { |
|||
"format": "prettier --write .", |
|||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", |
|||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", |
|||
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", |
|||
"typecheck": "npm run typecheck:node && npm run typecheck:web", |
|||
"start": "electron-vite preview", |
|||
"dev": "electron-vite dev", |
|||
"dev:watch": "electron-vite dev --watch", |
|||
"build": "npm run typecheck && electron-vite build", |
|||
"postinstall": "electron-builder install-app-deps", |
|||
"build:unpack": "npm run build && electron-builder --dir", |
|||
"build:win": "npm run build && electron-builder --win", |
|||
"build:mac": "npm run build && electron-builder --mac", |
|||
"build:linux": "npm run build && electron-builder --linux" |
|||
}, |
|||
"dependencies": { |
|||
"@electron-toolkit/preload": "^3.0.0", |
|||
"@electron-toolkit/utils": "^3.0.0", |
|||
"@unocss/reset": "^0.64.1", |
|||
"electron-updater": "^6.1.7", |
|||
"inversify": "^6.1.4", |
|||
"lowdb": "^7.0.1", |
|||
"reflect-metadata": "^0.2.2" |
|||
}, |
|||
"devDependencies": { |
|||
"@electron-toolkit/eslint-config": "^1.0.2", |
|||
"@electron-toolkit/eslint-config-ts": "^2.0.0", |
|||
"@electron-toolkit/tsconfig": "^1.0.1", |
|||
"@rushstack/eslint-patch": "^1.10.3", |
|||
"@types/node": "^20.14.8", |
|||
"@unocss/preset-rem-to-px": "^0.64.1", |
|||
"@vitejs/plugin-vue": "^5.0.5", |
|||
"@vue/eslint-config-prettier": "^9.0.0", |
|||
"@vue/eslint-config-typescript": "^13.0.0", |
|||
"electron": "^31.0.2", |
|||
"electron-builder": "^24.13.3", |
|||
"electron-vite": "^2.3.0", |
|||
"eslint": "^8.57.0", |
|||
"eslint-plugin-vue": "^9.26.0", |
|||
"prettier": "^3.3.2", |
|||
"typescript": "^5.5.2", |
|||
"unocss": "^0.64.1", |
|||
"vite": "^5.3.1", |
|||
"vue": "^3.4.30", |
|||
"vue-tsc": "^2.0.22" |
|||
} |
|||
"name": "my-app", |
|||
"type": "module", |
|||
"version": "1.0.0", |
|||
"description": "An Electron application with Vue and TypeScript", |
|||
"main": "./out/main/index.js", |
|||
"author": "example.com", |
|||
"homepage": "https://electron-vite.org", |
|||
"scripts": { |
|||
"format": "prettier --write .", |
|||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", |
|||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", |
|||
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", |
|||
"typecheck": "npm run typecheck:node && npm run typecheck:web", |
|||
"start": "electron-vite preview", |
|||
"dev": "electron-vite dev", |
|||
"dev:watch": "electron-vite dev --watch", |
|||
"build": "npm run typecheck && electron-vite build", |
|||
"postinstall": "electron-builder install-app-deps", |
|||
"build:unpack": "npm run build && electron-builder --dir", |
|||
"build:win": "npm run build && electron-builder --win", |
|||
"build:mac": "npm run build && electron-builder --mac", |
|||
"build:linux": "npm run build && electron-builder --linux" |
|||
}, |
|||
"dependencies": { |
|||
"@electron-toolkit/preload": "^3.0.0", |
|||
"@electron-toolkit/utils": "^3.0.0", |
|||
"@unocss/reset": "^0.64.1", |
|||
"electron-updater": "^6.1.7", |
|||
"inversify": "^6.1.4", |
|||
"lowdb": "^7.0.1", |
|||
"reflect-metadata": "^0.2.2" |
|||
}, |
|||
"devDependencies": { |
|||
"@electron-toolkit/eslint-config": "^1.0.2", |
|||
"@electron-toolkit/eslint-config-ts": "^2.0.0", |
|||
"@electron-toolkit/tsconfig": "^1.0.1", |
|||
"@rushstack/eslint-patch": "^1.10.3", |
|||
"@types/node": "^20.14.8", |
|||
"@unocss/preset-rem-to-px": "^0.64.1", |
|||
"@vitejs/plugin-vue": "^5.0.5", |
|||
"@vue/eslint-config-prettier": "^9.0.0", |
|||
"@vue/eslint-config-typescript": "^13.0.0", |
|||
"electron": "^31.0.2", |
|||
"electron-builder": "^24.13.3", |
|||
"electron-vite": "^2.3.0", |
|||
"eslint": "^8.57.0", |
|||
"eslint-plugin-vue": "^9.26.0", |
|||
"prettier": "^3.3.2", |
|||
"typescript": "^5.5.2", |
|||
"unocss": "^0.64.1", |
|||
"vite": "^5.3.1", |
|||
"vue": "^3.4.30", |
|||
"vue-tsc": "^2.0.22" |
|||
} |
|||
} |
|||
|
@ -1,3 +1,3 @@ |
|||
abstract class Base{ |
|||
constructor(){} |
|||
} |
|||
abstract class Base { |
|||
constructor() {} |
|||
} |
|||
|
@ -1,31 +1,25 @@ |
|||
import { injectable, inject } from "inversify" |
|||
import Setting from "./setting" |
|||
import DB from "./db" |
|||
|
|||
|
|||
@injectable() |
|||
class App { |
|||
private _setting: Setting |
|||
private _db: DB |
|||
|
|||
constructor( |
|||
@inject(Setting) setting: Setting, |
|||
@inject(DB) db: DB, |
|||
) { |
|||
console.log(`App inited`); |
|||
|
|||
this._setting = setting |
|||
this._db = db |
|||
} |
|||
|
|||
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 |
|||
} |
|||
import { injectable, inject } from "inversify" |
|||
import Setting from "./setting" |
|||
import DB from "./db" |
|||
|
|||
@injectable() |
|||
class App { |
|||
private _setting: Setting |
|||
private _db: DB |
|||
|
|||
constructor(@inject(Setting) setting: Setting, @inject(DB) db: DB) { |
|||
console.log(`App inited`) |
|||
|
|||
this._setting = setting |
|||
this._db = db |
|||
} |
|||
|
|||
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 } |
|||
|
@ -1,33 +1,33 @@ |
|||
import { JSONFile } from "lowdb/node" |
|||
import { Low } from 'lowdb' |
|||
import fs from 'fs-extra' |
|||
|
|||
export class CustomAdapter<T> extends JSONFile<T> { |
|||
constructor(filepath: string) { |
|||
super(filepath) |
|||
this.filepath = filepath |
|||
} |
|||
filepath: string = '' |
|||
async read() { |
|||
if (!fs.existsSync(this.filepath)) { |
|||
return null |
|||
} |
|||
let data = fs.readJSONSync(this.filepath, { throws: false }) |
|||
if (!data) { |
|||
return null |
|||
} |
|||
return data |
|||
} |
|||
|
|||
async write(data: T) { |
|||
fs.ensureFileSync(this.filepath) |
|||
await super.write(data) |
|||
} |
|||
} |
|||
export class CustomLow<T> extends Low<T> { |
|||
constructor(adapter: CustomAdapter<T>, defaultData: T) { |
|||
super(adapter, defaultData) |
|||
this.filepath = adapter.filepath |
|||
} |
|||
filepath: string = '' |
|||
} |
|||
import { JSONFile } from "lowdb/node" |
|||
import { Low } from "lowdb" |
|||
import fs from "fs-extra" |
|||
|
|||
export class CustomAdapter<T> extends JSONFile<T> { |
|||
constructor(filepath: string) { |
|||
super(filepath) |
|||
this.filepath = filepath |
|||
} |
|||
filepath: string = "" |
|||
async read() { |
|||
if (!fs.existsSync(this.filepath)) { |
|||
return null |
|||
} |
|||
const data = fs.readJSONSync(this.filepath, { throws: false }) |
|||
if (!data) { |
|||
return null |
|||
} |
|||
return data |
|||
} |
|||
|
|||
async write(data: T) { |
|||
fs.ensureFileSync(this.filepath) |
|||
await super.write(data) |
|||
} |
|||
} |
|||
export class CustomLow<T> extends Low<T> { |
|||
constructor(adapter: CustomAdapter<T>, defaultData: T) { |
|||
super(adapter, defaultData) |
|||
this.filepath = adapter.filepath |
|||
} |
|||
filepath: string = "" |
|||
} |
|||
|
@ -1,76 +1,74 @@ |
|||
import { inject, injectable } from "inversify"; |
|||
import Setting from "../setting"; |
|||
import { CustomAdapter, CustomLow } from "./custom"; |
|||
import path from "node:path"; |
|||
|
|||
@injectable() |
|||
class DB { |
|||
private _setting: Setting |
|||
Modules: Record<string, CustomLow<any>> = {} |
|||
|
|||
constructor(@inject(Setting) setting: Setting) { |
|||
console.log(`DB inited`); |
|||
|
|||
this._setting = setting |
|||
} |
|||
|
|||
create(filepath) { |
|||
let adapter = new CustomAdapter<any>(filepath) |
|||
const db = new CustomLow<{}>(adapter, {}) |
|||
db.filepath = filepath |
|||
return db |
|||
} |
|||
|
|||
getDB(dbName: string) { |
|||
if (this.Modules[dbName] === undefined) { |
|||
let filepath = path.resolve(this._setting.values("storagePath"), './db/' + dbName + '.json') |
|||
this.Modules[dbName] = this.create(filepath) |
|||
return this.Modules[dbName] |
|||
} else { |
|||
let cur = this.Modules[dbName] |
|||
let filepath = path.resolve(this._setting.values("storagePath"), './db/' + dbName + '.json') |
|||
if (cur.filepath != filepath) { |
|||
this.Modules[dbName] = this.create(filepath) |
|||
} |
|||
return this.Modules[dbName] |
|||
} |
|||
} |
|||
|
|||
async saveData(data: any): Promise<any> |
|||
async saveData(dbName: string, data: any): Promise<any> |
|||
async saveData(dbName: string, data?: any): Promise<any> { |
|||
let db, rData |
|||
if (arguments.length === 2) { |
|||
db = this.getDB(dbName) |
|||
rData = data |
|||
} else { |
|||
db = this.getDB('db') |
|||
rData = dbName |
|||
} |
|||
if (db) { |
|||
db.data = rData |
|||
await db.write() |
|||
return db.data |
|||
} |
|||
return null |
|||
} |
|||
|
|||
async getData(dbName?: string) { |
|||
let db |
|||
if (dbName) { |
|||
db = this.getDB(dbName) |
|||
} else { |
|||
db = this.getDB('db') |
|||
} |
|||
if (db) { |
|||
await db.read() |
|||
return db.data |
|||
} |
|||
return null |
|||
} |
|||
} |
|||
|
|||
export default DB |
|||
export { |
|||
DB |
|||
} |
|||
import { inject, injectable } from "inversify" |
|||
import Setting from "../setting" |
|||
import { CustomAdapter, CustomLow } from "./custom" |
|||
import path from "node:path" |
|||
|
|||
@injectable() |
|||
class DB { |
|||
private _setting: Setting |
|||
Modules: Record<string, CustomLow<any>> = {} |
|||
|
|||
constructor(@inject(Setting) setting: Setting) { |
|||
console.log(`DB inited`) |
|||
|
|||
this._setting = setting |
|||
} |
|||
|
|||
create(filepath) { |
|||
const adapter = new CustomAdapter<any>(filepath) |
|||
const db = new CustomLow<object>(adapter, {}) |
|||
db.filepath = filepath |
|||
return db |
|||
} |
|||
|
|||
getDB(dbName: string) { |
|||
if (this.Modules[dbName] === undefined) { |
|||
const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json") |
|||
this.Modules[dbName] = this.create(filepath) |
|||
return this.Modules[dbName] |
|||
} else { |
|||
const cur = this.Modules[dbName] |
|||
const filepath = path.resolve(this._setting.values("storagePath"), "./db/" + dbName + ".json") |
|||
if (cur.filepath != filepath) { |
|||
this.Modules[dbName] = this.create(filepath) |
|||
} |
|||
return this.Modules[dbName] |
|||
} |
|||
} |
|||
|
|||
async saveData(data: any): Promise<any> |
|||
async saveData(dbName: string, data: any): Promise<any> |
|||
async saveData(dbName: string, data?: any): Promise<any> { |
|||
let db, rData |
|||
if (arguments.length === 2) { |
|||
db = this.getDB(dbName) |
|||
rData = data |
|||
} else { |
|||
db = this.getDB("db") |
|||
rData = dbName |
|||
} |
|||
if (db) { |
|||
db.data = rData |
|||
await db.write() |
|||
return db.data |
|||
} |
|||
return null |
|||
} |
|||
|
|||
async getData(dbName?: string) { |
|||
let db |
|||
if (dbName) { |
|||
db = this.getDB(dbName) |
|||
} else { |
|||
db = this.getDB("db") |
|||
} |
|||
if (db) { |
|||
await db.read() |
|||
return db.data |
|||
} |
|||
return null |
|||
} |
|||
} |
|||
|
|||
export default DB |
|||
export { DB } |
|||
|
@ -1,11 +1,9 @@ |
|||
import { Container } from "inversify" |
|||
import module from "./module" |
|||
|
|||
const container = new Container() |
|||
|
|||
container.load(module) |
|||
|
|||
export default container |
|||
export { |
|||
container |
|||
} |
|||
import { Container } from "inversify" |
|||
import module from "./module" |
|||
|
|||
const container = new Container() |
|||
|
|||
container.load(module) |
|||
|
|||
export default container |
|||
export { container } |
|||
|
@ -1,15 +1,13 @@ |
|||
import { ContainerModule } from "inversify" |
|||
import { Setting } from "./setting" |
|||
import { DB } from "./db" |
|||
import App from "./App" |
|||
|
|||
const module = new ContainerModule((bind) => { |
|||
bind(Setting).toConstantValue(new Setting()) |
|||
bind(DB).toSelf().inSingletonScope() |
|||
bind(App).toSelf().inSingletonScope() |
|||
}) |
|||
|
|||
export default module |
|||
export { |
|||
module |
|||
} |
|||
import { ContainerModule } from "inversify" |
|||
import { Setting } from "./setting" |
|||
import { DB } from "./db" |
|||
import App from "./App" |
|||
|
|||
const module = new ContainerModule(bind => { |
|||
bind(Setting).toConstantValue(new Setting()) |
|||
bind(DB).toSelf().inSingletonScope() |
|||
bind(App).toSelf().inSingletonScope() |
|||
}) |
|||
|
|||
export default module |
|||
export { module } |
|||
|
@ -1,233 +1,226 @@ |
|||
import fs from "fs-extra" |
|||
import { app } from "electron" |
|||
import path from "path" |
|||
import { cloneDeep } from "lodash" |
|||
import { injectable } from "inversify" |
|||
import Config from "config" |
|||
|
|||
type IConfig = typeof Config.default_config |
|||
|
|||
type IOnFunc = (n: IConfig, c: IConfig, keys?: (keyof IConfig)[]) => void |
|||
type IT = (keyof IConfig)[] | keyof IConfig | "_" |
|||
|
|||
let storagePath = path.join(app.getPath("documents"), Config.app_title) |
|||
let storagePathDev = path.join(app.getPath("documents"), Config.app_title + "-dev") |
|||
|
|||
if (process.env.NODE_ENV === "development") { |
|||
storagePath = storagePathDev |
|||
} |
|||
|
|||
let _tempConfig = cloneDeep(Config.default_config as IConfig) |
|||
Object.keys(_tempConfig).forEach(key => { |
|||
if (typeof _tempConfig[key] === "string" && _tempConfig[key].includes("$storagePath$")) { |
|||
_tempConfig[key] = _tempConfig[key].replace(/\$storagePath\$/g, storagePath) |
|||
if (_tempConfig[key] && path.isAbsolute(_tempConfig[key])) { |
|||
_tempConfig[key] = path.normalize(_tempConfig[key]) |
|||
} |
|||
} |
|||
}) |
|||
|
|||
function isPath(str) { |
|||
// 使用正则表达式检查字符串是否以斜杠或盘符开头
|
|||
return /^(?:\/|[a-zA-Z]:\\)/.test(str); |
|||
} |
|||
|
|||
function init(config: IConfig) { |
|||
// 在配置初始化后执行
|
|||
Object.keys(config).forEach(key => { |
|||
if (config[key] && isPath(config[key]) && path.isAbsolute(config[key])) { |
|||
fs.ensureDirSync(config[key]) |
|||
} |
|||
}) |
|||
// 在配置初始化后执行
|
|||
// fs.ensureDirSync(config["snippet.storagePath"])
|
|||
// fs.ensureDirSync(config["bookmark.storagePath"])
|
|||
} |
|||
|
|||
// 判断是否是空文件夹
|
|||
function isEmptyDir(fPath: string) { |
|||
var pa = fs.readdirSync(fPath) |
|||
if (pa.length === 0) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
@injectable() |
|||
class Setting { |
|||
constructor() { |
|||
console.log(`Setting inited`); |
|||
|
|||
this.#init() |
|||
} |
|||
#cb: [IT, IOnFunc][] = [] |
|||
|
|||
onChange(fn: IOnFunc, that?: any) |
|||
onChange(key: IT, fn: IOnFunc, that?: any) |
|||
onChange(fnOrType: IT | IOnFunc, fnOrThat: IOnFunc | any = null, that: any = null) { |
|||
if (typeof fnOrType === "function") { |
|||
this.#cb.push(["_", fnOrType.bind(fnOrThat)]) |
|||
} else { |
|||
this.#cb.push([fnOrType, fnOrThat.bind(that)]) |
|||
} |
|||
} |
|||
|
|||
#runCB(n: IConfig, c: IConfig, keys: (keyof IConfig)[]) { |
|||
for (let i = 0; i < this.#cb.length; i++) { |
|||
const temp = this.#cb[i] |
|||
const k = temp[0] |
|||
const fn = temp[1] |
|||
if (k === "_") { |
|||
fn(n, c, keys) |
|||
} |
|||
if (typeof k === "string" && keys.includes(k as keyof IConfig)) { |
|||
fn(n, c) |
|||
} |
|||
if (Array.isArray(k) && k.filter(v => keys.indexOf(v) !== -1).length) { |
|||
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") |
|||
#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) |
|||
if (!fs.pathExistsSync(configFile)) { |
|||
fs.ensureFileSync(configFile) |
|||
fs.writeJSONSync(configFile, {}) |
|||
} |
|||
const config = fs.readJSONSync(configFile) as IConfig |
|||
confingPath && (config.storagePath = confingPath) |
|||
// 优先取本地的值
|
|||
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() { |
|||
console.log(`位置:${this.#pathFile}`) |
|||
if (fs.pathExistsSync(this.#pathFile)) { |
|||
const confingPath = fs.readFileSync(this.#pathFile, { encoding: "utf8" }) |
|||
if (confingPath && fs.pathExistsSync(this.#configPath(confingPath))) { |
|||
this.#syncVar(confingPath) |
|||
// 防止增加了配置本地却没变的情况
|
|||
this.#sync(confingPath) |
|||
} else { |
|||
this.#syncVar(confingPath) |
|||
this.#sync(confingPath) |
|||
} |
|||
} else { |
|||
this.#syncVar() |
|||
this.#sync() |
|||
} |
|||
init.call(this, this.#config) |
|||
} |
|||
config() { |
|||
return this.#config |
|||
} |
|||
#sync(c?: string) { |
|||
const config = cloneDeep(this.#config) |
|||
delete config.storagePath |
|||
const p = this.#configPath(c) |
|||
fs.ensureFileSync(p) |
|||
fs.writeJSONSync(this.#configPath(c), config) |
|||
} |
|||
#change(p: string) { |
|||
const storagePath = this.#config.storagePath |
|||
if (fs.existsSync(storagePath) && !fs.existsSync(p)) { |
|||
fs.moveSync(storagePath, p) |
|||
} |
|||
if (fs.existsSync(p) && fs.existsSync(storagePath) && isEmptyDir(p)) { |
|||
console.log("文件夹为空,直接覆盖") |
|||
fs.moveSync(storagePath, p, { overwrite: true }) |
|||
} |
|||
fs.writeFileSync(this.#pathFile, p, { encoding: "utf8" }) |
|||
} |
|||
reset(key: keyof IConfig) { |
|||
this.set(key, cloneDeep(_tempConfig[key])) |
|||
} |
|||
set(key: keyof IConfig | Partial<IConfig>, value?: any) { |
|||
let oldMainConfig = Object.assign({}, this.#config) |
|||
let isChange = false |
|||
let changeKeys: (keyof IConfig)[] = [] |
|||
let canChangeStorage = (targetPath: string) => { |
|||
if (fs.existsSync(oldMainConfig.storagePath) && fs.existsSync(targetPath) && !isEmptyDir(targetPath)) { |
|||
if (fs.existsSync(path.join(targetPath, "./config.json"))) { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
if (typeof key === "string") { |
|||
if (value != undefined && value !== this.#config[key]) { |
|||
if (key === "storagePath") { |
|||
if (!canChangeStorage(value)) { |
|||
throw "无法改变存储地址" |
|||
return |
|||
} |
|||
try { |
|||
this.#change(value) |
|||
} catch (error) { |
|||
throw error |
|||
} |
|||
changeKeys.push("storagePath") |
|||
this.#config["storagePath"] = value |
|||
} else { |
|||
changeKeys.push(key) |
|||
this.#config[key as string] = value |
|||
} |
|||
isChange = true |
|||
} |
|||
} else { |
|||
if (key['storagePath'] !== undefined && key['storagePath'] !== this.#config['storagePath']) { |
|||
if (!canChangeStorage(key['storagePath'])) { |
|||
throw "无法改变存储地址" |
|||
return |
|||
} |
|||
try { |
|||
this.#change(key['storagePath']) |
|||
} catch (error) { |
|||
throw error |
|||
} |
|||
this.#config['storagePath'] = key['storagePath'] |
|||
changeKeys.push('storagePath') |
|||
isChange = true |
|||
} |
|||
for (const _ in key as any) { |
|||
if (Object.prototype.hasOwnProperty.call(key, _)) { |
|||
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) |
|||
} |
|||
} |
|||
values<T extends keyof IConfig>(key: T): IConfig[T] { |
|||
return this.#config[key] |
|||
} |
|||
} |
|||
|
|||
export default Setting |
|||
export { |
|||
Setting |
|||
} |
|||
import fs from "fs-extra" |
|||
import { app } from "electron" |
|||
import path from "path" |
|||
import { cloneDeep } from "lodash" |
|||
import { injectable } from "inversify" |
|||
import Config from "config" |
|||
|
|||
type IConfig = typeof Config.default_config |
|||
|
|||
type IOnFunc = (n: IConfig, c: IConfig, keys?: (keyof IConfig)[]) => void |
|||
type IT = (keyof IConfig)[] | keyof IConfig | "_" |
|||
|
|||
let storagePath = path.join(app.getPath("documents"), Config.app_title) |
|||
const storagePathDev = path.join(app.getPath("documents"), Config.app_title + "-dev") |
|||
|
|||
if (process.env.NODE_ENV === "development") { |
|||
storagePath = storagePathDev |
|||
} |
|||
|
|||
const _tempConfig = cloneDeep(Config.default_config as IConfig) |
|||
Object.keys(_tempConfig).forEach(key => { |
|||
if (typeof _tempConfig[key] === "string" && _tempConfig[key].includes("$storagePath$")) { |
|||
_tempConfig[key] = _tempConfig[key].replace(/\$storagePath\$/g, storagePath) |
|||
if (_tempConfig[key] && path.isAbsolute(_tempConfig[key])) { |
|||
_tempConfig[key] = path.normalize(_tempConfig[key]) |
|||
} |
|||
} |
|||
}) |
|||
|
|||
function isPath(str) { |
|||
// 使用正则表达式检查字符串是否以斜杠或盘符开头
|
|||
return /^(?:\/|[a-zA-Z]:\\)/.test(str) |
|||
} |
|||
|
|||
function init(config: IConfig) { |
|||
// 在配置初始化后执行
|
|||
Object.keys(config).forEach(key => { |
|||
if (config[key] && isPath(config[key]) && path.isAbsolute(config[key])) { |
|||
fs.ensureDirSync(config[key]) |
|||
} |
|||
}) |
|||
// 在配置初始化后执行
|
|||
// fs.ensureDirSync(config["snippet.storagePath"])
|
|||
// fs.ensureDirSync(config["bookmark.storagePath"])
|
|||
} |
|||
|
|||
// 判断是否是空文件夹
|
|||
function isEmptyDir(fPath: string) { |
|||
const pa = fs.readdirSync(fPath) |
|||
if (pa.length === 0) { |
|||
return true |
|||
} else { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
@injectable() |
|||
class Setting { |
|||
constructor() { |
|||
console.log(`Setting inited`) |
|||
|
|||
this.#init() |
|||
} |
|||
#cb: [IT, IOnFunc][] = [] |
|||
|
|||
onChange(fn: IOnFunc, that?: any) |
|||
onChange(key: IT, fn: IOnFunc, that?: any) |
|||
onChange(fnOrType: IT | IOnFunc, fnOrThat: IOnFunc | any = null, that: any = null) { |
|||
if (typeof fnOrType === "function") { |
|||
this.#cb.push(["_", fnOrType.bind(fnOrThat)]) |
|||
} else { |
|||
this.#cb.push([fnOrType, fnOrThat.bind(that)]) |
|||
} |
|||
} |
|||
|
|||
#runCB(n: IConfig, c: IConfig, keys: (keyof IConfig)[]) { |
|||
for (let i = 0; i < this.#cb.length; i++) { |
|||
const temp = this.#cb[i] |
|||
const k = temp[0] |
|||
const fn = temp[1] |
|||
if (k === "_") { |
|||
fn(n, c, keys) |
|||
} |
|||
if (typeof k === "string" && keys.includes(k as keyof IConfig)) { |
|||
fn(n, c) |
|||
} |
|||
if (Array.isArray(k) && k.filter(v => keys.indexOf(v) !== -1).length) { |
|||
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") |
|||
#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) |
|||
if (!fs.pathExistsSync(configFile)) { |
|||
fs.ensureFileSync(configFile) |
|||
fs.writeJSONSync(configFile, {}) |
|||
} |
|||
const config = fs.readJSONSync(configFile) as IConfig |
|||
confingPath && (config.storagePath = confingPath) |
|||
// 优先取本地的值
|
|||
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() { |
|||
console.log(`位置:${this.#pathFile}`) |
|||
if (fs.pathExistsSync(this.#pathFile)) { |
|||
const confingPath = fs.readFileSync(this.#pathFile, { encoding: "utf8" }) |
|||
if (confingPath && fs.pathExistsSync(this.#configPath(confingPath))) { |
|||
this.#syncVar(confingPath) |
|||
// 防止增加了配置本地却没变的情况
|
|||
this.#sync(confingPath) |
|||
} else { |
|||
this.#syncVar(confingPath) |
|||
this.#sync(confingPath) |
|||
} |
|||
} else { |
|||
this.#syncVar() |
|||
this.#sync() |
|||
} |
|||
init.call(this, this.#config) |
|||
} |
|||
config() { |
|||
return this.#config |
|||
} |
|||
#sync(c?: string) { |
|||
const config = cloneDeep(this.#config) |
|||
delete config.storagePath |
|||
const p = this.#configPath(c) |
|||
fs.ensureFileSync(p) |
|||
fs.writeJSONSync(this.#configPath(c), config) |
|||
} |
|||
#change(p: string) { |
|||
const storagePath = this.#config.storagePath |
|||
if (fs.existsSync(storagePath) && !fs.existsSync(p)) { |
|||
fs.moveSync(storagePath, p) |
|||
} |
|||
if (fs.existsSync(p) && fs.existsSync(storagePath) && isEmptyDir(p)) { |
|||
console.log("文件夹为空,直接覆盖") |
|||
fs.moveSync(storagePath, p, { overwrite: true }) |
|||
} |
|||
fs.writeFileSync(this.#pathFile, p, { encoding: "utf8" }) |
|||
} |
|||
reset(key: keyof IConfig) { |
|||
this.set(key, cloneDeep(_tempConfig[key])) |
|||
} |
|||
set(key: keyof IConfig | Partial<IConfig>, value?: any) { |
|||
const oldMainConfig = Object.assign({}, this.#config) |
|||
let isChange = false |
|||
const changeKeys: (keyof IConfig)[] = [] |
|||
const canChangeStorage = (targetPath: string) => { |
|||
if (fs.existsSync(oldMainConfig.storagePath) && fs.existsSync(targetPath) && !isEmptyDir(targetPath)) { |
|||
if (fs.existsSync(path.join(targetPath, "./config.json"))) { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
if (typeof key === "string") { |
|||
if (value != undefined && value !== this.#config[key]) { |
|||
if (key === "storagePath") { |
|||
if (!canChangeStorage(value)) { |
|||
throw "无法改变存储地址" |
|||
return |
|||
} |
|||
this.#change(value) |
|||
changeKeys.push("storagePath") |
|||
this.#config["storagePath"] = value |
|||
} else { |
|||
changeKeys.push(key) |
|||
this.#config[key as string] = value |
|||
} |
|||
isChange = true |
|||
} |
|||
} else { |
|||
if (key["storagePath"] !== undefined && key["storagePath"] !== this.#config["storagePath"]) { |
|||
if (!canChangeStorage(key["storagePath"])) { |
|||
throw "无法改变存储地址" |
|||
return |
|||
} |
|||
this.#change(key["storagePath"]) |
|||
this.#config["storagePath"] = key["storagePath"] |
|||
changeKeys.push("storagePath") |
|||
isChange = true |
|||
} |
|||
for (const _ in key) { |
|||
if (Object.prototype.hasOwnProperty.call(key, _)) { |
|||
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) |
|||
} |
|||
} |
|||
values<T extends keyof IConfig>(key: T): IConfig[T] { |
|||
return this.#config[key] |
|||
} |
|||
} |
|||
|
|||
export default Setting |
|||
export { Setting } |
|||
|
@ -1,8 +1,8 @@ |
|||
import { ElectronAPI } from '@electron-toolkit/preload' |
|||
import { ElectronAPI } from "@electron-toolkit/preload" |
|||
|
|||
declare global { |
|||
interface Window { |
|||
electron: ElectronAPI |
|||
api: unknown |
|||
} |
|||
interface Window { |
|||
electron: ElectronAPI |
|||
api: unknown |
|||
} |
|||
} |
|||
|
@ -1,17 +1,17 @@ |
|||
<!doctype html> |
|||
<html> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<title>Electron</title> |
|||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
|||
<meta |
|||
http-equiv="Content-Security-Policy" |
|||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" |
|||
/> |
|||
</head> |
|||
<head> |
|||
<meta charset="UTF-8" /> |
|||
<title>Electron</title> |
|||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
|||
<meta |
|||
http-equiv="Content-Security-Policy" |
|||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" |
|||
/> |
|||
</head> |
|||
|
|||
<body> |
|||
<div id="app"></div> |
|||
<script type="module" src="/src/main.ts"></script> |
|||
</body> |
|||
<body> |
|||
<div id="app"></div> |
|||
<script type="module" src="/src/main.ts"></script> |
|||
</body> |
|||
</html> |
|||
|
@ -1,26 +1,30 @@ |
|||
<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> |
|||
|
|||
<template> |
|||
<img alt="logo" class="logo" src="./assets/electron.svg" /> |
|||
<div class="creator">Powered by electron-vite</div> |
|||
<div class="text"> |
|||
Build an Electron app with |
|||
<span class="vue">Vue</span> |
|||
and |
|||
<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> |
|||
<img alt="logo" class="logo" src="./assets/electron.svg" /> |
|||
<div class="creator">Powered by electron-vite</div> |
|||
<div class="text"> |
|||
Build an Electron app with |
|||
<span class="vue">Vue</span> |
|||
and |
|||
<span class="ts">TypeScript</span> |
|||
</div> |
|||
<div class="action"> |
|||
<a target="_blank" rel="noreferrer" @click="ipcHandle">Send IPC</a> |
|||
<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 class="action"> |
|||
<a target="_blank" rel="noreferrer" @click="ipcHandle">Send IPC</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<Versions /> |
|||
<Versions /> |
|||
</template> |
|||
|
@ -1,67 +1,67 @@ |
|||
:root { |
|||
--ev-c-white: #ffffff; |
|||
--ev-c-white-soft: #f8f8f8; |
|||
--ev-c-white-mute: #f2f2f2; |
|||
--ev-c-white: #ffffff; |
|||
--ev-c-white-soft: #f8f8f8; |
|||
--ev-c-white-mute: #f2f2f2; |
|||
|
|||
--ev-c-black: #1b1b1f; |
|||
--ev-c-black-soft: #222222; |
|||
--ev-c-black-mute: #282828; |
|||
--ev-c-black: #1b1b1f; |
|||
--ev-c-black-soft: #222222; |
|||
--ev-c-black-mute: #282828; |
|||
|
|||
--ev-c-gray-1: #515c67; |
|||
--ev-c-gray-2: #414853; |
|||
--ev-c-gray-3: #32363f; |
|||
--ev-c-gray-1: #515c67; |
|||
--ev-c-gray-2: #414853; |
|||
--ev-c-gray-3: #32363f; |
|||
|
|||
--ev-c-text-1: rgba(255, 255, 245, 0.86); |
|||
--ev-c-text-2: rgba(235, 235, 245, 0.6); |
|||
--ev-c-text-3: rgba(235, 235, 245, 0.38); |
|||
--ev-c-text-1: rgba(255, 255, 245, 0.86); |
|||
--ev-c-text-2: rgba(235, 235, 245, 0.6); |
|||
--ev-c-text-3: rgba(235, 235, 245, 0.38); |
|||
|
|||
--ev-button-alt-border: transparent; |
|||
--ev-button-alt-text: var(--ev-c-text-1); |
|||
--ev-button-alt-bg: var(--ev-c-gray-3); |
|||
--ev-button-alt-hover-border: transparent; |
|||
--ev-button-alt-hover-text: var(--ev-c-text-1); |
|||
--ev-button-alt-hover-bg: var(--ev-c-gray-2); |
|||
--ev-button-alt-border: transparent; |
|||
--ev-button-alt-text: var(--ev-c-text-1); |
|||
--ev-button-alt-bg: var(--ev-c-gray-3); |
|||
--ev-button-alt-hover-border: transparent; |
|||
--ev-button-alt-hover-text: var(--ev-c-text-1); |
|||
--ev-button-alt-hover-bg: var(--ev-c-gray-2); |
|||
} |
|||
|
|||
:root { |
|||
--color-background: var(--ev-c-black); |
|||
--color-background-soft: var(--ev-c-black-soft); |
|||
--color-background-mute: var(--ev-c-black-mute); |
|||
--color-background: var(--ev-c-black); |
|||
--color-background-soft: var(--ev-c-black-soft); |
|||
--color-background-mute: var(--ev-c-black-mute); |
|||
|
|||
--color-text: var(--ev-c-text-1); |
|||
--color-text: var(--ev-c-text-1); |
|||
} |
|||
|
|||
*, |
|||
*::before, |
|||
*::after { |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
font-weight: normal; |
|||
box-sizing: border-box; |
|||
margin: 0; |
|||
font-weight: normal; |
|||
} |
|||
|
|||
ul { |
|||
list-style: none; |
|||
list-style: none; |
|||
} |
|||
|
|||
body { |
|||
min-height: 100vh; |
|||
color: var(--color-text); |
|||
background: var(--color-background); |
|||
line-height: 1.6; |
|||
font-family: |
|||
Inter, |
|||
-apple-system, |
|||
BlinkMacSystemFont, |
|||
'Segoe UI', |
|||
Roboto, |
|||
Oxygen, |
|||
Ubuntu, |
|||
Cantarell, |
|||
'Fira Sans', |
|||
'Droid Sans', |
|||
'Helvetica Neue', |
|||
sans-serif; |
|||
text-rendering: optimizeLegibility; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
min-height: 100vh; |
|||
color: var(--color-text); |
|||
background: var(--color-background); |
|||
line-height: 1.6; |
|||
font-family: |
|||
Inter, |
|||
-apple-system, |
|||
BlinkMacSystemFont, |
|||
"Segoe UI", |
|||
Roboto, |
|||
Oxygen, |
|||
Ubuntu, |
|||
Cantarell, |
|||
"Fira Sans", |
|||
"Droid Sans", |
|||
"Helvetica Neue", |
|||
sans-serif; |
|||
text-rendering: optimizeLegibility; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
|||
|
@ -1,171 +1,171 @@ |
|||
@import './base.css'; |
|||
@import "./base.css"; |
|||
|
|||
body { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
background-image: url('./wavy-lines.svg'); |
|||
background-size: cover; |
|||
user-select: none; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
background-image: url("./wavy-lines.svg"); |
|||
background-size: cover; |
|||
user-select: none; |
|||
} |
|||
|
|||
code { |
|||
font-weight: 600; |
|||
padding: 3px 5px; |
|||
border-radius: 2px; |
|||
background-color: var(--color-background-mute); |
|||
font-family: |
|||
ui-monospace, |
|||
SFMono-Regular, |
|||
SF Mono, |
|||
Menlo, |
|||
Consolas, |
|||
Liberation Mono, |
|||
monospace; |
|||
font-size: 85%; |
|||
font-weight: 600; |
|||
padding: 3px 5px; |
|||
border-radius: 2px; |
|||
background-color: var(--color-background-mute); |
|||
font-family: |
|||
ui-monospace, |
|||
SFMono-Regular, |
|||
SF Mono, |
|||
Menlo, |
|||
Consolas, |
|||
Liberation Mono, |
|||
monospace; |
|||
font-size: 85%; |
|||
} |
|||
|
|||
#app { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
flex-direction: column; |
|||
margin-bottom: 80px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
flex-direction: column; |
|||
margin-bottom: 80px; |
|||
} |
|||
|
|||
.logo { |
|||
margin-bottom: 20px; |
|||
-webkit-user-drag: none; |
|||
height: 128px; |
|||
width: 128px; |
|||
will-change: filter; |
|||
transition: filter 300ms; |
|||
margin-bottom: 20px; |
|||
-webkit-user-drag: none; |
|||
height: 128px; |
|||
width: 128px; |
|||
will-change: filter; |
|||
transition: filter 300ms; |
|||
} |
|||
|
|||
.logo:hover { |
|||
filter: drop-shadow(0 0 1.2em #6988e6aa); |
|||
filter: drop-shadow(0 0 1.2em #6988e6aa); |
|||
} |
|||
|
|||
.creator { |
|||
font-size: 14px; |
|||
line-height: 16px; |
|||
color: var(--ev-c-text-2); |
|||
font-weight: 600; |
|||
margin-bottom: 10px; |
|||
font-size: 14px; |
|||
line-height: 16px; |
|||
color: var(--ev-c-text-2); |
|||
font-weight: 600; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
.text { |
|||
font-size: 28px; |
|||
color: var(--ev-c-text-1); |
|||
font-weight: 700; |
|||
line-height: 32px; |
|||
text-align: center; |
|||
margin: 0 10px; |
|||
padding: 16px 0; |
|||
font-size: 28px; |
|||
color: var(--ev-c-text-1); |
|||
font-weight: 700; |
|||
line-height: 32px; |
|||
text-align: center; |
|||
margin: 0 10px; |
|||
padding: 16px 0; |
|||
} |
|||
|
|||
.tip { |
|||
font-size: 16px; |
|||
line-height: 24px; |
|||
color: var(--ev-c-text-2); |
|||
font-weight: 600; |
|||
font-size: 16px; |
|||
line-height: 24px; |
|||
color: var(--ev-c-text-2); |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.vue { |
|||
background: -webkit-linear-gradient(315deg, #42d392 25%, #647eff); |
|||
background-clip: text; |
|||
-webkit-background-clip: text; |
|||
-webkit-text-fill-color: transparent; |
|||
font-weight: 700; |
|||
background: -webkit-linear-gradient(315deg, #42d392 25%, #647eff); |
|||
background-clip: text; |
|||
-webkit-background-clip: text; |
|||
-webkit-text-fill-color: transparent; |
|||
font-weight: 700; |
|||
} |
|||
|
|||
.ts { |
|||
background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e); |
|||
background-clip: text; |
|||
-webkit-background-clip: text; |
|||
-webkit-text-fill-color: transparent; |
|||
font-weight: 700; |
|||
background: -webkit-linear-gradient(315deg, #3178c6 45%, #f0dc4e); |
|||
background-clip: text; |
|||
-webkit-background-clip: text; |
|||
-webkit-text-fill-color: transparent; |
|||
font-weight: 700; |
|||
} |
|||
|
|||
.actions { |
|||
display: flex; |
|||
padding-top: 32px; |
|||
margin: -6px; |
|||
flex-wrap: wrap; |
|||
justify-content: flex-start; |
|||
display: flex; |
|||
padding-top: 32px; |
|||
margin: -6px; |
|||
flex-wrap: wrap; |
|||
justify-content: flex-start; |
|||
} |
|||
|
|||
.action { |
|||
flex-shrink: 0; |
|||
padding: 6px; |
|||
flex-shrink: 0; |
|||
padding: 6px; |
|||
} |
|||
|
|||
.action a { |
|||
cursor: pointer; |
|||
text-decoration: none; |
|||
display: inline-block; |
|||
border: 1px solid transparent; |
|||
text-align: center; |
|||
font-weight: 600; |
|||
white-space: nowrap; |
|||
border-radius: 20px; |
|||
padding: 0 20px; |
|||
line-height: 38px; |
|||
font-size: 14px; |
|||
border-color: var(--ev-button-alt-border); |
|||
color: var(--ev-button-alt-text); |
|||
background-color: var(--ev-button-alt-bg); |
|||
cursor: pointer; |
|||
text-decoration: none; |
|||
display: inline-block; |
|||
border: 1px solid transparent; |
|||
text-align: center; |
|||
font-weight: 600; |
|||
white-space: nowrap; |
|||
border-radius: 20px; |
|||
padding: 0 20px; |
|||
line-height: 38px; |
|||
font-size: 14px; |
|||
border-color: var(--ev-button-alt-border); |
|||
color: var(--ev-button-alt-text); |
|||
background-color: var(--ev-button-alt-bg); |
|||
} |
|||
|
|||
.action a:hover { |
|||
border-color: var(--ev-button-alt-hover-border); |
|||
color: var(--ev-button-alt-hover-text); |
|||
background-color: var(--ev-button-alt-hover-bg); |
|||
border-color: var(--ev-button-alt-hover-border); |
|||
color: var(--ev-button-alt-hover-text); |
|||
background-color: var(--ev-button-alt-hover-bg); |
|||
} |
|||
|
|||
.versions { |
|||
position: absolute; |
|||
bottom: 30px; |
|||
margin: 0 auto; |
|||
padding: 15px 0; |
|||
font-family: 'Menlo', 'Lucida Console', monospace; |
|||
display: inline-flex; |
|||
overflow: hidden; |
|||
align-items: center; |
|||
border-radius: 22px; |
|||
background-color: #202127; |
|||
backdrop-filter: blur(24px); |
|||
position: absolute; |
|||
bottom: 30px; |
|||
margin: 0 auto; |
|||
padding: 15px 0; |
|||
font-family: "Menlo", "Lucida Console", monospace; |
|||
display: inline-flex; |
|||
overflow: hidden; |
|||
align-items: center; |
|||
border-radius: 22px; |
|||
background-color: #202127; |
|||
backdrop-filter: blur(24px); |
|||
} |
|||
|
|||
.versions li { |
|||
display: block; |
|||
float: left; |
|||
border-right: 1px solid var(--ev-c-gray-1); |
|||
padding: 0 20px; |
|||
font-size: 14px; |
|||
line-height: 14px; |
|||
opacity: 0.8; |
|||
&:last-child { |
|||
border: none; |
|||
} |
|||
display: block; |
|||
float: left; |
|||
border-right: 1px solid var(--ev-c-gray-1); |
|||
padding: 0 20px; |
|||
font-size: 14px; |
|||
line-height: 14px; |
|||
opacity: 0.8; |
|||
&:last-child { |
|||
border: none; |
|||
} |
|||
} |
|||
|
|||
@media (max-width: 720px) { |
|||
.text { |
|||
font-size: 20px; |
|||
} |
|||
.text { |
|||
font-size: 20px; |
|||
} |
|||
} |
|||
|
|||
@media (max-width: 620px) { |
|||
.versions { |
|||
display: none; |
|||
} |
|||
.versions { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
@media (max-width: 350px) { |
|||
.tip, |
|||
.actions { |
|||
display: none; |
|||
} |
|||
.tip, |
|||
.actions { |
|||
display: none; |
|||
} |
|||
} |
|||
|
@ -1,13 +1,13 @@ |
|||
<script setup lang="ts"> |
|||
import { reactive } from 'vue' |
|||
import { reactive } from "vue" |
|||
|
|||
const versions = reactive({ ...window.electron.process.versions }) |
|||
</script> |
|||
|
|||
<template> |
|||
<ul class="versions"> |
|||
<li class="electron-version">Electron v{{ versions.electron }}</li> |
|||
<li class="chrome-version">Chromium v{{ versions.chrome }}</li> |
|||
<li class="node-version">Node v{{ versions.node }}</li> |
|||
</ul> |
|||
<ul class="versions"> |
|||
<li class="electron-version">Electron v{{ versions.electron }}</li> |
|||
<li class="chrome-version">Chromium v{{ versions.chrome }}</li> |
|||
<li class="node-version">Node v{{ versions.node }}</li> |
|||
</ul> |
|||
</template> |
|||
|
@ -1,8 +1,8 @@ |
|||
/// <reference types="vite/client" />
|
|||
|
|||
declare module '*.vue' { |
|||
import type { DefineComponent } from 'vue' |
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
|||
const component: DefineComponent<{}, {}, any> |
|||
export default component |
|||
declare module "*.vue" { |
|||
import type { DefineComponent } from "vue" |
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
|||
const component: DefineComponent<{}, {}, any> |
|||
export default component |
|||
} |
|||
|
@ -1,8 +1,8 @@ |
|||
import 'virtual:uno.css' |
|||
import '@unocss/reset/normalize.css' |
|||
import './assets/main.css' |
|||
import "virtual:uno.css" |
|||
import "@unocss/reset/normalize.css" |
|||
import "./assets/main.css" |
|||
|
|||
import { createApp } from 'vue' |
|||
import App from './App.vue' |
|||
import { createApp } from "vue" |
|||
import App from "./App.vue" |
|||
|
|||
createApp(App).mount('#app') |
|||
createApp(App).mount("#app") |
|||
|
@ -1,5 +1,5 @@ |
|||
import type { AttributifyAttributes } from '@unocss/preset-attributify' |
|||
|
|||
declare module '@vue/runtime-dom' { |
|||
interface HTMLAttributes extends AttributifyAttributes { } |
|||
} |
|||
import type { AttributifyAttributes } from "@unocss/preset-attributify" |
|||
|
|||
declare module "@vue/runtime-dom" { |
|||
interface HTMLAttributes extends AttributifyAttributes {} |
|||
} |
|||
|
@ -1,10 +1,6 @@ |
|||
import { defineConfig, presetAttributify, presetUno } from 'unocss' |
|||
import presetRemToPx from '@unocss/preset-rem-to-px' |
|||
|
|||
export default defineConfig({ |
|||
presets: [ |
|||
presetAttributify(), |
|||
presetUno(), |
|||
presetRemToPx(), |
|||
], |
|||
}) |
|||
import { defineConfig, presetAttributify, presetUno } from "unocss" |
|||
import presetRemToPx from "@unocss/preset-rem-to-px" |
|||
|
|||
export default defineConfig({ |
|||
presets: [presetAttributify(), presetUno(), presetRemToPx()], |
|||
}) |
|||
|
Loading…
Reference in new issue